batchCooking/apps/api/src/modules/planning/planning.service.ts
Nicolas 9b2b2c2e28 API: GET /planning?date= remplace GET /planning/current (step 2/4)
- getPlanningForDate(houseId, date: DateTime) — paramétré au lieu de
  toujours "aujourd'hui", même logique de recherche sinon
- GET /planning?date=YYYY-MM-DD, validation de forme (zod) puis de
  validité calendaire (parseDateOnly, 400 VALIDATION_ERROR sinon) —
  un seul endpoint générique au lieu de deux qui se recouvrent
- Tests Mocha + Cucumber adaptés, + cas date manquante/malformée/
  impossible et "semaine différente d'aujourd'hui"
2026-08-17 14:17:46 +02:00

65 lines
2.2 KiB
TypeScript

import { type DateTime, toDateOnly } from "@batch-cooking/date-tools";
import type { PlanningView } from "@batch-cooking/shared";
import { prisma } from "../../db/prisma.js";
/**
* Finds the household's planning that covers `date` and shapes it into a
* {@link PlanningView} (recipes resolved to `{id, name}`). `date` is
* whatever the caller wants "now" to mean — the current `/planning` route
* passes a `date-tools`-parsed `?date=` query param, letting a caller look
* up any week's planning, not just the one covering today.
*
* Returns `null` for two distinct, both entirely normal states — a
* `houseId` of `null` (the profile has no household yet — households are no
* longer created automatically at signup, see `auth.service.ts`) and "no
* planning row covers this date" (the expected case until planning
* creation is built) — neither is an error, so both collapse to the same
* "nothing to show yet" result rather than throwing.
*/
export async function getPlanningForDate(
houseId: number | null,
date: DateTime,
): Promise<PlanningView | null> {
if (houseId === null) {
return null;
}
// `startDate`/`finishDate` are `@db.Date` columns (no time-of-day
// component) — comparing against a UTC-midnight JS `Date` lines up with
// how Postgres stores/returns them, regardless of the server's local
// timezone.
const dateOnly = toDateOnly(date).toJSDate();
const planning = await prisma.planning.findFirst({
where: {
houseId,
startDate: { lte: dateOnly },
finishDate: { gte: dateOnly },
},
// A household should never have two plannings covering the same day,
// but nothing in the schema enforces that yet — pick the most recently
// started one rather than letting the query fail if it ever happens.
orderBy: { startDate: "desc" },
include: {
items: {
include: { recipe: { select: { id: true, name: true } } },
},
},
});
if (!planning) {
return null;
}
return {
id: planning.id,
startDate: planning.startDate.toISOString(),
finishDate: planning.finishDate.toISOString(),
items: planning.items.map((item) => ({
id: item.id,
weekDay: item.weekDay,
meal: item.meal,
recipe: item.recipe,
})),
};
}