fix: recalculer le score après correction manuelle

This commit is contained in:
Manus
2026-09-30 10:19:33 +00:00
committed by manus-admin
parent d85c975fb1
commit daa5fb8463
4 changed files with 111 additions and 14 deletions

View File

@@ -0,0 +1,57 @@
import { describe, expect, it } from "vitest";
import { calculateInvoiceQualityScore } from "@shared/invoiceQuality";
const defaultRequiredFields = [
{ fieldName: "supplierName", isRequired: 1 },
{ fieldName: "invoiceNumber", isRequired: 1 },
{ fieldName: "invoiceDate", isRequired: 1 },
{ fieldName: "totalAmount", isRequired: 1 },
{ fieldName: "deliveryNoteNumber", isRequired: 0 },
];
describe("calculateInvoiceQualityScore", () => {
it("passe à 100 % lorsque tous les champs obligatoires sont complétés manuellement", () => {
expect(calculateInvoiceQualityScore({
supplierName: "Microsoft France",
invoiceNumber: "FAC-2026-001",
invoiceDate: new Date("2026-09-30"),
totalAmount: "0.00",
}, defaultRequiredFields)).toBe(100);
});
it("recalcule proportionnellement lorsque seuls certains champs obligatoires sont remplis", () => {
expect(calculateInvoiceQualityScore({
supplierName: "Microsoft France",
invoiceNumber: "FAC-2026-001",
invoiceDate: new Date("2026-09-30"),
totalAmount: " ",
}, defaultRequiredFields)).toBe(75);
});
it("utilise la configuration personnalisée des champs obligatoires", () => {
const configuration = [
{ fieldName: "recipientName", isRequired: 1 },
{ fieldName: "supplierName", isRequired: 0 },
];
expect(calculateInvoiceQualityScore({
recipientName: "ITINOVA",
supplierName: "",
}, configuration)).toBe(100);
});
it("utilise les champs obligatoires par défaut lorsqu’aucune configuration n’existe encore", () => {
expect(calculateInvoiceQualityScore({
supplierName: "Fournisseur",
invoiceNumber: "F-1",
invoiceDate: new Date("2026-09-30"),
totalAmount: "120.00",
}, [])).toBe(100);
});
it("retourne 100 % lorsque le compte a explicitement rendu tous ses champs optionnels", () => {
expect(calculateInvoiceQualityScore({}, [
{ fieldName: "supplierName", isRequired: 0 },
])).toBe(100);
});
});

View File

@@ -1,6 +1,7 @@
import { z } from "zod";
import { COOKIE_NAME } from "@shared/const";
import { BAP_MIN_QUALITY_SCORE, meetsBapQualityThreshold } from "@shared/bapEligibility";
import { calculateInvoiceQualityScore } from "@shared/invoiceQuality";
interface Condition {
field: string;
@@ -476,13 +477,23 @@ export const appRouter = router({
if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: "NOT_FOUND" });
}
// Le score reflète toujours les valeurs actuellement enregistrées, y compris
// celles corrigées manuellement. La configuration est celle du propriétaire
// de la facture afin de conserver la même règle que lors de son extraction.
const fieldsConfiguration = await getLlmFieldsConfigByUser(invoice.userId);
const qualityScore = calculateInvoiceQualityScore(
{ ...invoice, ...input.data },
fieldsConfiguration,
);
await updateInvoice(input.id, {
...input.data,
qualityScore,
manuallyEdited: 1,
});
return { success: true };
return { success: true, qualityScore };
}),
delete: protectedProcedure