This commit is contained in:
Matthias Hochmeister
2026-03-26 09:29:59 +01:00
parent 884397b520
commit d5e5f2d44e
10 changed files with 428 additions and 154 deletions

View File

@@ -7,8 +7,10 @@ import {
Card,
CardContent,
Grid,
IconButton,
Tab,
Tabs,
Tooltip,
Typography,
Table,
TableBody,
@@ -25,13 +27,15 @@ import {
LinearProgress,
Divider,
} from '@mui/material';
import { Add as AddIcon, ExpandMore as ExpandMoreIcon, FilterList as FilterListIcon } from '@mui/icons-material';
import { Add as AddIcon, ExpandMore as ExpandMoreIcon, FilterList as FilterListIcon, PictureAsPdf as PdfIcon } from '@mui/icons-material';
import { useQuery } from '@tanstack/react-query';
import { useNavigate, useSearchParams } from 'react-router-dom';
import DashboardLayout from '../components/dashboard/DashboardLayout';
import ChatAwareFab from '../components/shared/ChatAwareFab';
import { usePermissionContext } from '../contexts/PermissionContext';
import { bestellungApi } from '../services/bestellung';
import { configApi } from '../services/config';
import { addPdfHeader, addPdfFooter } from '../utils/pdfExport';
import { BESTELLUNG_STATUS_LABELS, BESTELLUNG_STATUS_COLORS } from '../types/bestellung.types';
import type { BestellungStatus, Bestellung } from '../types/bestellung.types';
@@ -83,6 +87,7 @@ export default function Bestellungen() {
const [searchParams] = useSearchParams();
const { hasPermission } = usePermissionContext();
const canManageVendors = hasPermission('bestellungen:manage_vendors');
const canExport = hasPermission('bestellungen:export');
// Tab from URL
const [tab, setTab] = useState(() => {
@@ -178,11 +183,66 @@ export default function Bestellungen() {
return selectedVendors === null || selectedVendors.has(key);
}
// ── PDF Export ──
async function generateBestellungenPdf() {
const { jsPDF } = await import('jspdf');
const autoTable = (await import('jspdf-autotable')).default;
const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
let settings;
try { settings = await configApi.getPdfSettings(); } catch { settings = { pdf_header: '', pdf_footer: '', pdf_logo: '', pdf_org_name: '' }; }
const startY = addPdfHeader(doc, 'Bestellungen — Übersicht', settings, 210);
const rows = filteredOrders.map((o) => {
const brutto = calcBrutto(o);
return [
formatKennung(o),
o.lieferant_name || '',
BESTELLUNG_STATUS_LABELS[o.status],
String(o.items_count ?? 0),
formatCurrency(brutto),
formatDate(o.bestellt_am || o.erstellt_am),
];
});
autoTable(doc, {
head: [['Kennung', 'Lieferant', 'Status', 'Pos.', 'Betrag (brutto)', 'Datum']],
body: rows,
startY,
headStyles: { fillColor: [66, 66, 66], textColor: 255, fontStyle: 'bold' },
alternateRowStyles: { fillColor: [245, 245, 245] },
margin: { left: 10, right: 10 },
styles: { fontSize: 9, cellPadding: 2 },
columnStyles: {
0: { cellWidth: 22 },
1: { cellWidth: 40 },
2: { cellWidth: 30 },
3: { cellWidth: 15, halign: 'right' },
4: { cellWidth: 35, halign: 'right' },
5: { cellWidth: 25 },
},
didDrawPage: addPdfFooter(doc, settings),
});
const today = new Date().toISOString().slice(0, 10);
doc.save(`bestellungen_uebersicht_${today}.pdf`);
}
// ── Render ──
return (
<DashboardLayout>
<Typography variant="h4" sx={{ mb: 3 }}>Bestellungen</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 3 }}>
<Typography variant="h4" sx={{ flexGrow: 1 }}>Bestellungen</Typography>
{canExport && (
<Tooltip title="PDF Export">
<IconButton onClick={generateBestellungenPdf} color="primary">
<PdfIcon />
</IconButton>
</Tooltip>
)}
</Box>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={tab} onChange={(_e, v) => { setTab(v); navigate(`/bestellungen?tab=${v}`, { replace: true }); }} variant="scrollable" scrollButtons="auto">