diff --git a/src/components/manifest/BookDeliveryDialog.jsx b/src/components/manifest/BookDeliveryDialog.jsx
index 4e784c5..296bea7 100644
--- a/src/components/manifest/BookDeliveryDialog.jsx
+++ b/src/components/manifest/BookDeliveryDialog.jsx
@@ -27,6 +27,7 @@ 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 } from './manifestConfig';
@@ -34,7 +35,8 @@ import {
fetchDeliveryLedgerSummary,
bookDeliveryLines,
} from '../../utils/deliveryLedger';
-import { getArticle } from '../../automats/artikelAutomat';
+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';
@@ -141,6 +143,7 @@ export default class BookDeliveryDialog extends PureComponent {
selectedWarehouseId: initialWarehouses.length === 1 ? initialWarehouses[0].id : '',
warehouseError: null,
supplierArticlesMap: initialSupplierMap,
+ enablingTrackStockBySku: {},
};
this.handleAmountChange = this.handleAmountChange.bind(this);
@@ -149,6 +152,7 @@ export default class BookDeliveryDialog extends PureComponent {
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() {
@@ -175,6 +179,9 @@ export default class BookDeliveryDialog extends PureComponent {
};
});
});
+ this.unsubArticle = artikelAutomat.subscribe(() => {
+ this.forceUpdate();
+ });
void warehouseAutomat.actions.load().catch(() => {});
this.loadSupplierArticlesIfNeeded();
@@ -185,6 +192,75 @@ export default class BookDeliveryDialog extends PureComponent {
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) {
@@ -472,7 +548,7 @@ export default class BookDeliveryDialog extends PureComponent {
}
renderLinesTable() {
- const { summary, amountsToBook, isBooking, supplierArticlesMap } = this.state;
+ const { summary, amountsToBook, isBooking, supplierArticlesMap, enablingTrackStockBySku } = this.state;
const lines = summary?.lines || [];
if (lines.length === 0) {
@@ -493,6 +569,7 @@ export default class BookDeliveryDialog extends PureComponent {
Eigene SKU
EAN
Artikel
+ Bestand
Liefermenge
Bereits gebucht
Jetzt verbuchen
@@ -512,6 +589,46 @@ export default class BookDeliveryDialog extends PureComponent {
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 || [];
@@ -539,6 +656,57 @@ export default class BookDeliveryDialog extends PureComponent {
{line.name || '–'}
+
+ {isTracked ? (
+
+ {currentStock != null ? formatStock(currentStock) : '0'}
+
+ ) : targetSku ? (
+
+ ) : (
+
+ —
+
+ )}
+
{line.targetAmount}
diff --git a/src/components/manifest/ManifestFormLine.jsx b/src/components/manifest/ManifestFormLine.jsx
index 3153501..8b59624 100644
--- a/src/components/manifest/ManifestFormLine.jsx
+++ b/src/components/manifest/ManifestFormLine.jsx
@@ -212,7 +212,9 @@ class ManifestFormLine extends PureComponent {
? `Gebucht: ${ledgerLine.bookedAmount} Stk.`
: isPartial
? `Gebucht: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.`
- : 'Verbuchen: offen';
+ : null;
+
+ if (!label) return null;
return (
@@ -1158,9 +1160,11 @@ class ManifestFormLine extends PureComponent {
}
if (this.props.ledgerLine) {
+ const chip = this.renderLedgerChip();
+ if (!chip) return null;
return (
- {this.renderLedgerChip()}
+ {chip}
);
}