Disable menu/popover animations; update manifest handling
This commit is contained in:
@@ -557,7 +557,9 @@ export const supplier = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Company-scoped goods receipts (manifest type = delivery).
|
* Company-scoped goods receipts (manifest type = delivery).
|
||||||
* DeliverySummary = { id, number, type: 'delivery', supplierId, date }
|
* DeliverySummary = { id, number, externalNumber, type: 'delivery', supplierId, date }
|
||||||
|
* - number: server-allocated, read-only manifest number (from nextNumber('delivery')).
|
||||||
|
* - externalNumber: user-entered reference (nullable).
|
||||||
* DeliveryLine = { id, articleId, sku, amount, net, transactedAmount, rebate, tax, comment }
|
* DeliveryLine = { id, articleId, sku, amount, net, transactedAmount, rebate, tax, comment }
|
||||||
* Delivery = DeliverySummary & { lines: DeliveryLine[] }
|
* Delivery = DeliverySummary & { lines: DeliveryLine[] }
|
||||||
* DeliveryLineInput = { sku, amount, net, tax, rebate?, transactedAmount?, comment? }
|
* DeliveryLineInput = { sku, amount, net, tax, rebate?, transactedAmount?, comment? }
|
||||||
@@ -575,7 +577,9 @@ export const delivery = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/delivery
|
* POST /api/delivery
|
||||||
* Body: { number, date, supplierId?, lines: DeliveryLineInput[] }
|
* Body: { externalNumber?, date, supplierId?, lines: DeliveryLineInput[] }
|
||||||
|
* number is NOT sent — the server allocates it automatically. The
|
||||||
|
* caller-supplied reference goes in externalNumber (nullable).
|
||||||
* Line requires sku, amount, net, tax. rebate→0, transactedAmount→amount.
|
* Line requires sku, amount, net, tax. rebate→0, transactedAmount→amount.
|
||||||
* → 201 { delivery: Delivery }
|
* → 201 { delivery: Delivery }
|
||||||
*/
|
*/
|
||||||
@@ -585,7 +589,9 @@ export const delivery = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* PATCH /api/delivery/:id
|
* PATCH /api/delivery/:id
|
||||||
* Body: partial { number?, date?, supplierId?, lines? }.
|
* Body: partial { externalNumber?, date?, supplierId?, lines? }.
|
||||||
|
* number is read-only and cannot be edited. externalNumber is editable and
|
||||||
|
* can be cleared by sending null or an empty string.
|
||||||
* If lines present, replaces all lines; if omitted, lines unchanged.
|
* If lines present, replaces all lines; if omitted, lines unchanged.
|
||||||
* → 200 { delivery: Delivery }
|
* → 200 { delivery: Delivery }
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class ManifestForm extends PureComponent {
|
|||||||
date: '',
|
date: '',
|
||||||
partyId: null,
|
partyId: null,
|
||||||
number: '',
|
number: '',
|
||||||
|
externalNumber: '',
|
||||||
lines: [emptyLine()],
|
lines: [emptyLine()],
|
||||||
saving: false,
|
saving: false,
|
||||||
};
|
};
|
||||||
@@ -43,6 +44,8 @@ class ManifestForm extends PureComponent {
|
|||||||
this.handlePartyChange = this.handlePartyChange.bind(this);
|
this.handlePartyChange = this.handlePartyChange.bind(this);
|
||||||
this.handleDateChange = this.handleDateChange.bind(this);
|
this.handleDateChange = this.handleDateChange.bind(this);
|
||||||
this.handleNumberChange = this.handleNumberChange.bind(this);
|
this.handleNumberChange = this.handleNumberChange.bind(this);
|
||||||
|
this.handleExternalNumberChange =
|
||||||
|
this.handleExternalNumberChange.bind(this);
|
||||||
this.handleLineChange = this.handleLineChange.bind(this);
|
this.handleLineChange = this.handleLineChange.bind(this);
|
||||||
this.handleAddLine = this.handleAddLine.bind(this);
|
this.handleAddLine = this.handleAddLine.bind(this);
|
||||||
this.handleRemoveLine = this.handleRemoveLine.bind(this);
|
this.handleRemoveLine = this.handleRemoveLine.bind(this);
|
||||||
@@ -122,6 +125,8 @@ class ManifestForm extends PureComponent {
|
|||||||
date: dateInputValue(doc.date),
|
date: dateInputValue(doc.date),
|
||||||
partyId: doc[cfg.partyField],
|
partyId: doc[cfg.partyField],
|
||||||
number: doc.number ?? '',
|
number: doc.number ?? '',
|
||||||
|
externalNumber:
|
||||||
|
doc.externalNumber != null ? String(doc.externalNumber) : '',
|
||||||
lines,
|
lines,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -157,6 +162,10 @@ class ManifestForm extends PureComponent {
|
|||||||
this.setState({ number });
|
this.setState({ number });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleExternalNumberChange(externalNumber) {
|
||||||
|
this.setState({ externalNumber });
|
||||||
|
}
|
||||||
|
|
||||||
handleLineChange(key, field, value) {
|
handleLineChange(key, field, value) {
|
||||||
this.setState((prev) => ({
|
this.setState((prev) => ({
|
||||||
lines: prev.lines.map((line) =>
|
lines: prev.lines.map((line) =>
|
||||||
@@ -188,11 +197,16 @@ class ManifestForm extends PureComponent {
|
|||||||
body = buildManifestBody(
|
body = buildManifestBody(
|
||||||
{
|
{
|
||||||
number: this.state.number,
|
number: this.state.number,
|
||||||
|
externalNumber: this.state.externalNumber,
|
||||||
date: this.state.date,
|
date: this.state.date,
|
||||||
partyId: this.state.partyId,
|
partyId: this.state.partyId,
|
||||||
lines: this.state.lines,
|
lines: this.state.lines,
|
||||||
},
|
},
|
||||||
{ partyField: cfg.partyField, requireAtLeastOne },
|
{
|
||||||
|
partyField: cfg.partyField,
|
||||||
|
requireAtLeastOne,
|
||||||
|
externalNumber: cfg.externalNumber,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifyError(err);
|
notifyError(err);
|
||||||
@@ -225,6 +239,8 @@ class ManifestForm extends PureComponent {
|
|||||||
date: dateInputValue(doc.date),
|
date: dateInputValue(doc.date),
|
||||||
partyId: doc[cfg.partyField],
|
partyId: doc[cfg.partyField],
|
||||||
number: doc.number ?? '',
|
number: doc.number ?? '',
|
||||||
|
externalNumber:
|
||||||
|
doc.externalNumber != null ? String(doc.externalNumber) : '',
|
||||||
lines:
|
lines:
|
||||||
apiLines.length > 0 ? apiLines.map(lineFromApi) : [emptyLine()],
|
apiLines.length > 0 ? apiLines.map(lineFromApi) : [emptyLine()],
|
||||||
saving: false,
|
saving: false,
|
||||||
@@ -280,6 +296,7 @@ class ManifestForm extends PureComponent {
|
|||||||
date,
|
date,
|
||||||
partyId,
|
partyId,
|
||||||
number,
|
number,
|
||||||
|
externalNumber,
|
||||||
saving,
|
saving,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
@@ -307,10 +324,12 @@ class ManifestForm extends PureComponent {
|
|||||||
partyId={partyId}
|
partyId={partyId}
|
||||||
date={date}
|
date={date}
|
||||||
number={number}
|
number={number}
|
||||||
|
externalNumber={externalNumber}
|
||||||
saving={saving}
|
saving={saving}
|
||||||
onPartyChange={this.handlePartyChange}
|
onPartyChange={this.handlePartyChange}
|
||||||
onDateChange={this.handleDateChange}
|
onDateChange={this.handleDateChange}
|
||||||
onNumberChange={this.handleNumberChange}
|
onNumberChange={this.handleNumberChange}
|
||||||
|
onExternalNumberChange={this.handleExternalNumberChange}
|
||||||
/>
|
/>
|
||||||
{status === 'ready' ? this.renderArticles() : null}
|
{status === 'ready' ? this.renderArticles() : null}
|
||||||
</PanelTileGrid>
|
</PanelTileGrid>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Stack from '@mui/material/Stack';
|
|||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import SingleLineTextEdit from '../SingleLineTextEdit';
|
import SingleLineTextEdit from '../SingleLineTextEdit';
|
||||||
import { PanelTile } from '../panels';
|
import { PanelTile } from '../panels';
|
||||||
import { partyDisplayName } from './manifestConfig';
|
import { manifestConfig, partyDisplayName } from './manifestConfig';
|
||||||
|
|
||||||
class ManifestHeader extends PureComponent {
|
class ManifestHeader extends PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -27,12 +27,20 @@ class ManifestHeader extends PureComponent {
|
|||||||
partyId,
|
partyId,
|
||||||
date,
|
date,
|
||||||
number,
|
number,
|
||||||
|
externalNumber,
|
||||||
saving,
|
saving,
|
||||||
onDateChange,
|
onDateChange,
|
||||||
onNumberChange,
|
onNumberChange,
|
||||||
|
onExternalNumberChange,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const selected = parties.find((row) => row.id === partyId) ?? null;
|
// Delivery: number is server-allocated/read-only; the caller reference
|
||||||
|
// lives in the editable externalNumber field.
|
||||||
|
const cfg = manifestConfig(kind);
|
||||||
|
const useExternalNumber = Boolean(cfg.externalNumber);
|
||||||
|
|
||||||
|
const selected =
|
||||||
|
parties.find((row) => row.id === partyId) ?? null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelTile wide={3} minHeight={160} color={headerColor} title="Kopfdaten">
|
<PanelTile wide={3} minHeight={160} color={headerColor} title="Kopfdaten">
|
||||||
@@ -80,10 +88,20 @@ class ManifestHeader extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label="Nummer"
|
label="Nummer"
|
||||||
value={number}
|
value={number}
|
||||||
disabled={saving}
|
disabled={saving || useExternalNumber}
|
||||||
onChange={onNumberChange}
|
onChange={onNumberChange}
|
||||||
sx={{ minWidth: 180, flex: 1 }}
|
sx={{ minWidth: 180, flex: 1 }}
|
||||||
/>
|
/>
|
||||||
|
{useExternalNumber ? (
|
||||||
|
<SingleLineTextEdit
|
||||||
|
size="small"
|
||||||
|
label="Referenz"
|
||||||
|
value={externalNumber ?? ''}
|
||||||
|
disabled={saving}
|
||||||
|
onChange={onExternalNumberChange}
|
||||||
|
sx={{ minWidth: 180, flex: 1 }}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
</PanelTile>
|
</PanelTile>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
formatDate,
|
formatDate,
|
||||||
loadParties,
|
loadParties,
|
||||||
manifestConfig,
|
manifestConfig,
|
||||||
|
numberDisplay,
|
||||||
partyDisplayName,
|
partyDisplayName,
|
||||||
sortDocs,
|
sortDocs,
|
||||||
} from './manifestConfig';
|
} from './manifestConfig';
|
||||||
@@ -169,6 +170,9 @@ class ManifestList extends PureComponent {
|
|||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>Nummer</TableCell>
|
<TableCell>Nummer</TableCell>
|
||||||
|
{cfg.externalNumber ? (
|
||||||
|
<TableCell>Referenz</TableCell>
|
||||||
|
) : null}
|
||||||
<TableCell>{cfg.dateLabel}</TableCell>
|
<TableCell>{cfg.dateLabel}</TableCell>
|
||||||
<TableCell>{cfg.partyLabel}</TableCell>
|
<TableCell>{cfg.partyLabel}</TableCell>
|
||||||
<TableCell padding="checkbox" />
|
<TableCell padding="checkbox" />
|
||||||
@@ -183,6 +187,11 @@ class ManifestList extends PureComponent {
|
|||||||
onClick={() => this.handleEdit(row)}
|
onClick={() => this.handleEdit(row)}
|
||||||
>
|
>
|
||||||
<TableCell>{row.number}</TableCell>
|
<TableCell>{row.number}</TableCell>
|
||||||
|
{cfg.externalNumber ? (
|
||||||
|
<TableCell>
|
||||||
|
{numberDisplay(row.externalNumber)}
|
||||||
|
</TableCell>
|
||||||
|
) : null}
|
||||||
<TableCell>{formatDate(row.date)}</TableCell>
|
<TableCell>{formatDate(row.date)}</TableCell>
|
||||||
<TableCell>{this.partyName(row[partyField])}</TableCell>
|
<TableCell>{this.partyName(row[partyField])}</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
|
|||||||
@@ -44,6 +44,16 @@ export function formatDate(iso) {
|
|||||||
return d.toLocaleDateString('de-DE');
|
return d.toLocaleDateString('de-DE');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delivery documents have a server-allocated, read-only `number` plus an
|
||||||
|
* optional user-entered `externalNumber`. Returns the display value for a
|
||||||
|
* field, defaulting to a dash when empty.
|
||||||
|
*/
|
||||||
|
export function numberDisplay(value) {
|
||||||
|
if (value == null || String(value).trim() === '') return '–';
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
export function dateInputValue(iso) {
|
export function dateInputValue(iso) {
|
||||||
if (!iso) return '';
|
if (!iso) return '';
|
||||||
const d = new Date(iso);
|
const d = new Date(iso);
|
||||||
@@ -105,20 +115,35 @@ export function buildLines(lines, { requireAtLeastOne }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ number: string, date: string, partyId: number|null, lines: object[] }} form
|
* @param {{ number: string, externalNumber?: string, date: string, partyId: number|null, lines: object[] }} form
|
||||||
* @param {{ partyField: 'supplierId'|'entityId', requireAtLeastOne: boolean }} opts
|
* @param {{ partyField: 'supplierId'|'entityId', requireAtLeastOne: boolean, externalNumber?: boolean }} opts
|
||||||
*/
|
*/
|
||||||
export function buildManifestBody(form, { partyField, requireAtLeastOne }) {
|
export function buildManifestBody(
|
||||||
const number = String(form.number ?? '').trim();
|
form,
|
||||||
|
{ partyField, requireAtLeastOne, externalNumber: useExternalNumber },
|
||||||
|
) {
|
||||||
const date = form.date;
|
const date = form.date;
|
||||||
if (!number) throw new Error('Nummer fehlt.');
|
|
||||||
if (!date) throw new Error('Datum fehlt.');
|
if (!date) throw new Error('Datum fehlt.');
|
||||||
|
|
||||||
const body = {
|
let body;
|
||||||
|
if (useExternalNumber) {
|
||||||
|
// Delivery: number is server-allocated/read-only. The caller-supplied
|
||||||
|
// reference goes in externalNumber (nullable; empty string → null).
|
||||||
|
const externalNumber = String(form.externalNumber ?? '').trim();
|
||||||
|
body = {
|
||||||
|
externalNumber: externalNumber || null,
|
||||||
|
date,
|
||||||
|
lines: buildLines(form.lines, { requireAtLeastOne }),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const number = String(form.number ?? '').trim();
|
||||||
|
if (!number) throw new Error('Nummer fehlt.');
|
||||||
|
body = {
|
||||||
number,
|
number,
|
||||||
date,
|
date,
|
||||||
lines: buildLines(form.lines, { requireAtLeastOne }),
|
lines: buildLines(form.lines, { requireAtLeastOne }),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
if (form.partyId != null) body[partyField] = form.partyId;
|
if (form.partyId != null) body[partyField] = form.partyId;
|
||||||
else if (!requireAtLeastOne) body[partyField] = null;
|
else if (!requireAtLeastOne) body[partyField] = null;
|
||||||
return body;
|
return body;
|
||||||
@@ -192,6 +217,9 @@ export function manifestConfig(kind) {
|
|||||||
listPromise: (ctx) => ctx.deliveries ?? ctx.deliveryPromise,
|
listPromise: (ctx) => ctx.deliveries ?? ctx.deliveryPromise,
|
||||||
partiesByIdFromList: (parties) =>
|
partiesByIdFromList: (parties) =>
|
||||||
Object.fromEntries(parties.map((row) => [row.id, row])),
|
Object.fromEntries(parties.map((row) => [row.id, row])),
|
||||||
|
// Delivery uses a server-allocated read-only number plus an optional
|
||||||
|
// caller-supplied external reference.
|
||||||
|
externalNumber: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (kind === 'quote') {
|
if (kind === 'quote') {
|
||||||
|
|||||||
@@ -25,5 +25,17 @@ export function createDefaultTheme() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nav,
|
nav,
|
||||||
|
components: {
|
||||||
|
MuiMenu: {
|
||||||
|
defaultProps: {
|
||||||
|
transitionDuration: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPopover: {
|
||||||
|
defaultProps: {
|
||||||
|
transitionDuration: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user