This commit is contained in:
seb
2026-10-20 02:56:31 +02:00
parent 067207c58e
commit b727e00928
3 changed files with 204 additions and 185 deletions

View File

@@ -8,7 +8,6 @@ import IconButton from '@mui/material/IconButton';
import TableCell from '@mui/material/TableCell';
import TableRow from '@mui/material/TableRow';
import TextField from '@mui/material/TextField';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
@@ -608,9 +607,6 @@ class ManifestFormLine extends PureComponent {
: (rawWarehouses.length > 0 || isTracked
? rawWarehouses.reduce((sum, wh) => sum + (Number(wh.stock) || 0), 0)
: null));
const warehouseTooltip = rawWarehouses.length > 0
? rawWarehouses.map((w) => `${w.name || 'Lager'}: ${formatStock(w.stock)}`).join(' · ')
: (isTracked === false ? 'Keine Bestandsführung' : undefined);
const lineAmount = parseNum(getFirstVal(this.props.line?.amount, this.props.line?.menge)) ?? 0;
return (
@@ -730,93 +726,65 @@ class ManifestFormLine extends PureComponent {
}}
>
{/* Bestand */}
<Tooltip
title={
warehouseTooltip
? `Lagerbestand: ${formatStock(stockVal)} (${warehouseTooltip})`
: isTracked === false
? 'Keine Bestandsführung (unbegrenzt verfügbar)'
: `Verfügbarer Lagerbestand: ${formatStock(stockVal)}`
}
<Typography
variant="caption"
fontWeight={600}
sx={{
color:
stockVal == null
? 'text.disabled'
: stockVal <= 0
? 'error.main'
: lineAmount > stockVal
? 'warning.main'
: 'text.secondary',
fontSize: '0.72rem',
}}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
color:
stockVal == null
? 'text.disabled'
: stockVal <= 0
? 'error.main'
: lineAmount > stockVal
? 'warning.main'
: 'text.secondary',
fontSize: '0.72rem',
}}
>
Bestand: {isTracked === false ? '∞' : formatStock(stockVal)}
</Typography>
</Tooltip>
Bestand: {isTracked === false ? '∞' : formatStock(stockVal)}
</Typography>
{/* Marge */}
<Tooltip
title={
marge != null
? `Marge: ${marge.toFixed(1).replace('.', ',')}% (EK Netto ${formatPrice(ekNetto)} → VK Netto ${formatPrice(vkNetto)})${rebate > 0 && effectiveMarge != null ? ` · Nach ${rebate}% Rabatt: ${effectiveMarge.toFixed(1).replace('.', ',')}%` : ''}`
: 'Kein EK- oder VK-Preis hinterlegt'
}
<Typography
variant="caption"
fontWeight={600}
sx={{
color: marge != null ? (marge < 0 ? 'error.main' : 'text.secondary') : 'text.disabled',
fontSize: '0.72rem',
}}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
color: marge != null ? (marge < 0 ? 'error.main' : 'text.secondary') : 'text.disabled',
fontSize: '0.72rem',
}}
>
Marge: {marge != null ? `${Math.floor(marge)}%` : '—'}
</Typography>
</Tooltip>
Marge: {marge != null ? `${Math.floor(marge)}%` : '—'}
</Typography>
{/* 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',
}}
>
<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 Netto: {ekNetto != null ? formatPrice(ekNetto) : '—'}
</Typography>
{/* 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',
}}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
color: ekBrutto != null ? 'text.secondary' : 'text.disabled',
fontSize: '0.72rem',
}}
>
EK Brutto: {ekBrutto != null ? formatPrice(ekBrutto) : '—'}
</Typography>
</Tooltip>
EK Brutto: {ekBrutto != null ? formatPrice(ekBrutto) : '—'}
</Typography>
{/* VK Netto */}
<Typography
@@ -964,9 +932,6 @@ class ManifestFormLine extends PureComponent {
: (rawWarehouses.length > 0 || isDeliveryTracked
? rawWarehouses.reduce((sum, wh) => sum + (Number(wh.stock) || 0), 0)
: null));
const deliveryWarehouseTooltip = rawWarehouses.length > 0
? rawWarehouses.map((w) => `${w.name || 'Lager'}: ${formatStock(w.stock)}`).join(' · ')
: (isDeliveryTracked === false ? 'Keine Bestandsführung' : undefined);
return (
<Box
@@ -980,26 +945,18 @@ class ManifestFormLine extends PureComponent {
>
{/* Unlink button at the front */}
{this.props.onRemoveSupplierArticle && supplierArticle.id != null && (
<Tooltip
title={
this.isFullyLedgered()
? 'Position ist bereits vollständig im Ledger verbucht und Verknüpfung kann nicht aufgehoben werden'
: 'Lieferanten-Verknüpfung aufheben'
}
>
<span>
<IconButton
size="small"
color="error"
aria-label="Lieferanten-Verknüpfung entfernen"
disabled={this.props.saving || this.props.supplierArticleBusy || this.isFullyLedgered()}
onClick={this.handleRemoveSupplierArticle}
sx={{ p: 0.25, mr: -0.25 }}
>
<LinkOffOutlinedIcon sx={{ fontSize: 16 }} />
</IconButton>
</span>
</Tooltip>
<span>
<IconButton
size="small"
color="error"
aria-label="Lieferanten-Verknüpfung entfernen"
disabled={this.props.saving || this.props.supplierArticleBusy || this.isFullyLedgered()}
onClick={this.handleRemoveSupplierArticle}
sx={{ p: 0.25, mr: -0.25 }}
>
<LinkOffOutlinedIcon sx={{ fontSize: 16 }} />
</IconButton>
</span>
)}
{/* Article Photo Thumbnail */}
@@ -1109,48 +1066,34 @@ class ManifestFormLine extends PureComponent {
}}
>
{/* Bestand */}
<Tooltip
title={
deliveryWarehouseTooltip
? `Lagerbestand: ${formatStock(deliveryStockVal)} (${deliveryWarehouseTooltip})`
: isDeliveryTracked === false
? 'Keine Bestandsführung (unbegrenzt verfügbar)'
: `Aktueller Lagerbestand: ${formatStock(deliveryStockVal)}`
}
<Typography
variant="caption"
fontWeight={600}
sx={{
color:
deliveryStockVal == null
? 'text.disabled'
: deliveryStockVal <= 0
? 'error.main'
: 'text.secondary',
fontSize: '0.72rem',
}}
>
Bestand: {isDeliveryTracked === false ? '∞' : formatStock(deliveryStockVal)}
</Typography>
{/* Marge */}
{margeDelivery != null && (
<Typography
variant="caption"
fontWeight={600}
sx={{
color:
deliveryStockVal == null
? 'text.disabled'
: deliveryStockVal <= 0
? 'error.main'
: 'text.secondary',
color: margeDelivery < 0 ? 'error.main' : 'text.secondary',
fontSize: '0.72rem',
}}
>
Bestand: {isDeliveryTracked === false ? '∞' : formatStock(deliveryStockVal)}
Marge: {Math.floor(margeDelivery)}%
</Typography>
</Tooltip>
{/* Marge */}
{margeDelivery != null && (
<Tooltip
title={`Marge: ${margeDelivery.toFixed(1).replace('.', ',')}% (EK Netto ${formatPrice(priceNet)} → VK Netto ${formatPrice(vkNetto)})`}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
color: margeDelivery < 0 ? 'error.main' : 'text.secondary',
fontSize: '0.72rem',
}}
>
Marge: {Math.floor(margeDelivery)}%
</Typography>
</Tooltip>
)}
{priceNet != null && (
@@ -1363,13 +1306,6 @@ class ManifestFormLine extends PureComponent {
aria-label="Zeile entfernen"
disabled={saving || Boolean(this.props.isOnShipment) || isFullyLedgered}
onClick={this.handleRemove}
title={
isFullyLedgered
? 'Position ist bereits vollständig im Ledger verbucht und kann nicht entfernt werden'
: this.props.isOnShipment
? 'Artikel ist bereits auf einem Lieferschein und kann nicht entfernt werden'
: 'Zeile entfernen'
}
sx={{ p: 0.35 }}
>
<DeleteOutlinedIcon sx={{ fontSize: 18 }} />

View File

@@ -27,6 +27,9 @@ import entityAutomat from '../../automats/entityAutomat';
import customergroupAutomat from '../../automats/customergroupAutomat';
import supplierAutomat from '../../automats/supplierAutomat';
import numberAutomat from '../../automats/numberAutomat';
import quoteAutomat from '../../automats/quoteAutomat';
import orderAutomat from '../../automats/orderAutomat';
import deliveryAutomat from '../../automats/deliveryAutomat';
import { getNextDocumentNumber, ensureDocumentNumber } from '../../utils/documentNumber';
import {
SURFACE_RAISED,
@@ -233,11 +236,30 @@ class ManifestHeader extends PureComponent {
this.props.onPdfUpload?.(file);
}
subscribeListAutomat(kind) {
this.unsubList?.();
this.unsubList = null;
const automat =
kind === 'quote'
? quoteAutomat
: kind === 'order'
? orderAutomat
: kind === 'delivery'
? deliveryAutomat
: null;
if (automat) {
this.unsubList = automat.subscribe(this, () => {
if (!this._cancelled) this.forceUpdate();
});
}
}
componentDidMount() {
this._cancelled = false;
this.unsubNumber = numberAutomat.subscribe(this, () => {
this.forceUpdate();
});
this.subscribeListAutomat(this.props.kind);
void numberAutomat.actions.ensureLoaded().catch(() => {});
if (!this.props.number && this.props.docId == null) {
void ensureDocumentNumber(this.props.kind)
@@ -249,6 +271,9 @@ class ManifestHeader extends PureComponent {
}
componentDidUpdate(prevProps) {
if (prevProps.kind !== this.props.kind) {
this.subscribeListAutomat(this.props.kind);
}
if (
!this.props.number &&
this.props.docId == null &&
@@ -265,6 +290,7 @@ class ManifestHeader extends PureComponent {
componentWillUnmount() {
this._cancelled = true;
this.unsubNumber?.();
this.unsubList?.();
}
render() {