Compare commits
3 Commits
596d0dc6f1
...
94f385e753
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94f385e753 | ||
|
|
587170ce9b | ||
|
|
199bf869a6 |
@@ -113,8 +113,9 @@ class CategoryNameDialog extends Component {
|
|||||||
const { open, mode = 'create' } = this.props;
|
const { open, mode = 'create' } = this.props;
|
||||||
const { mode: formMode, name, bulkText, busy, error } = this.state;
|
const { mode: formMode, name, bulkText, busy, error } = this.state;
|
||||||
const isBulk = formMode === BULK_MODE;
|
const isBulk = formMode === BULK_MODE;
|
||||||
const canBulk = mode === 'create';
|
const canSubmitSingle =
|
||||||
const canSubmitSingle = name.trim() !== '';
|
name.trim() !== '' &&
|
||||||
|
(mode !== 'edit' || name.trim() !== (this.props.initialName ?? '').trim());
|
||||||
const bulkNames = parseBulkNames(bulkText);
|
const bulkNames = parseBulkNames(bulkText);
|
||||||
const canSubmitBulk = bulkNames.length > 0;
|
const canSubmitBulk = bulkNames.length > 0;
|
||||||
|
|
||||||
@@ -157,7 +158,13 @@ class CategoryNameDialog extends Component {
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
color={canSubmitSingle ? 'success' : 'inherit'}
|
||||||
disabled={busy || !canSubmitSingle}
|
disabled={busy || !canSubmitSingle}
|
||||||
|
sx={
|
||||||
|
canSubmitSingle
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{SUBMIT_LABELS[mode]}
|
{SUBMIT_LABELS[mode]}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -749,13 +749,27 @@ export default class CategoryTree extends PureComponent {
|
|||||||
>
|
>
|
||||||
Abbrechen
|
Abbrechen
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{(() => {
|
||||||
onClick={this.handleConfirmRename}
|
const canSave =
|
||||||
variant="contained"
|
Boolean(renameValue.trim()) &&
|
||||||
disabled={!renameValue.trim() || isSubmitting}
|
!isSubmitting &&
|
||||||
>
|
renameValue.trim() !== (renameTarget?.name ?? '').trim();
|
||||||
Speichern
|
return (
|
||||||
</Button>
|
<Button
|
||||||
|
onClick={this.handleConfirmRename}
|
||||||
|
variant="contained"
|
||||||
|
color={canSave ? 'success' : 'inherit'}
|
||||||
|
disabled={!canSave}
|
||||||
|
sx={
|
||||||
|
canSave
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Speichern
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import OrderDocumentsTiles from './OrderDocumentsTiles';
|
|||||||
import OrderShipmentDialog from './OrderShipmentDialog';
|
import OrderShipmentDialog from './OrderShipmentDialog';
|
||||||
import artikelAutomat, { getArticle } from '../../automats/artikelAutomat';
|
import artikelAutomat, { getArticle } from '../../automats/artikelAutomat';
|
||||||
import entityAutomat from '../../automats/entityAutomat';
|
import entityAutomat from '../../automats/entityAutomat';
|
||||||
|
import customergroupAutomat from '../../automats/customergroupAutomat';
|
||||||
import supplierAutomat from '../../automats/supplierAutomat';
|
import supplierAutomat from '../../automats/supplierAutomat';
|
||||||
import orderAutomat from '../../automats/orderAutomat';
|
import orderAutomat from '../../automats/orderAutomat';
|
||||||
import companyAutomat, { selectCompanyTaxrate } from '../../automats/companyAutomat';
|
import companyAutomat, { selectCompanyTaxrate } from '../../automats/companyAutomat';
|
||||||
@@ -33,6 +34,7 @@ import {
|
|||||||
manifestConfig,
|
manifestConfig,
|
||||||
apiFor,
|
apiFor,
|
||||||
parseNum,
|
parseNum,
|
||||||
|
filterNonSupplierEntities,
|
||||||
} from './manifestConfig';
|
} from './manifestConfig';
|
||||||
import { ensureDocumentNumber, getNextDocumentNumber } from '../../utils/documentNumber';
|
import { ensureDocumentNumber, getNextDocumentNumber } from '../../utils/documentNumber';
|
||||||
import {
|
import {
|
||||||
@@ -44,12 +46,15 @@ class ManifestForm extends PureComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.cfg = manifestConfig(props.kind);
|
this.cfg = manifestConfig(props.kind);
|
||||||
|
const groups = customergroupAutomat.state.items ?? [];
|
||||||
const initialParties = props.kind === 'delivery'
|
const initialParties = props.kind === 'delivery'
|
||||||
? (supplierAutomat.state.status === 'ready' ? (supplierAutomat.state.suppliers ?? []) : [])
|
? (supplierAutomat.state.status === 'ready' ? (supplierAutomat.state.suppliers ?? []) : [])
|
||||||
: (entityAutomat.state.status === 'ready' ? (entityAutomat.state.items ?? []) : []);
|
: (entityAutomat.state.status === 'ready'
|
||||||
|
? filterNonSupplierEntities(entityAutomat.state.items ?? [], groups)
|
||||||
|
: []);
|
||||||
const partiesReady = props.kind === 'delivery'
|
const partiesReady = props.kind === 'delivery'
|
||||||
? supplierAutomat.state.status === 'ready'
|
? supplierAutomat.state.status === 'ready'
|
||||||
: entityAutomat.state.status === 'ready';
|
: (entityAutomat.state.status === 'ready' && customergroupAutomat.state.status === 'ready');
|
||||||
|
|
||||||
this.draftAutomat = props.mode === 'create' ? getManifestDraftAutomat(props.kind) : null;
|
this.draftAutomat = props.mode === 'create' ? getManifestDraftAutomat(props.kind) : null;
|
||||||
this.editAutomat = props.mode === 'edit' ? getManifestEditAutomat(props.kind) : null;
|
this.editAutomat = props.mode === 'edit' ? getManifestEditAutomat(props.kind) : null;
|
||||||
@@ -150,10 +155,25 @@ class ManifestForm extends PureComponent {
|
|||||||
partiesLoading: state.status === 'loading',
|
partiesLoading: state.status === 'loading',
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
this.unsubParties = entityAutomat.subscribe(this, (state) => ({
|
const updateParties = () => {
|
||||||
parties: state.items ?? [],
|
const entities = entityAutomat.state.items ?? [];
|
||||||
partiesLoading: state.status === 'loading',
|
const groups = customergroupAutomat.state.items ?? [];
|
||||||
}));
|
this.setState({
|
||||||
|
parties: filterNonSupplierEntities(entities, groups),
|
||||||
|
partiesLoading:
|
||||||
|
entityAutomat.state.status === 'loading' ||
|
||||||
|
customergroupAutomat.state.status === 'loading',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.unsubParties = entityAutomat.subscribe(this, () => {
|
||||||
|
updateParties();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
this.unsubGroups = customergroupAutomat.subscribe(this, () => {
|
||||||
|
updateParties();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
void customergroupAutomat.actions.ensureLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (companyAutomat.state.status === 'idle') {
|
if (companyAutomat.state.status === 'idle') {
|
||||||
@@ -231,6 +251,38 @@ class ManifestForm extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.kind !== this.props.kind) {
|
||||||
|
this.cfg = manifestConfig(this.props.kind);
|
||||||
|
this.unsubParties?.();
|
||||||
|
this.unsubGroups?.();
|
||||||
|
if (this.props.kind === 'delivery') {
|
||||||
|
this.unsubParties = supplierAutomat.subscribe(this, (state) => ({
|
||||||
|
parties: state.status === 'ready' ? (state.suppliers ?? []) : [],
|
||||||
|
partiesLoading: state.status === 'loading',
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
const updateParties = () => {
|
||||||
|
const entities = entityAutomat.state.items ?? [];
|
||||||
|
const groups = customergroupAutomat.state.items ?? [];
|
||||||
|
this.setState({
|
||||||
|
parties: filterNonSupplierEntities(entities, groups),
|
||||||
|
partiesLoading:
|
||||||
|
entityAutomat.state.status === 'loading' ||
|
||||||
|
customergroupAutomat.state.status === 'loading',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.unsubParties = entityAutomat.subscribe(this, () => {
|
||||||
|
updateParties();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
this.unsubGroups = customergroupAutomat.subscribe(this, () => {
|
||||||
|
updateParties();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
void customergroupAutomat.actions.ensureLoaded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.props.mode === 'edit' &&
|
this.props.mode === 'edit' &&
|
||||||
prevProps.docId !== this.props.docId
|
prevProps.docId !== this.props.docId
|
||||||
@@ -291,6 +343,7 @@ class ManifestForm extends PureComponent {
|
|||||||
this.unsubDraft?.();
|
this.unsubDraft?.();
|
||||||
this.unsubEdit?.();
|
this.unsubEdit?.();
|
||||||
this.unsubParties?.();
|
this.unsubParties?.();
|
||||||
|
this.unsubGroups?.();
|
||||||
this.unsubCompany?.();
|
this.unsubCompany?.();
|
||||||
if (this._parseAbortController) {
|
if (this._parseAbortController) {
|
||||||
this._parseAbortController.abort();
|
this._parseAbortController.abort();
|
||||||
@@ -1095,6 +1148,13 @@ class ManifestForm extends PureComponent {
|
|||||||
if (this.state.saving) return false;
|
if (this.state.saving) return false;
|
||||||
if (mode === 'edit' && docId == null) return false;
|
if (mode === 'edit' && docId == null) return false;
|
||||||
|
|
||||||
|
if (cfg.kind === 'order' && (this.state.partyId == null || this.state.partyId === '')) {
|
||||||
|
this.setState({ partyError: true });
|
||||||
|
notifyErrorMessage('Für eine Bestellung ist ein Kunde erforderlich.');
|
||||||
|
this.headerRef.current?.focusPartyInput?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const isEdit = mode === 'edit';
|
const isEdit = mode === 'edit';
|
||||||
let body;
|
let body;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,7 +16,12 @@ import { enqueueSnackbar } from 'notistack';
|
|||||||
import RestoreIcon from '@mui/icons-material/Restore';
|
import RestoreIcon from '@mui/icons-material/Restore';
|
||||||
import FileDropzone from '../FileDropzone';
|
import FileDropzone from '../FileDropzone';
|
||||||
import AttachmentList from './AttachmentList';
|
import AttachmentList from './AttachmentList';
|
||||||
import { hasArticleLines, manifestConfig, partyDisplayName } from './manifestConfig';
|
import {
|
||||||
|
hasArticleLines,
|
||||||
|
manifestConfig,
|
||||||
|
partyDisplayName,
|
||||||
|
getLowestSortNonSupplierCustomerGroup,
|
||||||
|
} from './manifestConfig';
|
||||||
import { notifyError, notifyErrorMessage } from '../../utils/notify';
|
import { notifyError, notifyErrorMessage } from '../../utils/notify';
|
||||||
import entityAutomat from '../../automats/entityAutomat';
|
import entityAutomat from '../../automats/entityAutomat';
|
||||||
import customergroupAutomat from '../../automats/customergroupAutomat';
|
import customergroupAutomat from '../../automats/customergroupAutomat';
|
||||||
@@ -29,7 +34,11 @@ class ManifestHeader extends PureComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const selected =
|
const selected =
|
||||||
(props.parties || []).find((row) => row.id === props.partyId) ?? null;
|
(props.parties || []).find((row) => row.id === props.partyId) ??
|
||||||
|
(props.kind === 'delivery'
|
||||||
|
? (supplierAutomat.state.suppliers || []).find((row) => row.id === props.partyId)
|
||||||
|
: (entityAutomat.state.items || []).find((row) => row.id === props.partyId)) ??
|
||||||
|
null;
|
||||||
this.state = {
|
this.state = {
|
||||||
partyInput: selected ? partyDisplayName(props.kind, selected) : '',
|
partyInput: selected ? partyDisplayName(props.kind, selected) : '',
|
||||||
creatingParty: false,
|
creatingParty: false,
|
||||||
@@ -48,10 +57,13 @@ class ManifestHeader extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (prevProps.partyId !== this.props.partyId) {
|
if (prevProps.partyId !== this.props.partyId || prevProps.parties !== this.props.parties) {
|
||||||
const selected =
|
const selected =
|
||||||
(this.props.parties || []).find((row) => row.id === this.props.partyId) ??
|
(this.props.parties || []).find((row) => row.id === this.props.partyId) ??
|
||||||
(this.state.createdParty?.id === this.props.partyId ? this.state.createdParty : null);
|
(this.state.createdParty?.id === this.props.partyId ? this.state.createdParty : null) ??
|
||||||
|
(this.props.kind === 'delivery'
|
||||||
|
? (supplierAutomat.state.suppliers || []).find((row) => row.id === this.props.partyId)
|
||||||
|
: (entityAutomat.state.items || []).find((row) => row.id === this.props.partyId));
|
||||||
if (selected) {
|
if (selected) {
|
||||||
this.setState({ partyInput: partyDisplayName(this.props.kind, selected) });
|
this.setState({ partyInput: partyDisplayName(this.props.kind, selected) });
|
||||||
} else if (this.props.partyId == null) {
|
} else if (this.props.partyId == null) {
|
||||||
@@ -137,8 +149,8 @@ class ManifestHeader extends PureComponent {
|
|||||||
} else {
|
} else {
|
||||||
await customergroupAutomat.actions.ensureLoaded();
|
await customergroupAutomat.actions.ensureLoaded();
|
||||||
const groups = customergroupAutomat.state.items ?? [];
|
const groups = customergroupAutomat.state.items ?? [];
|
||||||
const defaultGroup = groups.find((g) => g.editable !== false) ?? groups[0];
|
const targetGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups[0];
|
||||||
if (!defaultGroup) {
|
if (!targetGroup) {
|
||||||
throw new Error('Keine Kundengruppe vorhanden.');
|
throw new Error('Keine Kundengruppe vorhanden.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +168,7 @@ class ManifestHeader extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
customergroupId: defaultGroup.id,
|
customergroupId: targetGroup.id,
|
||||||
name,
|
name,
|
||||||
};
|
};
|
||||||
if (firstName) body.firstName = firstName;
|
if (firstName) body.firstName = firstName;
|
||||||
@@ -260,6 +272,13 @@ class ManifestHeader extends PureComponent {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const hasArticles = this.hasArticles();
|
const hasArticles = this.hasArticles();
|
||||||
|
const isEdit = docId != null;
|
||||||
|
const hasAttachments = Array.isArray(attachments) && attachments.length > 0;
|
||||||
|
const hasParty = partyId != null && partyId !== '';
|
||||||
|
const partyValid = kind !== 'order' || hasParty;
|
||||||
|
const canSave =
|
||||||
|
partyValid &&
|
||||||
|
(isEdit ? Boolean(isDirty) : Boolean(hasArticles || hasAttachments));
|
||||||
|
|
||||||
// Delivery: number is server-allocated/read-only; the caller reference
|
// Delivery: number is server-allocated/read-only; the caller reference
|
||||||
// lives in the editable externalNumber field.
|
// lives in the editable externalNumber field.
|
||||||
@@ -358,8 +377,8 @@ class ManifestHeader extends PureComponent {
|
|||||||
{...params}
|
{...params}
|
||||||
inputRef={this.partyInputRef}
|
inputRef={this.partyInputRef}
|
||||||
label={dirtyFields?.partyId ? `${partyLabel} *` : partyLabel}
|
label={dirtyFields?.partyId ? `${partyLabel} *` : partyLabel}
|
||||||
|
required={kind === 'order'}
|
||||||
error={isPartyEmpty}
|
error={isPartyEmpty}
|
||||||
helperText={Boolean(partyError) && isPartyEmpty ? 'Kunde für Bestellung erforderlich' : undefined}
|
|
||||||
onKeyDown={this.handlePartyKeyDown}
|
onKeyDown={this.handlePartyKeyDown}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
...params.slotProps,
|
...params.slotProps,
|
||||||
@@ -435,20 +454,33 @@ class ManifestHeader extends PureComponent {
|
|||||||
<Stack spacing={1.5} sx={{ pt: 0.5 }}>
|
<Stack spacing={1.5} sx={{ pt: 0.5 }}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color={isDirty ? 'warning' : 'primary'}
|
color={canSave ? 'success' : 'inherit'}
|
||||||
disabled={saving || orderCreating}
|
disabled={Boolean(saving || orderCreating || !canSave)}
|
||||||
onClick={onSave}
|
onClick={onSave}
|
||||||
sx={{
|
sx={{
|
||||||
height: 40,
|
height: 40,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
...(isDirty && {
|
...(canSave && {
|
||||||
boxShadow: (theme) => `0 0 0 2px ${theme.palette.warning.main}`,
|
bgcolor: 'success.main',
|
||||||
|
color: '#fff',
|
||||||
|
'&:hover': {
|
||||||
|
bgcolor: 'success.dark',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
...(!canSave && {
|
||||||
|
bgcolor: 'action.disabledBackground',
|
||||||
|
color: 'text.disabled',
|
||||||
|
boxShadow: 'none',
|
||||||
|
'&.Mui-disabled': {
|
||||||
|
bgcolor: 'action.disabledBackground',
|
||||||
|
color: 'text.disabled',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{saving ? 'Speichern…' : isDirty ? 'Speichern *' : 'Speichern'}
|
{saving ? 'Speichern…' : 'Speichern'}
|
||||||
</Button>
|
</Button>
|
||||||
{isDirty && onRestore && (
|
{isDirty && onRestore && (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -362,9 +362,15 @@ export class TextEditorPanel extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
color={isDirty ? 'success' : 'inherit'}
|
||||||
startIcon={<SaveOutlinedIcon />}
|
startIcon={<SaveOutlinedIcon />}
|
||||||
onClick={this.handleSave}
|
onClick={this.handleSave}
|
||||||
disabled={loading || !isDirty}
|
disabled={loading || !isDirty}
|
||||||
|
sx={
|
||||||
|
isDirty
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -33,4 +33,6 @@ export {
|
|||||||
restoreAttachmentsFromApi,
|
restoreAttachmentsFromApi,
|
||||||
dateInputValue,
|
dateInputValue,
|
||||||
todayInputValue,
|
todayInputValue,
|
||||||
|
filterNonSupplierEntities,
|
||||||
|
getLowestSortNonSupplierCustomerGroup,
|
||||||
} from './manifestConfig';
|
} from './manifestConfig';
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import {
|
|||||||
/** Groups that are not sales customers (filter client-side). */
|
/** Groups that are not sales customers (filter client-side). */
|
||||||
export const EXCLUDED_CUSTOMER_GROUP_NAMES = new Set(['Lieferanten', 'POS']);
|
export const EXCLUDED_CUSTOMER_GROUP_NAMES = new Set(['Lieferanten', 'POS']);
|
||||||
|
|
||||||
|
export function isSupplierCustomerGroup(group) {
|
||||||
|
return String(group?.name ?? '').trim().toLowerCase() === 'lieferanten';
|
||||||
|
}
|
||||||
|
|
||||||
export function entityDisplayName(entity) {
|
export function entityDisplayName(entity) {
|
||||||
if (!entity) return '';
|
if (!entity) return '';
|
||||||
const fullName = [entity.firstName, entity.name].filter(Boolean).join(' ');
|
const fullName = [entity.firstName, entity.name].filter(Boolean).join(' ');
|
||||||
@@ -37,6 +41,31 @@ export function sortEntities(entities) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customers = entities whose customergroup is not Lieferanten.
|
||||||
|
*/
|
||||||
|
export function filterNonSupplierEntities(entities, groups) {
|
||||||
|
const supplierIds = new Set(
|
||||||
|
(groups || [])
|
||||||
|
.filter(isSupplierCustomerGroup)
|
||||||
|
.map((g) => g.id),
|
||||||
|
);
|
||||||
|
return sortEntities(
|
||||||
|
(entities || []).filter((e) => !supplierIds.has(e.customergroupId)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the customer group with the lowest sort number that is not Lieferanten.
|
||||||
|
*/
|
||||||
|
export function getLowestSortNonSupplierCustomerGroup(groups) {
|
||||||
|
const nonSuppliers = (groups || []).filter((g) => !isSupplierCustomerGroup(g));
|
||||||
|
if (nonSuppliers.length === 0) return null;
|
||||||
|
return [...nonSuppliers].sort(
|
||||||
|
(a, b) => (Number(a.sort) || 0) - (Number(b.sort) || 0),
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customers = entities whose customergroup.name is not Lieferanten/POS.
|
* Customers = entities whose customergroup.name is not Lieferanten/POS.
|
||||||
*/
|
*/
|
||||||
@@ -498,6 +527,8 @@ export async function buildManifestBody(
|
|||||||
if (!Number.isNaN(pid)) {
|
if (!Number.isNaN(pid)) {
|
||||||
body[partyField] = pid;
|
body[partyField] = pid;
|
||||||
}
|
}
|
||||||
|
} else if (kind === 'order') {
|
||||||
|
throw new Error('Für eine Bestellung ist ein Kunde erforderlich.');
|
||||||
} else if (isEdit) {
|
} else if (isEdit) {
|
||||||
body[partyField] = null;
|
body[partyField] = null;
|
||||||
}
|
}
|
||||||
@@ -588,8 +619,13 @@ export async function loadParties(cfgOrKind) {
|
|||||||
const list = await supplierAutomat.actions.load();
|
const list = await supplierAutomat.actions.load();
|
||||||
return sortSuppliers(list);
|
return sortSuppliers(list);
|
||||||
}
|
}
|
||||||
const entities = await entityAutomat.actions.ensureLoaded();
|
const [entities, groups] = await Promise.all([
|
||||||
return entities ?? entityAutomat.state.items ?? [];
|
entityAutomat.actions.ensureLoaded(),
|
||||||
|
customergroupAutomat.actions.ensureLoaded(),
|
||||||
|
]);
|
||||||
|
const entityList = entities ?? entityAutomat.state.items ?? [];
|
||||||
|
const groupList = groups ?? customergroupAutomat.state.items ?? [];
|
||||||
|
return filterNonSupplierEntities(entityList, groupList);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function partyDisplayName(cfgOrKind, party) {
|
export function partyDisplayName(cfgOrKind, party) {
|
||||||
|
|||||||
@@ -52,11 +52,17 @@ export default class CompanySettingsPanel extends Component {
|
|||||||
|
|
||||||
syncFormFromCompany(companyRow) {
|
syncFormFromCompany(companyRow) {
|
||||||
if (!companyRow) return;
|
if (!companyRow) return;
|
||||||
this.setState({
|
this._initial = {
|
||||||
name: companyRow.name ?? '',
|
name: companyRow.name ?? '',
|
||||||
iso639: companyRow.iso639 ?? 'de',
|
iso639: companyRow.iso639 ?? 'de',
|
||||||
taxrate: companyRow.taxrate != null ? String(companyRow.taxrate) : '',
|
taxrate: companyRow.taxrate != null ? String(companyRow.taxrate) : '',
|
||||||
reducedtaxrate: companyRow.reducedtaxrate != null ? String(companyRow.reducedtaxrate) : '',
|
reducedtaxrate: companyRow.reducedtaxrate != null ? String(companyRow.reducedtaxrate) : '',
|
||||||
|
};
|
||||||
|
this.setState({
|
||||||
|
name: this._initial.name,
|
||||||
|
iso639: this._initial.iso639,
|
||||||
|
taxrate: this._initial.taxrate,
|
||||||
|
reducedtaxrate: this._initial.reducedtaxrate,
|
||||||
status: 'ready',
|
status: 'ready',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -100,6 +106,12 @@ export default class CompanySettingsPanel extends Component {
|
|||||||
taxrate: parsedTaxrate,
|
taxrate: parsedTaxrate,
|
||||||
reducedtaxrate: parsedReducedtaxrate,
|
reducedtaxrate: parsedReducedtaxrate,
|
||||||
});
|
});
|
||||||
|
this._initial = {
|
||||||
|
name: name.trim(),
|
||||||
|
iso639,
|
||||||
|
taxrate: String(parsedTaxrate),
|
||||||
|
reducedtaxrate: String(parsedReducedtaxrate),
|
||||||
|
};
|
||||||
enqueueSnackbar('Unternehmen gespeichert.', { variant: 'success' });
|
enqueueSnackbar('Unternehmen gespeichert.', { variant: 'success' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.setState({ error: backendErrorFrom(err) });
|
this.setState({ error: backendErrorFrom(err) });
|
||||||
@@ -125,7 +137,14 @@ export default class CompanySettingsPanel extends Component {
|
|||||||
|
|
||||||
const parsedTaxrate = parseTaxRate(taxrate);
|
const parsedTaxrate = parseTaxRate(taxrate);
|
||||||
const parsedReducedtaxrate = parseTaxRate(reducedtaxrate);
|
const parsedReducedtaxrate = parseTaxRate(reducedtaxrate);
|
||||||
|
const isDirty =
|
||||||
|
Boolean(this._initial) &&
|
||||||
|
(name.trim() !== this._initial.name.trim() ||
|
||||||
|
iso639 !== this._initial.iso639 ||
|
||||||
|
taxrate.trim() !== this._initial.taxrate.trim() ||
|
||||||
|
reducedtaxrate.trim() !== this._initial.reducedtaxrate.trim());
|
||||||
const canSave =
|
const canSave =
|
||||||
|
isDirty &&
|
||||||
name.trim() !== '' &&
|
name.trim() !== '' &&
|
||||||
ISO639_OPTIONS.includes(iso639) &&
|
ISO639_OPTIONS.includes(iso639) &&
|
||||||
parsedTaxrate != null &&
|
parsedTaxrate != null &&
|
||||||
@@ -218,8 +237,14 @@ export default class CompanySettingsPanel extends Component {
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
color={canSave ? 'success' : 'inherit'}
|
||||||
disabled={busy || !canSave}
|
disabled={busy || !canSave}
|
||||||
startIcon={busy ? <CircularProgress size={16} color="inherit" /> : null}
|
startIcon={busy ? <CircularProgress size={16} color="inherit" /> : null}
|
||||||
|
sx={
|
||||||
|
canSave
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Speichern
|
Speichern
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ export default class VfsTextEditor extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color={isDirty ? 'primary' : 'inherit'}
|
color={isDirty ? 'success' : 'inherit'}
|
||||||
startIcon={isSaving ? <CircularProgress size={14} color="inherit" /> : <SaveRoundedIcon />}
|
startIcon={isSaving ? <CircularProgress size={14} color="inherit" /> : <SaveRoundedIcon />}
|
||||||
onClick={onSave}
|
onClick={onSave}
|
||||||
disabled={!isDirty || isSaving}
|
disabled={!isDirty || isSaving}
|
||||||
@@ -302,6 +302,11 @@ export default class VfsTextEditor extends PureComponent {
|
|||||||
py: 0.4,
|
py: 0.4,
|
||||||
fontSize: '0.78rem',
|
fontSize: '0.78rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
|
...(isDirty && {
|
||||||
|
bgcolor: 'success.main',
|
||||||
|
color: '#fff',
|
||||||
|
'&:hover': { bgcolor: 'success.dark' },
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSaving ? 'Speichern…' : 'Speichern (Strg+S)'}
|
{isSaving ? 'Speichern…' : 'Speichern (Strg+S)'}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { backendErrorFrom } from '../utils/backendError';
|
|||||||
import { notifyError } from '../utils/notify';
|
import { notifyError } from '../utils/notify';
|
||||||
import entityAutomat from '../automats/entityAutomat';
|
import entityAutomat from '../automats/entityAutomat';
|
||||||
import customergroupAutomat from '../automats/customergroupAutomat';
|
import customergroupAutomat from '../automats/customergroupAutomat';
|
||||||
|
import { getLowestSortNonSupplierCustomerGroup } from '../components/manifest/manifestConfig';
|
||||||
import { Kunde } from '../components/kunde';
|
import { Kunde } from '../components/kunde';
|
||||||
import { BLUE_PANEL } from '../colors';
|
import { BLUE_PANEL } from '../colors';
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ class KundeSearchField extends PureComponent {
|
|||||||
try {
|
try {
|
||||||
await customergroupAutomat.actions.ensureLoaded();
|
await customergroupAutomat.actions.ensureLoaded();
|
||||||
const groups = customergroupAutomat.state.items ?? [];
|
const groups = customergroupAutomat.state.items ?? [];
|
||||||
const defaultGroup = groups.find((g) => g.editable !== false) ?? groups[0];
|
const defaultGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups[0];
|
||||||
if (!defaultGroup) {
|
if (!defaultGroup) {
|
||||||
throw new Error('Keine Kundengruppe vorhanden.');
|
throw new Error('Keine Kundengruppe vorhanden.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,14 +579,28 @@ class KundeEditPage extends PureComponent {
|
|||||||
onChange={(next) => this.setState({ firstName: next })}
|
onChange={(next) => this.setState({ firstName: next })}
|
||||||
/>
|
/>
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||||
<Button
|
{(() => {
|
||||||
variant="contained"
|
const canSaveEntity =
|
||||||
size="small"
|
Boolean(name.trim()) &&
|
||||||
disabled={savingEntity || !name.trim()}
|
((name.trim() !== (entity?.name ?? '').trim()) ||
|
||||||
onClick={() => void this.handleSaveEntity()}
|
(firstName.trim() !== (entity?.firstName ?? '').trim()));
|
||||||
>
|
return (
|
||||||
{savingEntity ? 'Speichern…' : 'Speichern'}
|
<Button
|
||||||
</Button>
|
variant="contained"
|
||||||
|
size="small"
|
||||||
|
color={canSaveEntity ? 'success' : 'inherit'}
|
||||||
|
disabled={savingEntity || !canSaveEntity}
|
||||||
|
onClick={() => void this.handleSaveEntity()}
|
||||||
|
sx={
|
||||||
|
canSaveEntity
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{savingEntity ? 'Speichern…' : 'Speichern'}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PanelTile>
|
</PanelTile>
|
||||||
@@ -600,6 +614,10 @@ class KundeEditPage extends PureComponent {
|
|||||||
) : (
|
) : (
|
||||||
addresses.map((address) => {
|
addresses.map((address) => {
|
||||||
const draft = addressDrafts[address.id] ?? addressDraftFrom(address);
|
const draft = addressDrafts[address.id] ?? addressDraftFrom(address);
|
||||||
|
const orig = addressDraftFrom(address);
|
||||||
|
const isAddressDirty = Object.keys(orig).some(
|
||||||
|
(k) => String(draft[k] ?? '').trim() !== String(orig[k] ?? '').trim(),
|
||||||
|
);
|
||||||
const busy = busyAddressKey === address.id;
|
const busy = busyAddressKey === address.id;
|
||||||
return (
|
return (
|
||||||
<Box key={address.id}>
|
<Box key={address.id}>
|
||||||
@@ -626,9 +644,15 @@ class KundeEditPage extends PureComponent {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant={isAddressDirty ? 'contained' : 'outlined'}
|
||||||
disabled={addressBusy}
|
color={isAddressDirty ? 'success' : 'inherit'}
|
||||||
|
disabled={addressBusy || !isAddressDirty}
|
||||||
onClick={() => void this.handleSaveAddress(address)}
|
onClick={() => void this.handleSaveAddress(address)}
|
||||||
|
sx={
|
||||||
|
isAddressDirty
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{busy ? '…' : 'Speichern'}
|
{busy ? '…' : 'Speichern'}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -674,6 +698,9 @@ class KundeEditPage extends PureComponent {
|
|||||||
contactType: contact.contactType,
|
contactType: contact.contactType,
|
||||||
contact: contact.contact,
|
contact: contact.contact,
|
||||||
};
|
};
|
||||||
|
const isContactDirty =
|
||||||
|
String(draft.contact ?? '').trim() !== String(contact.contact ?? '').trim() ||
|
||||||
|
String(draft.contactType ?? '').trim() !== String(contact.contactType ?? '').trim();
|
||||||
const busy = busyContactKey === contact.id;
|
const busy = busyContactKey === contact.id;
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
@@ -713,9 +740,15 @@ class KundeEditPage extends PureComponent {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant={isContactDirty ? 'contained' : 'outlined'}
|
||||||
disabled={contactBusy}
|
color={isContactDirty ? 'success' : 'inherit'}
|
||||||
|
disabled={contactBusy || !isContactDirty}
|
||||||
onClick={() => void this.handleSaveContact(contact)}
|
onClick={() => void this.handleSaveContact(contact)}
|
||||||
|
sx={
|
||||||
|
isContactDirty
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{busy ? '…' : 'Speichern'}
|
{busy ? '…' : 'Speichern'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { PanelTile, PanelTileGrid } from 'dashgui/panels';
|
|||||||
import { entity as entityApi } from '../../api';
|
import { entity as entityApi } from '../../api';
|
||||||
import entityAutomat from '../../automats/entityAutomat';
|
import entityAutomat from '../../automats/entityAutomat';
|
||||||
import customergroupAutomat from '../../automats/customergroupAutomat';
|
import customergroupAutomat from '../../automats/customergroupAutomat';
|
||||||
|
import { getLowestSortNonSupplierCustomerGroup } from '../../components/manifest/manifestConfig';
|
||||||
import { router } from '../../appRouter';
|
import { router } from '../../appRouter';
|
||||||
import { requireDefined, requireObject } from '../../utils/apiShape';
|
import { requireDefined, requireObject } from '../../utils/apiShape';
|
||||||
import { notifyError } from '../../utils/notify';
|
import { notifyError } from '../../utils/notify';
|
||||||
@@ -45,9 +46,10 @@ class KundeNeuPage extends PureComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const groups = customergroupAutomat.state.items ?? [];
|
const groups = customergroupAutomat.state.items ?? [];
|
||||||
|
const defaultGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups[0];
|
||||||
const prefill = getInitialPrefill(props);
|
const prefill = getInitialPrefill(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
customergroupId: groups.length > 0 ? groups[0].id : null,
|
customergroupId: defaultGroup ? defaultGroup.id : null,
|
||||||
number: '',
|
number: '',
|
||||||
name: prefill.name,
|
name: prefill.name,
|
||||||
firstName: prefill.firstName,
|
firstName: prefill.firstName,
|
||||||
@@ -59,6 +61,14 @@ class KundeNeuPage extends PureComponent {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
void this.loadNumber();
|
void this.loadNumber();
|
||||||
|
if (this.state.customergroupId == null) {
|
||||||
|
void customergroupAutomat.actions.ensureLoaded().then((groups) => {
|
||||||
|
const defaultGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups?.[0];
|
||||||
|
if (defaultGroup && this.state.customergroupId == null) {
|
||||||
|
this.setState({ customergroupId: defaultGroup.id });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadNumber() {
|
async loadNumber() {
|
||||||
@@ -168,8 +178,14 @@ class KundeNeuPage extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
disabled={!canSave}
|
color={canSave ? 'success' : 'inherit'}
|
||||||
|
disabled={!canSave || saving}
|
||||||
onClick={() => void this.handleSave()}
|
onClick={() => void this.handleSave()}
|
||||||
|
sx={
|
||||||
|
canSave
|
||||||
|
? { bgcolor: 'success.main', color: '#fff', '&:hover': { bgcolor: 'success.dark' } }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{saving ? 'Speichern…' : 'Speichern'}
|
{saving ? 'Speichern…' : 'Speichern'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ import {
|
|||||||
ArticleSearchDialog,
|
ArticleSearchDialog,
|
||||||
BookDeliveryDialog,
|
BookDeliveryDialog,
|
||||||
} from '../../components/manifest';
|
} from '../../components/manifest';
|
||||||
import { loadParties, apiFor, resolveLineSkus } from '../../components/manifest/manifestConfig';
|
import { loadParties, apiFor, resolveLineSkus, sortSuppliers } from '../../components/manifest/manifestConfig';
|
||||||
|
import supplierAutomat from '../../automats/supplierAutomat';
|
||||||
import warehouseAutomat from '../../automats/warehouseAutomat';
|
import warehouseAutomat from '../../automats/warehouseAutomat';
|
||||||
import { article as articleApi, delivery as deliveryApi } from '../../api';
|
import { article as articleApi, delivery as deliveryApi } from '../../api';
|
||||||
import { getCategoryState } from '../../automats/categoryAutomat';
|
import { getCategoryState } from '../../automats/categoryAutomat';
|
||||||
@@ -108,6 +109,12 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
super(props);
|
super(props);
|
||||||
this.cfg = manifestConfig('delivery');
|
this.cfg = manifestConfig('delivery');
|
||||||
this.automat = getDeliveryEditorAutomat(props.docId);
|
this.automat = getDeliveryEditorAutomat(props.docId);
|
||||||
|
if (
|
||||||
|
supplierAutomat.state.status === 'ready' &&
|
||||||
|
(!this.automat.state.deliveryEntities || this.automat.state.deliveryEntities.length === 0)
|
||||||
|
) {
|
||||||
|
this.automat.actions.setDeliveryEntities(sortSuppliers(supplierAutomat.state.suppliers));
|
||||||
|
}
|
||||||
const initialDate = this.automat.state.date || (props.docId == null ? todayInputValue() : null);
|
const initialDate = this.automat.state.date || (props.docId == null ? todayInputValue() : null);
|
||||||
if (props.docId == null && !this.automat.state.date) {
|
if (props.docId == null && !this.automat.state.date) {
|
||||||
this.automat.actions.setDate(initialDate);
|
this.automat.actions.setDate(initialDate);
|
||||||
@@ -181,6 +188,11 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
this._warehouseUnsub = warehouseAutomat.subscribe(this, (state) => ({
|
this._warehouseUnsub = warehouseAutomat.subscribe(this, (state) => ({
|
||||||
warehouses: state.warehouses,
|
warehouses: state.warehouses,
|
||||||
}));
|
}));
|
||||||
|
this._supplierUnsub = supplierAutomat.subscribe(this, (state) => {
|
||||||
|
if (state.status === 'ready' && Array.isArray(state.suppliers)) {
|
||||||
|
this.automat.actions.setDeliveryEntities(sortSuppliers(state.suppliers));
|
||||||
|
}
|
||||||
|
});
|
||||||
void warehouseAutomat.actions.load().catch(() => { });
|
void warehouseAutomat.actions.load().catch(() => { });
|
||||||
this._cancelled = false;
|
this._cancelled = false;
|
||||||
if (this.props.docId != null) {
|
if (this.props.docId != null) {
|
||||||
@@ -202,6 +214,7 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
this.unsubscribe = this.automat.subscribe(this);
|
this.unsubscribe = this.automat.subscribe(this);
|
||||||
this.setState({ ...this.automat.state });
|
this.setState({ ...this.automat.state });
|
||||||
this._initialLines = null;
|
this._initialLines = null;
|
||||||
|
this._initialDoc = null;
|
||||||
if (this.props.docId != null) {
|
if (this.props.docId != null) {
|
||||||
void this.loadEdit();
|
void this.loadEdit();
|
||||||
} else {
|
} else {
|
||||||
@@ -225,6 +238,7 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
this.unsubscribe?.();
|
this.unsubscribe?.();
|
||||||
this._settingsUnsub?.();
|
this._settingsUnsub?.();
|
||||||
this._warehouseUnsub?.();
|
this._warehouseUnsub?.();
|
||||||
|
this._supplierUnsub?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadPartiesOnly() {
|
async loadPartiesOnly() {
|
||||||
@@ -283,11 +297,21 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
if (doc.externalNumber != null) this.automat.actions.setExternalNumber(String(doc.externalNumber));
|
if (doc.externalNumber != null) this.automat.actions.setExternalNumber(String(doc.externalNumber));
|
||||||
this.automat.actions.setLineitems(lines);
|
this.automat.actions.setLineitems(lines);
|
||||||
this._initialLines = lines;
|
this._initialLines = lines;
|
||||||
|
this._initialDoc = {
|
||||||
|
date: dateInputValue(doc.date),
|
||||||
|
partyId: doc[cfg.partyField] ?? null,
|
||||||
|
externalNumber: doc.externalNumber != null ? String(doc.externalNumber) : '',
|
||||||
|
lines,
|
||||||
|
attachments: [],
|
||||||
|
};
|
||||||
|
|
||||||
// Restore attachments with thumbnails
|
// Restore attachments with thumbnails
|
||||||
const apiFiles = doc.files ?? [];
|
const apiFiles = doc.files ?? [];
|
||||||
const attachments = await restoreAttachmentsFromApi(apiFiles, this.state.attachments);
|
const attachments = await restoreAttachmentsFromApi(apiFiles, this.state.attachments);
|
||||||
this.automat.actions.setAttachments(attachments);
|
this.automat.actions.setAttachments(attachments);
|
||||||
|
if (this._initialDoc) {
|
||||||
|
this._initialDoc.attachments = attachments;
|
||||||
|
}
|
||||||
|
|
||||||
let ledgerSummary = null;
|
let ledgerSummary = null;
|
||||||
try {
|
try {
|
||||||
@@ -1173,6 +1197,14 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
const attachments = await restoreAttachmentsFromApi(apiFiles, this.state.attachments);
|
const attachments = await restoreAttachmentsFromApi(apiFiles, this.state.attachments);
|
||||||
this.automat.actions.setAttachments(attachments);
|
this.automat.actions.setAttachments(attachments);
|
||||||
|
|
||||||
|
this._initialDoc = {
|
||||||
|
date: dateInputValue(doc.date),
|
||||||
|
partyId: doc[cfg.partyField] ?? null,
|
||||||
|
externalNumber: doc.externalNumber != null ? String(doc.externalNumber) : '',
|
||||||
|
lines,
|
||||||
|
attachments,
|
||||||
|
};
|
||||||
|
|
||||||
enqueueSnackbar(cfg.savedMsg, { variant: 'success' });
|
enqueueSnackbar(cfg.savedMsg, { variant: 'success' });
|
||||||
this.setState({ saving: false });
|
this.setState({ saving: false });
|
||||||
}
|
}
|
||||||
@@ -1246,6 +1278,27 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isEdit = this.props.docId != null;
|
||||||
|
let isDirty = false;
|
||||||
|
if (isEdit && this._initialDoc) {
|
||||||
|
const dateChanged = String(date ?? '').trim() !== String(this._initialDoc.date ?? '').trim();
|
||||||
|
const partyChanged = (partyId ?? null) !== (this._initialDoc.partyId ?? null);
|
||||||
|
const extChanged = String(externalNumber ?? '').trim() !== String(this._initialDoc.externalNumber ?? '').trim();
|
||||||
|
const linesChanged = !areLinesEqual(lines, this._initialDoc.lines);
|
||||||
|
const currentAtts = attachments || [];
|
||||||
|
const baseAtts = this._initialDoc.attachments || [];
|
||||||
|
let attsChanged = currentAtts.length !== baseAtts.length;
|
||||||
|
if (!attsChanged) {
|
||||||
|
for (const a of currentAtts) {
|
||||||
|
if (!a.isPersisted || a.isDirty) {
|
||||||
|
attsChanged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isDirty = dateChanged || partyChanged || extChanged || linesChanged || attsChanged;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PanelTileGrid>
|
<PanelTileGrid>
|
||||||
@@ -1266,6 +1319,7 @@ class LieferungNeuPage extends PureComponent {
|
|||||||
onPdfCreate={this.handleCreatePdf}
|
onPdfCreate={this.handleCreatePdf}
|
||||||
attachments={attachments}
|
attachments={attachments}
|
||||||
lines={lines}
|
lines={lines}
|
||||||
|
isDirty={isDirty}
|
||||||
onPdfUpload={this.handlePdfUpload}
|
onPdfUpload={this.handlePdfUpload}
|
||||||
onPdfRescan={this.handlePdfRescan}
|
onPdfRescan={this.handlePdfRescan}
|
||||||
onPdfDelete={this.handlePdfDelete}
|
onPdfDelete={this.handlePdfDelete}
|
||||||
|
|||||||
Reference in New Issue
Block a user