This commit is contained in:
seb
2026-09-26 23:05:48 +02:00
parent 454c5ab6bf
commit 3ab9d904e6
12 changed files with 1505 additions and 65 deletions

View File

@@ -18,9 +18,15 @@ 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 { StorageManagerContext } from '../../api/storageManagerContext';
import { enqueueSnackbar } from 'notistack';
import { notifyError } from '../../utils/notify';
@@ -29,6 +35,7 @@ import {
fetchDeliveryLedgerSummary,
bookDeliveryLines,
} from '../../utils/deliveryLedger';
import warehouseAutomat from '../../automats/warehouseAutomat';
export default class BookDeliveryDialog extends PureComponent {
static contextType = StorageManagerContext;
@@ -52,6 +59,8 @@ export default class BookDeliveryDialog extends PureComponent {
}
}
const initialWarehouses = warehouseAutomat.state.warehouses || [];
this.state = {
summary: props.summary ?? null,
loading: !props.summary,
@@ -61,9 +70,13 @@ export default class BookDeliveryDialog extends PureComponent {
? `Lieferung ${props.delivery.number}`
: 'Lieferung Wareneingang',
isBooking: false,
warehouses: initialWarehouses,
selectedWarehouseId: initialWarehouses.length === 1 ? initialWarehouses[0].id : '',
warehouseError: null,
};
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);
@@ -71,24 +84,48 @@ export default class BookDeliveryDialog extends PureComponent {
}
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,
};
});
});
void warehouseAutomat.actions.load().catch(() => {});
if (this.props.open) {
void this.loadSummary();
}
}
componentWillUnmount() {
this.unsubWarehouse?.();
}
componentDidUpdate(prevProps) {
if (this.props.open && !prevProps.open) {
const currentWarehouses = this.state.warehouses || warehouseAutomat.state.warehouses || [];
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?.number
? `Lieferung ${this.props.delivery.number}`
: 'Lieferung Wareneingang',
isBooking: false,
},
() => {
void warehouseAutomat.actions.load().catch(() => {});
if (this.props.summary) {
this.initAmountsFromSummary(this.props.summary, this.props.prefillLineId);
} else {
@@ -109,6 +146,14 @@ export default class BookDeliveryDialog extends PureComponent {
}
}
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;
@@ -201,7 +246,14 @@ export default class BookDeliveryDialog extends PureComponent {
return;
}
this.setState({ isBooking: true, error: null });
const { selectedWarehouseId } = this.state;
if (!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 {
const ledgerApi = this.context?.ledger;
const deliveryApi = this.context?.delivery;
@@ -209,6 +261,7 @@ export default class BookDeliveryDialog extends PureComponent {
await bookDeliveryLines({
delivery: summary.delivery || delivery,
linesToBook,
warehouseId: selectedWarehouseId,
ledgerApi,
transactionText,
});
@@ -443,6 +496,9 @@ export default class BookDeliveryDialog extends PureComponent {
amountsToBook,
transactionText,
isBooking,
warehouses,
selectedWarehouseId,
warehouseError,
} = this.state;
const lines = summary?.lines || [];
@@ -458,6 +514,8 @@ export default class BookDeliveryDialog extends PureComponent {
(l) => Number(amountsToBook[l.id]) > 0
).length;
const isFullyBooked = summary?.status === 'booked';
const selectedWarehouse = warehouses.find((w) => w.id === selectedWarehouseId);
const isWarehouseMissing = !selectedWarehouseId;
return (
<Dialog
@@ -529,7 +587,59 @@ export default class BookDeliveryDialog extends PureComponent {
</Box>
)}
<Box sx={{ mb: 2 }}>
{warehouses.length === 0 && !isFullyBooked && (
<Alert severity="error" sx={{ mb: 2 }}>
Es ist noch kein Lager eingerichtet. Bitte legen Sie unter „Artikel → Lager“ zuerst ein Lager an.
</Alert>
)}
<Box
sx={{
display: 'flex',
gap: 2,
mb: 2,
flexDirection: { xs: 'column', sm: 'row' },
}}
>
<FormControl
size="small"
required
error={Boolean(warehouseError)}
disabled={isBooking || isFullyBooked}
sx={{ minWidth: { sm: 240 }, flex: { sm: '0 0 auto' } }}
>
<InputLabel id="warehouse-select-label">Ziellager *</InputLabel>
<Select
labelId="warehouse-select-label"
id="warehouse-select"
value={selectedWarehouseId ?? ''}
label="Ziellager *"
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>
) : (
<FormHelperText>Pflichtfeld: Ziellager für die Buchung</FormHelperText>
)}
</FormControl>
<TextField
fullWidth
size="small"
@@ -546,9 +656,19 @@ export default class BookDeliveryDialog extends PureComponent {
Alle Artikel dieser Lieferung sind bereits vollständig im Lager verbucht.
</Alert>
) : totalToBook > 0 ? (
<Alert severity="info">
Es werden <strong>{totalToBook} Stück</strong> für{' '}
<strong>{countLinesToBook} Position{countLinesToBook === 1 ? '' : 'en'}</strong> als Lagerzugang (DELIVERY) im Ledger gebucht.
<Alert severity={selectedWarehouseId ? 'info' : 'warning'}>
{selectedWarehouseId ? (
<>
Es werden <strong>{totalToBook} Stück</strong> für{' '}
<strong>{countLinesToBook} Position{countLinesToBook === 1 ? '' : 'en'}</strong> als Lagerzugang (DELIVERY) in das Lager{' '}
<strong>„{selectedWarehouse?.name || '–'}“</strong> gebucht.
</>
) : (
<>
Bitte wählen Sie ein <strong>Ziellager</strong> aus, um die Buchung von{' '}
<strong>{totalToBook} Stück</strong> durchzuführen.
</>
)}
</Alert>
) : (
<Alert severity="warning">
@@ -569,7 +689,7 @@ export default class BookDeliveryDialog extends PureComponent {
variant="contained"
color="primary"
onClick={this.handleConfirmBook}
disabled={isBooking || totalToBook <= 0 || loading}
disabled={isBooking || totalToBook <= 0 || loading || isWarehouseMissing}
startIcon={
isBooking ? (
<CircularProgress size={16} color="inherit" />