diff --git a/src/components/article/boxes/ArticleViewLists.jsx b/src/components/article/boxes/ArticleViewLists.jsx
index d85a0e8..d031047 100644
--- a/src/components/article/boxes/ArticleViewLists.jsx
+++ b/src/components/article/boxes/ArticleViewLists.jsx
@@ -34,6 +34,9 @@ import { artikelDetailPath } from '../articleRoute';
import { article as articleApi } from '../../../api';
import attributeAutomat, { selectAttributes } from '../../../automats/attributeAutomat';
import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat';
+import companyAutomat from '../../../automats/companyAutomat';
+import { formatStock } from '../searchFormat';
+import { formatPrice, getArticleGross } from '../../../utils/price';
import { notifyError } from '../../../utils/notify';
import articleIndexAutomat from '../../../automats/articleIndexAutomat';
import {
@@ -43,6 +46,67 @@ import {
alpha,
} from '../../../colors';
+function getVariantDetails(child, allArticles, defaultTaxRate) {
+ const cached = child?.sku ? getArticle(child.sku) : null;
+ const fromIndex = !cached && child?.sku
+ ? (allArticles || []).find((a) => a.sku === child.sku)
+ : null;
+ const merged = cached || fromIndex || child || {};
+
+ // 1. Gross price
+ const gross = getArticleGross(merged, defaultTaxRate);
+ const priceDisplay = gross != null && Number.isFinite(gross) ? formatPrice(gross) : null;
+
+ // 2. Stock & Unit
+ let stockVal = null;
+ if (Array.isArray(merged.warehouses) && merged.warehouses.length > 0) {
+ stockVal = merged.warehouses.reduce((sum, wh) => sum + (Number(wh.stock) || 0), 0);
+ } else if (merged.stock != null && !Number.isNaN(Number(merged.stock))) {
+ stockVal = Number(merged.stock);
+ } else if (child?.stock != null && !Number.isNaN(Number(child.stock))) {
+ stockVal = Number(child.stock);
+ }
+
+ const rawUnit =
+ merged.unit?.symbol ||
+ merged.unit?.name ||
+ child?.unit?.symbol ||
+ child?.unit?.name ||
+ '';
+ const unit =
+ rawUnit && !['stk', 'stück', 'pc', 'stk.'].includes(rawUnit.trim().toLowerCase())
+ ? ` ${rawUnit}`
+ : '';
+ const stockDisplay = stockVal != null ? `${formatStock(stockVal)}${unit}` : null;
+
+ // 3. EAN(s)
+ const eanCandidates = [];
+ const rawEans = merged.eans ?? child?.eans;
+ if (Array.isArray(rawEans)) {
+ for (const item of rawEans) {
+ const val = typeof item === 'object' && item !== null ? item.ean : item;
+ if (val && String(val).trim()) eanCandidates.push(String(val).trim());
+ }
+ } else if (rawEans && typeof rawEans === 'string') {
+ eanCandidates.push(rawEans.trim());
+ }
+
+ const singleEan = merged.ean ?? child?.ean;
+ if (singleEan && typeof singleEan === 'string' && !eanCandidates.includes(singleEan.trim())) {
+ eanCandidates.push(singleEan.trim());
+ }
+
+ const primaryEan = eanCandidates[0] ?? null;
+
+ return {
+ priceDisplay,
+ stockVal,
+ stockDisplay,
+ primaryEan,
+ allEans: eanCandidates,
+ };
+}
+
export default class ArticleViewLists extends PureComponent {
constructor(props) {
super(props);
@@ -88,12 +152,16 @@ export default class ArticleViewLists extends PureComponent {
// Confirmation dialogs
removeTargetChild: null, // { sku, attributeValue }
confirmClearType: null, // 'stueckliste' | 'options' | null
+ company: companyAutomat.state.current,
};
this._cancelled = false;
}
componentDidMount() {
this._cancelled = false;
+ this.unsubCompany = companyAutomat.subscribe(this, (state) => ({
+ company: state.current,
+ }));
this.unsubAttr = attributeAutomat.subscribe((state) => {
if (this._cancelled) return;
this.setState((prev) => {
@@ -123,21 +191,48 @@ export default class ArticleViewLists extends PureComponent {
void this.loadAssignedAttributes(this.props.article.sku);
}
+ if (this.props.article?.isParent) {
+ this.ensureChildren(this.props.article.children);
+ }
+
if (this.props.article?.parentSku) {
- void artikelAutomat.actions.loadArticle(this.props.article.parentSku);
+ void artikelAutomat.actions.loadArticle(this.props.article.parentSku).then((parent) => {
+ if (!this._cancelled && parent?.children) {
+ this.ensureChildren(parent.children);
+ }
+ }).catch(() => {});
}
void this.loadAllArticles();
}
componentDidUpdate(prevProps) {
+ if (this.props.article?.isParent) {
+ if (
+ prevProps.article?.sku !== this.props.article?.sku ||
+ prevProps.article?.children !== this.props.article?.children
+ ) {
+ this.ensureChildren(this.props.article.children);
+ }
+ }
+ if (this.props.article?.parentSku) {
+ if (prevProps.article?.parentSku !== this.props.article?.parentSku) {
+ void artikelAutomat.actions.loadArticle(this.props.article.parentSku).then((parent) => {
+ if (!this._cancelled && parent?.children) {
+ this.ensureChildren(parent.children);
+ }
+ }).catch(() => {});
+ } else {
+ const parent = getArticle(this.props.article.parentSku);
+ if (parent?.children) {
+ this.ensureChildren(parent.children);
+ }
+ }
+ }
if (prevProps.article?.sku !== this.props.article?.sku) {
if (this.props.article?.sku) {
void this.loadAssignedAttributes(this.props.article.sku);
}
- if (this.props.article?.parentSku) {
- void artikelAutomat.actions.loadArticle(this.props.article.parentSku);
- }
const nextArticle = this.props.article;
const hasStueckliste = Boolean(
nextArticle?.articlelistId != null || nextArticle?.articlelist?.length > 0
@@ -166,10 +261,20 @@ export default class ArticleViewLists extends PureComponent {
componentWillUnmount() {
this._cancelled = true;
+ this.unsubCompany?.();
this.unsubAttr?.();
this.unsubArtikel?.();
}
+ ensureChildren(children) {
+ if (!Array.isArray(children)) return;
+ for (const child of children) {
+ if (child?.sku && !getArticle(child.sku)) {
+ void artikelAutomat.actions.loadArticle(child.sku).catch(() => {});
+ }
+ }
+ }
+
getApi() {
return articleApi;
}
@@ -1073,6 +1178,8 @@ export default class ArticleViewLists extends PureComponent {
renderChildView(parentSku, parentArticle, attributes, assignedAttributes) {
const { article: articleData } = this.props;
+ const { allArticles } = this.state;
+ const defaultTaxRate = this.state.company?.taxrate ?? companyAutomat.state.current?.taxrate ?? 19;
const parentAttrName =
parentArticle?.parentAttributeName ||
(parentArticle?.parentAttributeNameId
@@ -1235,6 +1342,13 @@ export default class ArticleViewLists extends PureComponent {
const isSelf =
sibling.sku === articleData.sku ||
(articleData.id != null && sibling.id === articleData.id);
+ const {
+ priceDisplay,
+ stockVal,
+ stockDisplay,
+ primaryEan,
+ allEans,
+ } = getVariantDetails(sibling, allArticles, defaultTaxRate);
return (
-
- {sibling.attributeValue || variationValue || sibling.name || sibling.sku}
-
-
+
+ {sibling.attributeValue || variationValue || sibling.name || sibling.sku}
+
+
+ {priceDisplay || '—'}
+
+
+
{sibling.sku}
+
+
+ ·
+
+
+ 1 ? `EANs: ${allEans.join(', ')}` : ''}
+ disableHoverListener={allEans.length <= 1}
+ >
+
+ {primaryEan ? `EAN ${primaryEan}${allEans.length > 1 ? ` (+${allEans.length - 1})` : ''}` : 'EAN —'}
+
+
+
+
+ ·
+
+
+ 0 ? 'success.dark' : 'error.main',
+ whiteSpace: 'nowrap',
+ }}
+ >
+ Bestand {stockDisplay ?? '—'}
+
) : (
@@ -1319,37 +1495,101 @@ export default class ArticleViewLists extends PureComponent {
},
}}
>
-
- {sibling.attributeValue || sibling.name || sibling.sku}
-
-
- {sibling.sku}
+ {sibling.attributeValue || sibling.name || sibling.sku}
+
+
+ {priceDisplay || '—'}
+
+
+
+
+
+ {sibling.sku}
+
+
+
+
+
+ ·
+
+
+ 1 ? `EANs: ${allEans.join(', ')}` : ''}
+ disableHoverListener={allEans.length <= 1}
+ >
+
+ {primaryEan ? `EAN ${primaryEan}${allEans.length > 1 ? ` (+${allEans.length - 1})` : ''}` : 'EAN —'}
+
+
+
+
+ ·
+
+
+ 0 ? 'success.dark' : 'error.main',
+ whiteSpace: 'nowrap',
+ }}
+ >
+ Bestand {stockDisplay ?? '—'}
-
)}
@@ -1645,100 +1885,181 @@ export default class ArticleViewLists extends PureComponent {
) : (
- {children.map((child) => (
-
- {
+ const defaultTaxRate = this.state.company?.taxrate ?? companyAutomat.state.current?.taxrate ?? 19;
+ const {
+ priceDisplay,
+ stockVal,
+ stockDisplay,
+ primaryEan,
+ allEans,
+ } = getVariantDetails(child, allArticles, defaultTaxRate);
+
+ return (
+
-
- {child.attributeValue || child.name || child.sku}
-
-
-
- {child.sku}
-
-
-
-
+
+ {child.attributeValue || child.name || child.sku}
+
+
+ {priceDisplay || '—'}
+
+
- this.handleOpenRemoveDialog(child)}
- title="Variante entfernen"
- sx={{
- p: 0.35,
- color: 'text.secondary',
- '&:hover': { color: 'error.main' },
- }}
- >
-
-
-
- ))}
+
+
+
+ {child.sku}
+
+
+
+
+
+ ·
+
+
+ 1 ? `EANs: ${allEans.join(', ')}` : ''}
+ disableHoverListener={allEans.length <= 1}
+ >
+
+ {primaryEan ? `EAN ${primaryEan}${allEans.length > 1 ? ` (+${allEans.length - 1})` : ''}` : 'EAN —'}
+
+
+
+
+ ·
+
+
+ 0 ? 'success.dark' : 'error.main',
+ whiteSpace: 'nowrap',
+ }}
+ >
+ Bestand {stockDisplay ?? '—'}
+
+
+
+
+ this.handleOpenRemoveDialog(child)}
+ title="Variante entfernen"
+ sx={{
+ p: 0.35,
+ color: 'text.secondary',
+ '&:hover': { color: 'error.main' },
+ }}
+ >
+
+
+
+ );
+ })}
)}