Preuve · code, extrait

backend/flexylead-manage-stats-periods.mjs

Dépôt jeremy-flexylead/flexylead-crm · commit ccbbaa07870e0f786729003dd6f8862e4bcc8256 · dossier de production synchronisé le 14 septembre 2026 à 08 h 46

Extrait tel qu'à ce commit : lignes 167–230, sur 354 (numérotation d'origine, 30 lignes de contexte autour de chaque citation) · fichier intégral sur GitHub (dépôt privé, accès requis)

Lignes citées par l'audit : 197–200

64 lignes reproduites · ← retour à l'audit · catalogue des preuves

167
168export function validatedInput(body) {
169  const label = clean(body?.label);
170  if (label.length < 2 || label.length > 120) throw new Error("Libellé de période invalide.");
171  const scopeType = clean(body?.scope_type).toLowerCase() || "global";
172  if (!["global", "centre"].includes(scopeType)) throw new Error("Périmètre de période invalide.");
173  const scopeId = scopeType === "centre" ? clean(body?.scope_id) : null;
174  if (scopeType === "centre" && !scopeId) throw new Error("Centre requis pour cette période.");
175  const startsAt = validIso(body?.starts_at);
176  const endsAt = validIso(body?.ends_at);
177  if (!startsAt || !endsAt || Date.parse(startsAt) >= Date.parse(endsAt)) {
178    throw new Error("Les dates de période sont invalides.");
179  }
180  const timezone = clean(body?.timezone) || "Europe/Paris";
181  assertTimeZone(timezone);
182  return {
183    label,
184    scope_type: scopeType,
185    scope_id: scopeId,
186    starts_at: startsAt,
187    ends_at: endsAt,
188    timezone,
189  };
190}
191
192async function assertNoOverlap(context, input, excludedId = 0) {
193  const found = await context.call("tables", "rows_search", {
194    table: TABLE,
195    where: [
196      { col: "scope_type", op: "eq", value: input.scope_type },
197      { col: "active", op: "eq", value: true },
198    ],
199    order_by: "id asc",
200    limit: 1000,
201  });
202  const conflicting = (found?.rows || [])
203    .filter((row) => Number(row.id) !== excludedId)
204    .filter((row) => clean(row.scope_id) === clean(input.scope_id))
205    .find((row) => rangesOverlap(row, input));
206  if (conflicting) {
207    throw new Error(`Cette période chevauche « ${clean(conflicting.label) || clean(conflicting.local_id)} ».`);
208  }
209}
210
211export function rangesOverlap(a, b) {
212  return Date.parse(a.starts_at) <= Date.parse(b.ends_at)
213    && Date.parse(b.starts_at) <= Date.parse(a.ends_at);
214}
215
216function containsTime(period, value) {
217  return Date.parse(period.starts_at) <= value && value <= Date.parse(period.ends_at);
218}
219
220async function findPeriod(context, localId) {
221  const result = await context.call("tables", "rows_search", {
222    table: TABLE,
223    where: [{ col: "local_id", op: "eq", value: localId }],
224    order_by: "id desc",
225    limit: 1,
226  });
227  return result?.rows?.[0] || null;
228}
229
230async function getRow(context, id) {