This commit is contained in:
seb
2026-10-20 02:31:49 +02:00
parent 90ee11fefb
commit 067207c58e
2 changed files with 180 additions and 27 deletions

View File

@@ -101,7 +101,7 @@ class ArticleView extends PureComponent {
article={article} article={article}
/> />
<ArticleViewStock article={article} /> <ArticleViewStock article={article} navigate={this.props.navigate} />
<ArticleViewBrand article={article} /> <ArticleViewBrand article={article} />

View File

@@ -4,11 +4,13 @@ import Typography from '@mui/material/Typography';
import WarehouseIcon from '@mui/icons-material/Warehouse'; import WarehouseIcon from '@mui/icons-material/Warehouse';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined'; import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
import ShoppingCartOutlinedIcon from '@mui/icons-material/ShoppingCartOutlined';
import Tooltip from '@mui/material/Tooltip'; import Tooltip from '@mui/material/Tooltip';
import { PanelTile } from 'dashgui/panels'; import { PanelTile } from 'dashgui/panels';
import { formatStock } from '../searchFormat'; import { formatStock } from '../searchFormat';
import ArticleStockAdjustDialog from '../ArticleStockAdjustDialog'; import ArticleStockAdjustDialog from '../ArticleStockAdjustDialog';
import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat'; import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat';
import { router } from '../../../appRouter';
import { import {
GREEN_PANEL, GREEN_PANEL,
PETROL_PANEL, PETROL_PANEL,
@@ -96,6 +98,58 @@ function extractIncoming(articleData, isParent) {
return items; return items;
} }
function extractUndeliveredOrders(articleData, isParent) {
const items = [];
const collect = (article, variantInfo = null) => {
if (!article) return;
if (Array.isArray(article.undeliveredOrders)) {
for (const ord of article.undeliveredOrders) {
const amount = Number(ord?.amount) || 0;
if (amount > 0) {
items.push({
orderId: ord.orderId,
orderNumber: ord.orderNumber || (ord.orderId ? `A-${ord.orderId}` : null),
date: ord.date || null,
customerName: ord.customerName || null,
amount,
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) {
const diff = new Date(a.date).getTime() - new Date(b.date).getTime();
if (diff !== 0) return diff;
} else if (a.date) {
return -1;
} else if (b.date) {
return 1;
}
return (Number(a.orderId) || 0) - (Number(b.orderId) || 0);
});
return items;
}
export default class ArticleViewStock extends PureComponent { export default class ArticleViewStock extends PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -104,6 +158,17 @@ export default class ArticleViewStock extends PureComponent {
}; };
this.handleOpenAdjust = this.handleOpenAdjust.bind(this); this.handleOpenAdjust = this.handleOpenAdjust.bind(this);
this.handleCloseAdjust = this.handleCloseAdjust.bind(this); this.handleCloseAdjust = this.handleCloseAdjust.bind(this);
this.handleOpenOrder = this.handleOpenOrder.bind(this);
}
handleOpenOrder(orderId, event) {
if (!orderId) return;
if (event?.metaKey || event?.ctrlKey) {
window.open(`/auftrag/bestellung/${orderId}`, '_blank');
return;
}
const nav = this.props.navigate || router.navigate;
void nav(`/auftrag/bestellung/${orderId}`);
} }
componentDidMount() { componentDidMount() {
@@ -205,6 +270,10 @@ export default class ArticleViewStock extends PureComponent {
const totalIncoming = incomingItems.reduce((sum, inc) => sum + inc.amount, 0); const totalIncoming = incomingItems.reduce((sum, inc) => sum + inc.amount, 0);
const hasIncoming = totalIncoming > 0; const hasIncoming = totalIncoming > 0;
const undeliveredOrders = extractUndeliveredOrders(articleData, isParent);
const totalUndelivered = undeliveredOrders.reduce((sum, ord) => sum + ord.amount, 0);
const hasUndelivered = totalUndelivered > 0;
return ( return (
<> <>
<PanelTile <PanelTile
@@ -301,8 +370,7 @@ export default class ArticleViewStock extends PureComponent {
onClick={this.handleOpenAdjust} onClick={this.handleOpenAdjust}
sx={{ sx={{
display: 'inline-flex', display: 'inline-flex',
alignItems: 'baseline', flexDirection: 'column',
gap: 0.75,
my: 0.5, my: 0.5,
cursor: 'pointer', cursor: 'pointer',
borderRadius: 1, borderRadius: 1,
@@ -314,6 +382,7 @@ export default class ArticleViewStock extends PureComponent {
}, },
}} }}
> >
<Box sx={{ display: 'inline-flex', alignItems: 'baseline', gap: 0.75 }}>
<Typography <Typography
component="div" component="div"
sx={{ sx={{
@@ -336,6 +405,7 @@ export default class ArticleViewStock extends PureComponent {
</Typography> </Typography>
) : null} ) : null}
</Box> </Box>
</Box>
</Tooltip> </Tooltip>
) : ( ) : (
<Box <Box
@@ -374,8 +444,8 @@ export default class ArticleViewStock extends PureComponent {
)} )}
</Box> </Box>
{/* Bottom section: Per-warehouse breakdown & incoming shipments */} {/* Bottom section: Per-warehouse breakdown, incoming shipments & open orders */}
{hasWarehouses || hasIncoming ? ( {hasWarehouses || hasIncoming || hasUndelivered ? (
<Box <Box
sx={{ sx={{
mt: 1, mt: 1,
@@ -461,12 +531,12 @@ export default class ArticleViewStock extends PureComponent {
<> <>
{incomingItems.map((inc, idx) => { {incomingItems.map((inc, idx) => {
const dateLabel = inc.date ? formatDate(inc.date) : null; const dateLabel = inc.date ? formatDate(inc.date) : null;
const label = [ const labelParts = [
inc.variantName,
inc.supplierName || 'Lieferant', inc.supplierName || 'Lieferant',
dateLabel, dateLabel,
] ].filter(Boolean);
.filter(Boolean) const label = labelParts.join(' – ');
.join(' – ');
return ( return (
<Box <Box
@@ -528,6 +598,89 @@ export default class ArticleViewStock extends PureComponent {
})} })}
</> </>
) : null} ) : null}
{/* Undelivered / Open Customer Orders */}
{hasUndelivered ? (
<>
{undeliveredOrders.map((ord, idx) => {
const dateLabel = ord.date ? formatDate(ord.date) : null;
const orderLabel = ord.orderNumber || (ord.orderId ? `Auftrag #${ord.orderId}` : 'Auftrag');
const labelParts = [
ord.variantName,
orderLabel,
ord.customerName,
dateLabel,
].filter(Boolean);
const label = labelParts.join(' – ');
const tooltipText = ord.orderId
? `Auftrag ${orderLabel}${ord.customerName ? ` (${ord.customerName})` : ''} öffnen`
: label;
return (
<Box
key={ord.orderId ? `order-${ord.orderId}` : `ord-${idx}`}
onClick={(e) => this.handleOpenOrder(ord.orderId, e)}
title={tooltipText}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
py: 0.35,
px: 0.75,
borderRadius: 1,
backgroundColor: VEIL_PANEL,
cursor: ord.orderId ? 'pointer' : 'default',
transition: 'background-color 0.15s ease',
'&:hover': ord.orderId ? {
backgroundColor: TINT_ROW_HOVER,
} : undefined,
}}
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
minWidth: 0,
mr: 1,
}}
>
<ShoppingCartOutlinedIcon
sx={{
fontSize: 15,
color: 'text.secondary',
mr: 0.6,
flexShrink: 0,
}}
/>
<Typography
variant="body2"
noWrap
sx={{
fontWeight: 500,
fontSize: '0.82rem',
color: 'text.primary',
}}
>
{label}
</Typography>
</Box>
<Typography
variant="body2"
sx={{
fontWeight: 700,
fontSize: '0.82rem',
flexShrink: 0,
color: 'text.primary',
}}
>
-{formatStock(ord.amount)}
</Typography>
</Box>
);
})}
</>
) : null}
</Box> </Box>
</Box> </Box>
) : null} ) : null}