Checkpoint: Factures BAP inclut désormais, après Année et Mois, les filtres Destinataires, Entités et Ventilations comptables de la page Factures. Leur logique est identique, avec valeurs absentes et règle SANTINOVA/ITINOVA fondée sur DSI SANTINOVA. Les filtres sont conservés au retour depuis le détail. Tests, TypeScript, build et rendu visuel validés en sandbox.
This commit is contained in:
@@ -116,6 +116,9 @@ export default function InvoicesBAP() {
|
||||
const [searchQuery, setSearchQuery] = useState(initialFilters.searchQuery);
|
||||
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
||||
const [statusFilter, setStatusFilter] = useState<string>(initialFilters.statusFilter);
|
||||
const [recipientFilter, setRecipientFilter] = useState<string>(initialFilters.recipientFilter);
|
||||
const [entityFilter, setEntityFilter] = useState<string>(initialFilters.entityFilter);
|
||||
const [ventilationFilter, setVentilationFilter] = useState<string>(initialFilters.ventilationFilter);
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [invoiceToDelete, setInvoiceToDelete] = useState<number | null>(null);
|
||||
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
||||
@@ -143,6 +146,9 @@ export default function InvoicesBAP() {
|
||||
statusFilter,
|
||||
selectedYear,
|
||||
selectedMonth,
|
||||
recipientFilter,
|
||||
entityFilter,
|
||||
ventilationFilter,
|
||||
sortField,
|
||||
sortDir,
|
||||
}));
|
||||
@@ -169,6 +175,14 @@ export default function InvoicesBAP() {
|
||||
const { data: allInvoices, isLoading } = trpc.invoices.list.useQuery();
|
||||
// Filter for BAP invoices only (Abonnement = NON, isSubscription = 0)
|
||||
const invoices = allInvoices?.filter(inv => inv.isSubscription === 0);
|
||||
const uniqueRecipients = useMemo(
|
||||
() => Array.from(new Set((invoices || []).map(inv => (inv as any).recipientName).filter(Boolean))).sort(),
|
||||
[invoices],
|
||||
);
|
||||
const uniqueVentilations = useMemo(
|
||||
() => Array.from(new Set((invoices || []).map(inv => (inv as any).ventilationComptable).filter(Boolean))).sort(),
|
||||
[invoices],
|
||||
);
|
||||
// IDs des factures déjà validées BAP (pour charger leurs pdfUrl depuis bapHistory)
|
||||
// Stabilisé avec useMemo pour éviter les re-renders infinis (anti-pattern tRPC)
|
||||
const validatedInvoiceIds = useMemo(
|
||||
@@ -440,6 +454,27 @@ export default function InvoicesBAP() {
|
||||
if (statusFilter === "bap_pending" && inv.bapValidated === 1) return false;
|
||||
if (statusFilter === "to_complete" && !isToComplete(inv)) return false;
|
||||
}
|
||||
|
||||
// Même convention que la page Factures : valeur vide et entités déduites du service.
|
||||
if (recipientFilter !== "all") {
|
||||
if (recipientFilter === "__empty__") {
|
||||
if ((inv as any).recipientName) return false;
|
||||
} else if ((inv as any).recipientName !== recipientFilter) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (entityFilter !== "all") {
|
||||
const service = ((inv as any).serviceConcerne || "").trim().toUpperCase();
|
||||
if (entityFilter === "santinova" && service !== "DSI SANTINOVA") return false;
|
||||
if (entityFilter === "itinova" && service === "DSI SANTINOVA") return false;
|
||||
}
|
||||
if (ventilationFilter !== "all") {
|
||||
if (ventilationFilter === "__empty__") {
|
||||
if ((inv as any).ventilationComptable) return false;
|
||||
} else if ((inv as any).ventilationComptable !== ventilationFilter) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
@@ -778,6 +813,40 @@ export default function InvoicesBAP() {
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={recipientFilter} onValueChange={setRecipientFilter}>
|
||||
<SelectTrigger className="w-[180px] h-8 text-sm bg-white border-emerald-200">
|
||||
<SelectValue placeholder="Destinataire" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">Tous destinataires</SelectItem>
|
||||
<SelectItem value="__empty__">Sans destinataire</SelectItem>
|
||||
{uniqueRecipients.map((recipient) => (
|
||||
<SelectItem key={recipient} value={recipient}>{recipient}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={entityFilter} onValueChange={setEntityFilter}>
|
||||
<SelectTrigger className="w-[150px] h-8 text-sm bg-white border-emerald-200">
|
||||
<SelectValue placeholder="Entité" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">Toutes entités</SelectItem>
|
||||
<SelectItem value="santinova">SANTINOVA</SelectItem>
|
||||
<SelectItem value="itinova">ITINOVA</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select value={ventilationFilter} onValueChange={setVentilationFilter}>
|
||||
<SelectTrigger className="w-[170px] h-8 text-sm bg-white border-emerald-200">
|
||||
<SelectValue placeholder="Ventilation" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">Toutes ventilations</SelectItem>
|
||||
<SelectItem value="__empty__">Sans ventilation</SelectItem>
|
||||
{uniqueVentilations.map((ventilation) => (
|
||||
<SelectItem key={ventilation} value={ventilation}>{ventilation}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{(selectedYear !== "all" || selectedMonth !== "all") && (
|
||||
<button
|
||||
onClick={() => { setSelectedYear(String(currentYear)); setSelectedMonth("all"); }}
|
||||
|
||||
Reference in New Issue
Block a user