Files
dashfront/src/components/manifest/BookDeliveryDialog.jsx
2026-10-17 18:36:18 +02:00

839 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PureComponent } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import CircularProgress from '@mui/material/CircularProgress';
import AppDialog from '../AppDialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import IconButton from '@mui/material/IconButton';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import Chip from '@mui/material/Chip';
import Alert from '@mui/material/Alert';
import Paper from '@mui/material/Paper';
import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel';
import Select from '@mui/material/Select';
import MenuItem from '@mui/material/MenuItem';
import FormHelperText from '@mui/material/FormHelperText';
import CloseIcon from '@mui/icons-material/Close';
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import WarehouseIcon from '@mui/icons-material/Warehouse';
import AddIcon from '@mui/icons-material/Add';
import { enqueueSnackbar } from 'notistack';
import { notifyError } from '../../utils/notify';
import { formatDate, resolveLineSkus } from './manifestConfig';
import {
fetchDeliveryLedgerSummary,
bookDeliveryLines,
} from '../../utils/deliveryLedger';
import { formatStock } from '../article/searchFormat';
import artikelAutomat, { getArticle } from '../../automats/artikelAutomat';
import warehouseAutomat from '../../automats/warehouseAutomat';
import { supplier as supplierApi } from '../../api';
import deliveryEditorAutomat, { buildSupplierArticlesMap } from '../../automats/deliveryEditorAutomat';
import {
SURFACE_BASE,
SURFACE_RAISED,
} from '../../colors';
export default class BookDeliveryDialog extends PureComponent {
constructor(props) {
super(props);
let initialAmounts = {};
if (Array.isArray(props.summary?.lines)) {
for (const line of props.summary.lines) {
if (props.prefillLineId != null) {
initialAmounts[line.id] =
line.id === props.prefillLineId
? line.remainingAmount > 0
? line.remainingAmount
: 0
: 0;
} else {
initialAmounts[line.id] = line.remainingAmount > 0 ? line.remainingAmount : 0;
}
}
}
const initialWarehouses = warehouseAutomat.state.warehouses || [];
const initialSupplierMap =
props.supplierArticlesMap ||
(deliveryEditorAutomat?.state?.supplierArticlesMap ?? {});
this.state = {
summary: props.summary ?? null,
loading: !props.summary,
error: null,
amountsToBook: initialAmounts,
transactionText: props.delivery?.externalNumber || (props.delivery?.number
? `Lieferung ${props.delivery.number}`
: 'Lieferung Wareneingang'),
isBooking: false,
warehouses: initialWarehouses,
selectedWarehouseId: initialWarehouses.length === 1 ? initialWarehouses[0].id : '',
warehouseError: null,
supplierArticlesMap: initialSupplierMap,
enablingTrackStockBySku: {},
};
this.handleAmountChange = this.handleAmountChange.bind(this);
this.handleWarehouseChange = this.handleWarehouseChange.bind(this);
this.handleTransactionTextChange = this.handleTransactionTextChange.bind(this);
this.handleBookAll = this.handleBookAll.bind(this);
this.handleResetAmounts = this.handleResetAmounts.bind(this);
this.handleConfirmBook = this.handleConfirmBook.bind(this);
this.handleEnableTrackStock = this.handleEnableTrackStock.bind(this);
}
loadSupplierArticlesIfNeeded() {
const supplierId = this.props.delivery?.supplierId ?? this.props.delivery?.partyId;
if (supplierId && Object.keys(this.state.supplierArticlesMap).length === 0) {
void supplierApi.get(supplierId).then((res) => {
const articles = res?.supplier?.articles || res?.articles || [];
const map = buildSupplierArticlesMap(articles);
this.setState({ supplierArticlesMap: map });
}).catch(() => {});
}
}
componentDidMount() {
this.unsubWarehouse = warehouseAutomat.subscribe((state) => {
const warehouses = state.warehouses || [];
this.setState((prev) => {
const selectedId =
prev.selectedWarehouseId ||
(warehouses.length === 1 ? warehouses[0].id : '');
return {
warehouses,
selectedWarehouseId: selectedId,
};
});
});
this.unsubArticle = artikelAutomat.subscribe(() => {
this.forceUpdate();
});
void warehouseAutomat.actions.load().catch(() => {});
this.loadSupplierArticlesIfNeeded();
if (this.props.open) {
void this.loadSummary();
}
}
componentWillUnmount() {
this.unsubWarehouse?.();
this.unsubArticle?.();
}
async handleEnableTrackStock(line, targetSku) {
if (!targetSku) return;
this.setState((prev) => ({
enablingTrackStockBySku: {
...prev.enablingTrackStockBySku,
[targetSku]: true,
},
}));
try {
await artikelAutomat.actions.setTrackStock(targetSku, true);
this.setState((prev) => {
if (!prev.summary?.lines) {
return {
enablingTrackStockBySku: {
...prev.enablingTrackStockBySku,
[targetSku]: false,
},
};
}
const updatedLines = prev.summary.lines.map((l) => {
const rawSku = l.sku ? String(l.sku).trim() : '';
const lineOwnSku = l.articleSku || l.article?.sku || '';
if (
l.id === line.id ||
rawSku === targetSku ||
lineOwnSku === targetSku ||
(l.article && String(l.article.sku) === targetSku)
) {
return {
...l,
trackStock: true,
article: l.article
? { ...l.article, track_stock: true }
: { sku: targetSku, track_stock: true },
};
}
return l;
});
return {
summary: {
...prev.summary,
hasTrackStock: true,
lines: updatedLines,
},
enablingTrackStockBySku: {
...prev.enablingTrackStockBySku,
[targetSku]: false,
},
};
});
enqueueSnackbar(`Lagerbestand für „${targetSku}“ aktiviert.`, { variant: 'success' });
} catch (err) {
console.error('[BookDeliveryDialog] Failed to enable track_stock:', err);
enqueueSnackbar(
err?.message || `Fehler beim Aktivieren des Lagerbestands für „${targetSku}“.`,
{ variant: 'error' }
);
this.setState((prev) => ({
enablingTrackStockBySku: {
...prev.enablingTrackStockBySku,
[targetSku]: false,
},
}));
}
}
componentDidUpdate(prevProps) {
if (this.props.open && !prevProps.open) {
const currentWarehouses = this.state.warehouses || warehouseAutomat.state.warehouses || [];
const currentSupplierMap =
this.props.supplierArticlesMap ||
(deliveryEditorAutomat?.state?.supplierArticlesMap ?? this.state.supplierArticlesMap ?? {});
this.setState(
{
summary: this.props.summary ?? null,
loading: !this.props.summary,
error: null,
warehouseError: null,
selectedWarehouseId:
this.state.selectedWarehouseId ||
(currentWarehouses.length === 1 ? currentWarehouses[0].id : ''),
transactionText: this.props.delivery?.externalNumber || (this.props.delivery?.number
? `Lieferung ${this.props.delivery.number}`
: 'Lieferung Wareneingang'),
isBooking: false,
supplierArticlesMap: currentSupplierMap,
},
() => {
void warehouseAutomat.actions.load().catch(() => {});
this.loadSupplierArticlesIfNeeded();
if (this.props.summary) {
this.initAmountsFromSummary(this.props.summary, this.props.prefillLineId);
} else {
void this.loadSummary();
}
}
);
} else if (
this.props.open &&
(this.props.prefillLineId !== prevProps.prefillLineId ||
(this.props.summary && this.props.summary !== prevProps.summary)) &&
!this.state.isBooking
) {
const activeSummary = this.state.summary || this.props.summary;
if (activeSummary) {
this.initAmountsFromSummary(activeSummary, this.props.prefillLineId);
}
}
}
handleWarehouseChange(event) {
const value = event.target.value;
this.setState({
selectedWarehouseId: value ? Number(value) : '',
warehouseError: null,
});
}
async loadSummary() {
const { delivery, prefillLineId } = this.props;
if (!delivery?.id) return;
this.setState({ loading: true, error: null });
try {
const summary = await fetchDeliveryLedgerSummary(delivery.id);
this.initAmountsFromSummary(summary, prefillLineId);
this.setState({ summary, loading: false });
} catch (err) {
console.error('[BookDeliveryDialog] Failed to load ledger summary:', err);
this.setState({ error: err.message || 'Laden fehlgeschlagen', loading: false });
}
}
initAmountsFromSummary(summary, prefillLineId = this.props.prefillLineId) {
const amounts = {};
if (Array.isArray(summary?.lines)) {
for (const line of summary.lines) {
if (prefillLineId != null) {
amounts[line.id] =
line.id === prefillLineId
? line.remainingAmount > 0
? line.remainingAmount
: 0
: 0;
} else {
amounts[line.id] = line.remainingAmount > 0 ? line.remainingAmount : 0;
}
}
}
this.setState({ amountsToBook: amounts });
}
handleAmountChange(lineId, value) {
const num = Math.max(0, Number(value) || 0);
this.setState((prev) => ({
amountsToBook: {
...prev.amountsToBook,
[lineId]: num,
},
}));
}
handleTransactionTextChange(e) {
this.setState({ transactionText: e.target.value });
}
handleBookAll() {
const { summary } = this.state;
if (!summary?.lines) return;
const amounts = {};
for (const line of summary.lines) {
amounts[line.id] = line.remainingAmount > 0 ? line.remainingAmount : 0;
}
this.setState({ amountsToBook: amounts });
}
handleResetAmounts() {
const { summary } = this.state;
if (!summary?.lines) return;
const amounts = {};
for (const line of summary.lines) {
amounts[line.id] = 0;
}
this.setState({ amountsToBook: amounts });
}
async handleConfirmBook() {
const { delivery, onBookSuccess, onClose } = this.props;
const { summary, amountsToBook, transactionText, selectedWarehouseId } = this.state;
if (!summary?.lines) return;
const linesToBook = summary.lines
.map((line) => ({
manifestarticleId: line.id,
sku: line.sku,
name: line.name,
amount: Number(amountsToBook[line.id] || 0),
trackStock: Boolean(line.trackStock),
}))
.filter((l) => l.amount > 0);
if (linesToBook.length === 0) {
enqueueSnackbar('Keine Mengen zum Buchen angegeben.', { variant: 'info' });
return;
}
const hasTracked = linesToBook.some((l) => l.trackStock);
if (hasTracked && !selectedWarehouseId) {
this.setState({ warehouseError: 'Bitte wählen Sie ein Ziellager aus.' });
enqueueSnackbar('Bitte wählen Sie ein Ziellager aus.', { variant: 'warning' });
return;
}
this.setState({ isBooking: true, error: null, warehouseError: null });
try {
await bookDeliveryLines({
delivery: summary.delivery || delivery,
linesToBook,
warehouseId: selectedWarehouseId,
transactionText,
});
const totalBookedQty = linesToBook.reduce((sum, l) => sum + l.amount, 0);
enqueueSnackbar(
`${linesToBook.length} Position${linesToBook.length === 1 ? '' : 'en'} (${totalBookedQty} Stk.) erfolgreich gebucht.`,
{ variant: 'success' }
);
// Refresh summary
const updatedSummary = await fetchDeliveryLedgerSummary(delivery.id);
if (onBookSuccess) {
onBookSuccess(updatedSummary);
}
if (onClose) {
onClose();
}
} catch (err) {
console.error('[BookDeliveryDialog] Booking failed:', err);
notifyError(err);
this.setState({
error: err.message || 'Buchung fehlgeschlagen',
isBooking: false,
});
}
}
renderHeaderInfo({ hasTrackedLinesToBook, isWarehouseMissing } = {}) {
const { delivery, partyName } = this.props;
const {
transactionText,
isBooking,
summary,
warehouses,
selectedWarehouseId,
warehouseError,
} = this.state;
const isFullyBooked = (summary?.lines?.length ?? 0) > 0 && summary?.status === 'booked';
return (
<Box sx={{ mb: 2 }}>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 1.5,
mb: 1.5,
}}
>
<TextField
size="small"
label="Datum"
value={formatDate(delivery?.date) || '–'}
disabled
sx={{ flex: { xs: '1 1 100%', sm: '1 1 110px' }, minWidth: { sm: 100 } }}
/>
<TextField
size="small"
label="Lieferant"
value={partyName || '–'}
disabled
sx={{ flex: { xs: '1 1 100%', sm: '2 1 180px' }, minWidth: { sm: 140 } }}
/>
<TextField
size="small"
label="Nummer"
value={delivery?.number || '–'}
disabled
sx={{ flex: { xs: '1 1 100%', sm: '1 1 110px' }, minWidth: { sm: 100 } }}
/>
</Box>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 1.5,
}}
>
<TextField
size="small"
label="Referenz"
value={transactionText}
onChange={this.handleTransactionTextChange}
disabled={isBooking || isFullyBooked}
sx={{ flex: { xs: '1 1 100%', sm: '1 1 200px' } }}
/>
<FormControl
size="small"
required={hasTrackedLinesToBook}
error={Boolean(warehouseError) || isWarehouseMissing}
disabled={isBooking || isFullyBooked}
sx={{ flex: { xs: '1 1 100%', sm: '1 1 200px' } }}
>
<InputLabel id="warehouse-select-label">
Ziellager{hasTrackedLinesToBook ? ' *' : ''}
</InputLabel>
<Select
labelId="warehouse-select-label"
id="warehouse-select"
value={selectedWarehouseId ?? ''}
label={`Ziellager${hasTrackedLinesToBook ? ' *' : ''}`}
onChange={this.handleWarehouseChange}
startAdornment={
<WarehouseIcon
sx={{ mr: 1, color: 'text.secondary', fontSize: 20 }}
/>
}
>
{warehouses.length === 0 ? (
<MenuItem value="" disabled>
Keine Lager vorhanden
</MenuItem>
) : (
warehouses.map((wh) => (
<MenuItem key={wh.id} value={wh.id}>
{wh.name}
</MenuItem>
))
)}
</Select>
{warehouseError && (
<FormHelperText>{warehouseError}</FormHelperText>
)}
</FormControl>
</Box>
</Box>
);
}
renderLinesTable() {
const { summary, amountsToBook, isBooking, supplierArticlesMap, enablingTrackStockBySku } = this.state;
const lines = summary?.lines || [];
if (lines.length === 0) {
return (
<Alert severity="info" sx={{ my: 2 }}>
Diese Lieferung enthält keine Positionen zum Verbuchen.
</Alert>
);
}
return (
<TableContainer component={Paper} variant="outlined" sx={{ mb: 2 }}>
<Table size="small">
<TableHead sx={{ backgroundColor: SURFACE_RAISED }}>
<TableRow>
<TableCell sx={{ fontWeight: 600, width: 44 }} align="center">Pos.</TableCell>
<TableCell sx={{ fontWeight: 600 }}>Lieferanten-SKU</TableCell>
<TableCell sx={{ fontWeight: 600 }}>Eigene SKU</TableCell>
<TableCell sx={{ fontWeight: 600 }}>EAN</TableCell>
<TableCell sx={{ fontWeight: 600 }}>Artikel</TableCell>
<TableCell align="right" sx={{ fontWeight: 600 }}>Bestand</TableCell>
<TableCell align="right" sx={{ fontWeight: 600 }}>Liefermenge</TableCell>
<TableCell align="right" sx={{ fontWeight: 600 }}>Bereits gebucht</TableCell>
<TableCell align="right" sx={{ fontWeight: 600, width: 140 }}>Jetzt verbuchen</TableCell>
</TableRow>
</TableHead>
<TableBody>
{lines.map((line, index) => {
const toBook = amountsToBook[line.id] ?? 0;
const isAlreadyFullyBooked = line.isBooked && line.remainingAmount <= 0;
const posNumber = line.pos ?? line.position ?? (index + 1);
const { supplierSku, ownSku } = resolveLineSkus(
line,
this.props.delivery,
supplierArticlesMap
);
const cachedArticle =
line.article ||
(ownSku && ownSku !== '–' ? getArticle(ownSku) : null) ||
(line.sku ? getArticle(line.sku) : null);
const targetSku =
cachedArticle?.sku ||
(ownSku && ownSku !== '–' ? ownSku : null) ||
(line.sku ? String(line.sku).trim() : null);
if (targetSku && !cachedArticle) {
void artikelAutomat.actions.loadArticle(targetSku).catch(() => {});
}
const isTracked =
Boolean(line.trackStock) ||
(cachedArticle?.track_stock !== undefined
? cachedArticle.track_stock !== false &&
cachedArticle.track_stock !== 0 &&
cachedArticle.track_stock !== '0'
: false);
const isEnablingThis = targetSku ? Boolean(enablingTrackStockBySku?.[targetSku]) : false;
let currentStock = null;
if (cachedArticle) {
if (Array.isArray(cachedArticle.warehouses) && cachedArticle.warehouses.length > 0) {
currentStock = cachedArticle.warehouses.reduce(
(sum, wh) => sum + (Number(wh.stock) || 0),
0
);
} else if (cachedArticle.stock != null) {
currentStock = Number(cachedArticle.stock);
}
} else if (line.stock != null) {
currentStock = Number(line.stock);
}
const warehouseBreakdown =
Array.isArray(cachedArticle?.warehouses) && cachedArticle.warehouses.length > 0
? cachedArticle.warehouses
.map((w) => `${w.name || 'Lager'}: ${formatStock(w.stock ?? 0)}`)
.join(' • ')
: null;
const eans = line.ean
? [line.ean]
: line.eans || cachedArticle?.eans || cachedArticle?.articleean || [];
const primaryEan = Array.isArray(eans) ? (eans[0] ?? '') : (eans ?? '');
return (
<TableRow key={line.id} hover>
<TableCell align="center" sx={{ color: 'text.secondary', fontSize: '0.85rem' }}>
{posNumber}
</TableCell>
<TableCell sx={{ fontFamily: 'monospace', fontSize: '0.85rem' }}>
{supplierSku}
</TableCell>
<TableCell sx={{ fontFamily: 'monospace', fontSize: '0.85rem' }}>
{ownSku}
</TableCell>
<TableCell
sx={{ fontFamily: 'monospace', fontSize: '0.85rem' }}
title={Array.isArray(eans) && eans.length > 1 ? eans.join(', ') : undefined}
>
{primaryEan || '–'}
</TableCell>
<TableCell sx={{ maxWidth: 260 }}>
<Typography variant="body2" sx={{ fontWeight: 500 }} noWrap>
{line.name || '–'}
</Typography>
</TableCell>
<TableCell align="right" title={warehouseBreakdown || undefined}>
{isTracked ? (
<Typography
variant="body2"
sx={{
fontFamily: 'monospace',
fontSize: '0.85rem',
color: 'text.primary',
}}
>
{currentStock != null ? formatStock(currentStock) : '0'}
</Typography>
) : targetSku ? (
<Button
size="small"
variant="outlined"
color="primary"
disabled={isBooking || isEnablingThis}
onClick={() => this.handleEnableTrackStock(line, targetSku)}
startIcon={
isEnablingThis ? (
<CircularProgress size={12} color="inherit" />
) : (
<AddIcon sx={{ fontSize: 13 }} />
)
}
sx={{
height: 22,
fontSize: '0.68rem',
textTransform: 'none',
py: 0,
px: 0.75,
minWidth: 'auto',
fontWeight: 600,
}}
>
Aktivieren
</Button>
) : (
<Typography
variant="body2"
sx={{
fontFamily: 'monospace',
fontSize: '0.85rem',
color: 'text.disabled',
}}
>
—
</Typography>
)}
</TableCell>
<TableCell align="right">
<Typography variant="body2" sx={{ fontWeight: 600 }}>
{line.targetAmount}
</Typography>
</TableCell>
<TableCell align="right">
<Box sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.5, justifyContent: 'flex-end' }}>
<Typography
variant="body2"
color={line.bookedAmount > 0 ? 'success.main' : 'text.secondary'}
sx={{ fontWeight: line.bookedAmount > 0 ? 600 : 400 }}
>
{line.bookedAmount}
</Typography>
{isAlreadyFullyBooked && (
<CheckCircleOutlineOutlinedIcon color="success" sx={{ fontSize: 16 }} />
)}
</Box>
</TableCell>
<TableCell align="right">
{isAlreadyFullyBooked ? (
<Chip
size="small"
label="Gebucht"
color="success"
variant="outlined"
sx={{ fontSize: '0.75rem', height: 24 }}
/>
) : (
<TextField
size="small"
type="number"
value={toBook}
onChange={(e) => this.handleAmountChange(line.id, e.target.value)}
disabled={isBooking}
slotProps={{
htmlInput: {
min: 0,
step: 1,
style: { textAlign: 'right', padding: '4px 8px' },
},
}}
sx={{ width: 90 }}
/>
)}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
);
}
render() {
const { open, onClose, delivery } = this.props;
const {
summary,
loading,
error,
amountsToBook,
transactionText,
isBooking,
warehouses,
selectedWarehouseId,
warehouseError,
} = this.state;
const lines = summary?.lines || [];
const hasLines = lines.length > 0;
const hasTrackStock = lines.some((l) => l.trackStock);
const linesWithAmountsToBook = lines.filter(
(l) => Number(amountsToBook[l.id]) > 0
);
const hasTrackedLinesToBook = linesWithAmountsToBook.some((l) => l.trackStock);
const prefillLine =
this.props.prefillLineId != null
? lines.find((l) => l.id === this.props.prefillLineId)
: null;
const totalToBook = lines.reduce(
(sum, l) => sum + (Number(amountsToBook[l.id]) || 0),
0
);
const countLinesToBook = linesWithAmountsToBook.length;
const isFullyBooked = hasLines && summary?.status === 'booked';
const selectedWarehouse = warehouses.find((w) => w.id === selectedWarehouseId);
const isWarehouseMissing = hasTrackedLinesToBook && !selectedWarehouseId;
return (
<AppDialog
open={Boolean(open)}
onClose={isBooking ? undefined : onClose}
maxWidth="lg"
fullWidth
>
<DialogTitle
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
pb: 1,
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Inventory2OutlinedIcon color="primary" />
<Typography variant="h6" component="span" sx={{ fontWeight: 600 }}>
Lieferung verbuchen
</Typography>
</Box>
<IconButton size="small" onClick={onClose} disabled={isBooking}>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent dividers sx={{ pt: 2, borderBottom: 'none' }}>
{this.renderHeaderInfo({ hasTrackedLinesToBook, isWarehouseMissing })}
{loading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', py: 6, gap: 2, alignItems: 'center' }}>
<CircularProgress size={24} />
<Typography color="text.secondary">Lade Positionen und Ledger-Status...</Typography>
</Box>
) : error ? (
<Alert severity="error" sx={{ my: 2 }}>
{error}
</Alert>
) : (
<>
{prefillLine && (
<Alert severity="info" sx={{ mb: 2 }}>
Vorausgewählt zum Verbuchen: Position{' '}
<strong>{prefillLine.sku ? `SKU ${prefillLine.sku}` : prefillLine.name}</strong> ({amountsToBook[prefillLine.id] || 0} Stk.). Andere Positionen stehen auf 0.
</Alert>
)}
{this.renderLinesTable()}
{hasTrackedLinesToBook && warehouses.length === 0 && !isFullyBooked && (
<Alert severity="error" sx={{ mb: 2 }}>
Es ist noch kein Lager eingerichtet. Für lagergeführte Artikel legen Sie bitte unter „Artikel → Lager“ zuerst ein Lager an.
</Alert>
)}
{isFullyBooked ? (
<Alert severity="success" icon={<CheckCircleOutlineOutlinedIcon />}>
Alle Positionen dieser Lieferung sind bereits vollständig im Ledger verbucht.
</Alert>
) : isWarehouseMissing ? (
<Alert severity="warning">
Bitte wählen Sie ein <strong>Ziellager</strong> aus, um die lagergeführten Artikel zu buchen.
</Alert>
) : countLinesToBook === 0 && hasLines ? (
<Alert severity="warning">
Wählen Sie mindestens eine Menge größer als 0 aus, um Positionen zu verbuchen.
</Alert>
) : null}
</>
)}
</DialogContent>
<DialogActions sx={{ px: 3, py: 2 }}>
<Button onClick={onClose} disabled={isBooking} color="inherit">
{isFullyBooked ? 'Schließen' : 'Abbrechen'}
</Button>
{!isFullyBooked && hasLines && (
<Button
variant="contained"
color="primary"
onClick={this.handleConfirmBook}
disabled={isBooking || totalToBook <= 0 || loading || isWarehouseMissing}
startIcon={
isBooking ? (
<CircularProgress size={16} color="inherit" />
) : (
<Inventory2OutlinedIcon />
)
}
sx={{ minWidth: 160, fontWeight: 600 }}
>
{isBooking ? 'Wird gebucht...' : 'Jetzt verbuchen'}
</Button>
)}
</DialogActions>
</AppDialog>
);
}
}