From 199bf869a6f2d7bd185ae766da7e4a7fc8f62730 Mon Sep 17 00:00:00 2001 From: seb Date: Sun, 18 Oct 2026 19:10:43 +0200 Subject: [PATCH] u --- src/components/manifest/ManifestForm.jsx | 65 ++++++++++++++++++++-- src/components/manifest/ManifestHeader.jsx | 26 ++++++--- src/components/manifest/index.js | 2 + src/components/manifest/manifestConfig.js | 38 ++++++++++++- src/routes/kunde.jsx | 3 +- src/routes/kunde/neu.jsx | 12 +++- src/routes/lieferung/neu.jsx | 15 ++++- 7 files changed, 143 insertions(+), 18 deletions(-) diff --git a/src/components/manifest/ManifestForm.jsx b/src/components/manifest/ManifestForm.jsx index 1b80dc0..2bb28e0 100644 --- a/src/components/manifest/ManifestForm.jsx +++ b/src/components/manifest/ManifestForm.jsx @@ -17,6 +17,7 @@ import OrderDocumentsTiles from './OrderDocumentsTiles'; import OrderShipmentDialog from './OrderShipmentDialog'; import artikelAutomat, { getArticle } from '../../automats/artikelAutomat'; import entityAutomat from '../../automats/entityAutomat'; +import customergroupAutomat from '../../automats/customergroupAutomat'; import supplierAutomat from '../../automats/supplierAutomat'; import orderAutomat from '../../automats/orderAutomat'; import companyAutomat, { selectCompanyTaxrate } from '../../automats/companyAutomat'; @@ -33,6 +34,7 @@ import { manifestConfig, apiFor, parseNum, + filterNonSupplierEntities, } from './manifestConfig'; import { ensureDocumentNumber, getNextDocumentNumber } from '../../utils/documentNumber'; import { @@ -44,12 +46,15 @@ class ManifestForm extends PureComponent { constructor(props) { super(props); this.cfg = manifestConfig(props.kind); + const groups = customergroupAutomat.state.items ?? []; const initialParties = props.kind === 'delivery' ? (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' ? 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.editAutomat = props.mode === 'edit' ? getManifestEditAutomat(props.kind) : null; @@ -150,10 +155,25 @@ class ManifestForm extends PureComponent { partiesLoading: state.status === 'loading', })); } else { - this.unsubParties = entityAutomat.subscribe(this, (state) => ({ - parties: state.items ?? [], - partiesLoading: state.status === 'loading', - })); + 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 (companyAutomat.state.status === 'idle') { @@ -231,6 +251,38 @@ class ManifestForm extends PureComponent { } 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 ( this.props.mode === 'edit' && prevProps.docId !== this.props.docId @@ -291,6 +343,7 @@ class ManifestForm extends PureComponent { this.unsubDraft?.(); this.unsubEdit?.(); this.unsubParties?.(); + this.unsubGroups?.(); this.unsubCompany?.(); if (this._parseAbortController) { this._parseAbortController.abort(); diff --git a/src/components/manifest/ManifestHeader.jsx b/src/components/manifest/ManifestHeader.jsx index a28de43..f7862da 100644 --- a/src/components/manifest/ManifestHeader.jsx +++ b/src/components/manifest/ManifestHeader.jsx @@ -16,7 +16,12 @@ import { enqueueSnackbar } from 'notistack'; import RestoreIcon from '@mui/icons-material/Restore'; import FileDropzone from '../FileDropzone'; import AttachmentList from './AttachmentList'; -import { hasArticleLines, manifestConfig, partyDisplayName } from './manifestConfig'; +import { + hasArticleLines, + manifestConfig, + partyDisplayName, + getLowestSortNonSupplierCustomerGroup, +} from './manifestConfig'; import { notifyError, notifyErrorMessage } from '../../utils/notify'; import entityAutomat from '../../automats/entityAutomat'; import customergroupAutomat from '../../automats/customergroupAutomat'; @@ -29,7 +34,11 @@ class ManifestHeader extends PureComponent { constructor(props) { super(props); 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 = { partyInput: selected ? partyDisplayName(props.kind, selected) : '', creatingParty: false, @@ -48,10 +57,13 @@ class ManifestHeader extends PureComponent { } componentDidUpdate(prevProps) { - if (prevProps.partyId !== this.props.partyId) { + if (prevProps.partyId !== this.props.partyId || prevProps.parties !== this.props.parties) { const selected = (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) { this.setState({ partyInput: partyDisplayName(this.props.kind, selected) }); } else if (this.props.partyId == null) { @@ -137,8 +149,8 @@ class ManifestHeader extends PureComponent { } else { await customergroupAutomat.actions.ensureLoaded(); const groups = customergroupAutomat.state.items ?? []; - const defaultGroup = groups.find((g) => g.editable !== false) ?? groups[0]; - if (!defaultGroup) { + const targetGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups[0]; + if (!targetGroup) { throw new Error('Keine Kundengruppe vorhanden.'); } @@ -156,7 +168,7 @@ class ManifestHeader extends PureComponent { } const body = { - customergroupId: defaultGroup.id, + customergroupId: targetGroup.id, name, }; if (firstName) body.firstName = firstName; diff --git a/src/components/manifest/index.js b/src/components/manifest/index.js index a38eda8..12a760c 100644 --- a/src/components/manifest/index.js +++ b/src/components/manifest/index.js @@ -33,4 +33,6 @@ export { restoreAttachmentsFromApi, dateInputValue, todayInputValue, + filterNonSupplierEntities, + getLowestSortNonSupplierCustomerGroup, } from './manifestConfig'; diff --git a/src/components/manifest/manifestConfig.js b/src/components/manifest/manifestConfig.js index 2549df3..916676c 100644 --- a/src/components/manifest/manifestConfig.js +++ b/src/components/manifest/manifestConfig.js @@ -23,6 +23,10 @@ import { /** Groups that are not sales customers (filter client-side). */ 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) { if (!entity) return ''; 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. */ @@ -588,8 +617,13 @@ export async function loadParties(cfgOrKind) { const list = await supplierAutomat.actions.load(); return sortSuppliers(list); } - const entities = await entityAutomat.actions.ensureLoaded(); - return entities ?? entityAutomat.state.items ?? []; + const [entities, groups] = await Promise.all([ + 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) { diff --git a/src/routes/kunde.jsx b/src/routes/kunde.jsx index 0a72966..e9d2e0d 100644 --- a/src/routes/kunde.jsx +++ b/src/routes/kunde.jsx @@ -14,6 +14,7 @@ import { backendErrorFrom } from '../utils/backendError'; import { notifyError } from '../utils/notify'; import entityAutomat from '../automats/entityAutomat'; import customergroupAutomat from '../automats/customergroupAutomat'; +import { getLowestSortNonSupplierCustomerGroup } from '../components/manifest/manifestConfig'; import { Kunde } from '../components/kunde'; import { BLUE_PANEL } from '../colors'; @@ -70,7 +71,7 @@ class KundeSearchField extends PureComponent { try { await customergroupAutomat.actions.ensureLoaded(); const groups = customergroupAutomat.state.items ?? []; - const defaultGroup = groups.find((g) => g.editable !== false) ?? groups[0]; + const defaultGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups[0]; if (!defaultGroup) { throw new Error('Keine Kundengruppe vorhanden.'); } diff --git a/src/routes/kunde/neu.jsx b/src/routes/kunde/neu.jsx index 9c34b88..729abb9 100644 --- a/src/routes/kunde/neu.jsx +++ b/src/routes/kunde/neu.jsx @@ -11,6 +11,7 @@ import { PanelTile, PanelTileGrid } from 'dashgui/panels'; import { entity as entityApi } from '../../api'; import entityAutomat from '../../automats/entityAutomat'; import customergroupAutomat from '../../automats/customergroupAutomat'; +import { getLowestSortNonSupplierCustomerGroup } from '../../components/manifest/manifestConfig'; import { router } from '../../appRouter'; import { requireDefined, requireObject } from '../../utils/apiShape'; import { notifyError } from '../../utils/notify'; @@ -45,9 +46,10 @@ class KundeNeuPage extends PureComponent { constructor(props) { super(props); const groups = customergroupAutomat.state.items ?? []; + const defaultGroup = getLowestSortNonSupplierCustomerGroup(groups) ?? groups[0]; const prefill = getInitialPrefill(props); this.state = { - customergroupId: groups.length > 0 ? groups[0].id : null, + customergroupId: defaultGroup ? defaultGroup.id : null, number: '', name: prefill.name, firstName: prefill.firstName, @@ -59,6 +61,14 @@ class KundeNeuPage extends PureComponent { componentDidMount() { 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() { diff --git a/src/routes/lieferung/neu.jsx b/src/routes/lieferung/neu.jsx index 6ddc2f8..0090711 100644 --- a/src/routes/lieferung/neu.jsx +++ b/src/routes/lieferung/neu.jsx @@ -37,7 +37,8 @@ import { ArticleSearchDialog, BookDeliveryDialog, } 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 { article as articleApi, delivery as deliveryApi } from '../../api'; import { getCategoryState } from '../../automats/categoryAutomat'; @@ -108,6 +109,12 @@ class LieferungNeuPage extends PureComponent { super(props); this.cfg = manifestConfig('delivery'); 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); if (props.docId == null && !this.automat.state.date) { this.automat.actions.setDate(initialDate); @@ -181,6 +188,11 @@ class LieferungNeuPage extends PureComponent { this._warehouseUnsub = warehouseAutomat.subscribe(this, (state) => ({ 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(() => { }); this._cancelled = false; if (this.props.docId != null) { @@ -225,6 +237,7 @@ class LieferungNeuPage extends PureComponent { this.unsubscribe?.(); this._settingsUnsub?.(); this._warehouseUnsub?.(); + this._supplierUnsub?.(); } async loadPartiesOnly() {