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() {
this.setState((prev) => {
let nextLines = [];
if (this.props.kind === 'order') {
const shippedSkus = new Set();
for (const sh of prev.shipments || []) {
@@ -876,7 +877,7 @@ class ManifestForm extends PureComponent {
}
}
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() : '';
return shippedSkus.has(raw);
});
@@ -885,10 +886,10 @@ class ManifestForm extends PureComponent {
return null;
}
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,
partyField: cfg.partyField,
requireAtLeastOne: true,
requireAtLeastOne: !isEdit,
externalNumber: cfg.externalNumber,
isUpdate: isEdit,
mode,
@@ -1334,7 +1335,7 @@ class ManifestForm extends PureComponent {
isDirty={this.state.isDirty}
dirtyFields={this.state.dirtyFields}
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}
onPdfDelete={this.handleAttachmentDelete}
onPdfCreate={kind === 'quote' || kind === 'order' ? this.handleCreatePdf : undefined}

View File

@@ -481,27 +481,45 @@ class ManifestHeader extends PureComponent {
>
{saving ? 'Speichern…' : 'Speichern'}
</Button>
{isDirty && onRestore && (
<Button
variant="outlined"
color="warning"
size="small"
startIcon={<RestoreIcon />}
disabled={saving || orderCreating}
onClick={onRestore}
sx={{
height: 34,
fontWeight: 700,
fontSize: '0.8rem',
whiteSpace: 'nowrap',
textTransform: 'none',
borderWidth: 1.5,
'&:hover': { borderWidth: 1.5 },
}}
>
Wiederherstellen
</Button>
)}
{(() => {
const canRestore = Boolean(onRestore && isDirty && !saving && !orderCreating);
return (
<Button
variant="outlined"
color={canRestore ? 'warning' : 'inherit'}
size="small"
startIcon={<RestoreIcon />}
disabled={!canRestore}
onClick={canRestore ? onRestore : undefined}
title={
canRestore
? 'Änderungen verwerfen und ursprünglichen Stand wiederherstellen'
: 'Keine Änderungen zum Wiederherstellen'
}
sx={{
height: 34,
width: '100%',
fontWeight: 700,
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)) && (
<Button
variant={ledgerSummary?.status === 'booked' ? 'outlined' : 'contained'}
@@ -561,27 +579,56 @@ class ManifestHeader extends PureComponent {
{orderCreating ? 'Wird erstellt…' : 'Bestellung erstellen'}
</Button>
)}
{(kind === 'quote' || kind === 'order' || kind === 'delivery') && onPdfCreate && docId != null && Boolean(number) && (
<Button
type="button"
variant="outlined"
startIcon={pdfCreating ? <CircularProgress size={16} color="inherit" /> : <PictureAsPdfIcon />}
disabled={saving || pdfCreating || orderCreating}
onClick={onPdfCreate}
title="PDF erstellen"
sx={{
whiteSpace: 'nowrap',
height: 40,
width: '100%',
px: 1.5,
boxSizing: 'border-box',
fontWeight: 600,
fontSize: '0.875rem',
}}
>
{pdfCreating ? 'Wird erstellt…' : 'PDF erstellen'}
</Button>
)}
{(kind === 'quote' || kind === 'order' || kind === 'delivery') && (() => {
const canCreatePdf = Boolean(
onPdfCreate &&
docId != null &&
Boolean(number) &&
!saving &&
!pdfCreating &&
!orderCreating
);
return (
<Button
type="button"
variant="outlined"
color={canCreatePdf ? 'primary' : 'inherit'}
startIcon={pdfCreating ? <CircularProgress size={16} color="inherit" /> : <PictureAsPdfIcon />}
disabled={!canCreatePdf}
onClick={canCreatePdf ? onPdfCreate : undefined}
title={
pdfCreating
? '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 ? (
<FileDropzone
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 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'
const shippedSkus = new Set();
if (kind === 'order' && Array.isArray(shipments)) {
for (const sh of shipments) {
for (const sl of sh.lines || []) {
if (sl.sku) shippedSkus.add(String(sl.sku).trim().toLowerCase());
}
}
}
const allLinesShipped =
kind === 'order' &&
shippedSkus.size > 0 &&
Array.isArray(lines) &&
lines.length > 0 &&
lines.every((l) => {
const raw = l?.sku != null ? String(l.sku).trim().toLowerCase() : '';
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';
return (
<Button

View File

@@ -160,6 +160,7 @@ class LieferungNeuPage extends PureComponent {
this.handleRemoveLine = this.handleRemoveLine.bind(this);
this.handleMoveLine = this.handleMoveLine.bind(this);
this.handleRemoveAllLines = this.handleRemoveAllLines.bind(this);
this.handleRestore = this.handleRestore.bind(this);
this.handleSave = this.handleSave.bind(this);
this.handlePdfUpload = this.handlePdfUpload.bind(this);
this.handlePdfRescan = this.handlePdfRescan.bind(this);
@@ -686,6 +687,24 @@ class LieferungNeuPage extends PureComponent {
handleRemoveAllLines() {
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) {
@@ -1331,6 +1350,7 @@ class LieferungNeuPage extends PureComponent {
onNumberChange={this.handleNumberChange}
onExternalNumberChange={this.handleExternalNumberChange}
docId={this.props.docId}
onRestore={isEdit ? this.handleRestore : undefined}
ledgerSummary={this.state.ledgerSummary}
onOpenBookDialog={this.handleOpenBookDialog}
/>