u
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -64,7 +64,20 @@ export {
|
|||||||
buildArticleIndex,
|
buildArticleIndex,
|
||||||
selectArticleIndexItems,
|
selectArticleIndexItems,
|
||||||
} from './articleIndexAutomat';
|
} from './articleIndexAutomat';
|
||||||
export { default as deliveryAutomat } from './deliveryAutomat';
|
export {
|
||||||
|
default as deliveryAutomat,
|
||||||
|
CHUNK_SIZE as DELIVERY_CHUNK_SIZE,
|
||||||
|
MAX_LRU_CHUNKS as DELIVERY_MAX_LRU_CHUNKS,
|
||||||
|
selectDeliveries,
|
||||||
|
selectDeliveryTotal,
|
||||||
|
selectDeliveryUnfilteredTotal,
|
||||||
|
selectDeliveryPagination,
|
||||||
|
selectOnlyUnbooked,
|
||||||
|
isDeliveryBooked,
|
||||||
|
getDeliveryUnbookedCount,
|
||||||
|
getChunkStats as getDeliveryChunkStats,
|
||||||
|
subscribeChunkStats as subscribeDeliveryChunkStats,
|
||||||
|
} from './deliveryAutomat';
|
||||||
export { default as quoteAutomat } from './quoteAutomat';
|
export { default as quoteAutomat } from './quoteAutomat';
|
||||||
export {
|
export {
|
||||||
default as orderAutomat,
|
default as orderAutomat,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import warehouseAutomat from './warehouseAutomat';
|
|||||||
import attributeAutomat from './attributeAutomat';
|
import attributeAutomat from './attributeAutomat';
|
||||||
import propertyAutomat from './propertyAutomat';
|
import propertyAutomat from './propertyAutomat';
|
||||||
import supplierAutomat from './supplierAutomat';
|
import supplierAutomat from './supplierAutomat';
|
||||||
|
import deliveryAutomat from './deliveryAutomat';
|
||||||
import orderAutomat from './orderAutomat';
|
import orderAutomat from './orderAutomat';
|
||||||
import quoteAutomat from './quoteAutomat';
|
import quoteAutomat from './quoteAutomat';
|
||||||
import shipmentAutomat from './shipmentAutomat';
|
import shipmentAutomat from './shipmentAutomat';
|
||||||
@@ -264,6 +265,12 @@ function connect() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addTrackedListener(es, 'delivery', () => {
|
||||||
|
void deliveryAutomat.actions.reload().catch((err) => {
|
||||||
|
console.error('[sse] delivery refresh failed', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
let initialAmounts = {};
|
let initialAmounts = {};
|
||||||
if (Array.isArray(props.summary?.lines)) {
|
if (Array.isArray(props.summary?.lines)) {
|
||||||
for (const line of props.summary.lines) {
|
for (const line of props.summary.lines) {
|
||||||
if (!line.trackStock) continue;
|
|
||||||
if (props.prefillLineId != null) {
|
if (props.prefillLineId != null) {
|
||||||
initialAmounts[line.id] =
|
initialAmounts[line.id] =
|
||||||
line.id === props.prefillLineId
|
line.id === props.prefillLineId
|
||||||
@@ -175,7 +174,6 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
const amounts = {};
|
const amounts = {};
|
||||||
if (Array.isArray(summary?.lines)) {
|
if (Array.isArray(summary?.lines)) {
|
||||||
for (const line of summary.lines) {
|
for (const line of summary.lines) {
|
||||||
if (!line.trackStock) continue;
|
|
||||||
if (prefillLineId != null) {
|
if (prefillLineId != null) {
|
||||||
amounts[line.id] =
|
amounts[line.id] =
|
||||||
line.id === prefillLineId
|
line.id === prefillLineId
|
||||||
@@ -210,7 +208,6 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
if (!summary?.lines) return;
|
if (!summary?.lines) return;
|
||||||
const amounts = {};
|
const amounts = {};
|
||||||
for (const line of summary.lines) {
|
for (const line of summary.lines) {
|
||||||
if (!line.trackStock) continue;
|
|
||||||
amounts[line.id] = line.remainingAmount > 0 ? line.remainingAmount : 0;
|
amounts[line.id] = line.remainingAmount > 0 ? line.remainingAmount : 0;
|
||||||
}
|
}
|
||||||
this.setState({ amountsToBook: amounts });
|
this.setState({ amountsToBook: amounts });
|
||||||
@@ -221,7 +218,6 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
if (!summary?.lines) return;
|
if (!summary?.lines) return;
|
||||||
const amounts = {};
|
const amounts = {};
|
||||||
for (const line of summary.lines) {
|
for (const line of summary.lines) {
|
||||||
if (!line.trackStock) continue;
|
|
||||||
amounts[line.id] = 0;
|
amounts[line.id] = 0;
|
||||||
}
|
}
|
||||||
this.setState({ amountsToBook: amounts });
|
this.setState({ amountsToBook: amounts });
|
||||||
@@ -229,16 +225,16 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
|
|
||||||
async handleConfirmBook() {
|
async handleConfirmBook() {
|
||||||
const { delivery, onBookSuccess, onClose } = this.props;
|
const { delivery, onBookSuccess, onClose } = this.props;
|
||||||
const { summary, amountsToBook, transactionText } = this.state;
|
const { summary, amountsToBook, transactionText, selectedWarehouseId } = this.state;
|
||||||
if (!summary?.lines) return;
|
if (!summary?.lines) return;
|
||||||
|
|
||||||
const linesToBook = summary.lines
|
const linesToBook = summary.lines
|
||||||
.filter((line) => line.trackStock)
|
|
||||||
.map((line) => ({
|
.map((line) => ({
|
||||||
manifestarticleId: line.id,
|
manifestarticleId: line.id,
|
||||||
sku: line.sku,
|
sku: line.sku,
|
||||||
name: line.name,
|
name: line.name,
|
||||||
amount: Number(amountsToBook[line.id] || 0),
|
amount: Number(amountsToBook[line.id] || 0),
|
||||||
|
trackStock: Boolean(line.trackStock),
|
||||||
}))
|
}))
|
||||||
.filter((l) => l.amount > 0);
|
.filter((l) => l.amount > 0);
|
||||||
|
|
||||||
@@ -247,8 +243,8 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { selectedWarehouseId } = this.state;
|
const hasTracked = linesToBook.some((l) => l.trackStock);
|
||||||
if (!selectedWarehouseId) {
|
if (hasTracked && !selectedWarehouseId) {
|
||||||
this.setState({ warehouseError: 'Bitte wählen Sie ein Ziellager aus.' });
|
this.setState({ warehouseError: 'Bitte wählen Sie ein Ziellager aus.' });
|
||||||
enqueueSnackbar('Bitte wählen Sie ein Ziellager aus.', { variant: 'warning' });
|
enqueueSnackbar('Bitte wählen Sie ein Ziellager aus.', { variant: 'warning' });
|
||||||
return;
|
return;
|
||||||
@@ -265,7 +261,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
|
|
||||||
const totalBookedQty = linesToBook.reduce((sum, l) => sum + l.amount, 0);
|
const totalBookedQty = linesToBook.reduce((sum, l) => sum + l.amount, 0);
|
||||||
enqueueSnackbar(
|
enqueueSnackbar(
|
||||||
`${linesToBook.length} Position${linesToBook.length === 1 ? '' : 'en'} (${totalBookedQty} Stk.) erfolgreich ins Lager gebucht.`,
|
`${linesToBook.length} Position${linesToBook.length === 1 ? '' : 'en'} (${totalBookedQty} Stk.) erfolgreich gebucht.`,
|
||||||
{ variant: 'success' }
|
{ variant: 'success' }
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -387,12 +383,12 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
|
|
||||||
renderLinesTable() {
|
renderLinesTable() {
|
||||||
const { summary, amountsToBook, isBooking } = this.state;
|
const { summary, amountsToBook, isBooking } = this.state;
|
||||||
const lines = (summary?.lines || []).filter((l) => l.trackStock);
|
const lines = summary?.lines || [];
|
||||||
|
|
||||||
if (lines.length === 0) {
|
if (lines.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Alert severity="info" sx={{ my: 2 }}>
|
<Alert severity="info" sx={{ my: 2 }}>
|
||||||
Diese Lieferung enthält keine lagergeführten Artikel zum Einbuchen.
|
Diese Lieferung enthält keine Positionen zum Verbuchen.
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -406,7 +402,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
<TableCell sx={{ fontWeight: 600 }}>Artikel</TableCell>
|
<TableCell sx={{ fontWeight: 600 }}>Artikel</TableCell>
|
||||||
<TableCell align="right" sx={{ fontWeight: 600 }}>Liefermenge</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 600 }}>Liefermenge</TableCell>
|
||||||
<TableCell align="right" sx={{ fontWeight: 600 }}>Bereits gebucht</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 600 }}>Bereits gebucht</TableCell>
|
||||||
<TableCell align="right" sx={{ fontWeight: 600, width: 140 }}>Jetzt einbuchen</TableCell>
|
<TableCell align="right" sx={{ fontWeight: 600, width: 140 }}>Jetzt verbuchen</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -420,9 +416,19 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
{line.sku || '–'}
|
{line.sku || '–'}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell sx={{ maxWidth: 280 }}>
|
<TableCell sx={{ maxWidth: 280 }}>
|
||||||
<Typography variant="body2" sx={{ fontWeight: 500 }} noWrap>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
{line.name || '–'}
|
<Typography variant="body2" sx={{ fontWeight: 500 }} noWrap>
|
||||||
</Typography>
|
{line.name || '–'}
|
||||||
|
</Typography>
|
||||||
|
{!line.trackStock && (
|
||||||
|
<Chip
|
||||||
|
size="small"
|
||||||
|
label="Ohne Lagerbestand"
|
||||||
|
variant="outlined"
|
||||||
|
sx={{ fontSize: '0.7rem', height: 20, color: 'text.secondary', borderColor: 'divider' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="right">
|
<TableCell align="right">
|
||||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||||
@@ -493,8 +499,14 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
warehouseError,
|
warehouseError,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const lines = (summary?.lines || []).filter((l) => l.trackStock);
|
const lines = summary?.lines || [];
|
||||||
const hasTrackStock = lines.length > 0;
|
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 =
|
const prefillLine =
|
||||||
this.props.prefillLineId != null
|
this.props.prefillLineId != null
|
||||||
? lines.find((l) => l.id === this.props.prefillLineId)
|
? lines.find((l) => l.id === this.props.prefillLineId)
|
||||||
@@ -503,12 +515,10 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
(sum, l) => sum + (Number(amountsToBook[l.id]) || 0),
|
(sum, l) => sum + (Number(amountsToBook[l.id]) || 0),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
const countLinesToBook = lines.filter(
|
const countLinesToBook = linesWithAmountsToBook.length;
|
||||||
(l) => Number(amountsToBook[l.id]) > 0
|
const isFullyBooked = hasLines && summary?.status === 'booked';
|
||||||
).length;
|
|
||||||
const isFullyBooked = hasTrackStock && summary?.status === 'booked';
|
|
||||||
const selectedWarehouse = warehouses.find((w) => w.id === selectedWarehouseId);
|
const selectedWarehouse = warehouses.find((w) => w.id === selectedWarehouseId);
|
||||||
const isWarehouseMissing = !selectedWarehouseId;
|
const isWarehouseMissing = hasTrackedLinesToBook && !selectedWarehouseId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppDialog
|
<AppDialog
|
||||||
@@ -528,7 +538,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<Inventory2OutlinedIcon color="primary" />
|
<Inventory2OutlinedIcon color="primary" />
|
||||||
<Typography variant="h6" component="span" sx={{ fontWeight: 600 }}>
|
<Typography variant="h6" component="span" sx={{ fontWeight: 600 }}>
|
||||||
Lieferung {delivery?.number || ''} ins Lager buchen
|
Lieferung {delivery?.number || ''} verbuchen
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<IconButton size="small" onClick={onClose} disabled={isBooking}>
|
<IconButton size="small" onClick={onClose} disabled={isBooking}>
|
||||||
@@ -552,13 +562,13 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
<>
|
<>
|
||||||
{prefillLine && (
|
{prefillLine && (
|
||||||
<Alert severity="info" sx={{ mb: 2 }}>
|
<Alert severity="info" sx={{ mb: 2 }}>
|
||||||
Vorausgewählt zum Einbuchen: Position{' '}
|
Vorausgewählt zum Verbuchen: Position{' '}
|
||||||
<strong>{prefillLine.sku ? `SKU ${prefillLine.sku}` : prefillLine.name}</strong> ({amountsToBook[prefillLine.id] || 0} Stk.). Andere Positionen stehen auf 0.
|
<strong>{prefillLine.sku ? `SKU ${prefillLine.sku}` : prefillLine.name}</strong> ({amountsToBook[prefillLine.id] || 0} Stk.). Andere Positionen stehen auf 0.
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
{this.renderLinesTable()}
|
{this.renderLinesTable()}
|
||||||
|
|
||||||
{!isFullyBooked && hasTrackStock && (
|
{!isFullyBooked && hasLines && (
|
||||||
<Box sx={{ display: 'flex', gap: 1, mb: 2 }}>
|
<Box sx={{ display: 'flex', gap: 1, mb: 2 }}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
@@ -580,13 +590,13 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasTrackStock && warehouses.length === 0 && !isFullyBooked && (
|
{hasTrackedLinesToBook && warehouses.length === 0 && !isFullyBooked && (
|
||||||
<Alert severity="error" sx={{ mb: 2 }}>
|
<Alert severity="error" sx={{ mb: 2 }}>
|
||||||
Es ist noch kein Lager eingerichtet. Bitte legen Sie unter „Artikel → Lager“ zuerst ein Lager an.
|
Es ist noch kein Lager eingerichtet. Für lagergeführte Artikel legen Sie bitte unter „Artikel → Lager“ zuerst ein Lager an.
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasTrackStock && (
|
{hasLines && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -595,44 +605,52 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
flexDirection: { xs: 'column', sm: 'row' },
|
flexDirection: { xs: 'column', sm: 'row' },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControl
|
{hasTrackStock && (
|
||||||
size="small"
|
<FormControl
|
||||||
required
|
size="small"
|
||||||
error={Boolean(warehouseError)}
|
required={hasTrackedLinesToBook}
|
||||||
disabled={isBooking || isFullyBooked}
|
error={Boolean(warehouseError)}
|
||||||
sx={{ minWidth: { sm: 240 }, flex: { sm: '0 0 auto' } }}
|
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 ? (
|
<InputLabel id="warehouse-select-label">
|
||||||
<MenuItem value="" disabled>
|
Ziellager {hasTrackedLinesToBook ? '*' : ''}
|
||||||
Keine Lager vorhanden
|
</InputLabel>
|
||||||
</MenuItem>
|
<Select
|
||||||
) : (
|
labelId="warehouse-select-label"
|
||||||
warehouses.map((wh) => (
|
id="warehouse-select"
|
||||||
<MenuItem key={wh.id} value={wh.id}>
|
value={selectedWarehouseId ?? ''}
|
||||||
{wh.name}
|
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>
|
</MenuItem>
|
||||||
))
|
) : (
|
||||||
|
warehouses.map((wh) => (
|
||||||
|
<MenuItem key={wh.id} value={wh.id}>
|
||||||
|
{wh.name}
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
{warehouseError ? (
|
||||||
|
<FormHelperText>{warehouseError}</FormHelperText>
|
||||||
|
) : (
|
||||||
|
<FormHelperText>
|
||||||
|
{hasTrackedLinesToBook
|
||||||
|
? 'Pflichtfeld: Ziellager für lagergeführte Artikel'
|
||||||
|
: 'Ziellager (optional)'}
|
||||||
|
</FormHelperText>
|
||||||
)}
|
)}
|
||||||
</Select>
|
</FormControl>
|
||||||
{warehouseError ? (
|
)}
|
||||||
<FormHelperText>{warehouseError}</FormHelperText>
|
|
||||||
) : (
|
|
||||||
<FormHelperText>Pflichtfeld: Ziellager für die Buchung</FormHelperText>
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
@@ -648,26 +666,29 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
|
|
||||||
{isFullyBooked ? (
|
{isFullyBooked ? (
|
||||||
<Alert severity="success" icon={<CheckCircleOutlineOutlinedIcon />}>
|
<Alert severity="success" icon={<CheckCircleOutlineOutlinedIcon />}>
|
||||||
Alle Artikel dieser Lieferung sind bereits vollständig im Lager verbucht.
|
Alle Positionen dieser Lieferung sind bereits vollständig im Ledger verbucht.
|
||||||
</Alert>
|
</Alert>
|
||||||
) : hasTrackStock && totalToBook > 0 ? (
|
) : countLinesToBook > 0 ? (
|
||||||
<Alert severity={selectedWarehouseId ? 'info' : 'warning'}>
|
<Alert severity={isWarehouseMissing ? 'warning' : 'info'}>
|
||||||
{selectedWarehouseId ? (
|
{isWarehouseMissing ? (
|
||||||
<>
|
<>
|
||||||
Es werden <strong>{totalToBook} Stück</strong> für{' '}
|
Bitte wählen Sie ein <strong>Ziellager</strong> aus, um die lagergeführten Artikel zu buchen.
|
||||||
<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{' '}
|
Es werden <strong>{totalToBook} Stück</strong> für{' '}
|
||||||
<strong>{totalToBook} Stück</strong> durchzuführen.
|
<strong>{countLinesToBook} Position{countLinesToBook === 1 ? '' : 'en'}</strong>{' '}
|
||||||
|
{hasTrackedLinesToBook && selectedWarehouse ? (
|
||||||
|
<>in das Lager <strong>„{selectedWarehouse?.name || '–'}“</strong> verbucht.</>
|
||||||
|
) : (
|
||||||
|
<>als Dokumentenbuchung im Ledger verbucht (ohne Lagerbestandsführung).</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
) : hasTrackStock ? (
|
) : hasLines ? (
|
||||||
<Alert severity="warning">
|
<Alert severity="warning">
|
||||||
Wählen Sie mindestens eine Menge größer als 0 aus, um den Lagerzugang zu buchen.
|
Wählen Sie mindestens eine Menge größer als 0 aus, um Positionen zu verbuchen.
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
@@ -679,7 +700,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
{isFullyBooked ? 'Schließen' : 'Abbrechen'}
|
{isFullyBooked ? 'Schließen' : 'Abbrechen'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{!isFullyBooked && lines.length > 0 && (
|
{!isFullyBooked && hasLines && (
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -694,7 +715,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
|||||||
}
|
}
|
||||||
sx={{ minWidth: 160, fontWeight: 600 }}
|
sx={{ minWidth: 160, fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
{isBooking ? 'Wird gebucht...' : 'Jetzt ins Lager buchen'}
|
{isBooking ? 'Wird gebucht...' : 'Jetzt verbuchen'}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -40,17 +40,20 @@ import orderAutomat, {
|
|||||||
} from '../automats/orderAutomat';
|
} from '../automats/orderAutomat';
|
||||||
import quoteAutomat from '../automats/quoteAutomat';
|
import quoteAutomat from '../automats/quoteAutomat';
|
||||||
import shipmentAutomat from '../automats/shipmentAutomat';
|
import shipmentAutomat from '../automats/shipmentAutomat';
|
||||||
|
import deliveryAutomat from '../automats/deliveryAutomat';
|
||||||
import { formatPrice } from '../utils/price';
|
import { formatPrice } from '../utils/price';
|
||||||
|
|
||||||
function getAutomatFor(target) {
|
function getAutomatFor(target) {
|
||||||
if (target === 'quote') return quoteAutomat;
|
if (target === 'quote') return quoteAutomat;
|
||||||
if (target === 'shipment') return shipmentAutomat;
|
if (target === 'shipment') return shipmentAutomat;
|
||||||
|
if (target === 'delivery') return deliveryAutomat;
|
||||||
return orderAutomat;
|
return orderAutomat;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInitialTarget() {
|
function getInitialTarget() {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
const p = window.location.pathname;
|
const p = window.location.pathname;
|
||||||
|
if (p.startsWith('/lieferung')) return 'delivery';
|
||||||
if (p.startsWith('/auftrag/ausliefern')) return 'shipment';
|
if (p.startsWith('/auftrag/ausliefern')) return 'shipment';
|
||||||
if (p.startsWith('/auftrag/bestellung')) return 'order';
|
if (p.startsWith('/auftrag/bestellung')) return 'order';
|
||||||
if (p.startsWith('/auftrag')) return 'quote';
|
if (p.startsWith('/auftrag')) return 'quote';
|
||||||
@@ -296,6 +299,10 @@ class ChunkCacheMonitor extends PureComponent {
|
|||||||
if (p.onlyUnpaid) automat.actions.setOnlyUnpaid(true);
|
if (p.onlyUnpaid) automat.actions.setOnlyUnpaid(true);
|
||||||
if (p.onlyUndelivered) automat.actions.setOnlyUndelivered(true);
|
if (p.onlyUndelivered) automat.actions.setOnlyUndelivered(true);
|
||||||
}
|
}
|
||||||
|
if (this.state.target === 'delivery') {
|
||||||
|
if (p.onlyUnbooked) automat.actions.setOnlyUnbooked(true);
|
||||||
|
if (p.supplierId) automat.actions.setSupplierId(p.supplierId);
|
||||||
|
}
|
||||||
if (this.state.target === 'shipment') {
|
if (this.state.target === 'shipment') {
|
||||||
if (p.stateFilter) automat.actions.setStateFilter(p.stateFilter);
|
if (p.stateFilter) automat.actions.setStateFilter(p.stateFilter);
|
||||||
}
|
}
|
||||||
@@ -723,11 +730,15 @@ class ChunkCacheMonitor extends PureComponent {
|
|||||||
{chunkOrders.map(({ item: order, index }) => {
|
{chunkOrders.map(({ item: order, index }) => {
|
||||||
const isOrd = this.state.target === 'order';
|
const isOrd = this.state.target === 'order';
|
||||||
const isShip = this.state.target === 'shipment';
|
const isShip = this.state.target === 'shipment';
|
||||||
|
const isDeliv = this.state.target === 'delivery';
|
||||||
const paid = isOrd ? isOrderPaid(order) : false;
|
const paid = isOrd ? isOrderPaid(order) : false;
|
||||||
const delivered = isOrd ? isOrderDelivered(order) : false;
|
const delivered = isOrd ? isOrderDelivered(order) : false;
|
||||||
const gross = order.grossTotal != null ? Number(order.grossTotal) : (order.lines ? orderGrossTotal(order.lines) : 0);
|
const gross = order.grossTotal != null ? Number(order.grossTotal) : (order.lines ? orderGrossTotal(order.lines) : 0);
|
||||||
const customer = order.customerName || (order.entityId ? `Kunde #${order.entityId}` : '–');
|
const customer = isDeliv
|
||||||
|
? (order.supplierName || (order.supplierId ? `Lieferant #${order.supplierId}` : '–'))
|
||||||
|
: (order.customerName || (order.entityId ? `Kunde #${order.entityId}` : '–'));
|
||||||
const posCount = order.positions != null ? order.positions : (order.lines?.length ?? 0);
|
const posCount = order.positions != null ? order.positions : (order.lines?.length ?? 0);
|
||||||
|
const unbooked = order.ledger?.unbookedPositions ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -780,6 +791,14 @@ class ChunkCacheMonitor extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{isDeliv && (
|
||||||
|
<Chip
|
||||||
|
size="small"
|
||||||
|
label={unbooked === 0 ? 'Gebucht' : `${unbooked} Ungebucht`}
|
||||||
|
color={unbooked === 0 ? 'success' : 'warning'}
|
||||||
|
sx={{ height: 20, fontSize: '0.7rem' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{isShip && (
|
{isShip && (
|
||||||
<>
|
<>
|
||||||
<Chip
|
<Chip
|
||||||
@@ -1072,6 +1091,14 @@ class ChunkCacheMonitor extends PureComponent {
|
|||||||
onClick={() => this.switchTarget('order')}
|
onClick={() => this.switchTarget('order')}
|
||||||
sx={{ cursor: 'pointer', height: 24, fontSize: '0.72rem', fontWeight: this.state.target === 'order' ? 700 : 500 }}
|
sx={{ cursor: 'pointer', height: 24, fontSize: '0.72rem', fontWeight: this.state.target === 'order' ? 700 : 500 }}
|
||||||
/>
|
/>
|
||||||
|
<Chip
|
||||||
|
size="small"
|
||||||
|
color={this.state.target === 'delivery' ? 'primary' : 'default'}
|
||||||
|
variant={this.state.target === 'delivery' ? 'filled' : 'outlined'}
|
||||||
|
label="Lieferungen (deliveryAutomat)"
|
||||||
|
onClick={() => this.switchTarget('delivery')}
|
||||||
|
sx={{ cursor: 'pointer', height: 24, fontSize: '0.72rem', fontWeight: this.state.target === 'delivery' ? 700 : 500 }}
|
||||||
|
/>
|
||||||
<Chip
|
<Chip
|
||||||
size="small"
|
size="small"
|
||||||
color={this.state.target === 'shipment' ? 'primary' : 'default'}
|
color={this.state.target === 'shipment' ? 'primary' : 'default'}
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def
|
|||||||
});
|
});
|
||||||
const bookedAmount = deliveryEntries.reduce((sum, e) => sum + Number(e.amount || 0), 0);
|
const bookedAmount = deliveryEntries.reduce((sum, e) => sum + Number(e.amount || 0), 0);
|
||||||
const targetAmount = Number(line.amount || 0);
|
const targetAmount = Number(line.amount || 0);
|
||||||
const remainingAmount = trackStock ? Math.max(0, targetAmount - bookedAmount) : 0;
|
const remainingAmount = Math.max(0, targetAmount - bookedAmount);
|
||||||
const isBooked = trackStock ? (targetAmount > 0 ? bookedAmount >= targetAmount : bookedAmount > 0) : true;
|
const isBooked = targetAmount > 0 ? bookedAmount >= targetAmount : bookedAmount > 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...line,
|
...line,
|
||||||
@@ -134,21 +134,20 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def
|
|||||||
remainingAmount,
|
remainingAmount,
|
||||||
isBooked,
|
isBooked,
|
||||||
trackStock,
|
trackStock,
|
||||||
isBookable: trackStock,
|
isBookable: true,
|
||||||
ledgerEntries: deliveryEntries,
|
ledgerEntries: deliveryEntries,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const stockLines = linesWithLedger.filter((l) => l.trackStock);
|
const totalPositions = linesWithLedger.length;
|
||||||
const totalPositions = stockLines.length;
|
const bookedPositions = linesWithLedger.filter((l) => l.isBooked).length;
|
||||||
const bookedPositions = stockLines.filter((l) => l.isBooked).length;
|
const totalQuantity = linesWithLedger.reduce((sum, l) => sum + l.targetAmount, 0);
|
||||||
const totalQuantity = stockLines.reduce((sum, l) => sum + l.targetAmount, 0);
|
const bookedQuantity = linesWithLedger.reduce((sum, l) => sum + l.bookedAmount, 0);
|
||||||
const bookedQuantity = stockLines.reduce((sum, l) => sum + l.bookedAmount, 0);
|
const hasTrackStock = linesWithLedger.some((l) => l.trackStock);
|
||||||
const hasTrackStock = stockLines.length > 0;
|
|
||||||
|
|
||||||
let status;
|
let status;
|
||||||
if (!hasTrackStock) {
|
if (totalPositions === 0) {
|
||||||
status = 'empty';
|
status = 'empty';
|
||||||
} else if (bookedPositions === totalPositions && bookedQuantity >= totalQuantity) {
|
} else if (bookedPositions === totalPositions && bookedQuantity >= totalQuantity) {
|
||||||
status = 'booked';
|
status = 'booked';
|
||||||
@@ -176,8 +175,8 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def
|
|||||||
*
|
*
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {Object} params.delivery - The delivery object (used for transaction memo and fallback)
|
* @param {Object} params.delivery - The delivery object (used for transaction memo and fallback)
|
||||||
* @param {Array<{ manifestarticleId?: number, id?: number, amount: number, sku?: string }>} params.linesToBook
|
* @param {Array<{ manifestarticleId?: number, id?: number, amount: number, sku?: string, trackStock?: boolean }>} params.linesToBook
|
||||||
* @param {number|string} params.warehouseId - Mandatory warehouse ID
|
* @param {number|string} [params.warehouseId] - Warehouse ID (mandatory only when at least one line tracks stock)
|
||||||
* @param {Object} [params.stockApi]
|
* @param {Object} [params.stockApi]
|
||||||
* @param {string} [params.transactionText]
|
* @param {string} [params.transactionText]
|
||||||
* @returns {Promise<Array<Object>>}
|
* @returns {Promise<Array<Object>>}
|
||||||
@@ -189,15 +188,17 @@ export async function bookDeliveryLines({
|
|||||||
stockApi = defaultStockApi,
|
stockApi = defaultStockApi,
|
||||||
transactionText,
|
transactionText,
|
||||||
}) {
|
}) {
|
||||||
const numericWarehouseId = Number(warehouseId);
|
|
||||||
if (!numericWarehouseId || numericWarehouseId <= 0) {
|
|
||||||
throw new Error('Bitte wählen Sie ein Lager aus.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(linesToBook) || linesToBook.length === 0) {
|
if (!Array.isArray(linesToBook) || linesToBook.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const numericWarehouseId = Number(warehouseId) || null;
|
||||||
|
const hasTrackedLines = linesToBook.some((l) => l.trackStock);
|
||||||
|
|
||||||
|
if (hasTrackedLines && (!numericWarehouseId || numericWarehouseId <= 0)) {
|
||||||
|
throw new Error('Bitte wählen Sie ein Lager aus.');
|
||||||
|
}
|
||||||
|
|
||||||
const memo = String(
|
const memo = String(
|
||||||
transactionText != null && transactionText !== ''
|
transactionText != null && transactionText !== ''
|
||||||
? transactionText
|
? transactionText
|
||||||
|
|||||||
@@ -136,7 +136,9 @@ export function getMaxDocumentNumber(kind = 'quote') {
|
|||||||
const prefixStyle = refSeq?.prefix ?? '';
|
const prefixStyle = refSeq?.prefix ?? '';
|
||||||
|
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
if (
|
if (kind === 'delivery') {
|
||||||
|
prefix = prefixStyle.startsWith('DEL-') ? 'DEL-' : (prefixStyle.startsWith('L-') ? 'L-' : 'LS-');
|
||||||
|
} else if (
|
||||||
prefixStyle.startsWith('ART-') ||
|
prefixStyle.startsWith('ART-') ||
|
||||||
prefixStyle.startsWith('LS-') ||
|
prefixStyle.startsWith('LS-') ||
|
||||||
prefixStyle.startsWith('KD-') ||
|
prefixStyle.startsWith('KD-') ||
|
||||||
|
|||||||
Reference in New Issue
Block a user