fix: relancer les sources vides après purge

This commit is contained in:
Manus
2026-10-02 20:46:21 +00:00
parent b09dccb078
commit 70f867d62b
5 changed files with 118 additions and 36 deletions

View File

@@ -30,6 +30,7 @@ import {
createSourceFile,
getSourceFileByContentHash,
getSourceFileById,
countInvoicesBySourceFileId,
updateSourceFile,
getUserSettings,
upsertUserSettings,
@@ -102,7 +103,8 @@ import fsSync from "fs";
import pathSync from "path";
import { extractInvoicesWithMistral, generateMetadataJSON } from "./invoiceExtractor";
import { calculateFileSha256 } from "./fileFingerprint";
import { localStoragePut, generateStorageKey } from "./localStorage";
import { localStoragePut, localStorageExists, generateStorageKey } from "./localStorage";
import { shouldReprocessEmptySource } from "@shared/sourceFileReimport";
import { testReferentialIaConnection } from "./referentialIa";
import { testSftpConnection, exportInvoiceToSftp, getUserSftpConfig } from "./sftpExport";
import { drawBapCartouche } from "./bapCartouche";
@@ -206,37 +208,62 @@ export const appRouter = router({
// 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);
let sourceFile;
let sourceFileKey: string;
let sourceFileUrl: string;
let reprocessed = false;
if (existingSource) {
throw new TRPCError({
code: "CONFLICT",
message: `Ce PDF a déjà été importé (${existingSource.fileName}).`,
const invoiceCount = await countInvoicesBySourceFileId(existingSource.id);
if (!shouldReprocessEmptySource({
sourceUserId: existingSource.userId,
requestingUserId: userId,
invoiceCount,
})) {
throw new TRPCError({
code: "CONFLICT",
message: `Ce PDF a déjà été importé (${existingSource.fileName}).`,
});
}
// Une purge peut supprimer les factures tout en conservant la source et son empreinte.
// On relance alors l'analyse depuis la source existante plutôt que de contourner l'anti-doublon.
sourceFile = existingSource;
sourceFileKey = existingSource.fileKey;
sourceFileUrl = existingSource.fileUrl;
reprocessed = true;
if (!await localStorageExists(sourceFileKey)) {
await localStoragePut(sourceFileKey, fileBuffer, "application/pdf");
}
await updateSourceFile(sourceFile.id, {
processingStatus: "processing",
totalInvoicesDetected: 0,
processingProgress: "Nouvelle analyse après purge...",
});
} else {
// Store source file
sourceFileKey = generateStorageKey(userId, input.fileName);
console.log(`[Upload] Generated storage key: ${sourceFileKey}`);
try {
const result = await localStoragePut(sourceFileKey, fileBuffer, "application/pdf");
sourceFileUrl = result.url;
console.log(`[Upload] File stored successfully at: ${sourceFileUrl}`);
} catch (error) {
console.error(`[Upload] FAILED to store file:`, error);
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to store PDF file" });
}
sourceFile = await createSourceFile({
userId,
fileName: input.fileName,
fileKey: sourceFileKey,
fileUrl: sourceFileUrl,
contentHash,
processingStatus: "processing",
});
}
// Store source file
const sourceFileKey = generateStorageKey(userId, input.fileName);
console.log(`[Upload] Generated storage key: ${sourceFileKey}`);
let sourceFileUrl: string;
try {
const result = await localStoragePut(sourceFileKey, fileBuffer, "application/pdf");
sourceFileUrl = result.url;
console.log(`[Upload] File stored successfully at: ${sourceFileUrl}`);
} catch (error) {
console.error(`[Upload] FAILED to store file:`, error);
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Failed to store PDF file" });
}
// Create source file record
const sourceFile = await createSourceFile({
userId,
fileName: input.fileName,
fileKey: sourceFileKey,
fileUrl: sourceFileUrl,
contentHash,
processingStatus: "processing",
});
// Start extraction process (async - don't wait)
(async () => {
try {
@@ -439,7 +466,7 @@ export const appRouter = router({
}
})();
return { sourceFileId: sourceFile.id };
return { sourceFileId: sourceFile.id, reprocessed };
}),
list: protectedProcedure.query(async ({ ctx }) => {