This commit is contained in:
seb
2026-10-18 20:21:27 +02:00
parent 328ba164ec
commit ac9034685f
4 changed files with 151 additions and 63 deletions

View File

@@ -868,6 +868,7 @@ class ManifestForm extends PureComponent {
handleRemoveAllLines() { handleRemoveAllLines() {
this.setState((prev) => { this.setState((prev) => {
let nextLines = [];
if (this.props.kind === 'order') { if (this.props.kind === 'order') {
const shippedSkus = new Set(); const shippedSkus = new Set();
for (const sh of prev.shipments || []) { for (const sh of prev.shipments || []) {
@@ -876,7 +877,7 @@ class ManifestForm extends PureComponent {
} }
} }
if (shippedSkus.size > 0) { if (shippedSkus.size > 0) {
const remaining = prev.lines.filter((l) => { const remaining = (prev.lines || []).filter((l) => {
const raw = l.sku != null ? String(l.sku).trim().toLowerCase() : ''; const raw = l.sku != null ? String(l.sku).trim().toLowerCase() : '';
return shippedSkus.has(raw); return shippedSkus.has(raw);
}); });
@@ -885,10 +886,10 @@ class ManifestForm extends PureComponent {
return null; return null;
} }
enqueueSnackbar('Nicht ausgelieferte Positionen wurden entfernt.', { variant: 'info' }); enqueueSnackbar('Nicht ausgelieferte Positionen wurden entfernt.', { variant: 'info' });
return { lines: remaining, isDirty: true }; nextLines = remaining;
} }
} }
return { lines: [], isDirty: true }; return { lines: nextLines, isDirty: true };
}); });
} }
@@ -1181,7 +1182,7 @@ class ManifestForm extends PureComponent {
{ {
kind: cfg.kind, kind: cfg.kind,
partyField: cfg.partyField, partyField: cfg.partyField,
requireAtLeastOne: true, requireAtLeastOne: !isEdit,
externalNumber: cfg.externalNumber, externalNumber: cfg.externalNumber,
isUpdate: isEdit, isUpdate: isEdit,
mode, mode,
@@ -1334,7 +1335,7 @@ class ManifestForm extends PureComponent {
isDirty={this.state.isDirty} isDirty={this.state.isDirty}
dirtyFields={this.state.dirtyFields} dirtyFields={this.state.dirtyFields}
dirtySummary={this.state.dirtySummary} dirtySummary={this.state.dirtySummary}
onRestore={this.props.mode === 'edit' && this.state.isDirty ? this.handleRestore : undefined} onRestore={this.props.mode === 'edit' ? this.handleRestore : undefined}
onPdfUpload={kind === 'delivery' ? this.handlePdfUpload : undefined} onPdfUpload={kind === 'delivery' ? this.handlePdfUpload : undefined}
onPdfDelete={this.handleAttachmentDelete} onPdfDelete={this.handleAttachmentDelete}
onPdfCreate={kind === 'quote' || kind === 'order' ? this.handleCreatePdf : undefined} onPdfCreate={kind === 'quote' || kind === 'order' ? this.handleCreatePdf : undefined}

View File

@@ -481,27 +481,45 @@ class ManifestHeader extends PureComponent {
> >
{saving ? 'Speichern…' : 'Speichern'} {saving ? 'Speichern…' : 'Speichern'}
</Button> </Button>
{isDirty && onRestore && ( {(() => {
<Button const canRestore = Boolean(onRestore && isDirty && !saving && !orderCreating);
variant="outlined" return (
color="warning" <Button
size="small" variant="outlined"
startIcon={<RestoreIcon />} color={canRestore ? 'warning' : 'inherit'}
disabled={saving || orderCreating} size="small"
onClick={onRestore} startIcon={<RestoreIcon />}
sx={{ disabled={!canRestore}
height: 34, onClick={canRestore ? onRestore : undefined}
fontWeight: 700, title={
fontSize: '0.8rem', canRestore
whiteSpace: 'nowrap', ? 'Änderungen verwerfen und ursprünglichen Stand wiederherstellen'
textTransform: 'none', : 'Keine Änderungen zum Wiederherstellen'
borderWidth: 1.5, }
'&:hover': { borderWidth: 1.5 }, sx={{
}} height: 34,
> width: '100%',
Wiederherstellen fontWeight: 700,
</Button> fontSize: '0.8rem',
)} whiteSpace: 'nowrap',
textTransform: 'none',
borderWidth: 1.5,
'&:hover': { borderWidth: 1.5 },
...(!canRestore && {
borderColor: 'action.disabled',
color: 'text.disabled',
'&.Mui-disabled': {
borderColor: 'action.disabled',
color: 'text.disabled',
borderWidth: 1.5,
},
}),
}}
>
Wiederherstellen
</Button>
);
})()}
{docId != null && kind === 'delivery' && onOpenBookDialog && Boolean(ledgerSummary && (ledgerSummary.totalPositions > 0 || ledgerSummary.lines?.length > 0 || ledgerSummary.hasTrackStock)) && ( {docId != null && kind === 'delivery' && onOpenBookDialog && Boolean(ledgerSummary && (ledgerSummary.totalPositions > 0 || ledgerSummary.lines?.length > 0 || ledgerSummary.hasTrackStock)) && (
<Button <Button
variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'} variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'}
@@ -561,27 +579,56 @@ class ManifestHeader extends PureComponent {
{orderCreating ? 'Wird erstellt…' : 'Bestellung erstellen'} {orderCreating ? 'Wird erstellt…' : 'Bestellung erstellen'}
</Button> </Button>
)} )}
{(kind === 'quote' || kind === 'order' || kind === 'delivery') && onPdfCreate && docId != null && Boolean(number) && ( {(kind === 'quote' || kind === 'order' || kind === 'delivery') && (() => {
<Button const canCreatePdf = Boolean(
type="button" onPdfCreate &&
variant="outlined" docId != null &&
startIcon={pdfCreating ? <CircularProgress size={16} color="inherit" /> : <PictureAsPdfIcon />} Boolean(number) &&
disabled={saving || pdfCreating || orderCreating} !saving &&
onClick={onPdfCreate} !pdfCreating &&
title="PDF erstellen" !orderCreating
sx={{ );
whiteSpace: 'nowrap', return (
height: 40, <Button
width: '100%', type="button"
px: 1.5, variant="outlined"
boxSizing: 'border-box', color={canCreatePdf ? 'primary' : 'inherit'}
fontWeight: 600, startIcon={pdfCreating ? <CircularProgress size={16} color="inherit" /> : <PictureAsPdfIcon />}
fontSize: '0.875rem', disabled={!canCreatePdf}
}} onClick={canCreatePdf ? onPdfCreate : undefined}
> title={
{pdfCreating ? 'Wird erstellt…' : 'PDF erstellen'} pdfCreating
</Button> ? 'PDF wird erstellt…'
)} : canCreatePdf
? 'PDF erstellen'
: docId == null
? 'PDF kann erst nach dem Speichern erstellt werden'
: !number
? 'Dokumentnummer erforderlich für PDF'
: 'PDF erstellen'
}
sx={{
whiteSpace: 'nowrap',
height: 40,
width: '100%',
px: 1.5,
boxSizing: 'border-box',
fontWeight: 600,
fontSize: '0.875rem',
...(!canCreatePdf && {
borderColor: 'action.disabled',
color: 'text.disabled',
'&.Mui-disabled': {
borderColor: 'action.disabled',
color: 'text.disabled',
},
}),
}}
>
{pdfCreating ? 'Wird erstellt…' : 'PDF erstellen'}
</Button>
);
})()}
{onPdfUpload ? ( {onPdfUpload ? (
<FileDropzone <FileDropzone
accept="application/pdf,.pdf,text/plain,.txt,text/csv,.csv" accept="application/pdf,.pdf,text/plain,.txt,text/csv,.csv"

View File

@@ -199,22 +199,42 @@ class ManifestLines extends PureComponent {
); );
})()} })()}
{(() => { {(() => {
const hasShippedLines = kind === 'order' && Array.isArray(shipments) && shipments.some((sh) => (sh.lines?.length || 0) > 0); const shippedSkus = new Set();
const hasLedgeredLines = kind === 'delivery' && Array.isArray(lines) && lines.some((line) => { if (kind === 'order' && Array.isArray(shipments)) {
const ledgerLine = line?.id != null ? ledgerLinesById?.[line.id] : null; for (const sh of shipments) {
return Boolean( for (const sl of sh.lines || []) {
ledgerLine?.isBooked || if (sl.sku) shippedSkus.add(String(sl.sku).trim().toLowerCase());
(ledgerLine && ledgerLine.targetAmount > 0 && ledgerLine.remainingAmount <= 0) || }
(ledgerLine && ledgerLine.bookedAmount > 0) || }
line?.isBooked || }
line?.is_booked const allLinesShipped =
); kind === 'order' &&
}); shippedSkus.size > 0 &&
const disabled = saving || !lines || lines.length === 0 || hasShippedLines || hasLedgeredLines; Array.isArray(lines) &&
const title = hasLedgeredLines lines.length > 0 &&
? 'Lieferung enthält bereits im Ledger verbuchte Positionen' lines.every((l) => {
: hasShippedLines const raw = l?.sku != null ? String(l.sku).trim().toLowerCase() : '';
? 'Auftrag enthält bereits ausgelieferte Positionen' return raw && shippedSkus.has(raw);
});
const allLinesLedgered =
kind === 'delivery' &&
Array.isArray(lines) &&
lines.length > 0 &&
lines.every((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 || allLinesShipped || allLinesLedgered;
const title = allLinesLedgered
? 'Alle Positionen der Lieferung sind bereits im Ledger verbucht'
: allLinesShipped
? 'Alle Positionen des Auftrags sind bereits ausgeliefert'
: 'Alle Zeilen entfernen'; : 'Alle Zeilen entfernen';
return ( return (
<Button <Button

View File

@@ -160,6 +160,7 @@ class LieferungNeuPage extends PureComponent {
this.handleRemoveLine = this.handleRemoveLine.bind(this); this.handleRemoveLine = this.handleRemoveLine.bind(this);
this.handleMoveLine = this.handleMoveLine.bind(this); this.handleMoveLine = this.handleMoveLine.bind(this);
this.handleRemoveAllLines = this.handleRemoveAllLines.bind(this); this.handleRemoveAllLines = this.handleRemoveAllLines.bind(this);
this.handleRestore = this.handleRestore.bind(this);
this.handleSave = this.handleSave.bind(this); this.handleSave = this.handleSave.bind(this);
this.handlePdfUpload = this.handlePdfUpload.bind(this); this.handlePdfUpload = this.handlePdfUpload.bind(this);
this.handlePdfRescan = this.handlePdfRescan.bind(this); this.handlePdfRescan = this.handlePdfRescan.bind(this);
@@ -686,6 +687,24 @@ class LieferungNeuPage extends PureComponent {
handleRemoveAllLines() { handleRemoveAllLines() {
this.automat.actions.clearLineitems(); this.automat.actions.clearLineitems();
this.setState({ lines: [] });
}
handleRestore() {
if (!this._initialDoc) return;
const base = this._initialDoc;
this.automat.actions.setDate(base.date);
if (base.partyId != null) {
this.automat.actions.setDeliveryEntity(base.partyId);
}
this.automat.actions.setExternalNumber(base.externalNumber || '');
const restoredLines = (base.lines || []).map((l) => ({ ...l }));
this.automat.actions.setLineitems(restoredLines);
this.automat.actions.setAttachments([...(base.attachments || [])]);
this.setState({
lines: restoredLines,
});
enqueueSnackbar('Änderungen verworfen und ursprünglicher Stand wiederhergestellt.', { variant: 'info' });
} }
async handlePdfUpload(file) { async handlePdfUpload(file) {
@@ -1331,6 +1350,7 @@ class LieferungNeuPage extends PureComponent {
onNumberChange={this.handleNumberChange} onNumberChange={this.handleNumberChange}
onExternalNumberChange={this.handleExternalNumberChange} onExternalNumberChange={this.handleExternalNumberChange}
docId={this.props.docId} docId={this.props.docId}
onRestore={isEdit ? this.handleRestore : undefined}
ledgerSummary={this.state.ledgerSummary} ledgerSummary={this.state.ledgerSummary}
onOpenBookDialog={this.handleOpenBookDialog} onOpenBookDialog={this.handleOpenBookDialog}
/> />