u
This commit is contained in:
@@ -20,13 +20,11 @@ import CloseIcon from '@mui/icons-material/Close';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import articleIndexAutomat from '../../automats/articleIndexAutomat';
|
||||
import ArticlePhoto from '../article/ArticlePhoto';
|
||||
import { BrandLabel } from '../brand';
|
||||
|
||||
export default class ArticleSearchDialog extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -68,13 +66,7 @@ export default class ArticleSearchDialog extends PureComponent {
|
||||
async loadArticles() {
|
||||
this.setState({ loading: true, error: null });
|
||||
try {
|
||||
let list = [];
|
||||
if (this.context?.articleIndexPromise) {
|
||||
list = await this.context.articleIndexPromise;
|
||||
} else if (this.context?.article?.index) {
|
||||
const res = await this.context.article.index();
|
||||
list = res?.articles || [];
|
||||
}
|
||||
const list = await articleIndexAutomat.actions.ensureLoaded();
|
||||
this.setState({ articles: Array.isArray(list) ? list : [], loading: false });
|
||||
} catch (err) {
|
||||
console.error('Fehler beim Laden des Artikel-Index:', err);
|
||||
|
||||
@@ -27,7 +27,6 @@ import CloseIcon from '@mui/icons-material/Close';
|
||||
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
|
||||
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
|
||||
import WarehouseIcon from '@mui/icons-material/Warehouse';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { notifyError } from '../../utils/notify';
|
||||
import { formatDate } from './manifestConfig';
|
||||
@@ -38,8 +37,6 @@ import {
|
||||
import warehouseAutomat from '../../automats/warehouseAutomat';
|
||||
|
||||
export default class BookDeliveryDialog extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -160,12 +157,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
||||
|
||||
this.setState({ loading: true, error: null });
|
||||
try {
|
||||
const ledgerApi = this.context?.ledger;
|
||||
const deliveryApi = this.context?.delivery;
|
||||
const summary = await fetchDeliveryLedgerSummary(delivery.id, {
|
||||
ledgerApi,
|
||||
deliveryApi,
|
||||
});
|
||||
const summary = await fetchDeliveryLedgerSummary(delivery.id);
|
||||
this.initAmountsFromSummary(summary, prefillLineId);
|
||||
this.setState({ summary, loading: false });
|
||||
} catch (err) {
|
||||
@@ -255,14 +247,10 @@ export default class BookDeliveryDialog extends PureComponent {
|
||||
|
||||
this.setState({ isBooking: true, error: null, warehouseError: null });
|
||||
try {
|
||||
const ledgerApi = this.context?.ledger;
|
||||
const deliveryApi = this.context?.delivery;
|
||||
|
||||
await bookDeliveryLines({
|
||||
delivery: summary.delivery || delivery,
|
||||
linesToBook,
|
||||
warehouseId: selectedWarehouseId,
|
||||
ledgerApi,
|
||||
transactionText,
|
||||
});
|
||||
|
||||
@@ -273,10 +261,7 @@ export default class BookDeliveryDialog extends PureComponent {
|
||||
);
|
||||
|
||||
// Refresh summary
|
||||
const updatedSummary = await fetchDeliveryLedgerSummary(delivery.id, {
|
||||
ledgerApi,
|
||||
deliveryApi,
|
||||
});
|
||||
const updatedSummary = await fetchDeliveryLedgerSummary(delivery.id);
|
||||
|
||||
if (onBookSuccess) {
|
||||
onBookSuccess(updatedSummary);
|
||||
|
||||
@@ -13,7 +13,7 @@ import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import BackendError from '../BackendError';
|
||||
import { RecordList } from 'dashgui/lists';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import deliveryAutomat from '../../automats/deliveryAutomat';
|
||||
import { delivery } from '../../api';
|
||||
import { supplierAutomat } from '../../automats';
|
||||
import { backendErrorFrom } from '../../utils/backendError';
|
||||
@@ -39,8 +39,6 @@ function formatFileSize(bytes) {
|
||||
}
|
||||
|
||||
export class DeliveryList extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -77,14 +75,7 @@ export class DeliveryList extends PureComponent {
|
||||
}
|
||||
|
||||
async fetchDeliveries() {
|
||||
if (this.context?.deliveries) {
|
||||
return this.context.deliveries;
|
||||
}
|
||||
if (this.context?.deliveryPromise) {
|
||||
return await this.context.deliveryPromise;
|
||||
}
|
||||
const res = await (this.context?.delivery?.list ? this.context.delivery.list() : delivery.list());
|
||||
return res?.deliveries ?? res ?? [];
|
||||
return deliveryAutomat.actions.ensureLoaded();
|
||||
}
|
||||
|
||||
async load() {
|
||||
@@ -103,12 +94,10 @@ export class DeliveryList extends PureComponent {
|
||||
}
|
||||
|
||||
async handleRetry() {
|
||||
if (this.context?.updateDelivery) {
|
||||
try {
|
||||
await this.context.updateDelivery();
|
||||
} catch {
|
||||
// Will be caught in load()
|
||||
}
|
||||
try {
|
||||
await deliveryAutomat.actions.reload();
|
||||
} catch {
|
||||
// Will be caught in load()
|
||||
}
|
||||
void this.load();
|
||||
}
|
||||
@@ -140,15 +129,13 @@ export class DeliveryList extends PureComponent {
|
||||
|
||||
async handleBookSuccess() {
|
||||
this.handleCloseBookDialog();
|
||||
if (this.context?.updateDelivery) {
|
||||
try {
|
||||
const updated = await this.context.updateDelivery();
|
||||
if (this._cancelled) return;
|
||||
this.setState({ docs: sortDeliveries(updated) });
|
||||
return;
|
||||
} catch {
|
||||
// Fall back to load()
|
||||
}
|
||||
try {
|
||||
const updated = await deliveryAutomat.actions.reload();
|
||||
if (this._cancelled) return;
|
||||
this.setState({ docs: sortDeliveries(updated) });
|
||||
return;
|
||||
} catch {
|
||||
// Fall back to load()
|
||||
}
|
||||
void this.load();
|
||||
}
|
||||
@@ -159,10 +146,8 @@ export class DeliveryList extends PureComponent {
|
||||
|
||||
this.setState({ busyId: doc.id });
|
||||
try {
|
||||
await (this.context?.delivery?.delete ? this.context.delivery.delete(doc.id) : delivery.delete(doc.id));
|
||||
if (this.context?.updateDelivery) {
|
||||
await this.context.updateDelivery();
|
||||
}
|
||||
await delivery.delete(doc.id);
|
||||
await deliveryAutomat.actions.reload();
|
||||
if (this._cancelled) return;
|
||||
await this.load();
|
||||
enqueueSnackbar('Lieferung gelöscht.', { variant: 'success' });
|
||||
|
||||
@@ -4,7 +4,7 @@ import CircularProgress from '@mui/material/CircularProgress';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import BackendError from '../BackendError';
|
||||
import { PanelTileGrid } from 'dashgui/panels';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import { delivery as deliveryApi } from '../../api';
|
||||
import { requireArray, requireDefined, requireObject } from '../../utils/apiShape';
|
||||
import { backendErrorFrom } from '../../utils/backendError';
|
||||
import { notifyError } from '../../utils/notify';
|
||||
@@ -20,11 +20,10 @@ import {
|
||||
lineFromApi,
|
||||
loadParties,
|
||||
manifestConfig,
|
||||
apiFor,
|
||||
} from './manifestConfig';
|
||||
|
||||
class ManifestForm extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.cfg = manifestConfig(props.kind);
|
||||
@@ -88,7 +87,7 @@ class ManifestForm extends PureComponent {
|
||||
async loadPartiesOnly() {
|
||||
this.setState({ status: 'loadingParties', error: null, partiesLoading: true });
|
||||
try {
|
||||
const parties = await loadParties(this.props.kind, this.context);
|
||||
const parties = await loadParties(this.props.kind);
|
||||
if (this._cancelled) return;
|
||||
this.setState({
|
||||
status: 'ready',
|
||||
@@ -120,8 +119,8 @@ class ManifestForm extends PureComponent {
|
||||
this.setState({ status: 'loading', error: null, partiesLoading: true });
|
||||
try {
|
||||
const [docRes, parties] = await Promise.all([
|
||||
this.context[cfg.api].get(docId),
|
||||
loadParties(kind, this.context),
|
||||
apiFor(cfg).get(docId),
|
||||
loadParties(kind),
|
||||
]);
|
||||
if (this._cancelled) return;
|
||||
|
||||
@@ -243,10 +242,10 @@ class ManifestForm extends PureComponent {
|
||||
};
|
||||
|
||||
try {
|
||||
const gen = this.context?.delivery?.parsePdfSse;
|
||||
const gen = deliveryApi.parsePdfSse;
|
||||
if (!gen) throw new Error('parsePdfSse nicht verfügbar.');
|
||||
|
||||
for await (const { event, data } of this.context.delivery.parsePdfSse(file, { signal: abortController.signal })) {
|
||||
for await (const { event, data } of gen(file, { signal: abortController.signal })) {
|
||||
if (this._cancelled || abortController.signal.aborted) return;
|
||||
switch (event) {
|
||||
case 'status':
|
||||
@@ -372,24 +371,25 @@ class ManifestForm extends PureComponent {
|
||||
}
|
||||
|
||||
this.setState({ saving: true });
|
||||
const api = apiFor(cfg);
|
||||
try {
|
||||
if (mode === 'create') {
|
||||
const data = await this.context[cfg.api].create(body);
|
||||
const data = await api.create(body);
|
||||
requireObject(data, cfg.postPathLabel);
|
||||
requireObject(data[cfg.docField], cfg.docField);
|
||||
requireDefined(data[cfg.docField].id, `${cfg.docField}.id`);
|
||||
await this.context[cfg.updateCache]();
|
||||
await cfg.listAutomat.actions.reload();
|
||||
if (this._cancelled) return;
|
||||
enqueueSnackbar(cfg.createdMsg, { variant: 'success' });
|
||||
this.props.navigate(cfg.editPath(data[cfg.docField].id));
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await this.context[cfg.api].update(docId, body);
|
||||
const data = await api.update(docId, body);
|
||||
requireObject(data, cfg.patchPathLabel);
|
||||
requireObject(data[cfg.docField], cfg.docField);
|
||||
requireDefined(data[cfg.docField].id, `${cfg.docField}.id`);
|
||||
await this.context[cfg.updateCache]();
|
||||
await cfg.listAutomat.actions.reload();
|
||||
if (this._cancelled) return;
|
||||
const doc = data[cfg.docField];
|
||||
const apiLines = requireArray(doc.lines, `${cfg.docField}.lines`);
|
||||
|
||||
@@ -2,7 +2,6 @@ import { PureComponent } from 'react';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import BackendError from '../BackendError';
|
||||
import { RecordList } from 'dashgui/lists';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import { backendErrorFrom } from '../../utils/backendError';
|
||||
import { notifyError } from '../../utils/notify';
|
||||
import withNavigate from '../withNavigate';
|
||||
@@ -14,11 +13,10 @@ import {
|
||||
numberDisplay,
|
||||
partyDisplayName,
|
||||
sortDocs,
|
||||
apiFor,
|
||||
} from './manifestConfig';
|
||||
|
||||
class ManifestList extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.cfg = manifestConfig(props.kind);
|
||||
@@ -52,8 +50,8 @@ class ManifestList extends PureComponent {
|
||||
this.setState({ status: 'loading', error: null });
|
||||
try {
|
||||
const [docs, parties] = await Promise.all([
|
||||
cfg.listPromise(this.context),
|
||||
loadParties(this.props.kind, this.context),
|
||||
cfg.listAutomat.actions.ensureLoaded(),
|
||||
loadParties(this.props.kind),
|
||||
]);
|
||||
if (this._cancelled) return;
|
||||
const sortedDocs = sortDocs(docs);
|
||||
@@ -96,8 +94,9 @@ class ManifestList extends PureComponent {
|
||||
const cfg = this.cfg;
|
||||
this.setState({ busyId: doc.id });
|
||||
try {
|
||||
await this.context[cfg.api].delete(doc.id);
|
||||
const list = await this.context[cfg.updateCache]();
|
||||
const api = apiFor(cfg.kind);
|
||||
await api.delete(doc.id);
|
||||
const list = await cfg.listAutomat.actions.reload();
|
||||
if (this._cancelled) return;
|
||||
this.setState({
|
||||
docs: sortDocs(list),
|
||||
|
||||
@@ -14,7 +14,6 @@ import VerticalAlignBottomIcon from '@mui/icons-material/VerticalAlignBottom';
|
||||
import ViewSidebarIcon from '@mui/icons-material/ViewSidebar';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { PanelTile } from 'dashgui/panels';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import { delivery as deliveryApi } from '../../api';
|
||||
|
||||
/**
|
||||
@@ -24,8 +23,6 @@ import { delivery as deliveryApi } from '../../api';
|
||||
* attachment is selected; unmounting closes it.
|
||||
*/
|
||||
export class PdfViewerPanel extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -77,10 +74,7 @@ export class PdfViewerPanel extends PureComponent {
|
||||
if (effectiveDocId != null && fileId != null) {
|
||||
this.setState({ loading: true, error: null });
|
||||
try {
|
||||
const getFile =
|
||||
this.context?.delivery?.getFile?.bind(this.context.delivery) ||
|
||||
deliveryApi.getFile;
|
||||
const blob = await getFile(effectiveDocId, fileId);
|
||||
const blob = await deliveryApi.getFile(effectiveDocId, fileId);
|
||||
this._objectUrl = URL.createObjectURL(blob);
|
||||
this.setState({ url: this._objectUrl, loading: false, error: null });
|
||||
return;
|
||||
|
||||
@@ -17,7 +17,6 @@ import VerticalAlignBottomIcon from '@mui/icons-material/VerticalAlignBottom';
|
||||
import ViewSidebarIcon from '@mui/icons-material/ViewSidebar';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { PanelTile } from 'dashgui/panels';
|
||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||
import { delivery as deliveryApi } from '../../api';
|
||||
|
||||
/**
|
||||
@@ -26,8 +25,6 @@ import { delivery as deliveryApi } from '../../api';
|
||||
* selected; unmounting closes it.
|
||||
*/
|
||||
export class TextEditorPanel extends PureComponent {
|
||||
static contextType = StorageManagerContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -89,10 +86,7 @@ export class TextEditorPanel extends PureComponent {
|
||||
if (effectiveDocId != null && fileId != null) {
|
||||
this.setState({ loading: true });
|
||||
try {
|
||||
const getFile =
|
||||
this.context?.delivery?.getFile?.bind(this.context.delivery) ||
|
||||
deliveryApi.getFile;
|
||||
const blob = await getFile(effectiveDocId, fileId);
|
||||
const blob = await deliveryApi.getFile(effectiveDocId, fileId);
|
||||
const text = await blob.text();
|
||||
this.setState({
|
||||
content: text,
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import {
|
||||
delivery as deliveryApi,
|
||||
quote as quoteApi,
|
||||
order as orderApi,
|
||||
} from '../../api';
|
||||
import supplierAutomat from '../../automats/supplierAutomat';
|
||||
import entityAutomat from '../../automats/entityAutomat';
|
||||
import customergroupAutomat from '../../automats/customergroupAutomat';
|
||||
import deliveryAutomat from '../../automats/deliveryAutomat';
|
||||
import quoteAutomat from '../../automats/quoteAutomat';
|
||||
import orderAutomat from '../../automats/orderAutomat';
|
||||
|
||||
/** Groups that are not sales customers (filter client-side). */
|
||||
export const EXCLUDED_CUSTOMER_GROUP_NAMES = new Set(['Lieferanten', 'POS']);
|
||||
@@ -481,12 +491,27 @@ export const DELIVERY_LINE_COLUMNS = [
|
||||
|
||||
/** @typedef {'delivery'|'quote'|'order'} ManifestKind */
|
||||
|
||||
/**
|
||||
* Resolves the api namespace for a manifest kind.
|
||||
*
|
||||
* Delivery, quote, and order share one editor/list shape but talk to three
|
||||
* different endpoints.
|
||||
*
|
||||
* @param {ManifestKind} kind Manifest kind.
|
||||
* @returns {{get: Function, create: Function, update: Function, delete: Function}}
|
||||
*/
|
||||
export function apiFor(kind) {
|
||||
if (kind === 'delivery') return deliveryApi;
|
||||
if (kind === 'quote') return quoteApi;
|
||||
if (kind === 'order') return orderApi;
|
||||
throw new Error(`Unknown manifest kind: ${kind}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load party options for Autocomplete / list name lookup.
|
||||
* @param {ManifestKind} kind
|
||||
* @param {object} ctx StorageManager instance
|
||||
*/
|
||||
export async function loadParties(kind, ctx) {
|
||||
export async function loadParties(kind) {
|
||||
if (kind === 'delivery') {
|
||||
if (supplierAutomat.state.status === 'ready') {
|
||||
return sortSuppliers(supplierAutomat.state.suppliers);
|
||||
@@ -495,8 +520,8 @@ export async function loadParties(kind, ctx) {
|
||||
return sortSuppliers(list);
|
||||
}
|
||||
const [entities, groups] = await Promise.all([
|
||||
ctx.entities ?? ctx.entityPromise,
|
||||
ctx.customergroups ?? ctx.customergroupPromise,
|
||||
entityAutomat.actions.ensureLoaded(),
|
||||
customergroupAutomat.actions.ensureLoaded(),
|
||||
]);
|
||||
return filterCustomerEntities(entities, groups);
|
||||
}
|
||||
@@ -538,8 +563,7 @@ export function manifestConfig(kind) {
|
||||
headerColor: '#fed7aa',
|
||||
linesColor: '#bbf7d0',
|
||||
requirePartyBeforeLines: false,
|
||||
updateCache: 'updateDelivery',
|
||||
listPromise: (ctx) => ctx.deliveries ?? ctx.deliveryPromise,
|
||||
listAutomat: deliveryAutomat,
|
||||
partiesByIdFromList: (parties) =>
|
||||
Object.fromEntries(parties.map((row) => [row.id, row])),
|
||||
// Delivery uses a server-allocated read-only number plus an optional
|
||||
@@ -575,8 +599,7 @@ export function manifestConfig(kind) {
|
||||
headerColor: '#fde68a',
|
||||
linesColor: '#bbf7d0',
|
||||
requirePartyBeforeLines: false,
|
||||
updateCache: 'updateQuote',
|
||||
listPromise: (ctx) => ctx.quotes ?? ctx.quotePromise,
|
||||
listAutomat: quoteAutomat,
|
||||
partiesByIdFromList: (parties) =>
|
||||
Object.fromEntries(parties.map((row) => [row.id, row])),
|
||||
columns: LINE_COLUMNS,
|
||||
@@ -609,8 +632,7 @@ export function manifestConfig(kind) {
|
||||
headerColor: '#c4b5fd',
|
||||
linesColor: '#bbf7d0',
|
||||
requirePartyBeforeLines: false,
|
||||
updateCache: 'updateOrder',
|
||||
listPromise: (ctx) => ctx.orders ?? ctx.orderPromise,
|
||||
listAutomat: orderAutomat,
|
||||
partiesByIdFromList: (parties) =>
|
||||
Object.fromEntries(parties.map((row) => [row.id, row])),
|
||||
columns: LINE_COLUMNS,
|
||||
|
||||
Reference in New Issue
Block a user