This commit is contained in:
seb
2026-10-14 15:00:31 +02:00
parent 4188d8f46f
commit b8fdef4e11
5 changed files with 87 additions and 96 deletions

View File

@@ -271,6 +271,7 @@ export function getManifestDirtyDetails(current, baseline) {
}; };
const dirtyLineKeys = []; const dirtyLineKeys = [];
const dirtyLineFields = {};
const curLines = current.lines || []; const curLines = current.lines || [];
const baseLines = baseline.lines || []; const baseLines = baseline.lines || [];
@@ -291,27 +292,49 @@ export function getManifestDirtyDetails(current, baseline) {
const bl = const bl =
(cl.key && baseLinesByKey.get(cl.key)) || (cl.key && baseLinesByKey.get(cl.key)) ||
(cl.id != null && baseLinesByKey.get(`id-${cl.id}`)) || (cl.id != null && baseLinesByKey.get(`id-${cl.id}`)) ||
baseLines[i]; (baseLinesByKey.size === 0 ? baseLines[i] : null);
if (!bl) { if (!bl) {
if (cl.sku || cl.name || (cl.amount && cl.amount !== '1') || cl.net || cl.gross) { dirtyLineKeys.push(key);
dirtyLineKeys.push(key); dirtyLineFields[key] = { __all__: true };
fields.lines = true; fields.lines = true;
}
} else { } else {
const lineChanged = const lineFieldDiffs = {};
String(cl.sku ?? '').trim() !== String(bl.sku ?? '').trim() || if (String(cl.sku ?? '').trim() !== String(bl.sku ?? '').trim()) {
String(cl.name ?? '').trim() !== String(bl.name ?? '').trim() || lineFieldDiffs.sku = true;
String(cl.amount ?? '').trim() !== String(bl.amount ?? '').trim() || }
String(cl.net ?? cl.netprice ?? '').trim() !== String(bl.net ?? bl.netprice ?? '').trim() || if (String(cl.name ?? '').trim() !== String(bl.name ?? '').trim()) {
String(cl.gross ?? '').trim() !== String(bl.gross ?? '').trim() || lineFieldDiffs.name = true;
String(cl.rebate ?? '').trim() !== String(bl.rebate ?? '').trim() || }
String(cl.tax ?? cl.taxrate ?? '').trim() !== String(bl.tax ?? bl.taxrate ?? '').trim() || if (String(cl.amount ?? cl.count ?? '').trim() !== String(bl.amount ?? bl.count ?? '').trim()) {
String(cl.pu ?? '').trim() !== String(bl.pu ?? '').trim() || lineFieldDiffs.amount = true;
String(cl.unit ?? '').trim() !== String(bl.unit ?? '').trim(); lineFieldDiffs.count = true;
}
if (String(cl.net ?? cl.netprice ?? '').trim() !== String(bl.net ?? bl.netprice ?? '').trim()) {
lineFieldDiffs.net = true;
lineFieldDiffs.netprice = true;
}
if (String(cl.gross ?? '').trim() !== String(bl.gross ?? '').trim()) {
lineFieldDiffs.gross = true;
}
if (String(cl.rebate ?? '').trim() !== String(bl.rebate ?? '').trim()) {
lineFieldDiffs.rebate = true;
}
if (String(cl.tax ?? cl.taxrate ?? '').trim() !== String(bl.tax ?? bl.taxrate ?? '').trim()) {
lineFieldDiffs.tax = true;
lineFieldDiffs.taxrate = true;
}
if (String(cl.pu ?? cl.vpe ?? '').trim() !== String(bl.pu ?? bl.vpe ?? '').trim()) {
lineFieldDiffs.pu = true;
lineFieldDiffs.vpe = true;
}
if (String(cl.unit ?? '').trim() !== String(bl.unit ?? '').trim()) {
lineFieldDiffs.unit = true;
}
if (lineChanged) { if (Object.keys(lineFieldDiffs).length > 0) {
dirtyLineKeys.push(key); dirtyLineKeys.push(key);
dirtyLineFields[key] = lineFieldDiffs;
fields.lines = true; fields.lines = true;
} }
} }
@@ -348,6 +371,7 @@ export function getManifestDirtyDetails(current, baseline) {
isDirty, isDirty,
fields, fields,
dirtyLineKeys, dirtyLineKeys,
dirtyLineFields,
summary, summary,
}; };
} }
@@ -413,6 +437,7 @@ export function createManifestEditAutomat({ kind, name }) {
isDirty: false, isDirty: false,
dirtyFields: { ...emptyFields }, dirtyFields: { ...emptyFields },
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
}); });
}, },
@@ -425,6 +450,7 @@ export function createManifestEditAutomat({ kind, name }) {
isDirty: dirtyInfo.isDirty, isDirty: dirtyInfo.isDirty,
dirtyFields: dirtyInfo.fields, dirtyFields: dirtyInfo.fields,
dirtyLineKeys: dirtyInfo.dirtyLineKeys, dirtyLineKeys: dirtyInfo.dirtyLineKeys,
dirtyLineFields: dirtyInfo.dirtyLineFields,
dirtySummary: dirtyInfo.summary, dirtySummary: dirtyInfo.summary,
}); });
}, },
@@ -442,6 +468,7 @@ export function createManifestEditAutomat({ kind, name }) {
isDirty: false, isDirty: false,
dirtyFields: { ...emptyFields }, dirtyFields: { ...emptyFields },
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
}); });
}, },
@@ -474,6 +501,7 @@ export function createManifestEditAutomat({ kind, name }) {
isDirty: false, isDirty: false,
dirtyFields: { ...emptyFields }, dirtyFields: { ...emptyFields },
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
}); });
}, },
@@ -493,6 +521,7 @@ export function createManifestEditAutomat({ kind, name }) {
isDirty: false, isDirty: false,
dirtyFields: { ...emptyFields }, dirtyFields: { ...emptyFields },
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
}); });
}, },
@@ -513,6 +542,7 @@ export function createManifestEditAutomat({ kind, name }) {
isDirty: false, isDirty: false,
dirtyFields: { ...emptyFields }, dirtyFields: { ...emptyFields },
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
}, },
actions, actions,

