Skip to content

Commit fa98823

Browse files
committed
feat(api): implement check and update for inactif confirme candidacies
- add cron job to check and update inactive candidacies to INACTIF_CONFIRME status - create archiveCandidacies function to handle archiving of candidacies - add new types for archiving candidacies in candidacy.types.ts - implement checkAndUpdateCandidaciesInactifConfirme function for batch processing
1 parent 4169fc1 commit fa98823

File tree

4 files changed

+199
-0
lines changed

4 files changed

+199
-0
lines changed

packages/reva-api/infra/cron.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import path from "path";
55
import { CronJob } from "cron";
66
import dotenv from "dotenv";
77

8+
import { checkAndUpdateCandidaciesInactifConfirme } from "@/modules/candidacy/features/checkAndUpdateCandidaciesInactifConfirme";
89
import { checkAndUpdateCandidaciesInactifEnAttente } from "@/modules/candidacy/features/checkAndUpdateCandidaciesInactifEnAttente";
910

1011
import { deleteExpiredCandidacies } from "../modules/candidacy/features/deleteExpiredCandidacies";
@@ -24,6 +25,7 @@ dotenv.config({ path: path.join(process.cwd(), "..", "..", ".env") });
2425

2526
const EVERY_DAY_AT_1_AM = "0 1 * * *";
2627
const EVERY_DAY_AT_1_30_AM = "30 1 * * *";
28+
const EVERY_DAY_AT_1_45_AM = "45 1 * * *";
2729
const EVERY_DAY_AT_2_AM = "0 2 * * *";
2830
const EVERY_DAY_AT_3_AM = "0 3 * * *";
2931

@@ -222,4 +224,23 @@ CronJob.from({
222224
timeZone: "Europe/Paris",
223225
});
224226

227+
// Vérification de l'activité des candidatures inactives en attente
228+
// Si la candidature est inactive depuis 15 jours, on la met à jour en INACTIF_CONFIRME
229+
// Mise à jour de l'activité des candidatures du statut INACTIF_EN_ATTENTE vers INACTIF_CONFIRME
230+
CronJob.from({
231+
cronTime:
232+
process.env.BATCH_UPDATE_CANDIDACIES_INACTIF_CONFIRME_CRONTIME ||
233+
EVERY_DAY_AT_1_45_AM,
234+
onTick: () =>
235+
runBatchIfActive({
236+
batchKey: "batch.update-candidacies-inactif-confirme",
237+
batchCallback: async () => {
238+
logger.info("Running batch.update-candidacies-inactif-confirme");
239+
await checkAndUpdateCandidaciesInactifConfirme();
240+
},
241+
}),
242+
start: true,
243+
timeZone: "Europe/Paris",
244+
});
245+
225246
logger.info(`Started cron jobs with APP_ENV = "${process.env.APP_ENV}"`);

