betagouv/service-national-universel

View on GitHub
api/src/crons/missionOutdated.js

Summary

Maintainability
A
0 mins
Test Coverage
const path = require("path");
const { capture } = require("../sentry");
const { MissionModel, ReferentModel, ApplicationModel, YoungModel } = require("../models");
const { sendTemplate } = require("../brevo");
const slack = require("../slack");
const { SENDINBLUE_TEMPLATES, APPLICATION_STATUS } = require("snu-lib");
const config = require("config");
const { logger } = require("../logger");
const { getCcOfYoung } = require("../utils");
const fileName = path.basename(__filename, ".js");

const clean = async () => {
  let countAutoArchived = 0;
  const cursor = await MissionModel.find({ endAt: { $lt: Date.now() }, status: "VALIDATED" })
    .cursor()
    .addCursorFlag("noCursorTimeout", true);
  await cursor.eachAsync(async function (mission) {
    countAutoArchived++;
    logger.info(`${mission._id} ${mission.name} archived.`);
    mission.set({ status: "ARCHIVED" });
    await mission.save({ fromUser: { firstName: `Cron ${fileName}` } });
    await cancelApplications(mission);

    // notify structure
    if (mission.tutorId) {
      const responsible = await ReferentModel.findById(mission.tutorId);
      if (responsible)
        await sendTemplate(SENDINBLUE_TEMPLATES.referent.MISSION_ARCHIVED, {
          emailTo: [{ name: `${responsible.firstName} ${responsible.lastName}`, email: responsible.email }],
          params: {
            missionName: mission.name,
            cta: `${config.ADMIN_URL}/mission/${mission._id}/youngs`,
          },
        });
    }
  });
  slack.success({ title: "outdated mission", text: `${countAutoArchived} missions has been archived !` });
};

const notify1Week = async () => {
  let countNotice = 0;
  const now = Date.now();
  const cursor = await MissionModel.find({ endAt: { $lt: addDays(now, 8), $gte: addDays(now, 7) }, status: "VALIDATED" })
    .cursor()
    .addCursorFlag("noCursorTimeout", true);
  await cursor.eachAsync(async function (mission) {
    countNotice++;
    logger.info(`${mission._id} ${mission.name} : 1 week notice.`);
    logger.info(`${mission._id} ${mission.name} : endAt ${mission.endAt}`);

    // notify structure
    if (mission.tutorId) {
      const responsible = await ReferentModel.findById(mission.tutorId);
      if (responsible)
        await sendTemplate(SENDINBLUE_TEMPLATES.referent.MISSION_ARCHIVED_1_WEEK_NOTICE, {
          emailTo: [{ name: `${responsible.firstName} ${responsible.lastName}`, email: responsible.email }],
          params: {
            missionName: mission.name,
            ctaMission: `${config.ADMIN_URL}/mission/${mission._id}`,
            ctaYoungMission: `${config.ADMIN_URL}/mission/${mission._id}/youngs`,
          },
        });
    }
  });
  slack.success({ title: "1 week notice outdated mission", text: `${countNotice} missions has been noticed !` });
};

const cancelApplications = async (mission) => {
  const applications = await ApplicationModel.find({
    missionId: mission._id,
    status: {
      $in: [
        APPLICATION_STATUS.WAITING_VALIDATION,
        APPLICATION_STATUS.WAITING_ACCEPTATION,
        APPLICATION_STATUS.WAITING_VERIFICATION,
        // todo maybe add other status later.
      ],
    },
  });
  for (let application of applications) {
    let statusComment = "La mission a été archivée.";
    let sendinblueTemplate = SENDINBLUE_TEMPLATES.young.MISSION_ARCHIVED_AUTO;

    application.set({ status: APPLICATION_STATUS.CANCEL, statusComment });
    await application.save({ fromUser: { firstName: `Cron ${fileName}` } });

    const young = await YoungModel.findById(application.youngId);
    if (young) {
      const applications = await ApplicationModel.find({ youngId: young._id.toString() });
      young.set({ phase2ApplicationStatus: applications.map((e) => e.status) });
      await young.save({ fromUser: { firstName: `Cron ${fileName}` } });
    }

    if (sendinblueTemplate) {
      const young = await YoungModel.findById(application.youngId);
      let cc = getCcOfYoung({ template: sendinblueTemplate, young });

      await sendTemplate(sendinblueTemplate, {
        emailTo: [{ name: `${application.youngFirstName} ${application.youngLastName}`, email: application.youngEmail }],
        params: {
          cta: `${config.APP_URL}/phase2`,
          missionName: mission.name,
          message: mission.statusComment,
        },
        cc,
      });
    }
  }
};

exports.handler = async () => {
  // slack.info({ title: "outdated mission", text: "I'm checking if there is any outdated mission in our database !" });
  try {
    clean();
  } catch (e) {
    capture(`ERROR`, JSON.stringify(e));
    capture(e);
    slack.error({ title: "outdated mission", text: JSON.stringify(e) });
    throw e;
  }
};

exports.handlerNotice1Week = async () => {
  // slack.info({ title: "1 week notice outdated mission", text: "I'm checking if there is any mission in our database that will be expired in 1 week !" });
  try {
    notify1Week();
  } catch (e) {
    capture(e);
    slack.error({ title: "1 week notice outdated mission", text: JSON.stringify(e) });
    throw e;
  }
};

const addDays = (d, days = 1) => {
  var date = new Date(d);
  date.setDate(date.getDate() + days);
  date.setUTCHours(0, 0, 0, 0);
  return date;
};