View File

@@ -69,6 +69,7 @@ class ManifestForm extends PureComponent {
isDirty: Boolean(initialEdit?.isDirty), isDirty: Boolean(initialEdit?.isDirty),
dirtyFields: initialEdit?.dirtyFields || null, dirtyFields: initialEdit?.dirtyFields || null,
dirtyLineKeys: initialEdit?.dirtyLineKeys || [], dirtyLineKeys: initialEdit?.dirtyLineKeys || [],
dirtyLineFields: initialEdit?.dirtyLineFields || {},
dirtySummary: initialEdit?.dirtySummary || [], dirtySummary: initialEdit?.dirtySummary || [],
saving: false, saving: false,
pdfUploading: false, pdfUploading: false,
@@ -172,6 +173,7 @@ class ManifestForm extends PureComponent {
isDirty: Boolean(state.isDirty), isDirty: Boolean(state.isDirty),
dirtyFields: state.dirtyFields, dirtyFields: state.dirtyFields,
dirtyLineKeys: state.dirtyLineKeys, dirtyLineKeys: state.dirtyLineKeys,
dirtyLineFields: state.dirtyLineFields,
dirtySummary: state.dirtySummary, dirtySummary: state.dirtySummary,
}; };
} }
@@ -225,13 +227,14 @@ class ManifestForm extends PureComponent {
lines: this.state.lines, lines: this.state.lines,
attachments: this.state.attachments, attachments: this.state.attachments,
}); });
const { isDirty, dirtyFields, dirtyLineKeys, dirtySummary } = this.editAutomat.state; const { isDirty, dirtyFields, dirtyLineKeys, dirtyLineFields, dirtySummary } = this.editAutomat.state;
if ( if (
this.state.isDirty !== isDirty || this.state.isDirty !== isDirty ||
this.state.dirtyFields !== dirtyFields || this.state.dirtyFields !== dirtyFields ||
this.state.dirtyLineFields !== dirtyLineFields ||
this.state.dirtySummary !== dirtySummary this.state.dirtySummary !== dirtySummary
) { ) {
this.setState({ isDirty, dirtyFields, dirtyLineKeys, dirtySummary }); this.setState({ isDirty, dirtyFields, dirtyLineKeys, dirtyLineFields, dirtySummary });
} }
} }
} }
@@ -297,6 +300,7 @@ class ManifestForm extends PureComponent {
isDirty: Boolean(this.editAutomat.state.isDirty), isDirty: Boolean(this.editAutomat.state.isDirty),
dirtyFields: this.editAutomat.state.dirtyFields, dirtyFields: this.editAutomat.state.dirtyFields,
dirtyLineKeys: this.editAutomat.state.dirtyLineKeys, dirtyLineKeys: this.editAutomat.state.dirtyLineKeys,
dirtyLineFields: this.editAutomat.state.dirtyLineFields,
dirtySummary: this.editAutomat.state.dirtySummary, dirtySummary: this.editAutomat.state.dirtySummary,
date: this.editAutomat.state.date, date: this.editAutomat.state.date,
partyId: this.editAutomat.state.partyId, partyId: this.editAutomat.state.partyId,
@@ -349,6 +353,7 @@ class ManifestForm extends PureComponent {
isDirty: false, isDirty: false,
dirtyFields: null, dirtyFields: null,
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
}); });
} catch (err) { } catch (err) {
@@ -380,6 +385,7 @@ class ManifestForm extends PureComponent {
isDirty: false, isDirty: false,
dirtyFields: state.dirtyFields, dirtyFields: state.dirtyFields,
dirtyLineKeys: state.dirtyLineKeys, dirtyLineKeys: state.dirtyLineKeys,
dirtyLineFields: state.dirtyLineFields,
dirtySummary: state.dirtySummary, dirtySummary: state.dirtySummary,
}); });
enqueueSnackbar('Änderungen verworfen und ursprünglicher Stand wiederhergestellt.', { variant: 'info' }); enqueueSnackbar('Änderungen verworfen und ursprünglicher Stand wiederhergestellt.', { variant: 'info' });
@@ -874,6 +880,7 @@ class ManifestForm extends PureComponent {
isDirty: false, isDirty: false,
dirtyFields: null, dirtyFields: null,
dirtyLineKeys: [], dirtyLineKeys: [],
dirtyLineFields: {},
dirtySummary: [], dirtySummary: [],
saving: false, saving: false,
}); });
@@ -887,7 +894,7 @@ class ManifestForm extends PureComponent {
renderArticles() { renderArticles() {
const cfg = this.cfg; const cfg = this.cfg;
const { lines, saving, dirtyFields, dirtyLineKeys } = this.state; const { lines, saving, dirtyLineFields } = this.state;
return ( return (
<ManifestLines <ManifestLines
@@ -897,8 +904,7 @@ class ManifestForm extends PureComponent {
saving={saving} saving={saving}
wide={4} wide={4}
linesColor={cfg.linesColor} linesColor={cfg.linesColor}
isLinesDirty={Boolean(dirtyFields?.lines)} dirtyLineFields={dirtyLineFields || {}}
dirtyLineKeys={dirtyLineKeys || []}
onLineChange={this.handleLineChange} onLineChange={this.handleLineChange}
onSkuBlur={this.handleSkuBlur} onSkuBlur={this.handleSkuBlur}
onAddLine={this.handleAddLine} onAddLine={this.handleAddLine}

View File

@@ -267,6 +267,11 @@ class ManifestFormLine extends PureComponent {
const isFirstInput = hasPosCol ? colIdx === 1 : colIdx === 0; const isFirstInput = hasPosCol ? colIdx === 1 : colIdx === 0;
const isLast = colIdx === totalCols - 1; const isLast = colIdx === totalCols - 1;
const { dirtyFields } = this.props;
const isFieldDirty = Boolean(
dirtyFields && (dirtyFields.__all__ || dirtyFields[id])
);
return ( return (
<TextField <TextField
size="small" size="small"
@@ -290,6 +295,9 @@ class ManifestFormLine extends PureComponent {
'& .MuiOutlinedInput-root': { '& .MuiOutlinedInput-root': {
borderRadius: isFirstInput ? '4px 0 0 4px' : isLast ? '0 4px 4px 0' : 0, borderRadius: isFirstInput ? '4px 0 0 4px' : isLast ? '0 4px 4px 0' : 0,
ml: isFirstInput ? 0 : '-1px', ml: isFirstInput ? 0 : '-1px',
...(isFieldDirty && {
bgcolor: 'rgba(237, 108, 2, 0.08)',
}),
'&:hover': { '&:hover': {
zIndex: 1, zIndex: 1,
}, },
@@ -297,6 +305,16 @@ class ManifestFormLine extends PureComponent {
zIndex: 2, zIndex: 2,
}, },
}, },
...(isFieldDirty && {
zIndex: 1,
'& .MuiOutlinedInput-notchedOutline': {
borderColor: 'warning.main',
borderWidth: 2,
},
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: 'warning.main',
},
}),
'& input[type=number]': { '& input[type=number]': {
MozAppearance: 'textfield', MozAppearance: 'textfield',
}, },
@@ -838,19 +856,13 @@ class ManifestFormLine extends PureComponent {
} }
render() { render() {
const { columns = LINE_COLUMNS, saving, isDirty } = this.props; const { columns = LINE_COLUMNS, saving } = this.props;
const secondLine = this.renderSecondLine(); const secondLine = this.renderSecondLine();
return ( return (
<Fragment> <Fragment>
{/* Row 1: Editable Line Inputs */} {/* Row 1: Editable Line Inputs */}
<TableRow <TableRow hover sx={{ '& > td': { borderBottom: 'none', p: 0 } }}>
hover
sx={{
'& > td': { borderBottom: 'none', p: 0 },
...(isDirty ? { bgcolor: 'rgba(237, 108, 2, 0.08)' } : {}),
}}
>
{columns.map((col, colIdx) => ( {columns.map((col, colIdx) => (
<TableCell <TableCell
key={col.id} key={col.id}
@@ -859,7 +871,6 @@ class ManifestFormLine extends PureComponent {
p: 0, p: 0,
width: col.width, width: col.width,
verticalAlign: 'middle', verticalAlign: 'middle',
...(isDirty && colIdx === 0 ? { borderLeft: '3px solid', borderColor: 'warning.main' } : {}),
}} }}
> >
{this.renderField(col, colIdx, columns.length)} {this.renderField(col, colIdx, columns.length)}
@@ -879,19 +890,13 @@ class ManifestFormLine extends PureComponent {
{/* Row 2: Matched Supplier Article Details */} {/* Row 2: Matched Supplier Article Details */}
{secondLine && ( {secondLine && (
<TableRow <TableRow sx={{ '& > td': { pt: 0.25, pb: 0.75, borderBottom: 'none' } }}>
sx={{
'& > td': { pt: 0.25, pb: 0.75, borderBottom: 'none' },
...(isDirty ? { bgcolor: 'rgba(237, 108, 2, 0.04)' } : {}),
}}
>
<TableCell <TableCell
colSpan={columns.length + 1} colSpan={columns.length + 1}
sx={{ sx={{
py: 0.4, py: 0.4,
px: 1, px: 1,
bgcolor: 'transparent', bgcolor: 'transparent',
...(isDirty ? { borderLeft: '3px solid', borderColor: 'warning.main' } : {}),
}} }}
> >
{secondLine} {secondLine}

View File

@@ -6,7 +6,6 @@ import CircularProgress from '@mui/material/CircularProgress';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import Chip from '@mui/material/Chip'; import Chip from '@mui/material/Chip';
import Typography from '@mui/material/Typography';
import UploadFileIcon from '@mui/icons-material/UploadFile'; import UploadFileIcon from '@mui/icons-material/UploadFile';
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined'; import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined'; import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
@@ -486,33 +485,6 @@ class ManifestHeader extends PureComponent {
Wiederherstellen Wiederherstellen
</Button> </Button>
)} )}
{isDirty && dirtySummary && dirtySummary.length > 0 && (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5, pt: 0.25 }}>
<Typography variant="caption" sx={{ color: 'warning.dark', fontWeight: 700, fontSize: '0.72rem' }}>
Geänderte Felder:
</Typography>
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{dirtySummary.map((item) => (
<Chip
key={item}
size="small"
color="warning"
variant="filled"
label={item}
sx={{
height: 20,
fontSize: '0.68rem',
fontWeight: 700,
bgcolor: 'rgba(237, 108, 2, 0.15)',
color: 'warning.dark',
border: '1px solid',
borderColor: 'warning.main',
}}
/>
))}
</Box>
</Box>
)}
{docId != null && kind === 'delivery' && onOpenBookDialog && ( {docId != null && kind === 'delivery' && onOpenBookDialog && (
<Button <Button
variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'} variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'}

View File

@@ -9,7 +9,6 @@ import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead'; import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow'; import TableRow from '@mui/material/TableRow';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Chip from '@mui/material/Chip';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import DeleteSweepOutlinedIcon from '@mui/icons-material/DeleteSweepOutlined'; import DeleteSweepOutlinedIcon from '@mui/icons-material/DeleteSweepOutlined';
@@ -25,8 +24,7 @@ class ManifestLines extends PureComponent {
lines, lines,
saving, saving,
linesColor, linesColor,
isLinesDirty = false, dirtyLineFields = {},
dirtyLineKeys = [],
onLineChange, onLineChange,
onAddLine, onAddLine,
onRemoveLine, onRemoveLine,
@@ -68,28 +66,7 @@ class ManifestLines extends PureComponent {
wide={wide} wide={wide}
minHeight={280} minHeight={280}
color={linesColor} color={linesColor}
title={ title="Artikel"
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<span>Artikel</span>
{isLinesDirty && (
<Chip
size="small"
color="warning"
variant="filled"
label="Geändert"
sx={{
height: 20,
fontSize: '0.68rem',
fontWeight: 700,
bgcolor: 'rgba(237, 108, 2, 0.15)',
color: 'warning.dark',
border: '1px solid',
borderColor: 'warning.main',
}}
/>
)}
</Box>
}
sx={{ sx={{
...(wide >= 4 ? { gridColumn: { xs: '1 / -1', sm: '1 / -1', md: 'span 4' } } : {}), ...(wide >= 4 ? { gridColumn: { xs: '1 / -1', sm: '1 / -1', md: 'span 4' } } : {}),
...(!hasArticles ...(!hasArticles
@@ -139,11 +116,12 @@ class ManifestLines extends PureComponent {
(supplierArticlesLoading && !matched) (supplierArticlesLoading && !matched)
: false; : false;
const ledgerLine = line.id != null ? ledgerLinesById[line.id] : null; const ledgerLine = line.id != null ? ledgerLinesById[line.id] : null;
const isDirty = const lineDirtyFields =
dirtyLineKeys.includes(lineKey) || dirtyLineFields[lineKey] ||
(line.key != null && dirtyLineKeys.includes(line.key)) || (line.key != null ? dirtyLineFields[line.key] : null) ||
(line.id != null && dirtyLineKeys.includes(`id-${line.id}`)) || (line.id != null ? dirtyLineFields[`id-${line.id}`] : null) ||
dirtyLineKeys.includes(`idx-${index}`); dirtyLineFields[`idx-${index}`] ||
null;
return ( return (
<ManifestFormLine <ManifestFormLine
@@ -154,7 +132,7 @@ class ManifestLines extends PureComponent {
line={line.key != null ? line : { ...line, key: index }} line={line.key != null ? line : { ...line, key: index }}
columns={columns} columns={columns}
saving={saving} saving={saving}
isDirty={Boolean(isDirty)} dirtyFields={lineDirtyFields}
supplierArticle={matched} supplierArticle={matched}
loading={Boolean(isLineLoading)} loading={Boolean(isLineLoading)}
supplierArticleBusy={Boolean(busySupplierArticleLineKeys[lineKey])} supplierArticleBusy={Boolean(busySupplierArticleLineKeys[lineKey])}