packages/reva-api/modules/candidacy/candidacy.types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,9 @@ export interface ArchiveCandidacyParams {
109109
archivingReason: CandidacyArchivingReason;
110110
archivingReasonAdditionalInformation?: string | null;
111111
}
112+
113+
export interface ArchiveCandidaciesParams {
114+
candidacyIds: string[];
115+
archivingReason: CandidacyArchivingReason;
116+
archivingReasonAdditionalInformation?: string | null;
117+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { logger } from "@/modules/shared/logger";
2+
import { prismaClient } from "@/prisma/client";
3+
4+
import { ArchiveCandidaciesParams } from "../candidacy.types";
5+
6+
export const archiveCandidacies = async (params: ArchiveCandidaciesParams) => {
7+
const candidaciesToArchive = await prismaClient.candidacy.findMany({
8+
where: {
9+
id: {
10+
in: params.candidacyIds,
11+
},
12+
status: {
13+
not: "ARCHIVE",
14+
},
15+
},
16+
select: {
17+
id: true,
18+
},
19+
});
20+
21+
if (candidaciesToArchive.length === 0) {
22+
return [];
23+
}
24+
25+
const candidaciesToArchiveIds = candidaciesToArchive.map(
26+
(candidacy) => candidacy.id,
27+
);
28+
29+
try {
30+
return prismaClient.$transaction(async (tx) => {
31+
await tx.candidacy.updateMany({
32+
where: {
33+
id: {
34+
in: candidaciesToArchiveIds,
35+
},
36+
},
37+
data: {
38+
status: "ARCHIVE",
39+
archivingReason: params.archivingReason,
40+
archivingReasonAdditionalInformation:
41+
params.archivingReasonAdditionalInformation || null,
42+
},
43+
});
44+
45+
await tx.candidaciesStatus.createMany({
46+
data: candidaciesToArchive.map((candidacy) => ({
47+
candidacyId: candidacy.id,
48+
status: "ARCHIVE",
49+
})),
50+
});
51+
});
52+
} catch (e) {
53+
logger.error(e);
54+
throw new Error(
55+
`Erreur lors de l'archivage des candidatures ${params.candidacyIds.join(", ")}`,
56+
);
57+
}
58+
};
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { ActiviteStatut } from "@prisma/client";
2+
import { subDays } from "date-fns";
3+
4+
import { logger } from "@/modules/shared/logger";
5+
import { prismaClient } from "@/prisma/client";
6+
7+
import {
8+
INACTIF_CONFIRME_TRIGGER_DAYS,
9+
INACTIF_TRIGGER_AFTER_FEASIBILITY_CANDIDACY_STATUSES,
10+
INACTIF_TRIGGER_BEFORE_FEASIBILITY_CANDIDACY_STATUSES,
11+
} from "../constants/candidacy-inactif.constant";
12+
13+
import { archiveCandidacies } from "./archiveCandidacies";
14+
import { dropOutCandidacies } from "./dropOutCandidacies";
15+
16+
export const checkAndUpdateCandidaciesInactifConfirme = async () => {
17+
const thresholdDateInactifConfirme = subDays(
18+
new Date(),
19+
INACTIF_CONFIRME_TRIGGER_DAYS,
20+
);
21+
22+
try {
23+
// Candidatures inactives en attente avant d'avoir un dossier de faisabilité admissible
24+
const candidaciesInactifEnAttenteBeforeFeasibilityAdmissibleToArchive =
25+
await prismaClient.candidacy.findMany({
26+
where: {
27+
activite: ActiviteStatut.INACTIF_EN_ATTENTE,
28+
status: {
29+
in: INACTIF_TRIGGER_BEFORE_FEASIBILITY_CANDIDACY_STATUSES,
30+
},
31+
dateInactifEnAttente: {
32+
lte: thresholdDateInactifConfirme,
33+
},
34+
candidacyDropOut: {
35+
is: null,
36+
},
37+
},
38+
select: {
39+
id: true,
40+
},
41+
});
42+
43+
const candidaciesToArchiveIds =
44+
candidaciesInactifEnAttenteBeforeFeasibilityAdmissibleToArchive.map(
45+
({ id }) => id,
46+
);
47+
if (candidaciesToArchiveIds.length) {
48+
await archiveCandidacies({
49+
candidacyIds: candidaciesToArchiveIds,
50+
archivingReason: "INACTIVITE_CANDIDAT",
51+
});
52+
await prismaClient.candidacy.updateMany({
53+
where: {
54+
id: {
55+
in: candidaciesToArchiveIds,
56+
},
57+
},
58+
data: {
59+
activite: ActiviteStatut.INACTIF_CONFIRME,
60+
},
61+
});
62+
}
63+
64+
// Candidatures inactives en attente après avoir un dossier de faisabilité admissible
65+
const candidaciesInactifEnAttenteAfterFeasibilityAdmissibleToDropOut =
66+
await prismaClient.candidacy.findMany({
67+
where: {
68+
activite: ActiviteStatut.INACTIF_EN_ATTENTE,
69+
status: {
70+
in: INACTIF_TRIGGER_AFTER_FEASIBILITY_CANDIDACY_STATUSES,
71+
},
72+
dateInactifEnAttente: {
73+
lte: thresholdDateInactifConfirme,
74+
},
75+
candidacyDropOut: {
76+
is: null,
77+
},
78+
},
79+
select: {
80+
id: true,
81+
},
82+
});
83+
84+
const candidacyToDropOutIds =
85+
candidaciesInactifEnAttenteAfterFeasibilityAdmissibleToDropOut.map(
86+
({ id }) => id,
87+
);
88+
89+
if (candidacyToDropOutIds.length) {
90+
const dropOutReasonId = await prismaClient.dropOutReason.findFirst({
91+
where: {
92+
label: "Inactivité depuis 6 mois",
93+
},
94+
});
95+
await dropOutCandidacies({
96+
candidacyIds: candidacyToDropOutIds,
97+
dropOutReasonId: dropOutReasonId?.id || "",
98+
});
99+
100+
await prismaClient.candidacy.updateMany({
101+
where: {
102+
id: {
103+
in: candidacyToDropOutIds,
104+
},
105+
},
106+
data: {
107+
activite: ActiviteStatut.INACTIF_CONFIRME,
108+
},
109+
});
110+
}
111+
} catch (error) {
112+
logger.error(error);
113+
}
114+
};

0 commit comments

Comments
 (0)