diff --git a/.env.dev.example.env b/.env.dev.example.env index 9a7c94a3d..4ba5d5ff6 100644 --- a/.env.dev.example.env +++ b/.env.dev.example.env @@ -47,6 +47,7 @@ BACKEND_URL="http://localhost:3010" BACKEND_CRON_REQUEST_DS8J15J_NOTIFY_CRON="0 22 * * *" BACKEND_CRON_REQUEST_DS8J15J_DEADLINE_REMIND="15" BACKEND_CRON_UPDATE_STATUT_DS_CRON="* * * * *" +BACKEND_CRON_REQUEST_ACTIONS_BO_CRON="0 9,12 * * 1-5" # Tokens TOKEN_SECRET="-----BEGIN EC PRIVATE KEY-----\nMIHcAgEBBEIAobMQpD5H2nAL7LrYLsxTxmE70HB6F3sWeAeq3DXdLQ/5zGFcV36e\nI9VdYyed8fzYq6t+RP42M7fDUzLAKpn46cKgBwYFK4EEACOhgYkDgYYABAFcZNGM\nL+ba9RXURG9yEUKzZsVt19jN+1xbjL1EIRC6IsvZeK58yd/Y924WtpklMSqMI6Fx\nSmGOpELXEe9BICNJHAG1x3KH0SUWN+gKC1mRbriSFd9HhrtRY7AgUdD6TR7H7un0\nxrJnFvr9iC1K+E6linASudSivbUhP8QzHt2k5JsTsQ==\n-----END EC PRIVATE KEY-----" diff --git a/.kontinuous/env/dev/templates/backend.configmap.yaml b/.kontinuous/env/dev/templates/backend.configmap.yaml index 2c511f22f..2f4a2167c 100644 --- a/.kontinuous/env/dev/templates/backend.configmap.yaml +++ b/.kontinuous/env/dev/templates/backend.configmap.yaml @@ -17,3 +17,4 @@ data: BACKEND_CRON_REQUEST_DS8J15J_NOTIFY_CRON: "*/15 * * * *" BACKEND_CRON_REQUEST_DS8J15J_DEADLINE_REMIND: "90" BACKEND_CRON_UPDATE_STATUT_DS_CRON: "*/15 * * * *" + BACKEND_CRON_REQUEST_ACTIONS_BO_CRON: "0 9,12 * * 1-5" \ No newline at end of file diff --git a/.kontinuous/env/preprod/templates/backend.configmap.yaml b/.kontinuous/env/preprod/templates/backend.configmap.yaml index 008bce3f7..e3b110828 100644 --- a/.kontinuous/env/preprod/templates/backend.configmap.yaml +++ b/.kontinuous/env/preprod/templates/backend.configmap.yaml @@ -16,4 +16,5 @@ data: TMP_DIRECTORY: "/tmp" BACKEND_CRON_REQUEST_DS8J15J_NOTIFY_CRON: "*/15 * * * *" BACKEND_CRON_REQUEST_DS8J15J_DEADLINE_REMIND: "90" - BACKEND_CRON_UPDATE_STATUT_DS_CRON: "*/15 * * * *" \ No newline at end of file + BACKEND_CRON_UPDATE_STATUT_DS_CRON: "*/15 * * * *" + BACKEND_CRON_REQUEST_ACTIONS_BO_CRON: "0 9,12 * * 1-5" \ No newline at end of file diff --git a/.kontinuous/env/prod/templates/backend.configmap.yaml b/.kontinuous/env/prod/templates/backend.configmap.yaml index 12357e846..222dde1e0 100644 --- a/.kontinuous/env/prod/templates/backend.configmap.yaml +++ b/.kontinuous/env/prod/templates/backend.configmap.yaml @@ -17,4 +17,5 @@ data: TMP_DIRECTORY: "/tmp" BACKEND_CRON_REQUEST_DS8J15J_NOTIFY_CRON: "0 22 * * *" BACKEND_CRON_REQUEST_DS8J15J_DEADLINE_REMIND: "15" - BACKEND_CRON_UPDATE_STATUT_DS_CRON: "0 02 * * *" \ No newline at end of file + BACKEND_CRON_UPDATE_STATUT_DS_CRON: "0 02 * * *" + BACKEND_CRON_REQUEST_ACTIONS_BO_CRON: "0 8 * * 1" \ No newline at end of file diff --git a/packages/backend/src/config.js b/packages/backend/src/config.js index 40ade5b2a..38c6340a4 100644 --- a/packages/backend/src/config.js +++ b/packages/backend/src/config.js @@ -29,6 +29,10 @@ module.exports = { process.env.BACKEND_CRON_REQUEST_DS8J15J_DEADLINE_REMIND, name: "REQUEST_DS8J15J", }, + notifyactionsbo: { + cron: process.env.BACKEND_CRON_REQUEST_ACTIONS_BO_CRON, + name: "REQUEST_ACTIONS_BO", + }, update: { cron: process.env.BACKEND_CRON_UPDATE_STATUT_DS_CRON, name: "UPDATE_STATUT_DS", diff --git a/packages/backend/src/crons/demandes-sejours/index.js b/packages/backend/src/crons/demandes-sejours/index.js index 559ff2ae0..c8e4c2817 100644 --- a/packages/backend/src/crons/demandes-sejours/index.js +++ b/packages/backend/src/crons/demandes-sejours/index.js @@ -1,2 +1,3 @@ +module.exports.notifyRappelActionsBO = require("./notifyRappelActionsBO").job; module.exports.notifyRappelds8j15j = require("./notifyRappelds8j15j").job; module.exports.updateStatutDS = require("./updateStatutDS").job; diff --git a/packages/backend/src/crons/demandes-sejours/notifyRappelActionsBO.js b/packages/backend/src/crons/demandes-sejours/notifyRappelActionsBO.js new file mode 100644 index 000000000..1fdec5163 --- /dev/null +++ b/packages/backend/src/crons/demandes-sejours/notifyRappelActionsBO.js @@ -0,0 +1,266 @@ +const { CronJob } = require("cron"); +const AppError = require("../../utils/error"); + +const run = require("../run"); +const logger = require("../../utils/logger"); +const pool = require("../../utils/pgpool").getPool(); +const { statuts } = require("../../helpers/ds-statuts"); +const Send = require("../../services/mail").mailService.send; +const sendTemplate = require("../../helpers/mail"); + +const { + senderEmail, + frontUsagersDomain, + frontBODomain, +} = require("../../config"); + +const { notifyRappelActionsBO } = require("."); + +const { name, cron } = require("../../config").crons.request.notifyactionsbo; + +const log = logger(module.filename); + +const generateRappelQuery = ( + statutsArray, + additionalColumns = "", + additionalJoins = "", + additionalGroupBy = "", + additionalOrderBy = "", +) => ` + WITH hebergement_exploded AS ( + SELECT + ds.id, + jsonb_array_elements(ds.hebergement->'hebergements') ->> 'dateDebut' AS date_debut_hebergement, + jsonb_array_elements(ds.hebergement->'hebergements') -> 'coordonnees' -> 'adresse' ->> 'codeInsee' AS code_insee + FROM + front.demande_sejour ds + ) + SELECT + ds.id, + ds.id_fonctionnelle, + ds.date_debut, + ds.statut, + ds.libelle as titre, + TO_CHAR(ds.date_debut, 'DD/MM/YYYY') as date_debut, + use.mail, + ${additionalColumns} + ((ds.date_debut - ((10) * INTERVAL '1 day'))::date <= now()::date) as isalerte, + string_agg(com.label, ', ' ORDER BY he.date_debut_hebergement::date ASC) AS Communes + FROM + hebergement_exploded he + INNER JOIN front.demande_sejour ds ON ds.id = he.id + INNER JOIN geo.communes com ON com.code_insee = he.code_insee AND com.date_fin is null + ${additionalJoins} + WHERE (ds.date_debut)::date>=now()::date + AND ds.statut IN (${statutsArray}) + GROUP BY + ds.id, + ds.id_fonctionnelle, + ds.date_debut, + ds.statut, + ds.libelle, + use.mail, + ${additionalGroupBy} + isalerte + ORDER BY + ${additionalOrderBy} + isalerte DESC, + ds.date_debut ASC; +`; + +const query = { + fetchRappelDSBO: generateRappelQuery( + `'${statuts.TRANSMISE}','${statuts.EN_COURS}','${statuts.TRANSMISE_8J}','${statuts.EN_COURS_8J}'`, + ``, + ` INNER JOIN geo.territoires ter ON ter.code = ds.departement_suivi INNER JOIN back.users use ON ter.code = use.ter_code `, + ``, + `use.mail,`, + ), + fetchRappelDSFUsager: generateRappelQuery( + `'${statuts.ATTENTE_8_JOUR}','${statuts.A_MODIFIER}','${statuts.A_MODIFIER_8J}'`, + `((ds.responsable_sejour::jsonb)->>'email')::text as mailresp, mail || ';' || ((ds.responsable_sejour::jsonb)->>'email')::text as mails,`, + ` INNER JOIN front.user_organisme uso ON uso.org_id = ds.organisme_id INNER JOIN front.users use ON use.id = uso.use_id `, + `mailresp,`, + `mail,`, + ), +}; + +const action = async () => { + log.i(`notifyRappelActionsBO - IN`); + // Création des contenus pour les envoies BackOffice + const { rowCount: countRappelActionsBO, rows: rowsRappelActionsBO } = + await pool.query(query.fetchRappelDSBO); + createContent({ + isBO: true, + rowCount: countRappelActionsBO, + rows: rowsRappelActionsBO, + }); + // Création des contenus pour les envoies FrontUsagers + const { + rowCount: countRappelActionsFUsager, + rows: rowsRappelActionsFUsager, + } = await pool.query(query.fetchRappelDSFUsager); + createContent({ + isBO: false, + rowCount: countRappelActionsFUsager, + rows: rowsRappelActionsFUsager, + }); + + log.i(`notifyRappelds8j15j - DONE`); +}; +async function createContent({ isBO, rowCount, rows }) { + if (rowCount === 0) return; + const mails = getUniqueMails({ isBO, rows }); + for (const mail of mails) { + const emailContent = []; + emailContent.push(...generateInitialEmailContent()); + const listeDsAvecAlerte = filterRowsWithAlert({ isBO, mail, rows }); + emailContent.push(...appendContentForAlert({ listeDsAvecAlerte })); + const listeDsSansAlerte = filterRowsWithoutAlert({ isBO, mail, rows }); + emailContent.push(...appendContentForNonAlert({ listeDsSansAlerte })); + try { + await sendMail({ emailContent, isBO, mail }); + } catch (error) { + log.w(error); + } + } +} + +function getUniqueMails({ isBO, rows }) { + return [ + ...new Set( + rows + .filter((ds) => (isBO ? ds.mail : ds.mails)) + .map((ds) => (isBO ? ds.mail : ds.mails)) + ), + ]; +} + +function generateInitialEmailContent() { + return [ + "
Bonjour,
", + "Vous trouverez ci-dessous la liste des déclarations VAO sur lesquelles une action de votre part est attendue,
", + ]; +} + +function filterRowsWithAlert({ isBO, rows, mail }) { + return rows.filter( + (ds) => ds.isalerte && (isBO ? ds.mail === mail : ds.mails === mail) + ); +} + +function filterRowsWithoutAlert({ isBO, rows, mail }) { + return rows.filter( + (ds) => !ds.isalerte && (isBO ? ds.mail === mail : ds.mails === mail) + ); +} + +function appendContentForAlert({ listeDsAvecAlerte }) { + const newContent = []; + if (listeDsAvecAlerte.length > 0) { + newContent.push( + "DECLARATIONS NECESSITANT UNE ACTION URGENTE DE VOTRE PART dont la date de début de séjour est à moins de 10 jours", + ); + newContent.push("
AUTRES DECLARATIONS DE SEJOUR NECESSITANT UNE ACTION DE VOTRE PART", + ); + newContent.push("
${ds.id_fonctionnelle} - ${ds.communes}
`);
+ content.push(`Statut de la déclaration : ${ds.statut}
`);
+ content.push(`Date de début du séjour : ${ds.date_debut}
Si vous avez des difficultés pour traiter vos déclarations, vous vous rappelons que vous pouvez contacter le support utilisateur.
`, + ); + textDeFin.push( + `De plus, vous avez toujours la possibilité d’annuler des déclarations de séjours qui ne sont plus d’actualité pour garder votre tableau à jour.
`, + ); + } + textDeFin.push( + `Vous pouvez accéder à la liste des déclarations de votre département en cliquant ici`, + ); + + if (!email) { + const message = `Le paramètre email manque à la requête`; + log.w(`sendNotificationMail - ${message}`); + throw new AppError(message); + } + const html = sendTemplate.getBody( + ``, + [ + { + p: [ + ` + ${content} + `, + textDeFin.join("\n"), + ], + type: "p", + }, + ], + "L'équipe du SI VAO