update
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user