Corriger la construction de l'image, pour de bon

`pnpm db:generate` échouait avant même le build : `prisma.config.ts` résolvait
DATABASE_URL par `env()`, qui lève dès le **chargement** du fichier de
configuration quand la variable manque. Or ce fichier est lu par toutes les
commandes du CLI, y compris `generate`, qui ne se connecte à rien — la
construction de l'image exigeait donc une base.

Ma vérification précédente ne valait rien : j'avais éprouvé `pnpm build` seul,
pas `pnpm db:generate && pnpm build`, la ligne qui échoue. Cette fois c'est la
commande exacte du Dockerfile qui a été jouée, sans .env, et elle sort en 0.

Corrigé au passage, du même genre que la troncature de l'écran de conservation :
la liste des périodes de paie s'arrête aux 24 plus récentes sans le dire. Une
période créée hors de cette fenêtre semblait ne pas s'être enregistrée. Le total
est désormais rapporté.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
This commit is contained in:
Claude committed 2026-08-09 10:55:36 +00:00
1 parent 006e2db833
commit db43356919
4 files changed
+35 -3

No files matched your search

+12 -2
View File
@@ -1,5 +1,5 @@
import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';
import { defineConfig } from 'prisma/config';
/**
* Prisma 7 moved the migration connection URL out of schema.prisma.
@@ -15,6 +15,16 @@ export default defineConfig({
seed: 'tsx prisma/seed.ts',
},
datasource: {
url: env('DATABASE_URL'),
/*
* Lu directement plutôt que par `env()`, qui échoue dès le **chargement**
* du fichier de configuration quand la variable manque.
*
* Or ce fichier est chargé par toutes les commandes du CLI, y compris
* `prisma generate`, qui ne se connecte à rien : la construction de l'image
* devenait impossible sans fournir une base au constructeur. Les commandes
* qui ont réellement besoin de l'URL — `migrate` — échouent d'elles-mêmes,
* au moment où elle leur manque.
*/
url: process.env.DATABASE_URL ?? '',
},
});
+8
View File
@@ -68,6 +68,14 @@ export default async function PeriodesPage({ searchParams }: PageProps) {
suggestedMonth={view.suggestedMonth}
/>
{view.totalPeriods > view.periods.length ? (
<p className="rounded-3 border border-line-1 bg-surface-2 p-3 text-micro text-ink-3">
{view.totalPeriods} périodes en tout ; les {view.periods.length} plus
récentes sont affichées. Une période plus ancienne reste consultable
par son mois.
</p>
) : null}
{view.periods.length === 0 ? (
<p className="rounded-3 border border-line-1 bg-surface p-4 text-sm text-ink-2">
Aucune période. Verrouiller une période fige les heures transmises au
+11 -1
View File
@@ -39,6 +39,8 @@ export interface PeriodsView {
location: { id: string; name: string };
locations: Array<{ id: string; name: string }>;
periods: PeriodCard[];
/** Total en base : la liste est bornée, et le taire induirait en erreur. */
totalPeriods: number;
/** Mois proposé par défaut à la création. */
suggestedMonth: string;
canLock: boolean;
@@ -62,10 +64,17 @@ export async function getPeriods(locationId?: string): Promise<PeriodsView | nul
locations[0];
if (!location) return null;
// Deux ans de périodes mensuelles : au-delà, la liste cesse d'être
// consultable. Le total est rapporté à côté, car une liste tronquée en
// silence laisse croire qu'une période créée n'a pas été enregistrée.
const PERIOD_PAGE = 24;
const totalPeriods = await db.payPeriod.count({
where: { locationId: location.id },
});
const periods = await db.payPeriod.findMany({
where: { locationId: location.id },
orderBy: { startDate: 'desc' },
take: 24,
take: PERIOD_PAGE,
});
const exports = await db.payrollExport.findMany({
@@ -130,6 +139,7 @@ export async function getPeriods(locationId?: string): Promise<PeriodsView | nul
location,
locations,
periods: cards,
totalPeriods,
suggestedMonth: formatMonthParam(monthOf(new Date())),
canLock: can(actor, 'payroll.period.lock'),
canUnlock: can(actor, 'payroll.period.unlock'),
+4
View File
@@ -111,6 +111,10 @@ test('supprimer une période exige de retaper son libellé', async ({ page }) =>
await create.getByLabel('Libellé').fill(label);
await create.getByRole('button', { name: 'Créer la période' }).click();
// La liste est bornée aux 24 périodes les plus récentes : sans nettoyage, les
// restes d'exécutions interrompues finissent par repousser la nouvelle hors
// de l'écran, et le test échoue pour une raison sans rapport avec ce qu'il
// vérifie.
const card = page.locator('section').filter({ hasText: label }).first();
await expect(card).toBeVisible();