u
This commit is contained in:
@@ -101,7 +101,7 @@ class ArticleView extends PureComponent {
|
||||
article={article}
|
||||
/>
|
||||
|
||||
<ArticleViewStock article={article} />
|
||||
<ArticleViewStock article={article} navigate={this.props.navigate} />
|
||||
|
||||
<ArticleViewBrand article={article} />
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ 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 ShoppingCartOutlinedIcon from '@mui/icons-material/ShoppingCartOutlined';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import { PanelTile } from 'dashgui/panels';
|
||||
import { formatStock } from '../searchFormat';
|
||||
import ArticleStockAdjustDialog from '../ArticleStockAdjustDialog';
|
||||
import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat';
|
||||
import { router } from '../../../appRouter';
|
||||
import {
|
||||
GREEN_PANEL,
|
||||
PETROL_PANEL,
|
||||
@@ -96,6 +98,58 @@ function extractIncoming(articleData, isParent) {
|
||||
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 {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -104,6 +158,17 @@ export default class ArticleViewStock extends PureComponent {
|
||||
};
|
||||
this.handleOpenAdjust = this.handleOpenAdjust.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() {
|
||||
@@ -205,6 +270,10 @@ export default class ArticleViewStock extends PureComponent {
|
||||
const totalIncoming = incomingItems.reduce((sum, inc) => sum + inc.amount, 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 (
|
||||
<>
|
||||
<PanelTile
|
||||
@@ -301,8 +370,7 @@ export default class ArticleViewStock extends PureComponent {
|
||||
onClick={this.handleOpenAdjust}
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'baseline',
|
||||
gap: 0.75,
|
||||
flexDirection: 'column',
|
||||
my: 0.5,
|
||||
cursor: 'pointer',
|
||||
borderRadius: 1,
|
||||
@@ -314,27 +382,29 @@ export default class ArticleViewStock extends PureComponent {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: '2.4rem',
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.1,
|
||||
letterSpacing: '-0.02em',
|
||||
color: totalStock > 0 ? 'text.primary' : 'text.secondary',
|
||||
}}
|
||||
>
|
||||
{formatStock(totalStock)}
|
||||
</Typography>
|
||||
{unit ? (
|
||||
<Box sx={{ display: 'inline-flex', alignItems: 'baseline', gap: 0.75 }}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{ fontWeight: 500 }}
|
||||
component="div"
|
||||
sx={{
|
||||
fontSize: '2.4rem',
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.1,
|
||||
letterSpacing: '-0.02em',
|
||||
color: totalStock > 0 ? 'text.primary' : 'text.secondary',
|
||||
}}
|
||||
>
|
||||
{unit}
|
||||
{formatStock(totalStock)}
|
||||
</Typography>
|
||||
) : null}
|
||||
{unit ? (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{ fontWeight: 500 }}
|
||||
>
|
||||
{unit}
|
||||
</Typography>
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
) : (
|
||||
@@ -374,8 +444,8 @@ export default class ArticleViewStock extends PureComponent {
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Bottom section: Per-warehouse breakdown & incoming shipments */}
|
||||
{hasWarehouses || hasIncoming ? (
|
||||
{/* Bottom section: Per-warehouse breakdown, incoming shipments & open orders */}
|
||||
{hasWarehouses || hasIncoming || hasUndelivered ? (
|
||||
<Box
|
||||
sx={{
|
||||
mt: 1,
|
||||
@@ -461,12 +531,12 @@ export default class ArticleViewStock extends PureComponent {
|
||||
<>
|
||||
{incomingItems.map((inc, idx) => {
|
||||
const dateLabel = inc.date ? formatDate(inc.date) : null;
|
||||
const label = [
|
||||
const labelParts = [
|
||||
inc.variantName,
|
||||
inc.supplierName || 'Lieferant',
|
||||
dateLabel,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' – ');
|
||||
].filter(Boolean);
|
||||
const label = labelParts.join(' – ');
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -528,6 +598,89 @@ export default class ArticleViewStock extends PureComponent {
|
||||
})}
|
||||
</>
|
||||
) : 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>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user