feat: les admins voient toutes les factures/logs/historique BAP, colonne Utilisateur dans Factures
This commit is contained in:
@@ -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}`)}
|
||||||
|
|||||||
18
server/db.ts
18
server/db.ts
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
9
todo.md
9
todo.md
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user