add linking between internal and external orders

This commit is contained in:
Matthias Hochmeister
2026-03-27 11:18:06 +01:00
parent 75e533c1fc
commit 90f691d607
8 changed files with 573 additions and 49 deletions

View File

@@ -8,7 +8,7 @@ import {
} from '@mui/material';
import {
ArrowBack, Add as AddIcon, Delete as DeleteIcon, Edit as EditIcon,
Check as CheckIcon, Close as CloseIcon, Link as LinkIcon,
Check as CheckIcon, Close as CloseIcon, ShoppingCart as ShoppingCartIcon,
} from '@mui/icons-material';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom';
@@ -17,13 +17,11 @@ import { useNotification } from '../contexts/NotificationContext';
import { usePermissionContext } from '../contexts/PermissionContext';
import { useAuth } from '../contexts/AuthContext';
import { ausruestungsanfrageApi } from '../services/ausruestungsanfrage';
import { bestellungApi } from '../services/bestellung';
import { AUSRUESTUNG_STATUS_LABELS, AUSRUESTUNG_STATUS_COLORS } from '../types/ausruestungsanfrage.types';
import type {
AusruestungAnfrage, AusruestungAnfrageDetailResponse,
AusruestungAnfrageFormItem, AusruestungAnfrageStatus,
} from '../types/ausruestungsanfrage.types';
import type { Bestellung } from '../types/bestellung.types';
// ── Helpers ──
@@ -58,8 +56,6 @@ export default function AusruestungsanfrageDetail() {
const [actionDialog, setActionDialog] = useState<{ action: 'genehmigt' | 'abgelehnt' } | null>(null);
const [adminNotizen, setAdminNotizen] = useState('');
const [statusChangeValue, setStatusChangeValue] = useState('');
const [linkDialog, setLinkDialog] = useState(false);
const [selectedBestellung, setSelectedBestellung] = useState<Bestellung | null>(null);
// Permissions
const showAdminActions = hasPermission('ausruestungsanfrage:approve');
@@ -80,12 +76,6 @@ export default function AusruestungsanfrageDetail() {
enabled: editing,
});
const { data: bestellungen = [] } = useQuery({
queryKey: ['bestellungen'],
queryFn: () => bestellungApi.getOrders(),
enabled: linkDialog,
});
// ── Mutations ──
const updateMut = useMutation({
mutationFn: (data: { bezeichnung?: string; notizen?: string; items?: AusruestungAnfrageFormItem[] }) =>
@@ -111,17 +101,6 @@ export default function AusruestungsanfrageDetail() {
onError: () => showError('Fehler beim Aktualisieren'),
});
const linkMut = useMutation({
mutationFn: (bestellungId: number) => ausruestungsanfrageApi.linkToOrder(requestId, bestellungId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['ausruestungsanfrage'] });
showSuccess('Verknüpfung erstellt');
setLinkDialog(false);
setSelectedBestellung(null);
},
onError: () => showError('Fehler beim Verknüpfen'),
});
const geliefertMut = useMutation({
mutationFn: ({ positionId, geliefert }: { positionId: number; geliefert: boolean }) =>
ausruestungsanfrageApi.updatePositionGeliefert(positionId, geliefert),
@@ -414,11 +393,12 @@ export default function AusruestungsanfrageDetail() {
)}
{showAdminActions && anfrage && anfrage.status === 'genehmigt' && canLink && (
<Button
variant="outlined"
startIcon={<LinkIcon />}
onClick={() => setLinkDialog(true)}
variant="contained"
color="primary"
startIcon={<ShoppingCartIcon />}
onClick={() => navigate(`/ausruestungsanfragen/${requestId}/bestellen`)}
>
Verknüpfen
Bestellungen erstellen
</Button>
)}
{canEdit && !editing && (
@@ -459,29 +439,6 @@ export default function AusruestungsanfrageDetail() {
</DialogActions>
</Dialog>
{/* Link to order sub-dialog */}
<Dialog open={linkDialog} onClose={() => { setLinkDialog(false); setSelectedBestellung(null); }} maxWidth="sm" fullWidth>
<DialogTitle>Mit Bestellung verknüpfen</DialogTitle>
<DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2, pt: '20px !important' }}>
<Autocomplete
options={bestellungen}
getOptionLabel={(o) => `#${o.id} ${o.bezeichnung}`}
value={selectedBestellung}
onChange={(_, v) => setSelectedBestellung(v)}
renderInput={params => <TextField {...params} label="Bestellung auswählen" />}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => { setLinkDialog(false); setSelectedBestellung(null); }}>Abbrechen</Button>
<Button
variant="contained"
disabled={!selectedBestellung || linkMut.isPending}
onClick={() => { if (selectedBestellung) linkMut.mutate(selectedBestellung.id); }}
>
Verknüpfen
</Button>
</DialogActions>
</Dialog>
</DashboardLayout>
);
}