feat: ajouter les factures litiges et leurs commentaires (recette)
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 2m14s

This commit is contained in:
Manus
2026-11-03 14:45:05 +00:00
parent 5f6c61d2f0
commit a6d90a90c3
16 changed files with 3320 additions and 13 deletions

31
server/invoiceDisputes.ts Normal file
View File

@@ -0,0 +1,31 @@
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { protectedProcedure } from "./_core/trpc";
import { getDisputedInvoices, getInvoiceById, updateInvoice } from "./db";
/** Le litige ne modifie ni le score, ni la validation BAP, ni l’export. */
export const disputeInput = z.object({
id: z.number().int().positive(),
isDisputed: z.boolean(),
comment: z.string().max(5000, "Le commentaire est limité à 5 000 caractères.").nullable().optional(),
});
export const listDisputes = protectedProcedure.query(({ ctx }) =>
getDisputedInvoices(ctx.user.role === "admin" ? undefined : ctx.user.id),
);
export const setDispute = protectedProcedure.input(disputeInput).mutation(async ({ ctx, input }) => {
if (String(ctx.user.role) === "readonly") {
throw new TRPCError({ code: "FORBIDDEN", message: "Votre profil permet uniquement la consultation." });
}
const invoice = await getInvoiceById(input.id);
if (!invoice || (ctx.user.role !== "admin" && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: "NOT_FOUND", message: "Facture introuvable." });
}
await updateInvoice(input.id, {
isDisputed: input.isDisputed ? 1 : 0,
// L’absence du champ conserve le commentaire ; null ou une chaîne vide l’efface.
...(input.comment !== undefined ? { disputeComment: input.comment?.trim() || null } : {}),
});
return { success: true } as const;
});