Checkpoint: Correction robuste des doublons de factures : empreinte SHA-256 unique des PDF sources, détection globale avant import manuel/email/dossier/web, verrou contre les vérifications IMAP concurrentes, attente réelle de fin de traitement avant marquage lu, migration et tests de non-régression.

This commit is contained in:
Manus
2026-08-22 10:31:54 +00:00
parent d8b2a8fe6f
commit d729a94a96
12 changed files with 2637 additions and 82 deletions

View File

@@ -24,6 +24,7 @@ import {
searchInvoices,
getInvoiceStats,
createSourceFile,
getSourceFileByContentHash,
getSourceFileById,
updateSourceFile,
getUserSettings,
@@ -87,6 +88,7 @@ import { loginLocal, hashPassword, getAzureAuthUrl, isAzureAdConfigured } from "
import fsSync from "fs";
import pathSync from "path";
import { extractInvoicesWithMistral, generateMetadataJSON } from "./invoiceExtractor";
import { calculateFileSha256 } from "./fileFingerprint";
import { localStoragePut, generateStorageKey } from "./localStorage";
import { testSftpConnection, exportInvoiceToSftp, getUserSftpConfig } from "./sftpExport";
import { drawBapCartouche } from "./bapCartouche";
@@ -181,6 +183,17 @@ export const appRouter = router({
// Decode base64 file data
const fileBuffer = Buffer.from(input.fileData, "base64");
console.log(`[Upload] Received file: ${input.fileName}, size: ${fileBuffer.length} bytes`);
// Le contrôle sur les octets du PDF intervient avant le stockage et l'appel IA.
// Il reste fiable même si le nom du fichier ou le compte utilisateur diffère.
const contentHash = calculateFileSha256(fileBuffer);
const existingSource = await getSourceFileByContentHash(contentHash);
if (existingSource) {
throw new TRPCError({
code: "CONFLICT",
message: `Ce PDF a déjà été importé (${existingSource.fileName}).`,
});
}
// Store source file
const sourceFileKey = generateStorageKey(userId, input.fileName);
@@ -202,6 +215,7 @@ export const appRouter = router({
fileName: input.fileName,
fileKey: sourceFileKey,
fileUrl: sourceFileUrl,
contentHash,
processingStatus: "processing",
});