Compare commits
2 Commits
82db75ac0f
...
0b0ffb2203
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b0ffb2203 | ||
|
|
b3a81efe46 |
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
@@ -184,21 +184,38 @@ class ManifestFormLine extends PureComponent {
|
|||||||
|
|
||||||
renderLedgerChip() {
|
renderLedgerChip() {
|
||||||
const { ledgerLine, onBookLine } = this.props;
|
const { ledgerLine, onBookLine } = this.props;
|
||||||
if (!ledgerLine || !ledgerLine.trackStock) return null;
|
if (!ledgerLine) return null;
|
||||||
const isBooked = ledgerLine.isBooked;
|
const isBooked = ledgerLine.isBooked;
|
||||||
const isPartial = !isBooked && ledgerLine.bookedAmount > 0;
|
const isPartial = !isBooked && ledgerLine.bookedAmount > 0;
|
||||||
const isClickable = Boolean(onBookLine);
|
const isClickable = Boolean(onBookLine);
|
||||||
|
const isTracked = Boolean(ledgerLine.trackStock);
|
||||||
|
|
||||||
|
const tooltipTitle = isTracked
|
||||||
|
? isBooked
|
||||||
|
? `Vollständig im Lager gebucht: ${ledgerLine.bookedAmount} Stück (Klicken für Details)`
|
||||||
|
: isPartial
|
||||||
|
? `Teilweise im Lager gebucht: ${ledgerLine.bookedAmount} von ${ledgerLine.targetAmount} Stück (Klicken zum Einbuchen dieser Position)`
|
||||||
|
: `Noch nicht im Lager gebucht (${ledgerLine.targetAmount} Stück – Klicken zum Einbuchen dieser Position)`
|
||||||
|
: isBooked
|
||||||
|
? `Vollständig im Ledger verbucht: ${ledgerLine.bookedAmount} Stück (Klicken für Details)`
|
||||||
|
: isPartial
|
||||||
|
? `Teilweise im Ledger verbucht: ${ledgerLine.bookedAmount} von ${ledgerLine.targetAmount} Stück (Klicken zum Verbuchen dieser Position)`
|
||||||
|
: `Noch nicht im Ledger verbucht (${ledgerLine.targetAmount} Stück – Klicken zum Verbuchen dieser Position)`;
|
||||||
|
|
||||||
|
const label = isTracked
|
||||||
|
? isBooked
|
||||||
|
? `Lager: ${ledgerLine.bookedAmount} Stk. gebucht`
|
||||||
|
: isPartial
|
||||||
|
? `Lager: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.`
|
||||||
|
: 'Lager: offen'
|
||||||
|
: isBooked
|
||||||
|
? `Gebucht: ${ledgerLine.bookedAmount} Stk.`
|
||||||
|
: isPartial
|
||||||
|
? `Gebucht: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.`
|
||||||
|
: 'Verbuchen: offen';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip title={tooltipTitle}>
|
||||||
title={
|
|
||||||
isBooked
|
|
||||||
? `Vollständig im Lager gebucht: ${ledgerLine.bookedAmount} Stück (Klicken für Details)`
|
|
||||||
: isPartial
|
|
||||||
? `Teilweise im Lager gebucht: ${ledgerLine.bookedAmount} von ${ledgerLine.targetAmount} Stück (Klicken zum Einbuchen dieser Position)`
|
|
||||||
: `Noch nicht im Lager gebucht (${ledgerLine.targetAmount} Stück – Klicken zum Einbuchen dieser Position)`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Chip
|
<Chip
|
||||||
size="small"
|
size="small"
|
||||||
clickable={isClickable}
|
clickable={isClickable}
|
||||||
@@ -212,35 +229,47 @@ class ManifestFormLine extends PureComponent {
|
|||||||
}
|
}
|
||||||
icon={
|
icon={
|
||||||
isBooked ? (
|
isBooked ? (
|
||||||
<CheckCircleOutlineOutlinedIcon sx={{ '&&': { fontSize: 13 } }} />
|
<CheckCircleOutlineOutlinedIcon sx={{ '&&': { fontSize: 13, color: 'inherit' } }} />
|
||||||
) : (
|
) : (
|
||||||
<Inventory2OutlinedIcon sx={{ '&&': { fontSize: 13 } }} />
|
<Inventory2OutlinedIcon sx={{ '&&': { fontSize: 13, color: 'inherit' } }} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
label={
|
label={label}
|
||||||
isBooked
|
variant={isBooked ? 'outlined' : 'filled'}
|
||||||
? `Lager: ${ledgerLine.bookedAmount} Stk. gebucht`
|
|
||||||
: isPartial
|
|
||||||
? `Lager: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.`
|
|
||||||
: 'Lager: offen'
|
|
||||||
}
|
|
||||||
color={isBooked ? 'success' : isPartial ? 'warning' : 'default'}
|
|
||||||
variant="outlined"
|
|
||||||
sx={{
|
sx={{
|
||||||
height: 20,
|
height: 22,
|
||||||
fontSize: '0.68rem',
|
fontSize: '0.7rem',
|
||||||
fontWeight: 600,
|
fontWeight: 700,
|
||||||
cursor: isClickable ? 'pointer' : 'default',
|
cursor: isClickable ? 'pointer' : 'default',
|
||||||
bgcolor: isBooked ? 'success.50' : undefined,
|
...(isBooked
|
||||||
'&:hover': isClickable
|
|
||||||
? {
|
? {
|
||||||
bgcolor: isBooked
|
bgcolor: 'rgba(21, 128, 61, 0.08)',
|
||||||
? 'success.100'
|
color: '#15803d',
|
||||||
: isPartial
|
borderColor: '#15803d',
|
||||||
? 'warning.100'
|
borderWidth: 1,
|
||||||
: 'action.hover',
|
borderStyle: 'solid',
|
||||||
|
'&:hover': isClickable ? { bgcolor: 'rgba(21, 128, 61, 0.16)' } : undefined,
|
||||||
}
|
}
|
||||||
: undefined,
|
: isPartial
|
||||||
|
? {
|
||||||
|
bgcolor: '#d97706',
|
||||||
|
color: '#ffffff',
|
||||||
|
border: 'none',
|
||||||
|
'&:hover': isClickable ? { bgcolor: '#b45309' } : undefined,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
bgcolor: 'primary.main',
|
||||||
|
color: '#ffffff',
|
||||||
|
border: 'none',
|
||||||
|
'&:hover': isClickable ? { bgcolor: 'primary.dark' } : undefined,
|
||||||
|
}),
|
||||||
|
'& .MuiChip-label': {
|
||||||
|
color: 'inherit',
|
||||||
|
px: 0.75,
|
||||||
|
},
|
||||||
|
'& .MuiChip-icon': {
|
||||||
|
color: 'inherit',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -1122,7 +1151,7 @@ class ManifestFormLine extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { columns = LINE_COLUMNS, saving } = this.props;
|
const { columns = LINE_COLUMNS, saving, ledgerLine } = this.props;
|
||||||
const secondLine = this.renderSecondLine();
|
const secondLine = this.renderSecondLine();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -1153,11 +1182,52 @@ class ManifestFormLine extends PureComponent {
|
|||||||
pl: 0.5,
|
pl: 0.5,
|
||||||
pr: 0.5,
|
pr: 0.5,
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
width: this.props.onMove ? 92 : 40,
|
width: (this.props.onMove ? 80 : 36) + (this.props.kind === 'delivery' && Boolean(this.props.onBookLine) ? 36 : 0),
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.25 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.25 }}>
|
||||||
|
{this.props.kind === 'delivery' && Boolean(this.props.onBookLine) && (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
!ledgerLine
|
||||||
|
? 'Lieferung zuerst speichern, um diese Position zu verbuchen'
|
||||||
|
: ledgerLine.isBooked
|
||||||
|
? 'Position bereits verbucht (Klicken zum Einsehen)'
|
||||||
|
: `Position verbuchen (${ledgerLine.remainingAmount > 0 ? `${ledgerLine.remainingAmount} Stk.` : ''})`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
aria-label="Position verbuchen"
|
||||||
|
disabled={saving || !ledgerLine}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (ledgerLine) {
|
||||||
|
this.props.onBookLine(ledgerLine);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
color={
|
||||||
|
!ledgerLine
|
||||||
|
? 'default'
|
||||||
|
: ledgerLine.isBooked
|
||||||
|
? 'success'
|
||||||
|
: ledgerLine.bookedAmount > 0
|
||||||
|
? 'warning'
|
||||||
|
: 'primary'
|
||||||
|
}
|
||||||
|
sx={{ p: 0.35 }}
|
||||||
|
>
|
||||||
|
{ledgerLine?.isBooked ? (
|
||||||
|
<CheckCircleOutlineOutlinedIcon sx={{ fontSize: 18 }} />
|
||||||
|
) : (
|
||||||
|
<Inventory2OutlinedIcon sx={{ fontSize: 18 }} />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
{this.props.onMove && (
|
{this.props.onMove && (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ class ManifestHeader extends PureComponent {
|
|||||||
Wiederherstellen
|
Wiederherstellen
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{docId != null && kind === 'delivery' && onOpenBookDialog && ledgerSummary?.hasTrackStock && (
|
{docId != null && kind === 'delivery' && onOpenBookDialog && Boolean(ledgerSummary && (ledgerSummary.totalPositions > 0 || ledgerSummary.lines?.length > 0 || ledgerSummary.hasTrackStock)) && (
|
||||||
<Button
|
<Button
|
||||||
variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'}
|
variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'}
|
||||||
color={
|
color={
|
||||||
@@ -535,13 +535,17 @@ class ManifestHeader extends PureComponent {
|
|||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
fontSize: '0.8125rem',
|
fontSize: '0.8125rem',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
|
...(ledgerSummary?.status !== 'booked' && {
|
||||||
|
color: '#ffffff',
|
||||||
|
'& .MuiButton-startIcon': { color: '#ffffff' },
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ledgerSummary?.status === 'booked'
|
{ledgerSummary?.status === 'booked'
|
||||||
? 'Lager gebucht'
|
? 'Vollständig gebucht'
|
||||||
: ledgerSummary?.status === 'partial'
|
: ledgerSummary?.status === 'partial'
|
||||||
? 'Rest einbuchen'
|
? 'Rest verbuchen'
|
||||||
: 'Ins Lager buchen'}
|
: 'Lieferung verbuchen'}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{kind === 'quote' && docId != null && onCreateOrder && (
|
{kind === 'quote' && docId != null && onCreateOrder && (
|
||||||
|
|||||||
@@ -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