This commit is contained in:
seb
2026-10-14 23:58:37 +02:00
parent e2a2d02cb4
commit ed7bb80006
5 changed files with 200 additions and 48 deletions

View File

@@ -19,11 +19,13 @@ import SearchIcon from '@mui/icons-material/Search';
import LinkOffOutlinedIcon from '@mui/icons-material/LinkOffOutlined';
import { getFirstVal, parseNum, LINE_COLUMNS } from './manifestConfig';
import ArticlePhoto from '../article/ArticlePhoto';
import { formatPrice } from '../../utils/price';
import { formatPrice, grossFromNet } from '../../utils/price';
import { primaryPrice, priceBrutto } from '../../utils/articleDetail';
import artikelAutomat, { createArticleSelector, getArticle } from '../../automats/artikelAutomat';
import { artikelDetailPath } from '../article/articleRoute';
import { BrandLabel } from '../brand';
import { articleSupplierAutomat } from '../../automats/articleDetailAutomat';
import { selectCompanyTaxrate } from '../../automats/companyAutomat';
import {
BORDER,
} from '../../colors';
@@ -35,6 +37,7 @@ class ManifestFormLine extends PureComponent {
this.state = {
cachedArticle: sku ? getArticle(sku) : null,
loadingArticle: false,
fallbackSuppliers: [],
};
this.handleRemove = this.handleRemove.bind(this);
this.handleBlur = this.handleBlur.bind(this);
@@ -64,14 +67,18 @@ class ManifestFormLine extends PureComponent {
componentWillUnmount() {
this.unsubArticle?.();
this.unsubSupplier?.();
}
subscribeArticle(sku) {
this.unsubArticle?.();
this.unsubArticle = null;
this.unsubSupplier?.();
this.unsubSupplier = null;
if (!sku) {
if (this.state.cachedArticle || this.state.loadingArticle) {
this.setState({ cachedArticle: null, loadingArticle: false });
if (this.state.cachedArticle || this.state.loadingArticle || this.state.fallbackSuppliers?.length) {
this.setState({ cachedArticle: null, loadingArticle: false, fallbackSuppliers: [] });
}
return;
}
@@ -99,6 +106,15 @@ class ManifestFormLine extends PureComponent {
...(art ? { loadingArticle: false } : {}),
};
});
// Fallback: subscribe to articleSupplierAutomat if cachedArticle doesn't have suppliers
if (!current?.suppliers?.length) {
const supKey = articleSupplierAutomat.key(sku);
this.unsubSupplier = supKey.subscribe(this, (item) => ({
fallbackSuppliers: Array.isArray(item?.data) ? item.data : [],
}));
void articleSupplierAutomat.load(sku).catch(() => {});
}
}
handleRemove() {
@@ -235,8 +251,9 @@ class ManifestFormLine extends PureComponent {
} else if (id === 'gross') {
const netNum = parseNum(line.netprice ?? line.net);
const taxNum = parseNum(line.taxrate ?? line.tax) ?? 0;
const rebateNum = parseNum(line.rebate) ?? 0;
if (netNum != null) {
val = String(Math.round((netNum * (1 + taxNum / 100) + Number.EPSILON) * 100) / 100);
val = String(grossFromNet(netNum, taxNum, rebateNum));
}
}
}
@@ -375,6 +392,41 @@ class ManifestFormLine extends PureComponent {
: null)
: null;
const suppliers =
Array.isArray(cachedArticle.suppliers) && cachedArticle.suppliers.length > 0
? cachedArticle.suppliers
: (this.state.fallbackSuppliers ?? []);
const topSupplier =
suppliers.find((s) => s.sort === 0) ??
(suppliers.length > 0
? [...suppliers].sort((a, b) => (Number(a.sort) || 0) - (Number(b.sort) || 0))[0]
: null);
const ekNetto =
topSupplier?.price?.net != null
? Number(topSupplier.price.net)
: topSupplier?.netPrice != null
? Number(topSupplier.netPrice)
: topSupplier?.net != null
? Number(topSupplier.net)
: null;
const ekTaxRate =
topSupplier?.price?.taxRate != null
? Number(topSupplier.price.taxRate)
: topSupplier?.taxRate != null
? Number(topSupplier.taxRate)
: parseNum(this.props.line?.taxrate ?? this.props.line?.tax) ?? selectCompanyTaxrate() ?? 19;
const ekBrutto =
ekNetto != null
? Math.round((ekNetto * (1 + Number(ekTaxRate) / 100) + Number.EPSILON) * 100) / 100
: null;
const hasPriceInfo =
vkNetto != null || vkBrutto != null || ekNetto != null || ekBrutto != null || kind === 'quote';
return (
<Box
sx={{
@@ -480,8 +532,8 @@ class ManifestFormLine extends PureComponent {
</Typography>
)}
{/* VK Netto & VK Brutto: omit end on 2nd line for order */}
{!isOrder && (vkNetto != null || vkBrutto != null) && (
{/* EK Netto, EK Brutto, VK Netto, VK Brutto */}
{!isOrder && hasPriceInfo && (
<Box
sx={{
display: 'flex',
@@ -491,6 +543,47 @@ class ManifestFormLine extends PureComponent {
whiteSpace: 'nowrap',
}}
>
{/* EK Netto */}
<Tooltip
title={
topSupplier?.supplierName
? `Lieferant: ${topSupplier.supplierName}${topSupplier.sku ? ` (Lief.-Art.-Nr.: ${topSupplier.sku})` : ''} (Sort: ${topSupplier.sort ?? 0})`
: 'Kein Lieferant mit EK-Preis hinterlegt'
}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
color: ekNetto != null ? 'text.secondary' : 'text.disabled',
fontSize: '0.72rem',
}}
>
EK Netto: {ekNetto != null ? formatPrice(ekNetto) : '—'}
</Typography>
</Tooltip>
{/* EK Brutto */}
<Tooltip
title={
topSupplier?.supplierName
? `Lieferant: ${topSupplier.supplierName} (inkl. ${ekTaxRate}% MwSt.)`
: 'Kein Lieferant mit EK-Preis hinterlegt'
}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
color: ekBrutto != null ? 'text.secondary' : 'text.disabled',
fontSize: '0.72rem',
}}
>
EK Brutto: {ekBrutto != null ? formatPrice(ekBrutto) : '—'}
</Typography>
</Tooltip>
{/* VK Netto */}
{vkNetto != null && (
<Typography
variant="caption"
@@ -500,6 +593,8 @@ class ManifestFormLine extends PureComponent {
VK Netto: {formatPrice(vkNetto)}
</Typography>
)}
{/* VK Brutto */}
{vkBrutto != null && (
<Typography
variant="caption"