diff --git a/src/components/article/boxes/ArticleViewStock.jsx b/src/components/article/boxes/ArticleViewStock.jsx index 42e3602..c6e49ba 100644 --- a/src/components/article/boxes/ArticleViewStock.jsx +++ b/src/components/article/boxes/ArticleViewStock.jsx @@ -3,7 +3,9 @@ import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import WarehouseIcon from '@mui/icons-material/Warehouse'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; import Tooltip from '@mui/material/Tooltip'; +import Chip from '@mui/material/Chip'; import { PanelTile } from 'dashgui/panels'; import { formatStock } from '../searchFormat'; import ArticleStockAdjustDialog from '../ArticleStockAdjustDialog'; @@ -18,6 +20,83 @@ import { const SECOND_ROW_MIN_HEIGHT = 220; +function formatDate(iso) { + if (!iso) return ''; + const d = new Date(iso); + if (Number.isNaN(d.getTime())) return String(iso); + return d.toLocaleDateString('de-DE'); +} + +function extractIncoming(articleData, isParent) { + const items = []; + + const collect = (article, variantInfo = null) => { + if (!article) return; + + if (Array.isArray(article.incoming)) { + for (const inc of article.incoming) { + const amount = Number(inc?.amount) || 0; + if (amount > 0) { + items.push({ + date: inc.date || null, + amount, + supplierName: inc.supplierName || null, + supplierSku: inc.sku || null, + pu: inc.pu || null, + variantSku: variantInfo?.sku || null, + variantName: variantInfo?.name || null, + }); + } + } + } + + if (Array.isArray(article.suppliers)) { + for (const sup of article.suppliers) { + if (Array.isArray(sup?.incoming)) { + for (const inc of sup.incoming) { + const amount = Number(inc?.amount) || 0; + if (amount > 0) { + items.push({ + date: inc.date || null, + amount, + supplierName: sup.supplierName || null, + supplierSku: sup.sku || null, + pu: sup.pu || null, + variantSku: variantInfo?.sku || null, + variantName: variantInfo?.name || null, + }); + } + } + } + } + } + }; + + collect(articleData); + + if (isParent) { + const children = Array.isArray(articleData?.children) ? articleData.children : []; + for (const child of children) { + const fullChild = getArticle(child.sku) || child; + collect(fullChild, { + sku: fullChild.sku || child.sku, + name: fullChild.name || child.name, + }); + } + } + + items.sort((a, b) => { + if (a.date && b.date) { + return new Date(a.date).getTime() - new Date(b.date).getTime(); + } + if (a.date) return -1; + if (b.date) return 1; + return 0; + }); + + return items; +} + export default class ArticleViewStock extends PureComponent { constructor(props) { super(props); @@ -66,6 +145,40 @@ export default class ArticleViewStock extends PureComponent { this.setState({ adjustOpen: false }); } + renderIncomingChip(totalIncoming, incomingItems, unit) { + const tooltipText = `${formatStock(totalIncoming)}${unit ? ` ${unit}` : ''} im Zulauf aus ${ + incomingItems.length === 1 + ? 'einer offenen Lieferung' + : `${incomingItems.length} offenen Lieferungen` + }`; + + return ( + + + } + label={`+${formatStock(totalIncoming)}${unit ? ` ${unit}` : ''} im Zulauf`} + onClick={(e) => e.stopPropagation()} + sx={{ + fontWeight: 600, + fontSize: '0.75rem', + height: 22, + backgroundColor: VEIL_PANEL, + cursor: 'default', + '& .MuiChip-icon': { + color: 'text.secondary', + ml: '6px', + }, + }} + /> + + ); + } + render() { const { article: articleData } = this.props; const { adjustOpen } = this.state; @@ -123,6 +236,10 @@ export default class ArticleViewStock extends PureComponent { ? rawUnit : ''; + const incomingItems = extractIncoming(articleData, isParent); + const totalIncoming = incomingItems.reduce((sum, inc) => sum + inc.amount, 0); + const hasIncoming = totalIncoming > 0; + return ( <> {isParent ? ( - anyVariantTracksStock || totalVariantsStock > 0 ? ( - + + {anyVariantTracksStock || totalVariantsStock > 0 ? ( Alle Varianten zusammen: {formatStock(totalVariantsStock)} - - ) : null + ) : null} + {hasIncoming ? ( + + {this.renderIncomingChip(totalIncoming, incomingItems, unit)} + + ) : null} + ) : isStueckliste ? ( - - - + + + + + 0 ? 'text.primary' : 'text.secondary', + }} + > + {formatStock(totalStock)} + + {unit ? ( + + {unit} + + ) : null} + + + Virtueller Bestand (aus Komponenten) + + + + {hasIncoming ? ( + + {this.renderIncomingChip(totalIncoming, incomingItems, unit)} + + ) : null} + + ) : isTrackStock ? ( + + + ) : null} - - Virtueller Bestand (aus Komponenten) - - - - ) : isTrackStock ? ( - + + {hasIncoming ? ( + + {this.renderIncomingChip(totalIncoming, incomingItems, unit)} + + ) : null} + + ) : ( + 0 ? 'text.primary' : 'text.secondary', + lineHeight: 1.2, + color: 'text.secondary', }} > - {formatStock(totalStock)} + Keine Erfassung + + + Bestand wird nicht überwacht - {unit ? ( - - {unit} - - ) : null} - - ) : ( - - - Keine Erfassung - - - Bestand wird nicht überwacht - + {hasIncoming ? ( + + {this.renderIncomingChip(totalIncoming, incomingItems, unit)} + + ) : null} )} - {/* Bottom section: Per-warehouse breakdown */} - {isTrackStock && hasWarehouses ? ( + {/* Bottom section: Per-warehouse breakdown & incoming shipments */} + {hasWarehouses || hasIncoming ? ( - {warehouses.map((wh) => ( - - - + {/* Warehouse stocks */} + {hasWarehouses ? ( + <> + {hasIncoming ? ( - {wh.name} + Lagerorte - + ) : null} + {warehouses.map((wh) => ( + + + + + {wh.name} + + + 0 + ? 'text.primary' + : 'text.secondary', + }} + > + {formatStock(wh.stock)} + + + ))} + + ) : null} + + {/* Incoming shipments */} + {hasIncoming ? ( + <> 0 - ? 'text.primary' - : 'text.secondary', + color: 'text.secondary', + textTransform: 'uppercase', + letterSpacing: 0.5, + px: 0.5, + mt: hasWarehouses ? 0.75 : 0, + mb: 0.1, + display: 'block', }} > - {formatStock(wh.stock)} + Im Zulauf - - ))} + {incomingItems.map((inc, idx) => { + const dateLabel = inc.date ? formatDate(inc.date) : null; + const label = [ + inc.supplierName || 'Lieferant', + dateLabel, + ] + .filter(Boolean) + .join(' – '); + + return ( + + + + + {label} + + + + + +{formatStock(inc.amount)} + + + ); + })} + + ) : null} ) : null}