diff --git a/.github/workflows/deactivate.yaml b/.github/workflows/deactivate.yaml index 931b23a69..4ff5c0452 100644 --- a/.github/workflows/deactivate.yaml +++ b/.github/workflows/deactivate.yaml @@ -1,7 +1,5 @@ name: ♻️ Deactivate on: - pull_request: - types: [closed] delete: branches: - "**" diff --git a/.github/workflows/review-auto.yaml b/.github/workflows/review-auto.yaml index d95cfc40b..e26b1cfcf 100644 --- a/.github/workflows/review-auto.yaml +++ b/.github/workflows/review-auto.yaml @@ -1,5 +1,6 @@ name: 👓 Review Auto on: + workflow_dispatch: push: branches: - main diff --git a/.kontinuous/env/prod/values.yaml b/.kontinuous/env/prod/values.yaml index 8fcecc35e..2b389f23f 100644 --- a/.kontinuous/env/prod/values.yaml +++ b/.kontinuous/env/prod/values.yaml @@ -1,14 +1,20 @@ backend: + autoscale: + enabled: true host: api-vao.social.gouv.fr redirectFrom: - "api-{{ .Values.global.host }}" frontend-usagers: + autoscale: + enabled: true host: vao.social.gouv.fr redirectFrom: - "{{ .Values.global.host }}" frontend-bo: + autoscale: + enabled: true host: admin-vao.social.gouv.fr redirectFrom: - "bo-{{ .Values.global.host }}" @@ -28,4 +34,4 @@ jobs: NUXT_PUBLIC_APP_VERSION: "{{ .Values.global.gitBranch }}" NUXT_PUBLIC_BACKEND_URL: "https://api-vao.social.gouv.fr" NUXT_PUBLIC_ENVIRONMENT: "production" - NUXT_PUBLIC_SENTRY_ENABLED: "true" \ No newline at end of file + NUXT_PUBLIC_SENTRY_ENABLED: "true" diff --git a/.talismanrc b/.talismanrc index b724c2c62..d26828dce 100644 --- a/.talismanrc +++ b/.talismanrc @@ -134,7 +134,7 @@ fileignoreconfig: - filename: packages/frontend-bo/src/components/demandes-sejour/Details.vue checksum: 2fcd6dcc607405fbbdf7dc8c5b9cf6229e8a65593503b5ebb55786b9788c514f - filename: packages/frontend-bo/src/components/demandes-sejour/DisplayFormulaire.vue - checksum: 7ca8cd5ca845caecf1d10ba0af46a4cff4e4f22537d7ba703ea04680143f8de0 + checksum: 0c1ba679433b38f376fbbecfa020e9e7375c8a816ac43b1e46ab2225e9f19342 - filename: packages/frontend-bo/src/components/demandes-sejour/DisplayPj.vue checksum: 7416e86be161886c048c7108e2ef95027e0dbaa858d2b4961e1effa469e98901 - filename: packages/frontend-bo/src/components/utils/TableFull.vue @@ -216,13 +216,3 @@ fileignoreconfig: scopeconfig: - scope: node version: "1.0" -aa193faebb2d - - filename: pg/scripts/03/03-3-front-data.sql - checksum: 413817c8133c69f883087a7042258dcb75e4f75a45897ea830f86d8a5cba561a - - filename: pg/scripts/03/03-3-ref-data.sql - checksum: 0738f07a54568f11530944b7dd793718435c7b1ab6cc6eacf6739f380fb1eb83 - - filename: pg/seeds/BO-1-back-user.sql - checksum: 2261cabf80475ffa0c06e59760d4a89ea3331b81062fe5e8640745741a562995 -scopeconfig: - - scope: node -version: "1.0" diff --git a/packages/backend/package.json b/packages/backend/package.json index c3032a7ca..0bb487784 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -8,7 +8,7 @@ "dev": "nodemon --inspect=0.0.0.0:9229 src/index.js" }, "dependencies": { - "@sentry/node": "~7.109.0", + "@sentry/node": "~7.117.0", "axios": "^1.6.8", "body-parser": "~1.20.2", "cookie-parser": "^1.4.6", @@ -21,7 +21,7 @@ "helmet": "~7.1.0", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", - "pg": "~8.11.3", + "pg": "~8.12.0", "pg-format": "~1.0.4", "yup": "~1.4.0", "nodemailer": "^6.9.13", diff --git a/packages/backend/src/controllers/demandeSejour/copy.js b/packages/backend/src/controllers/demandeSejour/copy.js new file mode 100644 index 000000000..7d811e5ba --- /dev/null +++ b/packages/backend/src/controllers/demandeSejour/copy.js @@ -0,0 +1,76 @@ +const DemandeSejour = require("../../services/DemandeSejour"); +const Organisme = require("../../services/Organisme"); +const { statuts } = require("../../helpers/ds-statuts"); + +const logger = require("../../utils/logger"); +const AppError = require("../../utils/error"); + +const log = logger(module.filename); + +module.exports = async function post(req, res, next) { + const demandeSejourId = req.params.id; + const { id: userId } = req.decoded; + + log.i("IN", { demandeSejourId }); + + try { + const organisme = await Organisme.getOne({ + use_id: userId, + }); + const sourceDeclaration = await DemandeSejour.getOne({ + "ds.id": demandeSejourId, + "o.id": organisme.organismeId, + }); + + if (!sourceDeclaration) { + log.w("DONE with error"); + return next( + new AppError("Déclaration non trouvée", { + statusCode: 404, + }), + ); + } + + if ( + sourceDeclaration.statut !== statuts.BROUILLON && + sourceDeclaration.statut !== statuts.TRANSMISE && + sourceDeclaration.statut !== statuts.EN_COURS + ) { + log.w("DONE with error"); + return next( + new AppError("Le statut de la déclaration ne permet pas sa copie", { + statusCode: 404, + }), + ); + } + + sourceDeclaration.files = sourceDeclaration.files?.files?.filter( + (f) => + f.type !== "declaration_2_mois" && f.type !== "AR_declaration_2_mois", + ); + + const demandeId = await DemandeSejour.copy(sourceDeclaration); + if (!demandeId) { + log.w("DONE with error"); + return next( + new AppError("Erreur de copie", { + statusCode: 400, + }), + ); + } + await DemandeSejour.insertEvent( + "Organisateur", + demandeId, + userId, + null, + "declaration_sejour", + "creation", + {}, + ); + log.i("DONE"); + + return res.status(200).json({ demandeId }); + } catch (err) { + log.w(err); + } +}; \ No newline at end of file diff --git a/packages/backend/src/controllers/demandeSejour/delete.js b/packages/backend/src/controllers/demandeSejour/delete.js new file mode 100644 index 000000000..bf4c4ed2a --- /dev/null +++ b/packages/backend/src/controllers/demandeSejour/delete.js @@ -0,0 +1,62 @@ +const { statuts } = require("../../helpers/ds-statuts"); +const DemandeSejour = require("../../services/DemandeSejour"); + +const logger = require("../../utils/logger"); +const AppError = require("../../utils/error"); + +const log = logger(module.filename); + +module.exports = async function post(req, res, next) { + const demandeSejourId = req.params.id; + const { id: userId } = req.decoded; + + log.i("IN", { demandeSejourId }); + + try { + const declaration = await DemandeSejour.getOne({ + "ds.id": demandeSejourId, + }); + + if (!declaration) { + log.w("DONE with error"); + return next( + new AppError("Déclaration non trouvée", { + statusCode: 404, + }), + ); + } + log.i(declaration.statut); + if (declaration.statut !== statuts.BROUILLON) { + log.w("DONE with error"); + return next( + new AppError( + "Impossible de supprimer une demande qui n'est pas au statut BROUILLON", + { + statusCode: 400, + }, + ), + ); + } + const deletedRows = await DemandeSejour.delete(declaration.id, userId); + if (deletedRows !== 1) { + log.w(`DONE with error, ${deletedRows} rows were deleted, expected one `); + return next( + new AppError( + "Erreur de suppression, trop de lignes supprimées ou pas assez", + { + statusCode: 400, + }, + ), + ); + } + log.i("DONE"); + return res.status(200).json({ deletedRows }); + } catch (err) { + log.w(err); + return next( + new AppError("Erreur de suppression", { + statusCode: 400, + }), + ); + } +}; \ No newline at end of file diff --git a/packages/backend/src/controllers/demandeSejour/demandeComplements.js b/packages/backend/src/controllers/demandeSejour/demandeComplements.js index d08aae181..207d4fff3 100644 --- a/packages/backend/src/controllers/demandeSejour/demandeComplements.js +++ b/packages/backend/src/controllers/demandeSejour/demandeComplements.js @@ -52,13 +52,15 @@ module.exports = async function post(req, res, next) { ); } - if (declaration.statut !== statuts.EN_COURS) { + if (declaration.statut !== statuts.EN_COURS && declaration.statut !== statuts.EN_COURS_8J) { log.w("Delaration should be in statut EN COURS"); return res.status(400).json({ message: "Statut non compatible", }); } + const AModifierType = declaration.statut === statuts.EN_COURS ? statuts.A_MODIFIER : statuts.A_MODIFIER_8J + const textTypePrecision = "Demande de compléments " + (declaration.statut === statuts.EN_COURS ? " 2 mois" : " 8 jours"); try { const destinataires = await DemandeSejour.getEmailToList( declaration.organismeId, @@ -66,14 +68,14 @@ module.exports = async function post(req, res, next) { await DemandeSejour.updateStatut( declarationId, - statuts.A_MODIFIER, + AModifierType, { boUserId: userId, declarationId, metaData: { commentaire }, source: `DDETS ${territoireCode}`, type: "declaration_sejour", - typePrecision: "Demande de complémements", + typePrecision: textTypePrecision, userId: null, }, () => diff --git a/packages/backend/src/controllers/demandeSejour/depose.js b/packages/backend/src/controllers/demandeSejour/depose.js index e027c6649..f94ff19e0 100644 --- a/packages/backend/src/controllers/demandeSejour/depose.js +++ b/packages/backend/src/controllers/demandeSejour/depose.js @@ -5,6 +5,8 @@ const DemandeSejour = require("../../services/DemandeSejour"); const Hebergement = require("../../services/Hebergement"); const Send = require("../../services/mail").mailService.send; const PdfDeclaration2Mois = require("../../services/pdf/declaration2mois/generate"); +const PdfDeclaration8jours = require("../../services/pdf/declaration8jours/generate"); +const ARDeclaration8jours = require("../../services/pdf/ARdeclaration8jours/generate"); const { schema: DeclarationSejourSchema, @@ -18,7 +20,12 @@ const AppError = require("../../utils/error"); const log = logger(module.filename); -const expectedStates = [statuts.BROUILLON, statuts.A_MODIFIER]; +const expectedStates = [ + statuts.BROUILLON, + statuts.A_MODIFIER, + statuts.ATTENTE_8_JOUR, + statuts.A_MODIFIER_8J, +]; module.exports = async function post(req, res, next) { const demandeSejourId = req.params.id; @@ -44,6 +51,7 @@ module.exports = async function post(req, res, next) { let declaration, DSuuid, ARuuid; declaration = await DemandeSejour.getOne({ "ds.id": demandeSejourId }); + let idFonctionnelle = declaration.idFonctionnelle; if (!declaration) { log.w("DONE with error"); @@ -53,7 +61,6 @@ module.exports = async function post(req, res, next) { }), ); } - const { dateDebut, dateFin, statut } = declaration; if (!expectedStates.includes(statut)) { @@ -65,27 +72,29 @@ module.exports = async function post(req, res, next) { ); } + const dateDeposeA2mois = declaration.declaration2mois?.attestation?.at ?? ""; const firstSubmit = statut === statuts.BROUILLON; Object.assign(declaration, { attestation }); try { - log.d("finalize - before validation", { declaration }); + log.i("finalize - before validation", { declaration }); declaration = await yup - .object(DeclarationSejourSchema(dateDebut, dateFin)) + .object(DeclarationSejourSchema(dateDebut, dateFin, statut)) .validate(declaration, { abortEarly: false, stripUnknown: true, }); - log.d("finalize - after validation", { declaration }); - + log.i("finalize - after validation", { declaration }); + log.d(declaration.informationsPersonnel); declaration.attestation.at = dayjs(declaration.attestation.at).format( "YYYY-MM-DD", ); declaration.dateDebut = dayjs(declaration.dateDebut).format("YYYY-MM-DD"); declaration.dateFin = dayjs(declaration.dateFin).format("YYYY-MM-DD"); } catch (error) { + log.i(error); return next(new ValidationAppError(error)); } @@ -101,109 +110,211 @@ module.exports = async function post(req, res, next) { }), ); } - const departementSuivi = hebergement.coordonnees.adresse.departement; - - const numSeq = await DemandeSejour.getNextIndex(); + const departementSuivi = hebergement.coordonnees.adresse.departement; const currentYear = dayjs(declaration.dateDebut).format("YY"); - const idFonctionnelle = `DS-${currentYear}-${departementSuivi}-${numSeq.padStart(4, "0")}`; - - await DemandeSejour.finalize( - demandeSejourId, - departementSuivi, - idFonctionnelle, - declaration, - ); - - declaration = await DemandeSejour.getOne({ "ds.id": demandeSejourId }); - - await DemandeSejour.insertEvent( - "Organisateur", - demandeSejourId, - userId, - null, - "declaration_sejour", - statut === statuts.BROUILLON ? "Dépôt DS à 2 mois" : "Ajout de compléments", - declaration, - ); + if (statut == statuts.BROUILLON) { + const numSeq = await DemandeSejour.getNextIndex(); + idFonctionnelle = `DS-${currentYear}-${departementSuivi}-${numSeq.padStart(4, "0")}`; + } - try { - const destinataires = await DemandeSejour.getEmailToList( - declaration.organismeId, - ); - const cc = await DemandeSejour.getEmailCcList( - declaration.organisme.personneMorale.siren ?? "personnePhysique", + if (statut == statuts.ATTENTE_8_JOUR || statut == statuts.A_MODIFIER_8J) { + log.d("Déclaration à 8 jours"); + await DemandeSejour.finalize8jours(demandeSejourId, declaration); + declaration = await DemandeSejour.getOne({ "ds.id": demandeSejourId }); + await DemandeSejour.insertEvent( + "Organisateur", + demandeSejourId, + userId, + null, + "declaration_sejour", + "Dépôt DS Complémentaire à 8 jours", + declaration, ); + try { + const destinataires = await DemandeSejour.getEmailToList( + declaration.organismeId, + ); + const filteredCc = declaration.organisme.personneMorale.siren + ? ( + await DemandeSejour.getEmailCcList( + declaration.organisme.personneMorale.siren, + ) + ).filter((d) => !destinataires.includes(d)) + : []; - const filteredCc = cc.filter((d) => !destinataires.includes(d)); - if (destinataires) { - await Send( - MailUtils.usagers.declarationSejour.sendAccuseTransmission2mois( - { + if (destinataires) { + await Send( + MailUtils.usagers.declarationSejour.sendAccuseTransmission8jours({ cc: filteredCc, declaration, dest: destinataires, + }), + ); + } + } catch (error) { + log.w("DONE with error"); + return next( + new AppError("Une erreur est survenue lors de l'envoi de mails", { + statusCode: 500, + }), + ); + } + try { + const destinatairesBack = await DemandeSejour.getEmailBack( + declaration.departementSuivi, + ); + + if (destinatairesBack) { + const departements = declaration.hebergement.hebergements.map( + (h) => h.coordonnees.adresse.departement, + ); + const destinatairesBackCc = + await DemandeSejour.getEmailBackCc(departements); + + const filteredBackCc = destinatairesBackCc.filter( + (d) => !destinatairesBack.includes(d), + ); + await Send( + MailUtils.bo.declarationSejour.sendDeclarationA8joursNotify({ + cc: filteredBackCc, + declaration, + departementSuivi: declaration.departementSuivi, + departementsSecondaires: departements.filter( + (d) => d !== declaration.departementSuivi, + ), + destinataires: destinatairesBack, + }), + ); + } + } catch (error) { + log.w(error); + return next( + new AppError( + "Une erreur est survenue lors de l'envoi de mails aux usagers back office", + { + statusCode: 500, }, - firstSubmit, ), ); } - } catch (error) { - log.w("DONE with error"); - return next( - new AppError("Une erreur est survenue lors de l'envoi de mails", { - statusCode: 500, - }), - ); - } - try { - const destinatairesBack = - await DemandeSejour.getEmailBack(departementSuivi); - if (destinatairesBack) { - const departements = declaration.hebergement.hebergements.map( - (h) => h.coordonnees.adresse.departement, + try { + DSuuid = await PdfDeclaration8jours( + declaration, + declaration.idFonctionnelle, + declaration.departementSuivi, + dateDeposeA2mois, ); - const destinatairesBackCc = - await DemandeSejour.getEmailBackCc(departements); + } catch (error) { + log.w(error); + } + + log.i("DONE"); + return res.status(200).json({ ARuuid, DSuuid }); + } else { + log.d("Déclaration à 2 mois"); - const filteredBackCc = destinatairesBackCc.filter( - (d) => !destinatairesBack.includes(d), + + await DemandeSejour.finalize( + demandeSejourId, + departementSuivi, + idFonctionnelle, + declaration, + ); + + declaration = await DemandeSejour.getOne({ "ds.id": demandeSejourId }); + + await DemandeSejour.insertEvent( + "Organisateur", + demandeSejourId, + userId, + null, + "declaration_sejour", + statut === statuts.BROUILLON + ? "Dépôt DS à 2 mois" + : "Ajout de compléments", + declaration, + ); + + try { + const destinataires = await DemandeSejour.getEmailToList( + declaration.organismeId, + ); + const cc = await DemandeSejour.getEmailCcList( + declaration.organisme.personneMorale.siren ?? "personnePhysique", ); - await Send( - MailUtils.bo.declarationSejour.sendDeclarationNotify({ - cc: filteredBackCc, - declaration, - departementSuivi, - departementsSecondaires: departements.filter( - (d) => d !== departementSuivi, + + const filteredCc = cc.filter((d) => !destinataires.includes(d)); + if (destinataires) { + await Send( + MailUtils.usagers.declarationSejour.sendAccuseTransmission2mois( + { + cc: filteredCc, + declaration, + dest: destinataires, + }, + firstSubmit, ), - destinataires: destinatairesBack, + ); + } + } catch (error) { + log.w("DONE with error"); + return next( + new AppError("Une erreur est survenue lors de l'envoi de mails", { + statusCode: 500, }), ); } - } catch (error) { - log.w(error); - return next( - new AppError( - "Une erreur est survenue lors de l'envoi de mails aux usagers back office", - { - statusCode: 500, - }, - ), - ); - } + try { + const destinatairesBack = + await DemandeSejour.getEmailBack(departementSuivi); - try { - DSuuid = await PdfDeclaration2Mois( - declaration, - idFonctionnelle, - departementSuivi, - ); - } catch (error) { - log.w(error); - } + if (destinatairesBack) { + const departements = declaration.hebergement.hebergements.map( + (h) => h.coordonnees.adresse.departement, + ); + const destinatairesBackCc = + await DemandeSejour.getEmailBackCc(departements); - log.i("DONE"); - return res.status(200).json({ ARuuid, DSuuid }); + const filteredBackCc = destinatairesBackCc.filter( + (d) => !destinatairesBack.includes(d), + ); + await Send( + MailUtils.bo.declarationSejour.sendDeclarationNotify({ + cc: filteredBackCc, + declaration, + departementSuivi, + departementsSecondaires: departements.filter( + (d) => d !== departementSuivi, + ), + destinataires: destinatairesBack, + }), + ); + } + } catch (error) { + log.w(error); + return next( + new AppError( + "Une erreur est survenue lors de l'envoi de mails aux usagers back office", + { + statusCode: 500, + }, + ), + ); + } + + try { + DSuuid = await PdfDeclaration2Mois( + declaration, + idFonctionnelle, + departementSuivi, + ); + } catch (error) { + log.w(error); + } + + log.i("DONE"); + return res.status(200).json({ ARuuid, DSuuid }); + } }; diff --git a/packages/backend/src/controllers/demandeSejour/enregistrementDemande2Mois.js b/packages/backend/src/controllers/demandeSejour/enregistrementDemande2Mois.js index a60721ea9..f283eb8f6 100644 --- a/packages/backend/src/controllers/demandeSejour/enregistrementDemande2Mois.js +++ b/packages/backend/src/controllers/demandeSejour/enregistrementDemande2Mois.js @@ -1,5 +1,6 @@ const DemandeSejour = require("../../services/DemandeSejour"); const PdfARDeclaration2Mois = require("../../services/pdf/ARdeclaration2mois/generate"); +const PdfARDeclaration8Jours = require("../../services/pdf/ARdeclaration8jours/generate"); const logger = require("../../utils/logger"); const { statuts } = require("../../helpers/ds-statuts"); @@ -48,16 +49,28 @@ module.exports = async function post(req, res, next) { ); } - if (declaration.statut !== statuts.EN_COURS) { + if ( + declaration.statut !== statuts.EN_COURS && + declaration.statut !== statuts.EN_COURS_8J + ) { return next( new AppError("Statut incompatible", { statusCode: 400, }), ); } + const enCoursTypeStatut = + declaration.statut === statuts.EN_COURS + ? statuts.ATTENTE_8_JOUR + : statuts.VALIDEE_8J; + const textTypePrecision = + "Enregistrement de la déclaration à " + + (declaration.statut === statuts.EN_COURS ? " 2 mois" : " 8 jours"); try { - await PdfARDeclaration2Mois(declaration); + if (declaration.statut === statuts.EN_COURS) + await PdfARDeclaration2Mois(declaration); + else await PdfARDeclaration8Jours(declaration); const destinataires = await DemandeSejour.getEmailToList( declaration.organismeId, @@ -65,14 +78,14 @@ module.exports = async function post(req, res, next) { await DemandeSejour.updateStatut( declarationId, - statuts.ATTENTE_8_JOUR, + enCoursTypeStatut, { boUserId: userId, declarationId, metaData: declaration, source: `DDETS ${territoireCode}`, type: "declaration_sejour", - typePrecision: "Enregistrement de la déclaration à 2 mois", + typePrecision: textTypePrecision, userId: null, }, () => @@ -84,6 +97,9 @@ module.exports = async function post(req, res, next) { ), ); + if (declaration.statut === statuts.EN_COURS) + await DemandeSejour.saveDS2M(declarationId, declaration); + return res.status(200).end(); } catch (error) { log.w("DONE with error"); diff --git a/packages/backend/src/controllers/demandeSejour/getByDepartementCodes.js b/packages/backend/src/controllers/demandeSejour/getByDepartementCodes.js index 6366e34f2..3c80cc081 100644 --- a/packages/backend/src/controllers/demandeSejour/getByDepartementCodes.js +++ b/packages/backend/src/controllers/demandeSejour/getByDepartementCodes.js @@ -42,6 +42,7 @@ module.exports = async function getByDepartementCodes(req, res, next) { "dateFin", "organismeId", "organisme", + "idFonctionnelle", ]) .nullable(), sortDirection: yup.string().oneOf(["ASC", "DESC"]).nullable(), @@ -54,7 +55,7 @@ module.exports = async function getByDepartementCodes(req, res, next) { return next(new ValidationAppError(error)); } const demandesWithPagination = await DemandeSejour.getByDepartementCodes( - params, + params, territoireCode, req.departements.map((d) => d.value), ); diff --git a/packages/backend/src/controllers/demandeSejour/index.js b/packages/backend/src/controllers/demandeSejour/index.js index f622e5ef5..d10e9ea4e 100644 --- a/packages/backend/src/controllers/demandeSejour/index.js +++ b/packages/backend/src/controllers/demandeSejour/index.js @@ -11,3 +11,5 @@ module.exports.prendEnCharge = require("./prendEnCharge"); module.exports.demandeComplements = require("./demandeComplements"); module.exports.refus = require("./refus"); module.exports.enregistrementDemande2Mois = require("./enregistrementDemande2Mois"); +module.exports.copy = require("./copy"); +module.exports.delete = require("./delete"); diff --git a/packages/backend/src/controllers/demandeSejour/prendEnCharge.js b/packages/backend/src/controllers/demandeSejour/prendEnCharge.js index 5c920d3f1..6ed3c1c39 100644 --- a/packages/backend/src/controllers/demandeSejour/prendEnCharge.js +++ b/packages/backend/src/controllers/demandeSejour/prendEnCharge.js @@ -14,6 +14,7 @@ module.exports = async function post(req, res, next) { /** Ce controller gere le passage de la prise en charge d'une demande par un admin. * il ne peut etre appelé que : * - pour un passage d'une demande TRANSMISE => EN COURS + * - pour un passage d'une demande TRANSMISE 8J => EN COURS 8J * - par un instructeur principal de la demande (le premier hebergement est dans le département de l'admin) */ @@ -51,7 +52,7 @@ module.exports = async function post(req, res, next) { ); } - if (declaration.statut !== statuts.TRANSMISE) { + if (declaration.statut !== statuts.TRANSMISE && declaration.statut !== statuts.TRANSMISE_8J) { log.w("Delaration is already at least in progress"); return next( new AppError("Statut incompatible", { @@ -59,15 +60,17 @@ module.exports = async function post(req, res, next) { }), ); } - + const enCoursTypeStatut = declaration.statut === statuts.TRANSMISE ? statuts.EN_COURS : statuts.EN_COURS_8J; + const textTypePrecision = "Prise en charge de la déclaration " + (declaration.statut === statuts.TRANSMISE ? " 2 mois" : " 8 jours"); + try { - await DemandeSejour.updateStatut(declarationId, statuts.EN_COURS, { + await DemandeSejour.updateStatut(declarationId, enCoursTypeStatut, { boUserId: userId, declarationId, metaData: declaration, source: `DDETS ${territoireCode}`, type: "declaration_sejour", - typePrecision: "Prise en charge de la déclaration", + typePrecision: textTypePrecision, userId: null, }); } catch (error) { diff --git a/packages/backend/src/controllers/demandeSejour/refus.js b/packages/backend/src/controllers/demandeSejour/refus.js index 4437117c6..89877764e 100644 --- a/packages/backend/src/controllers/demandeSejour/refus.js +++ b/packages/backend/src/controllers/demandeSejour/refus.js @@ -49,7 +49,7 @@ module.exports = async function post(req, res, next) { ); } - if (declaration.statut !== statuts.EN_COURS) { + if (declaration.statut !== statuts.EN_COURS && declaration.statut !== statuts.EN_COURS_8J) { log.w("Delaration should be in statut EN COURS"); return next( new AppError("Statut incompatible", { @@ -58,6 +58,8 @@ module.exports = async function post(req, res, next) { ); } + const RefuseType = declaration.statut === statuts.EN_COURS ? statuts.REFUSEE : statuts.REFUSEE_8J; + const textTypePrecision = "Refus de la demande " + (declaration.statut === statuts.EN_COURS ? " 2 mois" : " 8 jours"); try { const destinataires = await DemandeSejour.getEmailToList( declaration.organismeId, @@ -65,14 +67,14 @@ module.exports = async function post(req, res, next) { await DemandeSejour.updateStatut( declarationId, - statuts.REFUSEE, + RefuseType, { boUserId: userId, declarationId, metaData: { commentaire }, source: `DDETS ${territoireCode}`, type: "declaration_sejour", - typePrecision: "Refus de la demande", + typePrecision: textTypePrecision, userId: null, }, () => diff --git a/packages/backend/src/helpers/declaration/informations-personnel.js b/packages/backend/src/helpers/declaration/informations-personnel.js new file mode 100644 index 000000000..5f751fb11 --- /dev/null +++ b/packages/backend/src/helpers/declaration/informations-personnel.js @@ -0,0 +1,16 @@ +module.exports.fonctionOptions = [ + { + id: "1", + text: "Distribution des médicaments", + value: "distribution des médicaments", + }, + { + id: "2", + text: "Transport des vacanciers", + value: "transport des vacanciers", + }, + { id: "3", text: "Restauration", value: "restauration" }, + { id: "4", text: "Entretien des locaux", value: "entretien des locaux" }, + { id: "5", text: "Activités spécifiques", value: "activités spécifiques" }, + { id: "6", text: "Autre", value: "autre" }, +]; diff --git a/packages/backend/src/helpers/ds-statuts.js b/packages/backend/src/helpers/ds-statuts.js index 67c2bd99d..fc66583a1 100644 --- a/packages/backend/src/helpers/ds-statuts.js +++ b/packages/backend/src/helpers/ds-statuts.js @@ -1,11 +1,13 @@ module.exports.statuts = { ATTENTE_8_JOUR: "EN ATTENTE DECLARATION 8 JOURS", A_MODIFIER: "A MODIFIER", + A_MODIFIER_8J: "A MODIFIER 8J", BROUILLON: "BROUILLON", EN_COURS: "EN COURS", - MAJ_POST_8J: "MAJ POST 8J", + EN_COURS_8J: "EN COURS 8J", REFUSEE: "REFUSEE", + REFUSEE_8J: "REFUSEE 8J", TRANSMISE: "TRANSMISE", TRANSMISE_8J: "TRANSMISE 8J", - VALIDEE: "VALIDEE", + VALIDEE_8J: "VALIDEE 8J", }; diff --git a/packages/backend/src/routes/sejour.js b/packages/backend/src/routes/sejour.js index d19110fb0..96a003b22 100644 --- a/packages/backend/src/routes/sejour.js +++ b/packages/backend/src/routes/sejour.js @@ -68,7 +68,8 @@ router.get("/historique/:id", checkJWT, demandeSejourController.historique); router.get("/", checkJWT, demandeSejourController.get); router.post("/depose/:id", checkJWT, demandeSejourController.depose); +router.post("/:id/copy", checkJWT, demandeSejourController.copy); router.post("/:id", checkJWT, demandeSejourController.update); router.post("/", checkJWT, demandeSejourController.post); - +router.delete("/:id", checkJWT, demandeSejourController.delete); module.exports = router; diff --git a/packages/backend/src/schemas/declaration-sejour.js b/packages/backend/src/schemas/declaration-sejour.js index 7215f4452..c8452a5b2 100644 --- a/packages/backend/src/schemas/declaration-sejour.js +++ b/packages/backend/src/schemas/declaration-sejour.js @@ -8,7 +8,9 @@ const informationsVacanciersSchema = require("./parts/informations-vacanciers"); const projetSejourSchema = require("./parts/projet-sejour"); const protocoleTransportSchema = require("./parts/protocoleTransport"); const protocoleSanitaireSchema = require("./parts/protocoleSanitaire"); -const personneSchema = require("./parts/personne.js"); +const personne = require("./parts/personne.js"); +const prestataire = require("./parts/prestataire.js"); +const { statuts } = require("../helpers/ds-statuts.js"); const log = logger(module.filename); @@ -36,6 +38,10 @@ function isSejourComplet(hebergements, dateDebut, dateFin) { return true; } +function isUpdateFUsager8Jour(statut) { + return (statut === statuts.ATTENTE_8_JOUR || statut === statuts.A_MODIFIER_8J) +} + const baseSchema = { dateDebut: yup .date("Vous devez saisir une date valide au format JJ/MM/AAAA") @@ -59,29 +65,128 @@ const baseSchema = { .required(), responsableSejour: yup .object( - personneSchema({ + personne({ showAdresse: true, + showAttestation: false, + showCompetence: false, + showDateNaissance: false, showEmail: true, + showFonction: true, + showListeFonction: false, showTelephone: true, }), ) .required(), }; -const informationsPersonnelSchema = { - nombreAccompagnant: yup - .number("Ce champ doit contenir un nombre entier") - .integer("Ce champ doit contenir un nombre entier") - .typeError("Ce champ doit contenir un nombre entier") - .required("Ce champ doit contenir un nombre entier"), - nombreResponsable: yup - .number("Ce champ doit contenir un nombre entier") - .integer("Ce champ doit contenir un nombre entier") - .typeError("Ce champ doit contenir un nombre entier") - .required("Ce champ doit contenir un nombre entier"), - procedureRecrutementSupplementaire: yup - .bool("La saisie de ce champ est obligatoire") - .required("La saisie de ce champ est obligatoire"), +const informationsPersonnelSchema = (statut) => { + return { + nombreAccompagnant: yup + .number("Ce champ doit contenir un nombre entier") + .integer("Ce champ doit contenir un nombre entier") + .typeError("Ce champ doit contenir un nombre entier") + .min( + `${isUpdateFUsager8Jour(statut) ? 1 : 0}`, + "Vousdevez saisir au moins 1 accompagnant", + ) + .required("Ce champ doit contenir un nombre entier"), + nombreResponsable: yup + .number("Ce champ doit contenir un nombre entier") + .integer("Ce champ doit contenir un nombre entier") + .typeError("Ce champ doit contenir un nombre entier") + .min( + `${isUpdateFUsager8Jour(statut) ? 1 : 0}`, + "Vousdevez saisir au moins 1 responsable d'encadrement", + ) + .required("Ce champ doit contenir un nombre entier"), + procedureRecrutementSupplementaire: yup + .bool("La saisie de ce champ est obligatoire") + .required("La saisie de ce champ est obligatoire"), + ...(isUpdateFUsager8Jour(statut) && { + accompagnants: yup + .array() + .of( + yup.object( + personne({ + showAdresse: false, + showAttestation: true, + showCompetence: true, + showDateNaissance: true, + showEmail: false, + showFonction: false, + showListeFonction: true, + showTelephone: true, + }), + ), + ) + .min(1, "Vous devez saisir au moins un accompagnant") + .required(), + }), + ...(isUpdateFUsager8Jour(statut) && { + encadrants: yup + .array() + .of( + yup.object( + personne({ + showAdresse: false, + showAttestation: true, + showCompetence: true, + showDateNaissance: true, + showEmail: false, + showFonction: false, + showListeFonction: true, + showTelephone: true, + }), + ), + ) + .min(1, "Vous devez saisir au moins 1 encadrant") + .required(), + }), + ...(isUpdateFUsager8Jour(statut) && { + formation: yup + .string() + .min(5, "Ce champ est obligatoire") + .required() + .nullable(), + }), + ...(isUpdateFUsager8Jour(statut) && { + formation: yup + .string() + .min(5, "Ce champ est obligatoire") + .required() + .nullable(), + }), + ...(isUpdateFUsager8Jour(statut) && { + prestatairesActivites: yup + .array() + .of(yup.object(prestataire.schema)) + .nullable(), + }), + ...(isUpdateFUsager8Jour(statut) && { + prestatairesEntretien: yup + .array() + .of(yup.object(prestataire.schema)) + .nullable(), + }), + ...(isUpdateFUsager8Jour(statut) && { + prestatairesMedicaments: yup + .array() + .of(yup.object(prestataire.schema)) + .nullable(), + }), + ...(isUpdateFUsager8Jour(statut) && { + prestatairesRestauration: yup + .array() + .of(yup.object(prestataire.schema)) + .nullable(), + }), + ...(isUpdateFUsager8Jour(statut) && { + prestatairesTransport: yup + .array() + .of(yup.object(prestataire.schema)) + .nullable(), + }), + }; }; const hebergementDetailsSchema = { @@ -141,16 +246,18 @@ const hebergementSchema = (dateDebut, dateFin) => ({ sejourItinerant: yup.boolean().required(), }); -const schema = (dateDebut, dateFin) => ({ - ...baseSchema, - attestation: yup.object(attestationSchema), - hebergement: yup.object(hebergementSchema(dateDebut, dateFin)), - informationsPersonnel: yup.object(informationsPersonnelSchema), - informationsSanitaires: yup.object(protocoleSanitaireSchema()), - informationsTransport: yup.object(protocoleTransportSchema()), - informationsVacanciers: yup.object(informationsVacanciersSchema()), - projetSejour: yup.object(projetSejourSchema()), -}); +const schema = (dateDebut, dateFin, statut) => { + return { + ...baseSchema, + attestation: yup.object(attestationSchema), + hebergement: yup.object(hebergementSchema(dateDebut, dateFin)), + informationsPersonnel: yup.object(informationsPersonnelSchema(statut)), + informationsSanitaires: yup.object(protocoleSanitaireSchema()), + informationsTransport: yup.object(protocoleTransportSchema()), + informationsVacanciers: yup.object(informationsVacanciersSchema()), + projetSejour: yup.object(projetSejourSchema()), + }; +}; module.exports = { baseSchema, diff --git a/packages/backend/src/schemas/organisme.js b/packages/backend/src/schemas/organisme.js index 716b9aa60..6e85d677e 100644 --- a/packages/backend/src/schemas/organisme.js +++ b/packages/backend/src/schemas/organisme.js @@ -133,7 +133,12 @@ const personneMoraleSchema = () => ({ responsableSejour: yup.object({ ...personneSchema({ showAdresse: true, + showAttestation: false, + showCompetence: false, + showDateNaissance: false, showEmail: true, + showFonction: true, + showListeFonction: false, showTelephone: true, }), }), diff --git a/packages/backend/src/schemas/parts/personne.js b/packages/backend/src/schemas/parts/personne.js index 9a9f597af..6c67ca621 100644 --- a/packages/backend/src/schemas/parts/personne.js +++ b/packages/backend/src/schemas/parts/personne.js @@ -1,16 +1,25 @@ const yup = require("yup"); +const dayjs = require("dayjs"); const adresseSchema = require("./adresse"); const emailSchema = require("./email"); const nomSchema = require("./nom"); const prenomSchema = require("./prenom"); const telephoneSchema = require("./telephone"); +const { + fonctionOptions, +} = require("../../helpers/declaration/informations-personnel"); -const schema = ( - { showAdresse, showEmail, showTelephone, showFonction = true } = { - showFonction: true, - }, -) => { +const schema = ({ + showAdresse, + showAttestation, + showCompetence, + showDateNaissance, + showEmail, + showFonction, + showListeFonction, + showTelephone, +}) => { return { nom: nomSchema(), prenom: prenomSchema(), @@ -23,9 +32,39 @@ const schema = ( ...(showAdresse && { adresse: yup.object(adresseSchema()), }), + ...(showAttestation && { + attestation: yup + .boolean() + .oneOf([true], "Vous devez certifier de ces informations") + .required(), + }), + ...(showCompetence && { + competence: yup.string().required(), + }), + ...(showDateNaissance && { + dateNaissance: yup + .date() + .typeError("La date n'est pas au format attendu") + .max( + dayjs(), + "La date de naissance ne peut être supérieure à la date du jour", + ) + .required(), + }), ...(showFonction && { fonction: yup.string().required(), }), + ...(showListeFonction && { + listeFonction: yup + .array() + .of( + yup.string().oneOf( + fonctionOptions.map((o) => o.value), + "la valeur insérée ne fait pas partie de la liste des possibles", + ), + ) + .required(), + }), }; }; diff --git a/packages/backend/src/schemas/parts/prestataire.js b/packages/backend/src/schemas/parts/prestataire.js new file mode 100644 index 000000000..a9ca9ae94 --- /dev/null +++ b/packages/backend/src/schemas/parts/prestataire.js @@ -0,0 +1,92 @@ +const adresseSchema = require("./adresse.js")({ isFromAPIAdresse: true }); +const regex = require("../../utils/regex"); +const yup = require("yup"); +const dayjs = require("dayjs"); + +const typePrestataireOptions = [ + { + label: "Personne morale", + value: "personne_morale", + }, + { + label: "Personne physique", + value: "personne_physique", + }, +]; + +const schema = { + adresse: yup.object().when("typePrestataire", { + is: (val) => val === "personne_morale", + otherwise: (adresse) => adresse.nullable().strip(), + then: () => yup.object(adresseSchema), + }), + competence: yup.string().when("typePrestataire", { + is: (val) => val === "personne_physique", + otherwise: (competence) => competence.nullable().strip(), + then: (competence) => + competence.min(5, "Ce champ est obligatoire").required(), + }), + dateNaissance: yup.date().when("typePrestataire", { + is: (val) => val === "personne_physique", + otherwise: (dateNaissance) => dateNaissance.nullable().strip(), + then: (dateNaissance) => + dateNaissance + .typeError("La date n'est pas au format attendu") + .max( + dayjs(), + "La date de naissance ne peut être supérieure à la date du jour", + ) + .required(), + }), + nom: yup + .string() + .test( + "doubleSpaces", + "Le nom ne peut contenir deux espaces successifs", + (nom) => !regex.doubleSpacesRegex.test(nom), + ) + .test( + "spaceFollowingDash", + "Le nom ne peut contenir d'espace suivant un tiret", + (nom) => !regex.spaceFollowingDashRegex.test(nom), + ) + .test( + "tripleDash", + "Le nom ne peut contenir trois tirets consécutifs", + (nom) => !regex.tripleDashRegex.test(nom), + ) + .required(), + prenom: yup + .string() + .test( + "doubleSpaces", + "Le prénom ne peut contenir deux espaces successifs", + (prenom) => !regex.doubleSpacesRegex.test(prenom), + ) + .test( + "spaceFollowingDash", + "Le prénom ne peut contenir d'espace suivant un tiret", + (prenom) => !regex.spaceFollowingDashRegex.test(prenom), + ) + .test( + "doubleDash", + "Le prénom ne peut contenir deux tirets consécutifs", + (prenom) => !regex.doubleDashRegex.test(prenom), + ) + .required(), + telephone: yup + .string() + .test("telephone", "Format de numéro de téléphone invalide", (telephone) => + regex.numTelephoneRegex.test(telephone), + ) + .required(), + typePrestataire: yup + .string() + .oneOf(["personne_physique", "personne_morale"]) + .required(), +}; + +module.exports = { + schema, + typePrestataireOptions, +}; diff --git a/packages/backend/src/schemas/register.js b/packages/backend/src/schemas/register.js index 62db276fc..4b0b2dd00 100644 --- a/packages/backend/src/schemas/register.js +++ b/packages/backend/src/schemas/register.js @@ -6,8 +6,13 @@ const personneSchema = require("./parts/personne"); const schema = () => object({ ...personneSchema({ + showAdresse: false, + showAttestation: false, + showCompetence: false, + showDateNaissance: false, showEmail: true, showFonction: false, + showListeFonction: false, showTelephone: true, }), password: passwordSchema(), diff --git a/packages/backend/src/services/DemandeSejour.js b/packages/backend/src/services/DemandeSejour.js index 5a2ffe5f5..011bbd37e 100644 --- a/packages/backend/src/services/DemandeSejour.js +++ b/packages/backend/src/services/DemandeSejour.js @@ -25,6 +25,64 @@ const query = { WHERE id = $1 RETURNING id as "declarationId" `, + copy: ( + organismeId, + libelle, + dateDebut, + dateFin, + duree, + periode, + responsableSejour, + organisme, + hebergement, + vacanciers, + personnel, + transport, + projet_sejour, + sanitaires, + files, + ) => [ + ` + INSERT INTO front.demande_sejour( + statut, + organisme_id, + libelle, + date_debut, + date_fin, + duree, + periode, + responsable_sejour, + organisme, + hebergement, + vacanciers, + personnel, + transport, + projet_sejour, + sanitaires, + files + ) + VALUES ('BROUILLON',$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15) + RETURNING + id as "demandeId" + ;`, + [ + organismeId, + libelle, + dateDebut, + dateFin, + duree, + periode, + responsableSejour, + organisme, + hebergement, + vacanciers, + personnel, + transport, + projet_sejour, + sanitaires, + files, + ], + ], create: ( organismeId, libelle, @@ -70,6 +128,18 @@ const query = { [], ], ], + delete: (declarationId, userId) => [ + ` + DELETE FROM front.demande_sejour d + USING front.organismes o, front.user_organisme uo + WHERE + o.id = d.organisme_id + AND uo.org_id = o.id + AND d.id = $1 + AND uo.use_id = $2 + ;`, + [declarationId, userId], + ], finalize: ( demandeSejourId, idFonctionnelle, @@ -114,6 +184,29 @@ RETURNING attestation, ], ], + finalize8jours: ( + demandeSejourId, + vacanciers, + personnel, + hebergement, + attestation, + ) => [ + ` +UPDATE front.demande_sejour ds +SET + statut = 'TRANSMISE 8J', + vacanciers = $2, + personnel = $3, + hebergement = $4, + attestation = $5, + edited_at = NOW() +WHERE + ds.id = $1 +RETURNING + id as "demandeId" +;`, + [demandeSejourId, vacanciers, personnel, hebergement, attestation], + ], get: (organismeIds) => [ ` SELECT @@ -156,13 +249,14 @@ SELECT ds.date_debut::text as "dateDebut", ds.date_fin::text as "dateFin", ds.organisme as "organisme", + ds.id_fonctionnelle as "idFonctionnelle", o.personne_morale as "personneMorale", o.personne_physique as "personnePhysique", o.type_organisme as "typeOrganisme", ds.hebergement #>> '{hebergements, 0, coordonnees, adresse, departement}' = ANY ($${params.length}) as "estInstructeurPrincipal" FROM front.demande_sejour ds JOIN front.organismes o ON o.id = ds.organisme_id - LEFT JOIN front.agrements a ON a.organisme_id = ds.organisme_id + LEFT JOIN front.agrements a ON a.organisme_id = ds.organisme_id WHERE statut <> 'BROUILLON' AND ((${departementQuery}) @@ -175,7 +269,7 @@ WHERE SELECT COUNT(DISTINCT ds.id) FROM front.demande_sejour ds JOIN front.organismes o ON o.id = ds.organisme_id -LEFT JOIN front.agrements a ON a.organisme_id = ds.organisme_id +LEFT JOIN front.agrements a ON a.organisme_id = ds.organisme_id WHERE statut <> 'BROUILLON' AND ((${departementQuery}) @@ -215,7 +309,7 @@ WHERE ds.edited_at as "editedAt" FROM front.demande_sejour ds JOIN front.organismes o ON o.id = ds.organisme_id - LEFT JOIN front.agrements a ON a.organisme_id = ds.organisme_id + LEFT JOIN front.agrements a ON a.organisme_id = ds.organisme_id where ((${departementQuery}) OR a.region_obtention = '${territoireCode}') AND ds.id = $1 @@ -316,6 +410,7 @@ SELECT ds.organisme as "organisme", ds.files as "files", ds.attestation as "attestation", + ds.declaration_2m as "declaration2mois", o.personne_morale->>'siret' as "siret", ds.edited_at as "editedAt" FROM front.demande_sejour ds @@ -360,6 +455,12 @@ WHERE RETURNING id as "eventId" `, + saveDS2M: ` + UPDATE front.demande_sejour + SET declaration_2m = $2 + WHERE id = $1 + RETURNING id as "declarationId" +`, updateHebergement: ` UPDATE front.demande_sejour ds SET @@ -526,6 +627,39 @@ module.exports.create = async ({ return demandeId; }; +module.exports.copy = async (declaration) => { + log.i("copy - IN"); + const response = await pool.query( + ...query.copy( + declaration.organismeId, + `COPIE - ${declaration.libelle}`, + declaration.dateDebut, + declaration.dateFin, + declaration.duree, + declaration.periode, + declaration.responsableSejour, + declaration.organisme, + declaration.hebergement, + declaration.informationsVacanciers, + declaration.informationsPersonnel, + declaration.informationsTransport, + declaration.projetSejour, + declaration.informationsSanitaires, + declaration.files, + ), + ); + log.d(response); + const { demandeId } = response.rows[0]; + log.i("copy - DONE", { demandeId }); + return demandeId; +}; + +module.exports.delete = async (declarationId, userId) => { + log.i("delete - IN"); + const { rowCount } = await pool.query(...query.delete(declarationId, userId)); + log.i("delete - DONE"); + return rowCount; +}; module.exports.get = async (organismesId) => { log.i("get - IN", { organismesId }); const response = await pool.query(...query.get(organismesId)); @@ -565,6 +699,10 @@ module.exports.getByDepartementCodes = async ( const searchQuery = []; // Search management + if (search?.idFonctionnelle && search.idFonctionnelle.length) { + searchQuery.push(`id_fonctionnelle ilike $${params.length + 1}`); + params.push(`%${search.idFonctionnelle}%`); + } if (search?.libelle && search.libelle.length) { searchQuery.push(`libelle ilike $${params.length + 1}`); params.push(`%${search.libelle}%`); @@ -643,7 +781,11 @@ module.exports.getByDepartementCodes = async ( } const total = await pool.query( - query.getByDepartementCodesTotal(searchQuery, territoireCode,departementQuery), + query.getByDepartementCodesTotal( + searchQuery, + territoireCode, + departementQuery, + ), params, ); @@ -654,7 +796,11 @@ module.exports.getByDepartementCodes = async ( }; }; -module.exports.getById = async (demandeId, territoireCode, departementCodes) => { +module.exports.getById = async ( + demandeId, + territoireCode, + departementCodes, +) => { log.i("getById - IN", { demandeId }); if (departementCodes && departementCodes.length === 0) { @@ -761,6 +907,32 @@ module.exports.update = async (type, demandeSejourId, parametre) => { return demandeId; }; +module.exports.finalize8jours = async ( + demandeSejourId, + { informationsVacanciers, informationsPersonnel, hebergement, attestation }, +) => { + log.i("finalize - IN", { + declaration: { + attestation, + hebergement, + informationsPersonnel, + informationsVacanciers, + }, + demandeSejourId, + }); + + await pool.query( + ...query.finalize8jours( + demandeSejourId, + informationsVacanciers, + informationsPersonnel, + hebergement, + attestation, + ), + ); + log.i("finalize - DONE"); +}; + module.exports.finalize = async ( demandeSejourId, departementSuivi, @@ -862,6 +1034,12 @@ module.exports.insertEvent = async ( return response[0].eventId ?? null; }; +module.exports.saveDS2M = async (declarationId, declaration) => { + log.i("saveDS2M - IN"); + await pool.query(query.saveDS2M, [declarationId, declaration]); + log.i("saveDS2M - DONE"); +}; + module.exports.addFile = async (declarationId, file) => { log.i("addFile - IN", { declarationId }); const { rows: response } = await pool.query(query.addFile, [ diff --git a/packages/backend/src/services/pdf/ARdeclaration8jours/build.js b/packages/backend/src/services/pdf/ARdeclaration8jours/build.js new file mode 100644 index 000000000..919f1e509 --- /dev/null +++ b/packages/backend/src/services/pdf/ARdeclaration8jours/build.js @@ -0,0 +1,170 @@ +const PdfPrinter = require("pdfmake"); +const path = require("path"); +const dayjs = require("dayjs"); +const logger = require("../../../utils/logger"); + +const log = logger(module.filename); + +const fonts = { + Marianne: { + bold: path.join(__dirname, "..", "..", "../fonts/Marianne-Bold.woff"), + italics: path.join( + __dirname, + "..", + "..", + "../fonts/Marianne-Light_Italic.woff", + ), + normal: path.join(__dirname, "..", "..", "../fonts/Marianne-Regular.woff"), + }, +}; + +const printer = new PdfPrinter(fonts); + +function buildHeader() { + return { + columns: [ + { + stack: [ + { + alignment: "left", + image: path.join(__dirname, "..", "..", "../images/logo.png"), + width: 80, + }, + ], + }, + ], + margin: [10, 10, 10, 10], + }; +} + +function buildTitre(declaration, departementSuivi) { + return { + stack: [ + { + alignment: "center", + margin: [0, 30, 0, 0], + table: { + body: [ + [ + { + stack: [ + { + bold: true, + fontSize: 12, + text: `ACCUSE DE RECEPTION POUR LA DECLARATION COMPLEMENTAIRE ${declaration.idFonctionnelle}`, + }, + ], + }, + ], + ], + headerRows: 1, + layout: "headerLineOnly", + widths: ["*"], + }, + }, + { + columnGap: 10, + columns: [ + { + alignment: "left", + stack: [ + { + columns: [ + { + decoration: "underline", + text: "Département instructeur :", + width: 150, + }, + { + bold: true, + text: `DDETS ${departementSuivi}`, + width: 150, + }, + ], + }, + { + columns: [ + { + decoration: "underline", + text: "Numéro d'enregistrement :", + width: 150, + }, + { + bold: true, + text: `${declaration.idFonctionnelle}`, + width: 150, + }, + ], + }, + ], + width: "100%", + }, + ], + margin: [0, 10, 0, 0], + }, + { + text: "\n\n\n", + }, + ], + }; +} + +const build = (declaration = {}) => { + log.i("build - IN"); + const departementSuivi = declaration.departementSuivi; + return new Promise((resolve) => { + const docDefinition = { + content: [ + buildHeader(), + buildTitre(declaration, departementSuivi), + { + style: "header", + text: `Vous êtes titulaire de l’agrément « VACANCES ADAPTEES ORGANISEES » délivré le ${dayjs(declaration.organisme.agrement.dateObtention).format("DD/MM/YYYY")} et avez déposé à ce titre, en date du ${dayjs(declaration.attestation.at).format("DD/MM/YYYY")}, une déclaration pour le séjour '${declaration.libelle}', enregistrée sous le numéro ${declaration.idFonctionnelle}, que vous organisez du ${dayjs(declaration.dateDebut).format("DD/MM/YYYY")} au ${dayjs(declaration.dateFin).format("DD/MM/YYYY")}.\n\n`, + }, + { + style: "header", + text: `Nous accusons ce jour, le ${dayjs().format("DD/MM/YYYY")}, réception de votre déclaration complémentaire.\n\n`, + }, + ], + defaultStyle: { + font: "Marianne", + fontSize: 9, + }, + footer: function (currentPage, pageCount) { + return [ + { + alignment: "right", + margin: [5, 5, 15, 5], + text: `${currentPage.toString()}/${pageCount}`, + }, + ]; + }, + pageBreakBefore: function (currentNode) { + return currentNode.headlineLevel === 1; + }, + style: { + header: { + alignment: "justify", + font: "Marianne", + fontSize: 9, + }, + }, + }; + + const pdfDoc = printer.createPdfKitDocument(docDefinition); + const chunks = []; + let result; + + pdfDoc.on("data", (chunk) => { + chunks.push(chunk); + }); + pdfDoc.on("end", () => { + result = Buffer.concat(chunks); + log.i("build - DONE"); + resolve(result); + }); + pdfDoc.end(); + }); +}; + +module.exports = build; diff --git a/packages/backend/src/services/pdf/ARdeclaration8jours/generate.js b/packages/backend/src/services/pdf/ARdeclaration8jours/generate.js new file mode 100644 index 000000000..12cf06456 --- /dev/null +++ b/packages/backend/src/services/pdf/ARdeclaration8jours/generate.js @@ -0,0 +1,50 @@ +/* eslint-disable no-param-reassign */ +const logger = require("../../../utils/logger"); +const build = require("./build"); +const Document = require("../../Document"); +const DemandeSejour = require("../../DemandeSejour"); +const dayjs = require("dayjs"); + +const log = logger(module.filename); + +const generate = async (declaration) => { + log.i("IN"); + try { + // générer document + const buffer = await build(declaration ?? {}); + + // insert into documents table + const uuid = await Document.createFile( + `AR_${declaration.idFonctionnelle}_8J.pdf`, + "AR_declaration_8_jours", + "application/pdf", + buffer, + ); + log.d(`http://localhost:3010/documents/${uuid}`); + + // insert into demande_sejour table + if (declaration.files === null || declaration.files === undefined) { + declaration.files = { files: [] }; + } else if ( + declaration.files.files === null || + declaration.files?.files === undefined + ) { + declaration.files.files = []; + } + const files = declaration.files.files; + const fileToAdd = { + createdAt: dayjs().format(), + name: `AR_${declaration.idFonctionnelle}_8J.pdf`, + type: "AR_declaration_8_jours", + uuid, + }; + files.push(fileToAdd); + await DemandeSejour.addFile(declaration.id, { files }); + return uuid; + } catch (e) { + log.w(e); + return null; + } +}; + +module.exports = generate; diff --git a/packages/backend/src/services/pdf/declaration2mois/build.js b/packages/backend/src/services/pdf/declaration2mois/build.js index dadae8d07..01abf4eac 100644 --- a/packages/backend/src/services/pdf/declaration2mois/build.js +++ b/packages/backend/src/services/pdf/declaration2mois/build.js @@ -2,7 +2,7 @@ const PdfPrinter = require("pdfmake"); const path = require("path"); const logger = require("../../../utils/logger"); -const parts = require("./parts"); +const parts = require("../parts"); const log = logger(module.filename); diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Titre.js b/packages/backend/src/services/pdf/declaration2mois/parts/Titre.js deleted file mode 100644 index 1091ce4f7..000000000 --- a/packages/backend/src/services/pdf/declaration2mois/parts/Titre.js +++ /dev/null @@ -1,88 +0,0 @@ -const dayjs = require("dayjs"); - -module.exports = function buildTitre(declaration, departementSuivi) { - return { - stack: [ - { - alignment: "center", - margin: [0, 30, 0, 0], - table: { - body: [ - [ - { - stack: [ - { - bold: true, - fontSize: 12, - text: "DECLARATION PREALABLE D'UN SEJOUR « VACANCES ADAPTEES ORGANISEES » destiné à des personnes handicapées", - }, - { - italics: true, - text: "Article R412-14 du code du tourisme", - }, - ], - }, - ], - ], - headerRows: 1, - layout: "headerLineOnly", - widths: ["*"], - }, - }, - { - columnGap: 10, - columns: [ - { - alignment: "left", - stack: [ - { - columns: [ - { - decoration: "underline", - text: "Département instructeur :", - width: 150, - }, - { - bold: true, - text: `DDETS ${departementSuivi}`, - width: 150, - }, - ], - }, - { - columns: [ - { - decoration: "underline", - text: "Numéro d'enregistrement :", - width: 150, - }, - { - bold: true, - text: `${declaration.idFonctionnelle}`, - width: 150, - }, - ], - }, - { - columns: [ - { - decoration: "underline", - text: "Date de dépôt :", - width: 150, - }, - { - bold: true, - text: `${dayjs(declaration.editedAt).format("DD/MM/YYYY HH:mm")}`, - width: 150, - }, - ], - }, - ], - width: "100%", - }, - ], - margin: [0, 10, 0, 0], - }, - ], - }; -}; diff --git a/packages/backend/src/services/pdf/declaration8jours/build.js b/packages/backend/src/services/pdf/declaration8jours/build.js new file mode 100644 index 000000000..04f13c5c0 --- /dev/null +++ b/packages/backend/src/services/pdf/declaration8jours/build.js @@ -0,0 +1,78 @@ +const PdfPrinter = require("pdfmake"); + +const path = require("path"); +const logger = require("../../../utils/logger"); +const parts = require("../parts"); + +const log = logger(module.filename); + +const fonts = { + Marianne: { + bold: path.join(__dirname, "../../../fonts/Marianne-Bold.woff"), + italics: path.join(__dirname, "../../../fonts/Marianne-Light_Italic.woff"), + normal: path.join(__dirname, "../../../fonts/Marianne-Regular.woff"), + }, +}; + +const printer = new PdfPrinter(fonts); + +const build = async (declaration = {}, departementSuivi, dateDeposeA2mois) => { + log.i("build - IN"); + log.d("declaration.informationsPersonnel", declaration.informationsPersonnel); + + const docDefinition = { + content: [ + parts.Header(), + parts.Titre(declaration, departementSuivi, "8jours", dateDeposeA2mois), + parts.InformationsGenerales(declaration, "8jours"), + { + headlineLevel: 1, + stack: [parts.Header()], + }, + parts.InformationsVacanciers( + declaration.informationsVacanciers, + "8jours", + ), + parts.InformationsPersonnel8jours(declaration.informationsPersonnel), + ...(await parts.FicheAnnexe(declaration.hebergement)), + { + headlineLevel: 1, + stack: [parts.Header()], + }, + parts.Attestation(declaration.attestation), + ], + defaultStyle: { + font: "Marianne", + fontSize: 9, + }, + footer: function (currentPage, pageCount) { + return [ + { + alignment: "right", + margin: [5, 5, 15, 5], + text: `${currentPage.toString()}/${pageCount}`, + }, + ]; + }, + pageBreakBefore: function (currentNode) { + return currentNode.headlineLevel === 1; + }, + }; + return new Promise((resolve) => { + const pdfDoc = printer.createPdfKitDocument(docDefinition); + const chunks = []; + let result; + + pdfDoc.on("data", (chunk) => { + chunks.push(chunk); + }); + pdfDoc.on("end", () => { + result = Buffer.concat(chunks); + log.i("build - DONE"); + resolve(result); + }); + pdfDoc.end(); + }); +}; + +module.exports = build; diff --git a/packages/backend/src/services/pdf/declaration8jours/generate.js b/packages/backend/src/services/pdf/declaration8jours/generate.js new file mode 100644 index 000000000..7f7887c69 --- /dev/null +++ b/packages/backend/src/services/pdf/declaration8jours/generate.js @@ -0,0 +1,59 @@ +/* eslint-disable no-param-reassign */ +const logger = require("../../../utils/logger"); +const build = require("./build"); +const Document = require("../../Document"); +const DemandeSejour = require("../../DemandeSejour"); +const dayjs = require("dayjs"); + +const log = logger(module.filename); + +const generate = async ( + declaration, + idFonctionnelle, + departementSuivi, + dateDeposeA2mois, +) => { + log.i("IN"); + try { + // générer document + const buffer = await build( + declaration ?? {}, + departementSuivi, + dateDeposeA2mois, + ); + + // insert into documents table + const uuid = await Document.createFile( + `${idFonctionnelle}_8jours.pdf`, + "declaration_8jours", + "application/pdf", + buffer, + ); + log.d(`http://localhost:3010/documents/${uuid}`); + + // insert into demande_sejour table + if (declaration.files === null || declaration.files === undefined) { + declaration.files = { files: [] }; + } else if ( + declaration.files.files === null || + declaration.files?.files === undefined + ) { + declaration.files.files = []; + } + const files = declaration.files.files; + const fileToAdd = { + createdAt: dayjs().format(), + name: `${idFonctionnelle}_8jours.pdf`, + type: "declaration_8jours", + uuid, + }; + files.push(fileToAdd); + await DemandeSejour.addFile(declaration.id, { files }); + return uuid; + } catch (e) { + log.w(e); + return null; + } +}; + +module.exports = generate; diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Agrement.js b/packages/backend/src/services/pdf/parts/Agrement.js similarity index 97% rename from packages/backend/src/services/pdf/declaration2mois/parts/Agrement.js rename to packages/backend/src/services/pdf/parts/Agrement.js index ca53d9b15..e252e029d 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/Agrement.js +++ b/packages/backend/src/services/pdf/parts/Agrement.js @@ -1,5 +1,5 @@ const dayjs = require("dayjs"); -const Regions = require("../../../geo/Region"); +const Regions = require("../../geo/Region"); module.exports = async function buildAgrement(agrement) { return { diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Attestation.js b/packages/backend/src/services/pdf/parts/Attestation.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/Attestation.js rename to packages/backend/src/services/pdf/parts/Attestation.js diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/FicheAnnexe.js b/packages/backend/src/services/pdf/parts/FicheAnnexe.js similarity index 78% rename from packages/backend/src/services/pdf/declaration2mois/parts/FicheAnnexe.js rename to packages/backend/src/services/pdf/parts/FicheAnnexe.js index d4095c77d..8d25d912e 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/FicheAnnexe.js +++ b/packages/backend/src/services/pdf/parts/FicheAnnexe.js @@ -4,10 +4,10 @@ const { prestationsHotelieres, accessibilites, types, -} = require("../../../../helpers/declaration/hebergement"); +} = require("../../../helpers/declaration/hebergement"); module.exports = function buildFicheAnnexe(hebergement) { - return hebergement.hebergements.map((hebergement) => ({ + return hebergement.hebergements.map((h) => ({ stack: [ { margin: [0, 30, 0, 0], @@ -65,7 +65,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${types[hebergement.informationsLocaux.type] ?? hebergement.informationsLocaux.type}`, + text: `${types[h.informationsLocaux.type] ?? h.informationsLocaux.type}`, width: "*", }, ], @@ -78,7 +78,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.nom}`, + text: `${h.nom}`, width: "*", }, ], @@ -91,7 +91,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.coordonnees.adresse.label}`, + text: `${h.coordonnees.adresse.label}`, width: "*", }, ], @@ -104,7 +104,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.coordonnees.nomGestionnaire}`, + text: `${h.coordonnees.nomGestionnaire}`, width: "*", }, ], @@ -117,7 +117,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.coordonnees.numTelephone1}`, + text: `${h.coordonnees.numTelephone1}`, width: "*", }, ], @@ -130,7 +130,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.coordonnees.numTelephone2 ?? ""}`, + text: `${h.coordonnees.numTelephone2 ?? ""}`, width: "*", }, ], @@ -143,7 +143,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.coordonnees.email}`, + text: `${h.coordonnees.email}`, width: "*", }, ], @@ -156,7 +156,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${dayjs(hebergement.dateDebut).format("DD/MM/YYYY")}`, + text: `${dayjs(h.dateDebut).format("DD/MM/YYYY")}`, width: "*", }, ], @@ -169,7 +169,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${dayjs(hebergement.dateFin).format("DD/MM/YYYY")}`, + text: `${dayjs(h.dateFin).format("DD/MM/YYYY")}`, width: "*", }, ], @@ -198,13 +198,13 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.visiteLocaux ? " Oui" : "Non"}`, + text: `${h.informationsLocaux.visiteLocaux ? " Oui" : "Non"}`, width: "*", }, ], }, - ...(hebergement.informationsLocaux.visiteLocaux && - hebergement.informationsLocaux.visiteLocauxAt + ...(h.informationsLocaux.visiteLocaux && + h.informationsLocaux.visiteLocauxAt ? [ { columns: [ @@ -214,7 +214,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${dayjs(hebergement.informationsLocaux.visiteLocauxAt).format("DD/MM/YYYY")}`, + text: `${dayjs(h.informationsLocaux.visiteLocauxAt).format("DD/MM/YYYY")}`, width: "*", }, ], @@ -229,7 +229,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.reglementationErp ? " Oui" : "Non"}`, + text: `${h.informationsLocaux.reglementationErp ? " Oui" : "Non"}`, width: "*", }, ], @@ -242,7 +242,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${accessibilites[hebergement.informationsLocaux.accessibilite] ?? hebergement.informationsLocaux.accessibilite}`, + text: `${accessibilites[h.informationsLocaux.accessibilite] ?? h.informationsLocaux.accessibilite}`, width: "*", }, ], @@ -255,7 +255,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${pensions[hebergement.informationsLocaux.pension] ?? hebergement.informationsLocaux.pension}`, + text: `${pensions[h.informationsLocaux.pension] ?? h.informationsLocaux.pension}`, width: "*", }, ], @@ -268,7 +268,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.prestationsHotelieres.map((p) => prestationsHotelieres[p] ?? p).join(", ")}`, + text: `${h.informationsLocaux.prestationsHotelieres.map((p) => prestationsHotelieres[p] ?? p).join(", ")}`, width: "*", }, ], @@ -281,7 +281,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.descriptionLieuHebergement}`, + text: `${h.informationsLocaux.descriptionLieuHebergement}`, width: "*", }, ], @@ -294,12 +294,12 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.nombreLits}`, + text: `${h.informationsLocaux.nombreLits}`, width: "*", }, ], }, - ...(hebergement.informationsLocaux.nombreLitsSuperposes + ...(h.informationsLocaux.nombreLitsSuperposes ? [ { columns: [ @@ -309,7 +309,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.nombreLitsSuperposes}`, + text: `${h.informationsLocaux.nombreLitsSuperposes}`, width: "*", }, ], @@ -322,7 +322,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.litsDessus ? " Oui" : "Non"}`, + text: `${h.informationsLocaux.litsDessus ? " Oui" : "Non"}`, width: "*", }, ], @@ -337,7 +337,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.nombreMaxPersonnesCouchage}`, + text: `${h.informationsLocaux.nombreMaxPersonnesCouchage}`, width: "*", }, ], @@ -350,7 +350,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.couchageIndividuel ? "Oui" : "Non"}`, + text: `${h.informationsLocaux.couchageIndividuel ? "Oui" : "Non"}`, width: "*", }, ], @@ -363,7 +363,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.rangementIndividuel ? "Oui" : "Non"}`, + text: `${h.informationsLocaux.rangementIndividuel ? "Oui" : "Non"}`, width: "*", }, ], @@ -376,7 +376,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.chambresUnisexes ? "Non" : "Oui"}`, + text: `${h.informationsLocaux.chambresUnisexes ? "Non" : "Oui"}`, width: "*", }, ], @@ -389,7 +389,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.amenagementsSpecifiques ? "Oui" : "Non "}`, + text: `${h.informationsLocaux.amenagementsSpecifiques ? "Oui" : "Non "}`, width: "*", }, ], @@ -402,17 +402,16 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.precisionAmenagementsSpecifiques ?? ""}`, + text: `${h.informationsLocaux.precisionAmenagementsSpecifiques ?? ""}`, width: "*", }, ], margin: [0, 0, 0, 10], }, - ...(hebergement.informationsLocaux.reglementationErp + ...(h.informationsLocaux.reglementationErp ? [ - hebergement.informationsLocaux - .fileDerniereAttestationSecurite && { + h.informationsLocaux.fileDerniereAttestationSecurite && { columns: [ { text: "Attestation de passage de la comission sécurité :", @@ -420,12 +419,12 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.fileDerniereAttestationSecurite.name}`, + text: `${h.informationsLocaux.fileDerniereAttestationSecurite.name}`, width: "*", }, ], }, - hebergement.informationsLocaux + h.informationsLocaux .fileDernierArreteAutorisationMaire && { columns: [ { @@ -434,7 +433,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.fileDernierArreteAutorisationMaire.name}`, + text: `${h.informationsLocaux.fileDernierArreteAutorisationMaire.name}`, width: "*", }, ], @@ -449,7 +448,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsLocaux.fileReponseExploitantOuProprietaire.name}`, + text: `${h.informationsLocaux.fileReponseExploitantOuProprietaire.name}`, width: "*", }, ], diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Header.js b/packages/backend/src/services/pdf/parts/Header.js similarity index 66% rename from packages/backend/src/services/pdf/declaration2mois/parts/Header.js rename to packages/backend/src/services/pdf/parts/Header.js index 0fd839457..b25fadf9d 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/Header.js +++ b/packages/backend/src/services/pdf/parts/Header.js @@ -7,7 +7,7 @@ module.exports = function buildHeader() { stack: [ { alignment: "left", - image: path.join(__dirname, "../../../../images/logo.png"), + image: path.join(__dirname, "../../../images/logo.png"), width: 80, }, ], @@ -16,10 +16,7 @@ module.exports = function buildHeader() { stack: [ { alignment: "right", - image: path.join( - __dirname, - "../../../../images/cerfa-12672-03.png", - ), + image: path.join(__dirname, "../../../images/cerfa-12672-03.png"), width: 50, }, ], diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsGenerales.js b/packages/backend/src/services/pdf/parts/InformationsGenerales.js similarity index 82% rename from packages/backend/src/services/pdf/declaration2mois/parts/InformationsGenerales.js rename to packages/backend/src/services/pdf/parts/InformationsGenerales.js index 4c28621ff..7cc3af8e1 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsGenerales.js +++ b/packages/backend/src/services/pdf/parts/InformationsGenerales.js @@ -1,7 +1,7 @@ const dayjs = require("dayjs"); const Organisme = require("./Organisme"); -module.exports = function buildInformationsGenerales(declaration) { +module.exports = function buildInformationsGenerales(declaration, type) { return { stack: [ { @@ -51,7 +51,10 @@ module.exports = function buildInformationsGenerales(declaration) { { columns: [ { - text: "Date indicative de début du séjour :", + text: + type === "8jours" + ? "Date de début du séjour :" + : "Date indicative de début du séjour :", width: 250, }, { @@ -64,7 +67,10 @@ module.exports = function buildInformationsGenerales(declaration) { { columns: [ { - text: "Date indicative de fin du séjour :", + text: + type === "8jours" + ? "Date de fin du séjour :" + : "Date indicative de fin du séjour :", width: 250, }, { @@ -130,6 +136,19 @@ module.exports = function buildInformationsGenerales(declaration) { }, ] : []), + { + columns: [ + { + text: "Liste des départements de séjour :", + width: 250, + }, + { + bold: true, + text: `${declaration.hebergement.hebergements.map((h) => h.coordonnees.adresse.departement).join(", ")}`, + width: "*", + }, + ], + }, Organisme(declaration.responsableSejour, declaration?.organisme), ], }, diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsPersonnel.js b/packages/backend/src/services/pdf/parts/InformationsPersonnel.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/InformationsPersonnel.js rename to packages/backend/src/services/pdf/parts/InformationsPersonnel.js diff --git a/packages/backend/src/services/pdf/parts/InformationsPersonnel8jours.js b/packages/backend/src/services/pdf/parts/InformationsPersonnel8jours.js new file mode 100644 index 000000000..ed2b9dd7f --- /dev/null +++ b/packages/backend/src/services/pdf/parts/InformationsPersonnel8jours.js @@ -0,0 +1,136 @@ +const Personnel = require("./Personnel"); +const Prestataire = require("./Prestataire"); + +module.exports = function buildInformationsPersonnel(info) { + return [ + { + margin: [0, 20, 0, 0], + stack: [ + { + margin: [0, 30, 0, 0], + table: { + body: [ + [ + { + alignment: "left", + fillColor: "#000091", + stack: [ + { + bold: true, + color: "#F5F5FE", + fontSize: 10, + text: "Informations sur le personnel présent au cours du séjour", + width: "300", + }, + ], + }, + ], + ], + headerRows: 1, + layout: ["headerLineOnly", "noBorders"], + widths: ["*"], + }, + }, + { + columns: [ + { + alignment: "left", + columnGap: 10, + stack: [ + { + columns: [ + { + text: "Nombre de personnes responsables présentes :", + width: 250, + }, + { + bold: true, + text: `${info.nombreResponsable}`, + width: "*", + }, + ], + }, + ], + }, + ], + margin: [0, 0, 0, 10], + }, + Personnel(info.encadrants), + { + columns: [ + { + alignment: "left", + columnGap: 10, + stack: [ + { + columns: [ + { + text: "Nombre d'accompagnants présents :", + width: 250, + }, + { + bold: true, + text: `${info.nombreAccompagnant}`, + width: "*", + }, + ], + }, + ], + }, + ], + margin: [0, 15, 0, 10], + }, + Personnel(info.accompagnants), + { + columnGap: 10, + columns: [ + { + text: "Procédure de recrutement supplémentaires durant le séjour :", + width: 250, + }, + { + bold: true, + text: `${info.procedureRecrutementSupplementaire ? "Oui" : "Non"}`, + width: "*", + }, + ], + }, + { + columnGap: 10, + columns: [ + { + text: "Organisation, contenu et durée d’une session de formation en amont de l'arrivée des vacanciers en vue de la coordination des équipes :", + width: 250, + }, + { + bold: true, + text: `${info.formation}`, + width: "*", + }, + ], + margin: [0, 0, 0, 10], + }, + ...Prestataire( + info.prestatairesMedicaments, + "Prestataires extérieur en charges des médicaments", + ), + ...Prestataire( + info.prestatairesTransport, + "Prestataires extérieur en charge du transport des vacanciers", + ), + ...Prestataire( + info.prestatairesRestauration, + "Prestataires extérieur en charge de la restauration", + ), + ...Prestataire( + info.prestatairesEntretien, + "Prestataires extérieur en charge de l'entretien et du ménage", + ), + ...Prestataire( + info.prestatairesActivites, + "Prestataires extérieur en charge d'encadrer les activités spécifiques", + ), + ], + }, + ]; +}; diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsSanitaires.js b/packages/backend/src/services/pdf/parts/InformationsSanitaires.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/InformationsSanitaires.js rename to packages/backend/src/services/pdf/parts/InformationsSanitaires.js diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsTransport.js b/packages/backend/src/services/pdf/parts/InformationsTransport.js similarity index 97% rename from packages/backend/src/services/pdf/declaration2mois/parts/InformationsTransport.js rename to packages/backend/src/services/pdf/parts/InformationsTransport.js index 7f4b9bae4..05c7d19c4 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsTransport.js +++ b/packages/backend/src/services/pdf/parts/InformationsTransport.js @@ -1,4 +1,4 @@ -const Transport = require("./Transport"); +const Transport = require("./Transport.js"); const TransportHebergement = require("./TransportHebergement.js"); module.exports = function buildInformationsTransport(info, hebergement) { diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsVacanciers.js b/packages/backend/src/services/pdf/parts/InformationsVacanciers.js similarity index 87% rename from packages/backend/src/services/pdf/declaration2mois/parts/InformationsVacanciers.js rename to packages/backend/src/services/pdf/parts/InformationsVacanciers.js index 050b959cc..191ef2b37 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/InformationsVacanciers.js +++ b/packages/backend/src/services/pdf/parts/InformationsVacanciers.js @@ -1,9 +1,9 @@ const { trancheAges, typeDeficiences, -} = require("../../../../helpers/declaration/informations-vacanciers"); +} = require("../../../helpers/declaration/informations-vacanciers"); -module.exports = function buildInformationsVacanciers(info) { +module.exports = function buildInformationsVacanciers(info, type) { return { stack: [ { @@ -19,7 +19,7 @@ module.exports = function buildInformationsVacanciers(info) { bold: true, color: "#F5F5FE", fontSize: 10, - text: "Informations prévisionnelles sur les vacanciers", + text: `${type === "8jours" ? "Informations sur les vacanciers" : "Informations prévisionnelles sur les vacanciers"}`, width: "300", }, ], @@ -40,7 +40,7 @@ module.exports = function buildInformationsVacanciers(info) { { columns: [ { - text: "Effectif prévisionnel des vacanciers :", + text: `${type === "8jours" ? "Effectif des vacanciers :" : "Effectif prévisionnel des vacanciers :"}`, width: 250, }, { diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Organisme.js b/packages/backend/src/services/pdf/parts/Organisme.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/Organisme.js rename to packages/backend/src/services/pdf/parts/Organisme.js diff --git a/packages/backend/src/services/pdf/parts/Personnel.js b/packages/backend/src/services/pdf/parts/Personnel.js new file mode 100644 index 000000000..445541546 --- /dev/null +++ b/packages/backend/src/services/pdf/parts/Personnel.js @@ -0,0 +1,38 @@ +const dayjs = require("dayjs"); + +module.exports = function buildPersonnelEncadrant(personnes) { + return { + layout: { + fillColor: function (rowIndex) { + return rowIndex === 0 ? "#CCCCCC" : null; + }, + }, + style: { + fontSize: 9, + margin: [0, 5, 0, 0], + }, + table: { + body: [ + [ + "Nom", + "Prénom", + "Date de naissance", + "Competence", + "Fonctions", + "Téléphone", + ], + ...personnes.map((personne) => [ + personne.nom ?? "", + personne.prenom ?? "", + personne?.dateNaissance + ? dayjs(personne?.dateNaissance).format("DD/MM/YYYY") + : "", + personne.competence ?? "", + personne.listeFonction?.join(", "), + personne.telephone ?? "", + ]), + ], + widths: [80, 80, 60, 90, 90, 60], + }, + }; +}; diff --git a/packages/backend/src/services/pdf/parts/Prestataire.js b/packages/backend/src/services/pdf/parts/Prestataire.js new file mode 100644 index 000000000..ca7de62f1 --- /dev/null +++ b/packages/backend/src/services/pdf/parts/Prestataire.js @@ -0,0 +1,60 @@ +const dayjs = require("dayjs"); + +module.exports = function buildPrestataire(personnes, label) { + if (personnes.length > 0) { + return [ + { + columns: [ + { + alignment: "left", + columnGap: 10, + stack: [ + { + columns: [ + { + text: `${label} :`, + width: 250, + }, + ], + }, + ], + }, + ], + margin: [0, 0, 0, 10], + }, + { + layout: { + fillColor: function (rowIndex) { + return rowIndex === 0 ? "#CCCCCC" : null; + }, + }, + style: { + margin: [0, 5, 0, 10], + }, + table: { + body: [ + [ + "Nom / Raison commerciale", + "Prénom / Nom commercial", + "Téléphone", + "Adresse", + "Date de naissance", + "Compétences", + ], + ...personnes.map((personne) => [ + personne.nom, + personne.prenom, + personne.telephone, + personne?.adresse?.label ?? "", + personne?.dateNaissance + ? dayjs(personne?.dateNaissance).format("DD/MM/YYYY") + : "", + personne?.competence ?? "", + ]), + ], + widths: [80, 80, 70, 80, 80, 70], + }, + }, + ]; + } else return []; +}; diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/ProjetSejour.js b/packages/backend/src/services/pdf/parts/ProjetSejour.js similarity index 97% rename from packages/backend/src/services/pdf/declaration2mois/parts/ProjetSejour.js rename to packages/backend/src/services/pdf/parts/ProjetSejour.js index b18423bb4..b8c015a8e 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/ProjetSejour.js +++ b/packages/backend/src/services/pdf/parts/ProjetSejour.js @@ -1,6 +1,4 @@ -const { - destinations, -} = require("../../../../helpers/declaration/projet-sejour"); +const { destinations } = require("../../../helpers/declaration/projet-sejour"); module.exports = function buildProjetSejour(info) { return { diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/RepresentantLegaux.js b/packages/backend/src/services/pdf/parts/RepresentantLegaux.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/RepresentantLegaux.js rename to packages/backend/src/services/pdf/parts/RepresentantLegaux.js diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/ResponsableOrganisation.js b/packages/backend/src/services/pdf/parts/ResponsableOrganisation.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/ResponsableOrganisation.js rename to packages/backend/src/services/pdf/parts/ResponsableOrganisation.js diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Sanitaire.js b/packages/backend/src/services/pdf/parts/Sanitaire.js similarity index 99% rename from packages/backend/src/services/pdf/declaration2mois/parts/Sanitaire.js rename to packages/backend/src/services/pdf/parts/Sanitaire.js index fc5c22578..1c3f4f240 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/Sanitaire.js +++ b/packages/backend/src/services/pdf/parts/Sanitaire.js @@ -2,7 +2,7 @@ const { constitutionEquipes, preparationPillulierss, responsableAdministrationMedicaments, -} = require("../../../../helpers/declaration/protocole-sanitaire"); +} = require("../../../helpers/declaration/protocole-sanitaire"); module.exports = function displayInfosSanitaires(info) { const liste = []; diff --git a/packages/backend/src/services/pdf/parts/Titre.js b/packages/backend/src/services/pdf/parts/Titre.js new file mode 100644 index 000000000..a125904ce --- /dev/null +++ b/packages/backend/src/services/pdf/parts/Titre.js @@ -0,0 +1,113 @@ +const dayjs = require("dayjs"); + +module.exports = function buildTitre( + declaration, + departementSuivi, + type, + dateDeposeA2mois, +) { + const infoPrecises = [ + { + columns: [ + { + decoration: "underline", + text: "Département instructeur :", + width: 150, + }, + { + bold: true, + text: `DDETS ${departementSuivi}`, + width: 150, + }, + ], + }, + { + columns: [ + { + decoration: "underline", + text: "Numéro d'enregistrement :", + width: 150, + }, + { + bold: true, + text: `${declaration.idFonctionnelle}`, + width: 150, + }, + ], + }, + { + columns: [ + { + decoration: "underline", + text: "Date de dépôt :", + width: 150, + }, + { + bold: true, + text: `${dayjs(declaration.editedAt).format("DD/MM/YYYY HH:mm")}`, + width: 150, + }, + ], + }, + ]; + if (type === "8jours" && dateDeposeA2mois) { + infoPrecises.push({ + columns: [ + { + decoration: "underline", + text: "Date de dépôt de la déclaration initiale :", + width: 150, + }, + { + bold: true, + text: `${dayjs(dateDeposeA2mois).format("DD/MM/YYYY") ?? ""}`, + width: 150, + }, + ], + }); + } + return { + stack: [ + { + alignment: "center", + margin: [0, 30, 0, 0], + table: { + body: [ + [ + { + stack: [ + { + bold: true, + fontSize: 12, + text: + type === "8jours" + ? "FICHE COMPLEMENTAIRE A LA DECLARATION D'UN SEJOUR « VACANCES ADAPTEES ORGANISEES » destiné à des personnes handicapées majeures" + : "DECLARATION PREALABLE D'UN SEJOUR « VACANCES ADAPTEES ORGANISEES » destiné à des personnes handicapées majeures", + }, + { + italics: true, + text: "Article R412-14 du code du tourisme", + }, + ], + }, + ], + ], + headerRows: 1, + layout: "headerLineOnly", + widths: ["*"], + }, + }, + { + columnGap: 10, + columns: [ + { + alignment: "left", + stack: infoPrecises, + width: "100%", + }, + ], + margin: [0, 10, 0, 0], + }, + ], + }; +}; diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/Transport.js b/packages/backend/src/services/pdf/parts/Transport.js similarity index 100% rename from packages/backend/src/services/pdf/declaration2mois/parts/Transport.js rename to packages/backend/src/services/pdf/parts/Transport.js diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/TransportHebergement.js b/packages/backend/src/services/pdf/parts/TransportHebergement.js similarity index 81% rename from packages/backend/src/services/pdf/declaration2mois/parts/TransportHebergement.js rename to packages/backend/src/services/pdf/parts/TransportHebergement.js index 1c42f3e11..ed43311d3 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/TransportHebergement.js +++ b/packages/backend/src/services/pdf/parts/TransportHebergement.js @@ -1,5 +1,5 @@ module.exports = function buildFicheAnnexe(hebergement) { - return hebergement.hebergements.map((hebergement) => ({ + return hebergement.hebergements.map((h) => ({ stack: [ { columns: [ @@ -15,7 +15,7 @@ module.exports = function buildFicheAnnexe(hebergement) { { bold: true, decoration: "underline", - text: `Caractéristiques relative à l'hébergement ${hebergement.nom}`, + text: `Caractéristiques relative à l'hébergement ${h.nom}`, width: "100%", }, ], @@ -31,7 +31,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsTransport.vehiculesAdaptes ? "Oui" : "Non"}`, + text: `${h.informationsTransport.vehiculesAdaptes ? "Oui" : "Non"}`, width: "*", }, ], @@ -44,7 +44,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsTransport.deplacementProximite}`, + text: `${h.informationsTransport.deplacementProximite}`, width: "*", }, ], @@ -57,12 +57,12 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsTransport.excursion}`, + text: `${h.informationsTransport.excursion}`, width: "*", }, ], }, - ...(hebergement.informationsTransport.rejoindreEtape + ...(h.informationsTransport.rejoindreEtape ? [ { columns: [ @@ -72,7 +72,7 @@ module.exports = function buildFicheAnnexe(hebergement) { }, { bold: true, - text: `${hebergement.informationsTransport.rejoindreEtape}`, + text: `${h.informationsTransport.rejoindreEtape}`, width: "*", }, ], diff --git a/packages/backend/src/services/pdf/declaration2mois/parts/index.js b/packages/backend/src/services/pdf/parts/index.js similarity index 78% rename from packages/backend/src/services/pdf/declaration2mois/parts/index.js rename to packages/backend/src/services/pdf/parts/index.js index 193b873ab..6af94f4c0 100644 --- a/packages/backend/src/services/pdf/declaration2mois/parts/index.js +++ b/packages/backend/src/services/pdf/parts/index.js @@ -4,10 +4,13 @@ const FicheAnnexe = require("./FicheAnnexe"); const Header = require("./Header"); const InformationsGenerales = require("./InformationsGenerales"); const InformationsPersonnel = require("./InformationsPersonnel"); +const InformationsPersonnel8jours = require("./InformationsPersonnel8jours"); const InformationsSanitaires = require("./InformationsSanitaires"); const InformationsTransport = require("./InformationsTransport"); const InformationsVacanciers = require("./InformationsVacanciers"); const ProjetSejour = require("./ProjetSejour"); +const Prestataire = require("./Prestataire"); +const Personnel = require("./Personnel"); const Titre = require("./Titre"); module.exports = { @@ -17,9 +20,12 @@ module.exports = { Header, InformationsGenerales, InformationsPersonnel, + InformationsPersonnel8jours, InformationsSanitaires, InformationsTransport, InformationsVacanciers, + Personnel, + Prestataire, ProjetSejour, Titre, }; diff --git a/packages/backend/src/services/static/agrements_VAO_non_actifs_05_06_2024.pdf b/packages/backend/src/services/static/agrements_VAO_non_actifs_05_06_2024.pdf new file mode 100644 index 000000000..aea790d12 Binary files /dev/null and b/packages/backend/src/services/static/agrements_VAO_non_actifs_05_06_2024.pdf differ diff --git a/packages/backend/src/services/static/agrements_non_actifs.xlsx b/packages/backend/src/services/static/agrements_non_actifs.xlsx index 0c6a30878..e69de29bb 100644 Binary files a/packages/backend/src/services/static/agrements_non_actifs.xlsx and b/packages/backend/src/services/static/agrements_non_actifs.xlsx differ diff --git a/packages/backend/src/utils/mail.js b/packages/backend/src/utils/mail.js index 1319fc098..ab19cb697 100644 --- a/packages/backend/src/utils/mail.js +++ b/packages/backend/src/utils/mail.js @@ -115,6 +115,46 @@ module.exports = { }, }, declarationSejour: { + sendDeclarationA8joursNotify: ({ + declaration, + destinataires, + cc, + departementSuivi, + departementsSecondaires, + }) => { + log.i("sendDeclarationA8joursNotify - In", { + destinataires, + }); + if (!destinataires) { + const message = `Le paramètre destinataires manque à la requête`; + log.w(`sendDeclarationA8joursNotify - ${message}`); + throw new AppError(message); + } + + const params = { + cc, + from: senderEmail, + html: ` +
Bonjour,
+ +La déclaration de séjour à 8 jours ${declaration.idFonctionnelle} vient d'être déposée sur le portail VAO
+La DDETS du département ${departementSuivi} est en charge de l'instruction de cette déclaration.
+ ${ + departementsSecondaires.length > 0 + ? `Les départements ${departementsSecondaires.join(", ")} sont en charge du contrôle d'au moins un hébergement.
` + : "" + } + `, + replyTo: senderEmail, + subject: `Portail VAO - déclaration à 8 jours déposée : ${declaration.idFonctionnelle}`, + to: destinataires, + }; + log.d("sendDeclarationA8joursNotify post email", { + params, + }); + + return params; + }, sendDeclarationNotify: ({ declaration, destinataires, @@ -278,6 +318,7 @@ module.exports = { log.i("sendEnregistrementA2MoisMail - In", { destinataires, }); + if (!destinataires) { const message = `Le paramètre destinataires manque à la requête`; log.w(`sendEnregistrementA2MoisMail - ${message}`); @@ -289,8 +330,8 @@ module.exports = { html: `Bonjour,
Vous êtes titulaire de l’agrément « Vacances adaptées organisées » délivré le ${dayjs(declaration.organisme.agrement.dateObtention).format("DD/MM/YYYY")} et avez déposé en date du - ${dayjs(declaration.attestation.at).format("DD/MM/YYYY")}, une déclaration pour le séjour « ${declaration.libelle} » que vous organisez du ${dayjs(declaration.hebergement.dateDebut).format("DD/MM/YYYY")} au ${dayjs(declaration.hebergement.dateFin).format("DD/MM/YYYY")}.
-Nous accusons ce jour, le ${dayjs().format("DD/MM/YYYY")}, réception de votre déclaration ${declaration.idFonctionnelle}.<.p> + ${dayjs(declaration.attestation.at).format("DD/MM/YYYY")}, une déclaration pour le séjour « ${declaration.libelle} » que vous organisez du ${dayjs(declaration.dateDebut).format("DD/MM/YYYY")} au ${dayjs(declaration.dateFin).format("DD/MM/YYYY")}.
+Nous accusons ce jour, le ${dayjs().format("DD/MM/YYYY")}, réception de votre déclaration ${declaration.idFonctionnelle}.
Vous devrez, huit jours avant le déroulement de ce séjour, réaliser la déclaration complémentaire prévue à l’article R. 412-14 du code du tourisme.
Cordialement,
L'équipe VAO
@@ -336,6 +377,37 @@ module.exports = { return params; }, + sendAccuseTransmission8jours: ({ cc, declaration, dest }) => { + log.i("sendAccuseTransmission8jours - In", { + cc, + dest, + }); + if (!dest || !declaration) { + const message = `paramètre manquant à la requête`; + log.w(`sendForgottenPassword - ${message}`); + throw new AppError(message); + } + + log.d( + "sendAccuseTransmission8jours - sending sendAccuseTransmission8jours mail", + ); + const params = { + cc: cc, + from: senderEmail, + html: ` +Bonjour,
+Votre déclaration à 8 jours n°${declaration.idFonctionnelle} a bien été transmise au(x) service(s) instructeur(s) le ${dayjs().format("DD/MM/YYYY")}.
+Cordialement,
+L'équipe VAO
+ `, + replyTo: senderEmail, + subject: `Portail VAO - "Transmission de la déclaration de séjour à 8 jours n°${declaration.idFonctionnelle}`, + to: dest, + }; + log.d("sendAccuseTransmission8jours post email", { params }); + + return params; + }, sendRefusMail: ({ destinataires, comment, declaration }) => { log.i("sendRefusMail - In", { destinataires, diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock index 1dfbd5bc9..1c2647897 100644 --- a/packages/backend/yarn.lock +++ b/packages/backend/yarn.lock @@ -662,44 +662,55 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@sentry-internal/tracing@7.109.0": - version "7.109.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.109.0.tgz#3effaa132c41a65378fa98146aea61228d528953" - integrity sha512-PzK/joC5tCuh2R/PRh+7dp+uuZl7pTsBIjPhVZHMTtb9+ls65WkdZJ1/uKXPouyz8NOo9Xok7aEvEo9seongyw== - dependencies: - "@sentry/core" "7.109.0" - "@sentry/types" "7.109.0" - "@sentry/utils" "7.109.0" - -"@sentry/core@7.109.0": - version "7.109.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.109.0.tgz#7a02f4af4a676950f6555f552a2a232d4458fcd5" - integrity sha512-xwD4U0IlvvlE/x/g/W1I8b4Cfb16SsCMmiEuBf6XxvAa3OfWBxKoqLifb3GyrbxMC4LbIIZCN/SvLlnGJPgszA== - dependencies: - "@sentry/types" "7.109.0" - "@sentry/utils" "7.109.0" - -"@sentry/node@~7.109.0": - version "7.109.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.109.0.tgz#dbf152212e42a9b1648ff758ec5bffcb6bb0fa49" - integrity sha512-tqMNAES4X/iBl1eZRCmc29p//0id01FBLEiesNo5nk6ECl6/SaGMFAEwu1gsn90h/Bjgr04slwFOS4cR45V2PQ== - dependencies: - "@sentry-internal/tracing" "7.109.0" - "@sentry/core" "7.109.0" - "@sentry/types" "7.109.0" - "@sentry/utils" "7.109.0" - -"@sentry/types@7.109.0": - version "7.109.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.109.0.tgz#d8778358114ed05be734661cc9e1e261f4494947" - integrity sha512-egCBnDv3YpVFoNzRLdP0soVrxVLCQ+rovREKJ1sw3rA2/MFH9WJ+DZZexsX89yeAFzy1IFsCp7/dEqudusml6g== - -"@sentry/utils@7.109.0": - version "7.109.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.109.0.tgz#7078e1400197abc1b0c436679bef980639500a86" - integrity sha512-3RjxMOLMBwZ5VSiH84+o/3NY2An4Zldjz0EbfEQNRY9yffRiCPJSQiCJID8EoylCFOh/PAhPimBhqbtWJxX6iw== - dependencies: - "@sentry/types" "7.109.0" +"@sentry-internal/tracing@7.117.0": + version "7.117.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.117.0.tgz#c7d2357dae8d7ea2bc130e4513ac4ffc8dc7553c" + integrity sha512-fAIyijNvKBZNA12IcKo+dOYDRTNrzNsdzbm3DP37vJRKVQu19ucqP4Y6InvKokffDP2HZPzFPDoGXYuXkDhUZg== + dependencies: + "@sentry/core" "7.117.0" + "@sentry/types" "7.117.0" + "@sentry/utils" "7.117.0" + +"@sentry/core@7.117.0": + version "7.117.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.117.0.tgz#eebdb6e700d5fbdf3102c4abfb4ff92ef79ae9a5" + integrity sha512-1XZ4/d/DEwnfM2zBMloXDwX+W7s76lGKQMgd8bwgPJZjjEztMJ7X0uopKAGwlQcjn242q+hsCBR6C+fSuI5kvg== + dependencies: + "@sentry/types" "7.117.0" + "@sentry/utils" "7.117.0" + +"@sentry/integrations@7.117.0": + version "7.117.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.117.0.tgz#4613dae3bc1d257c3c870461327fd4f70dbda229" + integrity sha512-U3suSZysmU9EiQqg0ga5CxveAyNbi9IVdsapMDq5EQGNcVDvheXtULs+BOc11WYP3Kw2yWB38VDqLepfc/Fg2g== + dependencies: + "@sentry/core" "7.117.0" + "@sentry/types" "7.117.0" + "@sentry/utils" "7.117.0" + localforage "^1.8.1" + +"@sentry/node@~7.117.0": + version "7.117.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.117.0.tgz#a548e7c2d395e01818814573b5f420eb061ebcf9" + integrity sha512-0MWXdT8dv1MtQGF0aeB8LQTBTJS1L1Vz24+wvdXroR3/52mPYrPWlzuc7+Ew/Dlqdlb5LKVIlkuDSRWj8UKpTQ== + dependencies: + "@sentry-internal/tracing" "7.117.0" + "@sentry/core" "7.117.0" + "@sentry/integrations" "7.117.0" + "@sentry/types" "7.117.0" + "@sentry/utils" "7.117.0" + +"@sentry/types@7.117.0": + version "7.117.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.117.0.tgz#c4d89aba487c05f4e5cbfa2f1c65180b536393b4" + integrity sha512-5dtdulcUttc3F0Te7ekZmpSp/ebt/CA71ELx0uyqVGjWsSAINwskFD77sdcjqvZWek//WjiYX1+GRKlpJ1QqsA== + +"@sentry/utils@7.117.0": + version "7.117.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.117.0.tgz#ac367a6f623bd09440b39d947437009c0ffe52b2" + integrity sha512-KkcLY8643SGBiDyPvMQOubBkwVX5IPknMHInc7jYC8pDVncGp7C65Wi506bCNPpKCWspUd/0VDNWOOen51/qKA== + dependencies: + "@sentry/types" "7.117.0" "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -2611,6 +2622,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -3393,6 +3409,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== + dependencies: + immediate "~3.0.5" + lilconfig@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" @@ -3431,6 +3454,13 @@ listr2@8.0.1: rfdc "^1.3.0" wrap-ansi "^9.0.0" +localforage@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== + dependencies: + lie "3.1.1" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -3963,7 +3993,7 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@^8.11.2, pg@~8.11.3: +pg@^8.11.2: version "8.11.5" resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.5.tgz#e722b0a5f1ed92931c31758ebec3ddf878dd4128" integrity sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw== @@ -3976,6 +4006,19 @@ pg@^8.11.2, pg@~8.11.3: optionalDependencies: pg-cloudflare "^1.1.1" +pg@~8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.12.0.tgz#9341724db571022490b657908f65aee8db91df79" + integrity sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ== + dependencies: + pg-connection-string "^2.6.4" + pg-pool "^3.6.2" + pg-protocol "^1.6.1" + pg-types "^2.1.0" + pgpass "1.x" + optionalDependencies: + pg-cloudflare "^1.1.1" + pgpass@1.x: version "1.0.5" resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" diff --git a/packages/frontend-bo/package.json b/packages/frontend-bo/package.json index ccaa41a4e..b2c300682 100644 --- a/packages/frontend-bo/package.json +++ b/packages/frontend-bo/package.json @@ -13,20 +13,20 @@ }, "dependencies": { "@gouvfr/dsfr": "~1.11.2", - "@gouvminint/vue-dsfr": "~5.13.0", + "@gouvminint/vue-dsfr": "~5.17.0", "@pinia/nuxt": "~0.5.1", - "@sentry/vue": "~7.109.0", + "@sentry/vue": "~7.117.0", "@socialgouv/dsfr-toaster-nuxt-module": "~1.2.1", - "@vee-validate/i18n": "~4.12.6", - "@vee-validate/rules": "~4.12.6", + "@vee-validate/i18n": "~4.13.0", + "@vee-validate/rules": "~4.13.0", "@vueform/multiselect": "~2.6.7", "date-fns": "~3.6.0", "debug": "~4.3.4", - "nuxt": "~3.11.1", + "nuxt": "~3.12.0", "oh-vue-icons": "~1.0.0-rc3", - "nuxt-security": "~1.3.1", + "nuxt-security": "~1.4.0", "pinia": "~2.1.7", - "vee-validate": "~4.12.6", + "vee-validate": "~4.13.0", "vue-matomo": "~4.2.0", "yup": "~1.4.0" }, @@ -37,8 +37,8 @@ "eslint-plugin-prettier": "^5.1.3", "lint-staged": "^15.2.2", "prettier": "^3.2.5", - "sass": "~1.72.0", - "sass-loader": "~14.1.1", + "sass": "~1.77.0", + "sass-loader": "~14.2.0", "typescript": "~5.4.3" }, "lint-staged": { diff --git a/packages/frontend-bo/src/components/demandes-sejour/DemandeStatusBadge.vue b/packages/frontend-bo/src/components/demandes-sejour/DemandeStatusBadge.vue index 99efeeda0..976d3b3a0 100644 --- a/packages/frontend-bo/src/components/demandes-sejour/DemandeStatusBadge.vue +++ b/packages/frontend-bo/src/components/demandes-sejour/DemandeStatusBadge.vue @@ -23,15 +23,18 @@ const props = defineProps({ const type = computed(() => { switch (props.statut) { case demandesSejours.statuts.EN_COURS: + case demandesSejours.statuts.EN_COURS_8J: case demandesSejours.statuts.TRANSMISE: case demandesSejours.statuts.ATTENTE_8_JOUR: case demandesSejours.statuts.TRANSMISE_8J: return "new"; - case demandesSejours.statuts.VALIDEE: + case demandesSejours.statuts.VALIDEE_8J: return "success"; case demandesSejours.statuts.A_MODIFIER: + case demandesSejours.statuts.A_MODIFIER_8J: return "warning"; case demandesSejours.statuts.REFUSEE: + case demandesSejours.statuts.REFUSEE_8J: return "error"; default: return "union"; diff --git a/packages/frontend-bo/src/components/demandes-sejour/DisplayEncadrementAccompagnement.vue b/packages/frontend-bo/src/components/demandes-sejour/DisplayEncadrementAccompagnement.vue new file mode 100644 index 000000000..1f457fae4 --- /dev/null +++ b/packages/frontend-bo/src/components/demandes-sejour/DisplayEncadrementAccompagnement.vue @@ -0,0 +1,55 @@ + +