Checkpoint: Les admins voient désormais toutes les factures (tous userId) : getById, update, delete, validateBAP, devalidateBAP, validateBAPBulk, reprocessSelected, regenerateBapPdf, search, exportToExcel, exportToPdf, exportSFTP, bapHistory.delete/regenerate — tous corrigés pour les admins.

This commit is contained in:
Manus
2026-07-13 08:13:49 -04:00
parent a994ec2e61
commit 1951b9b9d1
2 changed files with 25 additions and 16 deletions

View File

@@ -268,11 +268,17 @@ export async function deleteInvoice(id: number) {
await db.delete(invoices).where(eq(invoices.id, id)); await db.delete(invoices).where(eq(invoices.id, id));
} }
export async function searchInvoices(userId: number, query: string): Promise<Invoice[]> { export async function searchInvoices(userId: number | null, query: string): Promise<Invoice[]> {
const db = await getDb(); const db = await getDb();
if (!db) return []; if (!db) return [];
const searchPattern = `%${query}%`; const searchPattern = `%${query}%`;
if (userId === null) {
// Admin : recherche globale sans filtre userId
return db.select().from(invoices)
.where(sql`(${invoices.supplierName} LIKE ${searchPattern} OR ${invoices.invoiceNumber} LIKE ${searchPattern})`)
.orderBy(desc(invoices.createdAt));
}
return db.select().from(invoices) return db.select().from(invoices)
.where( .where(
and( and(

View File

@@ -399,7 +399,7 @@ export const appRouter = router({
.input(z.object({ id: z.number() })) .input(z.object({ id: z.number() }))
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const invoice = await getInvoiceById(input.id); const invoice = await getInvoiceById(input.id);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: "NOT_FOUND" }); throw new TRPCError({ code: "NOT_FOUND" });
} }
return invoice; return invoice;
@@ -425,7 +425,7 @@ export const appRouter = router({
})) }))
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const invoice = await getInvoiceById(input.id); const invoice = await getInvoiceById(input.id);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: "NOT_FOUND" }); throw new TRPCError({ code: "NOT_FOUND" });
} }
@@ -441,7 +441,7 @@ export const appRouter = router({
.input(z.object({ id: z.number() })) .input(z.object({ id: z.number() }))
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const invoice = await getInvoiceById(input.id); const invoice = await getInvoiceById(input.id);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: "NOT_FOUND" }); throw new TRPCError({ code: "NOT_FOUND" });
} }
@@ -453,7 +453,7 @@ export const appRouter = router({
.input(z.object({ id: z.number() })) .input(z.object({ id: z.number() }))
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const invoice = await getInvoiceById(input.id); const invoice = await getInvoiceById(input.id);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: "NOT_FOUND" }); throw new TRPCError({ code: "NOT_FOUND" });
} }
// Vérifier les critères de validation BAP // Vérifier les critères de validation BAP
@@ -666,7 +666,7 @@ export const appRouter = router({
let processed = 0; let processed = 0;
for (const id of input.invoiceIds) { for (const id of input.invoiceIds) {
const invoice = await getInvoiceById(id); const invoice = await getInvoiceById(id);
if (!invoice || invoice.userId !== ctx.user.id) continue; if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) continue;
await updateInvoice(id, { await updateInvoice(id, {
bapValidated: 0, bapValidated: 0,
bapValidatedAt: null, bapValidatedAt: null,
@@ -680,7 +680,7 @@ export const appRouter = router({
// ── Validation BAP en masse ────────────────────────────── // ── Validation BAP en masse ──────────────────────────────
validateBAPBulk: protectedProcedure validateBAPBulk: protectedProcedure
.mutation(async ({ ctx }) => { .mutation(async ({ ctx }) => {
const allInvoices = await getInvoicesByUser(ctx.user.id); const allInvoices = ctx.user.role === 'admin' ? await getAllInvoices() : await getInvoicesByUser(ctx.user.id);
// Filtrer les factures éligibles (non déjà validées) // Filtrer les factures éligibles (non déjà validées)
const eligible = allInvoices.filter((inv: any) => const eligible = allInvoices.filter((inv: any) =>
(inv.qualityScore || 0) >= 100 && (inv.qualityScore || 0) >= 100 &&
@@ -862,7 +862,7 @@ export const appRouter = router({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
// Relance UNIQUEMENT les automatismes (sans re-extraction LLM) // Relance UNIQUEMENT les automatismes (sans re-extraction LLM)
const { applyAutomationRules } = await import("./automationEngine"); const { applyAutomationRules } = await import("./automationEngine");
const allInvoices = await getInvoicesByUser(ctx.user.id); const allInvoices = ctx.user.role === 'admin' ? await getAllInvoices() : await getInvoicesByUser(ctx.user.id);
const selected = allInvoices.filter(inv => input.invoiceIds.includes(inv.id)); const selected = allInvoices.filter(inv => input.invoiceIds.includes(inv.id));
if (selected.length === 0) throw new TRPCError({ code: 'NOT_FOUND', message: 'Aucune facture trouvée' }); if (selected.length === 0) throw new TRPCError({ code: 'NOT_FOUND', message: 'Aucune facture trouvée' });
@@ -893,7 +893,7 @@ export const appRouter = router({
.input(z.object({ invoiceId: z.number() })) .input(z.object({ invoiceId: z.number() }))
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const invoice = await getInvoiceById(input.invoiceId); const invoice = await getInvoiceById(input.invoiceId);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: 'NOT_FOUND', message: 'Facture introuvable' }); throw new TRPCError({ code: 'NOT_FOUND', message: 'Facture introuvable' });
} }
if (!invoice.bapValidated) { if (!invoice.bapValidated) {
@@ -983,7 +983,7 @@ export const appRouter = router({
const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf'); const { url } = await localStoragePut(bapKey, Buffer.from(signedPdfBytes), 'application/pdf');
// Mettre à jour la dernière entrée bapHistory de cette facture avec le nouveau pdfUrl // Mettre à jour la dernière entrée bapHistory de cette facture avec le nouveau pdfUrl
const allEntries = await getBapHistoryByUser(ctx.user.id); const allEntries = ctx.user.role === 'admin' ? await getAllBapHistory() : await getBapHistoryByUser(ctx.user.id);
const latestEntry = allEntries const latestEntry = allEntries
.filter(e => e.invoiceId === input.invoiceId) .filter(e => e.invoiceId === input.invoiceId)
.sort((a, b) => new Date(b.validatedAt).getTime() - new Date(a.validatedAt).getTime())[0]; .sort((a, b) => new Date(b.validatedAt).getTime() - new Date(a.validatedAt).getTime())[0];
@@ -1000,6 +1000,9 @@ export const appRouter = router({
search: protectedProcedure search: protectedProcedure
.input(z.object({ query: z.string() })) .input(z.object({ query: z.string() }))
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
if (ctx.user.role === 'admin') {
return searchInvoices(null, input.query);
}
return searchInvoices(ctx.user.id, input.query); return searchInvoices(ctx.user.id, input.query);
}), }),
@@ -1020,7 +1023,7 @@ export const appRouter = router({
delete: protectedProcedure delete: protectedProcedure
.input(z.object({ id: z.number() })) .input(z.object({ id: z.number() }))
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const entries = await getBapHistoryByUser(ctx.user.id); const entries = ctx.user.role === 'admin' ? await getAllBapHistory() : await getBapHistoryByUser(ctx.user.id);
const entry = entries.find(e => e.id === input.id); const entry = entries.find(e => e.id === input.id);
if (!entry) throw new TRPCError({ code: 'NOT_FOUND' }); if (!entry) throw new TRPCError({ code: 'NOT_FOUND' });
await deleteBapHistoryEntry(input.id); await deleteBapHistoryEntry(input.id);
@@ -1031,12 +1034,12 @@ export const appRouter = router({
regenerate: protectedProcedure regenerate: protectedProcedure
.input(z.object({ id: z.number() })) .input(z.object({ id: z.number() }))
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const entries = await getBapHistoryByUser(ctx.user.id); const entries = ctx.user.role === 'admin' ? await getAllBapHistory() : await getBapHistoryByUser(ctx.user.id);
const entry = entries.find(e => e.id === input.id); const entry = entries.find(e => e.id === input.id);
if (!entry) throw new TRPCError({ code: 'NOT_FOUND' }); if (!entry) throw new TRPCError({ code: 'NOT_FOUND' });
const invoice = await getInvoiceById(entry.invoiceId); const invoice = await getInvoiceById(entry.invoiceId);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
throw new TRPCError({ code: 'NOT_FOUND', message: 'Facture source introuvable' }); throw new TRPCError({ code: 'NOT_FOUND', message: 'Facture source introuvable' });
} }
@@ -1250,7 +1253,7 @@ export const appRouter = router({
); );
const validInvoices = invoices.filter( const validInvoices = invoices.filter(
inv => inv && inv.userId === ctx.user.id inv => inv && (ctx.user.role === 'admin' || inv.userId === ctx.user.id)
); );
// Return invoice data for Excel generation on client side // Return invoice data for Excel generation on client side
@@ -1301,7 +1304,7 @@ export const appRouter = router({
); );
const invalidInvoices = invoices.filter( const invalidInvoices = invoices.filter(
inv => !inv || inv.userId !== ctx.user.id || (inv.qualityScore || 0) < 100 inv => !inv || (ctx.user.role !== 'admin' && inv.userId !== ctx.user.id) || (inv.qualityScore || 0) < 100
); );
if (invalidInvoices.length > 0) { if (invalidInvoices.length > 0) {
@@ -1560,7 +1563,7 @@ export const appRouter = router({
for (const invoiceId of input.invoiceIds) { for (const invoiceId of input.invoiceIds) {
try { try {
const invoice = await getInvoiceById(invoiceId); const invoice = await getInvoiceById(invoiceId);
if (!invoice || invoice.userId !== ctx.user.id) { if (!invoice || (ctx.user.role !== 'admin' && invoice.userId !== ctx.user.id)) {
errorCount++; errorCount++;
continue; continue;
} }