feat: les admins voient toutes les factures/logs/historique BAP, colonne Utilisateur dans Factures

This commit is contained in:
Manus
2026-05-03 10:56:20 -04:00
parent 973cc67603
commit 37cc9442da
4 changed files with 52 additions and 0 deletions

View File

@@ -1,5 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import DashboardLayout from "@/components/DashboardLayout"; import DashboardLayout from "@/components/DashboardLayout";
import { useAuth } from "@/_core/hooks/useAuth";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -52,9 +53,12 @@ export default function Invoices() {
const [pendingInvoiceId, setPendingInvoiceId] = useState<number | null>(null); const [pendingInvoiceId, setPendingInvoiceId] = useState<number | null>(null);
const [textDialogOpen, setTextDialogOpen] = useState(false); const [textDialogOpen, setTextDialogOpen] = useState(false);
const [selectedInvoiceText, setSelectedInvoiceText] = useState<string | null>(null); const [selectedInvoiceText, setSelectedInvoiceText] = useState<string | null>(null);
const { user } = useAuth();
const isAdmin = user?.role === 'admin';
const { data: invoices, isLoading } = trpc.invoices.list.useQuery(); const { data: invoices, isLoading } = trpc.invoices.list.useQuery();
const { data: departments } = trpc.departments.getByUser.useQuery(); const { data: departments } = trpc.departments.getByUser.useQuery();
const { data: allocations } = trpc.accountingAllocations.getByUser.useQuery(); const { data: allocations } = trpc.accountingAllocations.getByUser.useQuery();
const { data: allUsers } = trpc.admin.getAllUsers.useQuery(undefined, { enabled: isAdmin });
const utils = trpc.useUtils(); const utils = trpc.useUtils();
const exportExcelMutation = trpc.sftp.exportToExcel.useMutation({ const exportExcelMutation = trpc.sftp.exportToExcel.useMutation({
@@ -434,6 +438,7 @@ export default function Invoices() {
onCheckedChange={handleSelectAll} onCheckedChange={handleSelectAll}
/> />
</TableHead> </TableHead>
{isAdmin && <TableHead>Utilisateur</TableHead>}
<TableHead>Fournisseur</TableHead> <TableHead>Fournisseur</TableHead>
<TableHead>Destinataire</TableHead> <TableHead>Destinataire</TableHead>
<TableHead>N° Facture</TableHead> <TableHead>N° Facture</TableHead>
@@ -456,6 +461,11 @@ export default function Invoices() {
onCheckedChange={(checked) => handleSelectOne(invoice.id, checked as boolean)} onCheckedChange={(checked) => handleSelectOne(invoice.id, checked as boolean)}
/> />
</TableCell> </TableCell>
{isAdmin && (
<TableCell className="text-xs text-gray-500">
{allUsers?.find(u => u.id === invoice.userId)?.name || `#${invoice.userId}`}
</TableCell>
)}
<TableCell className="font-medium"> <TableCell className="font-medium">
<button <button
onClick={() => setLocation(`/invoices/${invoice.id}`)} onClick={() => setLocation(`/invoices/${invoice.id}`)}

View File

@@ -250,6 +250,12 @@ export async function getInvoicesByUser(userId: number): Promise<Invoice[]> {
return getInvoicesByUserId(userId); return getInvoicesByUserId(userId);
} }
export async function getAllInvoices(): Promise<Invoice[]> {
const db = await getDb();
if (!db) return [];
return db.select().from(invoices).orderBy(desc(invoices.createdAt));
}
export async function updateInvoice(id: number, data: Partial<Invoice>) { export async function updateInvoice(id: number, data: Partial<Invoice>) {
const db = await getDb(); const db = await getDb();
if (!db) throw new Error("Database not available"); if (!db) throw new Error("Database not available");
@@ -471,6 +477,12 @@ export async function getImportLogsByUser(userId: number): Promise<ImportLog[]>
return db.select().from(importLogs).where(eq(importLogs.userId, userId)).orderBy(desc(importLogs.importedAt)); return db.select().from(importLogs).where(eq(importLogs.userId, userId)).orderBy(desc(importLogs.importedAt));
} }
export async function getAllImportLogs(): Promise<ImportLog[]> {
const db = await getDb();
if (!db) return [];
return db.select().from(importLogs).orderBy(desc(importLogs.importedAt));
}
export async function deleteAllImportLogs(userId: number): Promise<void> { export async function deleteAllImportLogs(userId: number): Promise<void> {
const db = await getDb(); const db = await getDb();
if (!db) throw new Error("Database not available"); if (!db) throw new Error("Database not available");
@@ -809,6 +821,12 @@ export async function getBapHistoryByUser(userId: number): Promise<BapHistory[]>
.orderBy(desc(bapHistory.validatedAt)); .orderBy(desc(bapHistory.validatedAt));
} }
export async function getAllBapHistory(): Promise<BapHistory[]> {
const db = await getDb();
if (!db) return [];
return db.select().from(bapHistory).orderBy(desc(bapHistory.validatedAt));
}
export async function deleteBapHistoryEntry(id: number): Promise<void> { export async function deleteBapHistoryEntry(id: number): Promise<void> {
const db = await getDb(); const db = await getDb();
if (!db) return; if (!db) return;

View File

@@ -34,6 +34,9 @@ import {
toggleUserActive, toggleUserActive,
deleteUser, deleteUser,
findDuplicateInvoice, findDuplicateInvoice,
getAllInvoices,
getAllImportLogs,
getAllBapHistory,
createImportLog, createImportLog,
getImportLogsByUser, getImportLogsByUser,
deleteAllImportLogs, deleteAllImportLogs,
@@ -374,6 +377,10 @@ export const appRouter = router({
}), }),
list: protectedProcedure.query(async ({ ctx }) => { list: protectedProcedure.query(async ({ ctx }) => {
// Les admins voient toutes les factures, les utilisateurs standard voient les leurs
if (ctx.user.role === 'admin') {
return getAllInvoices();
}
return getInvoicesByUser(ctx.user.id); return getInvoicesByUser(ctx.user.id);
}), }),
@@ -788,6 +795,10 @@ export const appRouter = router({
// ============= BAP HISTORY ROUTES ============= // ============= BAP HISTORY ROUTES =============
bapHistory: router({ bapHistory: router({
getAll: protectedProcedure.query(async ({ ctx }) => { getAll: protectedProcedure.query(async ({ ctx }) => {
// Les admins voient tout l'historique BAP
if (ctx.user.role === 'admin') {
return getAllBapHistory();
}
return getBapHistoryByUser(ctx.user.id); return getBapHistoryByUser(ctx.user.id);
}), }),
delete: protectedProcedure delete: protectedProcedure
@@ -1311,6 +1322,10 @@ export const appRouter = router({
// ============= IMPORT LOGS ROUTES ============= // ============= IMPORT LOGS ROUTES =============
importLogs: router({ importLogs: router({
getByUser: protectedProcedure.query(async ({ ctx }) => { getByUser: protectedProcedure.query(async ({ ctx }) => {
// Les admins voient tous les logs d'import
if (ctx.user.role === 'admin') {
return getAllImportLogs();
}
return getImportLogsByUser(ctx.user.id); return getImportLogsByUser(ctx.user.id);
}), }),

View File

@@ -617,3 +617,12 @@
- [ ] Ajouter la route validateBAPBulk côté serveur (traitement séquentiel de toutes les factures éligibles) - [ ] Ajouter la route validateBAPBulk côté serveur (traitement séquentiel de toutes les factures éligibles)
- [ ] Ajouter le bouton "Valider tout en BAP" dans InvoicesBAP.tsx avec barre de progression - [ ] Ajouter le bouton "Valider tout en BAP" dans InvoicesBAP.tsx avec barre de progression
- [ ] Déployer sur le VPS - [ ] Déployer sur le VPS
## Visibilité admin - toutes les factures
- [x] Ajouter getAllInvoices() dans db.ts (sans filtre userId)
- [x] Ajouter getAllImportLogs() dans db.ts (sans filtre userId)
- [x] Ajouter getAllBapHistory() dans db.ts (sans filtre userId)
- [x] Modifier invoices.list : les admins voient toutes les factures, les users voient les leurs
- [x] Modifier importLogs.getByUser : les admins voient tous les logs
- [x] Modifier bapHistory.getAll : les admins voient tout l'historique BAP
- [x] Ajouter colonne "Utilisateur" dans Invoices.tsx (visible uniquement pour les admins)