diff --git a/src/components/manifest/ManifestFormLine.jsx b/src/components/manifest/ManifestFormLine.jsx index 8b59624..3828b57 100644 --- a/src/components/manifest/ManifestFormLine.jsx +++ b/src/components/manifest/ManifestFormLine.jsx @@ -30,8 +30,15 @@ import { articleSupplierAutomat } from '../../automats/articleDetailAutomat'; import { selectCompanyTaxrate } from '../../automats/companyAutomat'; import { BORDER, + GREEN, + alpha, } from '../../colors'; +const LOCKED_ROW_BG = alpha(GREEN, 0.08); +const LOCKED_ROW_HOVER_BG = alpha(GREEN, 0.12); +const LOCKED_CELL_BORDER = alpha(GREEN, 0.32); +const LOCKED_CELL_BG = alpha(GREEN, 0.08); + class ManifestFormLine extends PureComponent { constructor(props) { super(props); @@ -60,6 +67,16 @@ class ManifestFormLine extends PureComponent { return props.line?.sku ? String(props.line.sku).trim() : null; } + isFullyLedgered(props = this.props) { + const { ledgerLine, line } = props; + return Boolean( + ledgerLine?.isBooked || + (ledgerLine && ledgerLine.targetAmount > 0 && ledgerLine.remainingAmount <= 0) || + line?.isBooked || + line?.is_booked + ); + } + componentDidMount() { this.subscribeArticle(this.getArticleSku()); } @@ -125,7 +142,7 @@ class ManifestFormLine extends PureComponent { } handleRemove() { - if (this.props.isOnShipment) return; + if (this.props.isOnShipment || this.isFullyLedgered()) return; this.props.onRemove(this.props.line.key); } @@ -139,7 +156,8 @@ class ManifestFormLine extends PureComponent { handleStartEdit(columnId) { const { saving } = this.props; - const isDisabled = saving || (columnId === 'sku' && Boolean(this.props.isOnShipment)); + const isFullyLedgered = this.isFullyLedgered(); + const isDisabled = saving || isFullyLedgered || (columnId === 'sku' && Boolean(this.props.isOnShipment)); if (isDisabled) return; if (this.state.editingField !== columnId) { this.setState({ editingField: columnId }); @@ -162,6 +180,7 @@ class ManifestFormLine extends PureComponent { } handleCreateArticle() { + if (this.isFullyLedgered()) return; const { line, onCreateArticle } = this.props; if (onCreateArticle) { onCreateArticle(line); @@ -169,6 +188,7 @@ class ManifestFormLine extends PureComponent { } handleSearchArticle() { + if (this.isFullyLedgered()) return; const { line, onSearchArticle } = this.props; if (onSearchArticle) { onSearchArticle(line); @@ -176,107 +196,14 @@ class ManifestFormLine extends PureComponent { } handleRemoveSupplierArticle() { + if (this.isFullyLedgered()) return; const { line, supplierArticle, onRemoveSupplierArticle } = this.props; if (onRemoveSupplierArticle) { onRemoveSupplierArticle(line, supplierArticle); } } - renderLedgerChip() { - const { ledgerLine, onBookLine } = this.props; - if (!ledgerLine) return null; - const isBooked = ledgerLine.isBooked; - const isPartial = !isBooked && ledgerLine.bookedAmount > 0; - const isClickable = Boolean(onBookLine); - const isTracked = Boolean(ledgerLine.trackStock); - const tooltipTitle = isTracked - ? isBooked - ? `Vollständig im Lager gebucht: ${ledgerLine.bookedAmount} Stück (Klicken für Details)` - : isPartial - ? `Teilweise im Lager gebucht: ${ledgerLine.bookedAmount} von ${ledgerLine.targetAmount} Stück (Klicken zum Einbuchen dieser Position)` - : `Noch nicht im Lager gebucht (${ledgerLine.targetAmount} Stück – Klicken zum Einbuchen dieser Position)` - : isBooked - ? `Vollständig im Ledger verbucht: ${ledgerLine.bookedAmount} Stück (Klicken für Details)` - : isPartial - ? `Teilweise im Ledger verbucht: ${ledgerLine.bookedAmount} von ${ledgerLine.targetAmount} Stück (Klicken zum Verbuchen dieser Position)` - : `Noch nicht im Ledger verbucht (${ledgerLine.targetAmount} Stück – Klicken zum Verbuchen dieser Position)`; - - const label = isTracked - ? isBooked - ? `Lager: ${ledgerLine.bookedAmount} Stk. gebucht` - : isPartial - ? `Lager: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.` - : 'Lager: offen' - : isBooked - ? `Gebucht: ${ledgerLine.bookedAmount} Stk.` - : isPartial - ? `Gebucht: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.` - : null; - - if (!label) return null; - - return ( - - { - e.stopPropagation(); - onBookLine(ledgerLine); - } - : undefined - } - icon={ - isBooked ? ( - - ) : ( - - ) - } - label={label} - variant={isBooked ? 'outlined' : 'filled'} - sx={{ - height: 22, - fontSize: '0.7rem', - fontWeight: 700, - cursor: isClickable ? 'pointer' : 'default', - ...(isBooked - ? { - bgcolor: 'rgba(21, 128, 61, 0.08)', - color: '#15803d', - borderColor: '#15803d', - borderWidth: 1, - borderStyle: 'solid', - '&:hover': isClickable ? { bgcolor: 'rgba(21, 128, 61, 0.16)' } : undefined, - } - : isPartial - ? { - bgcolor: '#d97706', - color: '#ffffff', - border: 'none', - '&:hover': isClickable ? { bgcolor: '#b45309' } : undefined, - } - : { - bgcolor: 'primary.main', - color: '#ffffff', - border: 'none', - '&:hover': isClickable ? { bgcolor: 'primary.dark' } : undefined, - }), - '& .MuiChip-label': { - color: 'inherit', - px: 0.75, - }, - '& .MuiChip-icon': { - color: 'inherit', - }, - }} - /> - - ); - } renderField(column, colIdx, totalCols) { const { id, align, type } = column; @@ -365,10 +292,13 @@ class ManifestFormLine extends PureComponent { ); const isEditing = this.state.editingField === id; - const isDisabled = saving || (id === 'sku' && Boolean(this.props.isOnShipment)); - const titleText = id === 'sku' && this.props.isOnShipment - ? 'Artikel ist bereits auf einem Lieferschein und kann nicht geändert werden' - : undefined; + const isFullyLedgered = this.isFullyLedgered(); + const isDisabled = saving || isFullyLedgered || (id === 'sku' && Boolean(this.props.isOnShipment)); + const titleText = isFullyLedgered + ? 'Position ist bereits vollständig im Ledger verbucht und kann nicht geändert werden' + : id === 'sku' && this.props.isOnShipment + ? 'Artikel ist bereits auf einem Lieferschein und kann nicht geändert werden' + : undefined; if (isEditing) { return ( @@ -456,9 +386,10 @@ class ManifestFormLine extends PureComponent { this.handleStartEdit(id)} - onFocus={() => this.handleStartEdit(id)} + onClick={isDisabled ? undefined : () => this.handleStartEdit(id)} + onFocus={isDisabled ? undefined : () => this.handleStartEdit(id)} onKeyDown={(e) => { + if (isDisabled) return; if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.handleStartEdit(id); @@ -485,11 +416,16 @@ class ManifestFormLine extends PureComponent { ? 'warning.main' : isError ? 'error.main' - : 'rgba(0, 0, 0, 0.23)', + : isFullyLedgered + ? LOCKED_CELL_BORDER + : 'rgba(0, 0, 0, 0.23)', cursor: isDisabled ? 'default' : 'text', touchAction: 'manipulation', userSelect: 'none', zIndex: 1, + ...(isFullyLedgered && !isFieldDirty && !isError && { + bgcolor: LOCKED_CELL_BG, + }), ...(isFieldDirty && { bgcolor: 'rgba(237, 108, 2, 0.08)', zIndex: 2, @@ -943,13 +879,19 @@ class ManifestFormLine extends PureComponent { > {/* Unlink button at the front */} {this.props.onRemoveSupplierArticle && supplierArticle.id != null && ( - + @@ -1095,8 +1037,6 @@ class ManifestFormLine extends PureComponent { )} )} - - {this.renderLedgerChip()} ); } @@ -1109,6 +1049,7 @@ class ManifestFormLine extends PureComponent { size="small" variant="text" startIcon={} + disabled={this.isFullyLedgered()} onClick={this.handleCreateArticle} sx={{ fontSize: '0.7rem', @@ -1131,6 +1072,7 @@ class ManifestFormLine extends PureComponent { size="small" variant="text" startIcon={} + disabled={this.isFullyLedgered()} onClick={this.handleSearchArticle} sx={{ fontSize: '0.7rem', @@ -1154,17 +1096,6 @@ class ManifestFormLine extends PureComponent { ? `Kein Lieferanten-Artikel für SKU „${rawSku}“ gefunden.` : `Kein Artikel für SKU „${rawSku}“ gefunden.`} - {this.renderLedgerChip()} - - ); - } - - if (this.props.ledgerLine) { - const chip = this.renderLedgerChip(); - if (!chip) return null; - return ( - - {chip} ); } @@ -1174,12 +1105,24 @@ class ManifestFormLine extends PureComponent { render() { const { columns = LINE_COLUMNS, saving, ledgerLine } = this.props; + const isFullyLedgered = this.isFullyLedgered(); const secondLine = this.renderSecondLine(); return ( {/* Row 1: Editable Line Inputs */} - td': { borderBottom: 'none', p: 0 } }}> + td': { borderBottom: 'none', p: 0 }, + ...(isFullyLedgered && { + bgcolor: LOCKED_ROW_BG, + '&:hover': { + bgcolor: LOCKED_ROW_HOVER_BG, + }, + }), + }} + > {columns.map((col, colIdx) => ( @@ -1269,9 +1215,15 @@ class ManifestFormLine extends PureComponent { @@ -1282,7 +1234,14 @@ class ManifestFormLine extends PureComponent { {/* Row 2: Matched Supplier Article Details */} {secondLine && ( - td': { pt: 0.25, pb: 0.75, borderBottom: 'none' } }}> + td': { pt: 0.25, pb: 0.75, borderBottom: 'none' }, + ...(isFullyLedgered && { + bgcolor: LOCKED_ROW_BG, + }), + }} + > { const hasShippedLines = kind === 'order' && Array.isArray(shipments) && shipments.some((sh) => (sh.lines?.length || 0) > 0); + const hasLedgeredLines = kind === 'delivery' && Array.isArray(lines) && lines.some((line) => { + const ledgerLine = line?.id != null ? ledgerLinesById?.[line.id] : null; + return Boolean( + ledgerLine?.isBooked || + (ledgerLine && ledgerLine.targetAmount > 0 && ledgerLine.remainingAmount <= 0) || + (ledgerLine && ledgerLine.bookedAmount > 0) || + line?.isBooked || + line?.is_booked + ); + }); + const disabled = saving || !lines || lines.length === 0 || hasShippedLines || hasLedgeredLines; + const title = hasLedgeredLines + ? 'Lieferung enthält bereits im Ledger verbuchte Positionen' + : hasShippedLines + ? 'Auftrag enthält bereits ausgelieferte Positionen' + : 'Alle Zeilen entfernen'; return (