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:
@@ -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",
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user