From 2f2906b8b5e83cb67faee1ccd237b131c69900bd Mon Sep 17 00:00:00 2001 From: seb Date: Wed, 30 Sep 2026 09:47:47 +0200 Subject: [PATCH] u --- src/api/ApiClient.js | 16 + src/api/index.js | 38 +- src/assets/quixpos-logo.png | Bin 0 -> 49669 bytes src/components/LoginPage.jsx | 65 +- src/components/article/ArticleTileTab.jsx | 22 +- src/components/article/articleRoute.js | 8 +- .../article/boxes/ArticleViewLists.jsx | 1334 +++++++++++++++-- .../article/boxes/ArticleViewPrice.jsx | 49 +- .../article/boxes/ArticleViewStock.jsx | 54 +- .../article/tabs/ArticleSupplierTab.jsx | 27 + src/components/tiles/ArticleTile.jsx | 28 + 11 files changed, 1479 insertions(+), 162 deletions(-) create mode 100644 src/assets/quixpos-logo.png diff --git a/src/api/ApiClient.js b/src/api/ApiClient.js index c3f56ff..01aa56e 100644 --- a/src/api/ApiClient.js +++ b/src/api/ApiClient.js @@ -497,6 +497,22 @@ export class ApiClient { 'DELETE', `/api/article/${enc(sku)}/children/${enc(childSku)}${deleteArticle ? '?deleteArticle=true' : ''}` ), + /** GET /api/article/{sku}/articlelist */ + getArticlelist: (sku) => + this.#json('GET', `/api/article/${enc(sku)}/articlelist`), + /** PUT /api/article/{sku}/articlelist */ + setArticlelist: (sku, items) => + this.#json('PUT', `/api/article/${enc(sku)}/articlelist`, { + body: Array.isArray(items) ? { items } : items, + }), + /** GET /api/article/{sku}/optionslist */ + getOptionslist: (sku) => + this.#json('GET', `/api/article/${enc(sku)}/optionslist`), + /** PUT /api/article/{sku}/optionslist */ + setOptionslist: (sku, options) => + this.#json('PUT', `/api/article/${enc(sku)}/optionslist`, { + body: Array.isArray(options) ? { options } : options, + }), }; this.posterminal = { diff --git a/src/api/index.js b/src/api/index.js index e82e6b2..e21f093 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -519,6 +519,42 @@ export const article = { { method: 'DELETE' } ); }, + + /** + * GET /api/article/:sku/articlelist → { items: [...] } + */ + getArticlelist(sku) { + return http(`/api/article/${encodeURIComponent(sku)}/articlelist`); + }, + + /** + * PUT /api/article/:sku/articlelist + * Body: { items: [{ articleId, sku, count, sort }] } + */ + setArticlelist(sku, items) { + return http(`/api/article/${encodeURIComponent(sku)}/articlelist`, { + method: 'PUT', + body: Array.isArray(items) ? { items } : items, + }); + }, + + /** + * GET /api/article/:sku/optionslist → { options: [...] } + */ + getOptionslist(sku) { + return http(`/api/article/${encodeURIComponent(sku)}/optionslist`); + }, + + /** + * PUT /api/article/:sku/optionslist + * Body: { options: [{ articleId, sku, count, sort, mandated }] } + */ + setOptionslist(sku, options) { + return http(`/api/article/${encodeURIComponent(sku)}/optionslist`, { + method: 'PUT', + body: Array.isArray(options) ? { options } : options, + }); + }, }; /** @@ -611,7 +647,7 @@ export const category = { /** * GET /api/category/:id/articles → { articles: [...] } - * Article rows: sku, name, net, taxRate, fullHash, thumbW, thumbH, thumbHash, … + * Article rows: sku, name, net, taxRate, fullHash, thumbW, thumbH, thumbHash, childCount, … */ getArticles(categoryId) { return http(`/api/category/${categoryId}/articles`); diff --git a/src/assets/quixpos-logo.png b/src/assets/quixpos-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7df2d5f7fac0393e2aecfba3bca0af8186aa6bf3 GIT binary patch literal 49669 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 literal 0 HcmV?d00001 diff --git a/src/components/LoginPage.jsx b/src/components/LoginPage.jsx index 3787d36..5041f26 100644 --- a/src/components/LoginPage.jsx +++ b/src/components/LoginPage.jsx @@ -1,6 +1,8 @@ import { PureComponent } from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; import GoogleIcon from '@mui/icons-material/Google'; import { enqueueSnackbar } from 'notistack'; import { notifyErrorMessage } from '../utils/notify'; @@ -15,6 +17,8 @@ import { requestGoogleIdToken, } from '../auth/googleSignIn'; +import quixposLogo from '../assets/quixpos-logo.png'; + class LoginPage extends PureComponent { static contextType = StorageManagerContext; @@ -93,17 +97,62 @@ class LoginPage extends PureComponent { justifyContent: 'center', minHeight: '100vh', bgcolor: 'background.default', + p: 2, }} > - + + + + QuixPOS + + + Management Interface Login + + + + ); } diff --git a/src/components/article/ArticleTileTab.jsx b/src/components/article/ArticleTileTab.jsx index 5e615db..3bad37f 100644 --- a/src/components/article/ArticleTileTab.jsx +++ b/src/components/article/ArticleTileTab.jsx @@ -79,8 +79,19 @@ const SETTINGS_TAB = { help: 'Artikeleinstellungen, Einheiten, Steuersatz und Löschen', }; -function visibleTabs() { - return [...ALWAYS_TABS, SETTINGS_TAB]; +function visibleTabs(article) { + const isFather = Boolean(article?.isFather); + const tabs = ALWAYS_TABS.map((row) => { + if (row.id === ARTICLE_TAB.supplier && isFather) { + return { + ...row, + disabled: true, + help: 'Lieferanten sind für Vaterartikel deaktiviert (werden auf Variantenebene gepflegt).', + }; + } + return row; + }); + return [...tabs, SETTINGS_TAB]; } function readRouteTab() { @@ -264,6 +275,7 @@ class ArticleTileTab extends PureComponent { @@ -300,6 +312,12 @@ class ArticleTileTab extends PureComponent { color: 'text.primary', borderBottom: 'none', }, + '&.Mui-disabled': { + opacity: 0.4, + bgcolor: '#e2e8f0', + cursor: 'not-allowed', + pointerEvents: 'auto', + }, }} /> ); diff --git a/src/components/article/articleRoute.js b/src/components/article/articleRoute.js index 9a617fa..705c209 100644 --- a/src/components/article/articleRoute.js +++ b/src/components/article/articleRoute.js @@ -30,8 +30,12 @@ export function articleSku(article) { return sku || null; } -export function articleTabIds() { - return [...BASE_ARTICLE_TAB_IDS, ARTICLE_TAB.settings]; +export function articleTabIds(article) { + let ids = [...BASE_ARTICLE_TAB_IDS]; + if (article?.isFather) { + ids = ids.filter((id) => id !== ARTICLE_TAB.supplier); + } + return [...ids, ARTICLE_TAB.settings]; } export function isArticleTabId(tab) { diff --git a/src/components/article/boxes/ArticleViewLists.jsx b/src/components/article/boxes/ArticleViewLists.jsx index 6b85fd8..b981e13 100644 --- a/src/components/article/boxes/ArticleViewLists.jsx +++ b/src/components/article/boxes/ArticleViewLists.jsx @@ -1,7 +1,9 @@ import { PureComponent } from 'react'; import { Link as RouterLink } from 'react-router-dom'; +import Autocomplete from '@mui/material/Autocomplete'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; +import Checkbox from '@mui/material/Checkbox'; import Chip from '@mui/material/Chip'; import CircularProgress from '@mui/material/CircularProgress'; import Dialog from '@mui/material/Dialog'; @@ -10,6 +12,7 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; import FormControl from '@mui/material/FormControl'; +import FormControlLabel from '@mui/material/FormControlLabel'; import IconButton from '@mui/material/IconButton'; import InputLabel from '@mui/material/InputLabel'; import Link from '@mui/material/Link'; @@ -21,6 +24,8 @@ import Typography from '@mui/material/Typography'; import AddIcon from '@mui/icons-material/Add'; import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined'; import AccountTreeIcon from '@mui/icons-material/AccountTree'; +import LayersIcon from '@mui/icons-material/Layers'; +import PlaylistAddCheckIcon from '@mui/icons-material/PlaylistAddCheck'; import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; import LinkOffIcon from '@mui/icons-material/LinkOff'; import { PanelTile } from 'dashgui/panels'; @@ -38,18 +43,47 @@ export default class ArticleViewLists extends PureComponent { constructor(props) { super(props); const initialAttributes = selectAttributes() || []; + const hasStueckliste = Boolean( + props.article?.articlelistId != null || props.article?.articlelist?.length > 0 + ); + const hasOptions = Boolean( + props.article?.optionslistId != null || props.article?.options?.length > 0 + ); + let activeListTab = 'stueckliste'; + if (!hasStueckliste && hasOptions) { + activeListTab = 'options'; + } + this.state = { attributes: initialAttributes, assignedAttributes: [], + allArticles: [], busy: false, - isActivating: false, + + // Active sub-tab for compound article: 'stueckliste' | 'options' + activeListTab, + + // Creator mode for standard article: null | 'father' | 'stueckliste' | 'options' + activeCreatorMode: null, + + // Father mode state selectedAttributeId: '', selectedChildValueId: '', newChildText: '', isCustomChildValue: false, - // Remove dialog + // Stückliste item add state + stuecklisteTargetArticle: null, + stuecklisteCount: 1, + + // Options item add state + optionTargetArticle: null, + optionCount: 1, + optionMandated: false, + + // Confirmation dialogs removeTargetChild: null, // { sku, attributeValue } + confirmClearType: null, // 'stueckliste' | 'options' | null }; this._cancelled = false; } @@ -79,6 +113,8 @@ export default class ArticleViewLists extends PureComponent { if (this.props.article?.sku) { void this.loadAssignedAttributes(this.props.article.sku); } + + void this.loadAllArticles(); } componentDidUpdate(prevProps) { @@ -86,6 +122,26 @@ export default class ArticleViewLists extends PureComponent { if (this.props.article?.sku) { void this.loadAssignedAttributes(this.props.article.sku); } + const nextArticle = this.props.article; + const hasStueckliste = Boolean( + nextArticle?.articlelistId != null || nextArticle?.articlelist?.length > 0 + ); + const hasOptions = Boolean( + nextArticle?.optionslistId != null || nextArticle?.options?.length > 0 + ); + let activeListTab = 'stueckliste'; + if (!hasStueckliste && hasOptions) { + activeListTab = 'options'; + } + this.setState({ + activeListTab, + activeCreatorMode: null, + stuecklisteTargetArticle: null, + stuecklisteCount: 1, + optionTargetArticle: null, + optionCount: 1, + optionMandated: false, + }); } } @@ -98,6 +154,18 @@ export default class ArticleViewLists extends PureComponent { return this.context?.article || articleApi; } + async loadAllArticles() { + try { + const data = await (this.context?.articleIndexPromise || this.getApi().index()); + const articles = Array.isArray(data) ? data : (data?.articles || []); + if (!this._cancelled) { + this.setState({ allArticles: articles }); + } + } catch { + // Ignore + } + } + async loadAssignedAttributes(sku) { if (!sku) return; try { @@ -124,16 +192,24 @@ export default class ArticleViewLists extends PureComponent { } } - handleStartActivating = async () => { - this.setState({ isActivating: true }); - await this.loadAssignedAttributes(this.props.article?.sku); - }; + // ── Father Actions ────────────────────────────────────────────────────────── handleEnableFather = async () => { const { article } = this.props; const { selectedAttributeId, assignedAttributes, busy } = this.state; if (busy || !article?.sku || !selectedAttributeId) return; + const isStueckliste = article.articlelistId != null || (article.articleListCount ?? 0) > 0; + const hasOptions = article.optionslistId != null || (article.optionsCount ?? 0) > 0; + if (isStueckliste) { + enqueueSnackbar('Ein Stücklisten-Artikel kann kein Vaterartikel sein.', { variant: 'error' }); + return; + } + if (hasOptions) { + enqueueSnackbar('Ein Artikel mit Optionen kann kein Vaterartikel sein.', { variant: 'error' }); + return; + } + const assignedIds = new Set(assignedAttributes.map((a) => a.attributeId)); if (assignedIds.has(Number(selectedAttributeId))) { enqueueSnackbar('Dieses Merkmal ist dem Artikel bereits zugewiesen.', { variant: 'warning' }); @@ -156,7 +232,7 @@ export default class ArticleViewLists extends PureComponent { } enqueueSnackbar('Vaterartikel-Modus aktiviert.', { variant: 'success' }); - this.setState({ busy: false, isActivating: false }); + this.setState({ busy: false, activeCreatorMode: null }); } catch (err) { if (this._cancelled) return; notifyError(err); @@ -264,18 +340,1020 @@ export default class ArticleViewLists extends PureComponent { } }; + // ── Stückliste Actions ────────────────────────────────────────────────────── + + handleAddItemToStueckliste = async () => { + const { article } = this.props; + const { stuecklisteTargetArticle, stuecklisteCount, busy } = this.state; + if (busy || !article?.sku || !stuecklisteTargetArticle?.sku) return; + + if (stuecklisteTargetArticle.sku === article.sku) { + enqueueSnackbar('Ein Artikel kann sich nicht selbst als Komponente enthalten.', { variant: 'error' }); + return; + } + + const currentItems = Array.isArray(article.articlelist) ? article.articlelist : []; + const count = Math.max(1, Number(stuecklisteCount) || 1); + + const existingIndex = currentItems.findIndex((i) => i.sku === stuecklisteTargetArticle.sku); + let nextItems = []; + if (existingIndex >= 0) { + nextItems = currentItems.map((item, idx) => + idx === existingIndex ? { ...item, count: item.count + count } : item + ); + } else { + nextItems = [ + ...currentItems, + { + articleId: stuecklisteTargetArticle.id, + sku: stuecklisteTargetArticle.sku, + count, + sort: currentItems.length, + }, + ]; + } + + this.setState({ busy: true }); + try { + const api = this.getApi(); + await api.setArticlelist(article.sku, nextItems); + if (this._cancelled) return; + + await artikelAutomat.actions.reloadArticle(article.sku); + if (this._cancelled) return; + + enqueueSnackbar('Komponente hinzugefügt.', { variant: 'success' }); + this.setState({ + busy: false, + stuecklisteTargetArticle: null, + stuecklisteCount: 1, + activeCreatorMode: null, + activeListTab: 'stueckliste', + }); + } catch (err) { + if (this._cancelled) return; + notifyError(err); + this.setState({ busy: false }); + } + }; + + handleRemoveStuecklisteItem = async (itemSku) => { + const { article } = this.props; + const { busy } = this.state; + if (busy || !article?.sku) return; + + const currentItems = Array.isArray(article.articlelist) ? article.articlelist : []; + const remainingItems = currentItems + .filter((i) => i.sku !== itemSku) + .map((item, idx) => ({ + articleId: item.articleId, + sku: item.sku, + count: item.count, + sort: idx, + })); + + this.setState({ busy: true }); + try { + const api = this.getApi(); + await api.setArticlelist(article.sku, remainingItems); + if (this._cancelled) return; + + await artikelAutomat.actions.reloadArticle(article.sku); + if (this._cancelled) return; + + enqueueSnackbar('Komponente entfernt.', { variant: 'success' }); + this.setState({ busy: false }); + } catch (err) { + if (this._cancelled) return; + notifyError(err); + this.setState({ busy: false }); + } + }; + + handleClearStueckliste = async () => { + const { article } = this.props; + const { busy } = this.state; + if (busy || !article?.sku) return; + + this.setState({ busy: true, confirmClearType: null }); + try { + const api = this.getApi(); + await api.setArticlelist(article.sku, []); + if (this._cancelled) return; + + await artikelAutomat.actions.reloadArticle(article.sku); + if (this._cancelled) return; + + enqueueSnackbar('Stückliste aufgelöst.', { variant: 'success' }); + this.setState({ + busy: false, + activeListTab: 'options', + }); + } catch (err) { + if (this._cancelled) return; + notifyError(err); + this.setState({ busy: false }); + } + }; + + // ── Optionen Actions ──────────────────────────────────────────────────────── + + handleAddOption = async () => { + const { article } = this.props; + const { optionTargetArticle, optionCount, optionMandated, busy } = this.state; + if (busy || !article?.sku || !optionTargetArticle?.sku) return; + + if (optionTargetArticle.sku === article.sku) { + enqueueSnackbar('Ein Artikel kann sich nicht selbst als Option enthalten.', { variant: 'error' }); + return; + } + + const currentOptions = Array.isArray(article.options) ? article.options : []; + const count = Math.max(1, Number(optionCount) || 1); + + const existingIndex = currentOptions.findIndex((o) => o.sku === optionTargetArticle.sku); + let nextOptions = []; + if (existingIndex >= 0) { + nextOptions = currentOptions.map((opt, idx) => + idx === existingIndex + ? { ...opt, count: opt.count + count, mandated: Boolean(optionMandated) } + : opt + ); + } else { + nextOptions = [ + ...currentOptions, + { + articleId: optionTargetArticle.id, + sku: optionTargetArticle.sku, + count, + sort: currentOptions.length, + mandated: Boolean(optionMandated), + }, + ]; + } + + this.setState({ busy: true }); + try { + const api = this.getApi(); + await api.setOptionslist(article.sku, nextOptions); + if (this._cancelled) return; + + await artikelAutomat.actions.reloadArticle(article.sku); + if (this._cancelled) return; + + enqueueSnackbar('Option hinzugefügt.', { variant: 'success' }); + this.setState({ + busy: false, + optionTargetArticle: null, + optionCount: 1, + optionMandated: false, + activeCreatorMode: null, + activeListTab: 'options', + }); + } catch (err) { + if (this._cancelled) return; + notifyError(err); + this.setState({ busy: false }); + } + }; + + handleRemoveOption = async (optionSku) => { + const { article } = this.props; + const { busy } = this.state; + if (busy || !article?.sku) return; + + const currentOptions = Array.isArray(article.options) ? article.options : []; + const remainingOptions = currentOptions + .filter((o) => o.sku !== optionSku) + .map((opt, idx) => ({ + articleId: opt.articleId, + sku: opt.sku, + count: opt.count, + sort: idx, + mandated: Boolean(opt.mandated), + })); + + this.setState({ busy: true }); + try { + const api = this.getApi(); + await api.setOptionslist(article.sku, remainingOptions); + if (this._cancelled) return; + + await artikelAutomat.actions.reloadArticle(article.sku); + if (this._cancelled) return; + + enqueueSnackbar('Option entfernt.', { variant: 'success' }); + this.setState({ busy: false }); + } catch (err) { + if (this._cancelled) return; + notifyError(err); + this.setState({ busy: false }); + } + }; + + handleClearOptions = async () => { + const { article } = this.props; + const { busy } = this.state; + if (busy || !article?.sku) return; + + this.setState({ busy: true, confirmClearType: null }); + try { + const api = this.getApi(); + await api.setOptionslist(article.sku, []); + if (this._cancelled) return; + + await artikelAutomat.actions.reloadArticle(article.sku); + if (this._cancelled) return; + + enqueueSnackbar('Optionen entfernt.', { variant: 'success' }); + this.setState({ + busy: false, + activeListTab: 'stueckliste', + }); + } catch (err) { + if (this._cancelled) return; + notifyError(err); + this.setState({ busy: false }); + } + }; + + renderStuecklisteContent(stuecklisteItems, selectableOtherArticles) { + const { busy, stuecklisteTargetArticle, stuecklisteCount } = this.state; + return ( + + + + + + Stückliste + + + + + + + this.setState({ confirmClearType: 'stueckliste' })} + disabled={busy} + sx={{ p: 0.4, color: 'text.secondary', '&:hover': { color: 'error.main' } }} + > + + + + + + + {/* Add Component Row */} + + this.setState({ stuecklisteTargetArticle: val })} + getOptionLabel={(opt) => + typeof opt === 'string' ? opt : `${opt.sku} - ${opt.name || ''}` + } + isOptionEqualToValue={(opt, val) => opt.sku === val?.sku} + renderInput={(params) => ( + + )} + sx={{ flex: 1, minWidth: 0 }} + /> + + + this.setState({ + stuecklisteCount: Math.max(1, parseInt(e.target.value, 10) || 1), + }) + } + inputProps={{ + min: 1, + style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' }, + }} + sx={{ width: 44, bgcolor: 'background.paper', flexShrink: 0 }} + /> + + + + + {busy ? : } + + + + + + {/* Items List */} + + {stuecklisteItems.length === 0 ? ( + + Noch keine Komponenten in Stückliste. + + ) : ( + stuecklisteItems.map((item) => ( + + + + + + {item.name || item.sku} + + + {item.sku} + + + + + this.handleRemoveStuecklisteItem(item.sku)} + sx={{ + p: 0.25, + color: 'text.secondary', + '&:hover': { color: 'error.main' }, + }} + > + + + + )) + )} + + + ); + } + + renderStuecklisteCreator(selectableOtherArticles, hasOptions) { + const { busy, stuecklisteTargetArticle, stuecklisteCount } = this.state; + return ( + + + Erste Komponente hinzufügen: + + + this.setState({ stuecklisteTargetArticle: val })} + getOptionLabel={(opt) => + typeof opt === 'string' ? opt : `${opt.sku} - ${opt.name || ''}` + } + isOptionEqualToValue={(opt, val) => opt.sku === val?.sku} + renderInput={(params) => ( + + )} + sx={{ width: '100%' }} + /> + + + + this.setState({ + stuecklisteCount: Math.max(1, parseInt(e.target.value, 10) || 1), + }) + } + inputProps={{ + min: 1, + style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' }, + }} + sx={{ width: 70 }} + /> + + + + + + + + ); + } + + renderOptionsContent(optionsItems, selectableOtherArticles) { + const { busy, optionTargetArticle, optionCount, optionMandated } = this.state; + return ( + + + + + + Optionen + + + + + + + this.setState({ confirmClearType: 'options' })} + disabled={busy} + sx={{ p: 0.4, color: 'text.secondary', '&:hover': { color: 'error.main' } }} + > + + + + + + + {/* Add Option Row */} + + this.setState({ optionTargetArticle: val })} + getOptionLabel={(opt) => + typeof opt === 'string' ? opt : `${opt.sku} - ${opt.name || ''}` + } + isOptionEqualToValue={(opt, val) => opt.sku === val?.sku} + renderInput={(params) => ( + + )} + sx={{ flex: 1, minWidth: 0 }} + /> + + + this.setState({ + optionCount: Math.max(1, parseInt(e.target.value, 10) || 1), + }) + } + inputProps={{ + min: 1, + style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' }, + }} + sx={{ width: 44, bgcolor: 'background.paper', flexShrink: 0 }} + /> + + + + + {busy ? : } + + + + + + {/* Mandated checkbox toggle */} + + this.setState({ optionMandated: e.target.checked })} + sx={{ p: 0.5 }} + /> + } + label={ + + Pflicht-Zubehör (mandated) + + } + sx={{ ml: 0.5 }} + /> + + + {/* Options List */} + + {optionsItems.length === 0 ? ( + + Noch keine Optionen hinterlegt. + + ) : ( + optionsItems.map((opt) => ( + + + + + + {opt.name || opt.sku} + + + + {opt.sku} + + {opt.mandated ? ( + + ) : null} + + + + + this.handleRemoveOption(opt.sku)} + sx={{ + p: 0.25, + color: 'text.secondary', + '&:hover': { color: 'error.main' }, + }} + > + + + + )) + )} + + + ); + } + + renderOptionsCreator(selectableOtherArticles, isStueckliste) { + const { busy, optionTargetArticle, optionCount, optionMandated } = this.state; + return ( + + + Erste Option hinzufügen: + + + this.setState({ optionTargetArticle: val })} + getOptionLabel={(opt) => + typeof opt === 'string' ? opt : `${opt.sku} - ${opt.name || ''}` + } + isOptionEqualToValue={(opt, val) => opt.sku === val?.sku} + renderInput={(params) => ( + + )} + sx={{ width: '100%' }} + /> + + + + this.setState({ + optionCount: Math.max(1, parseInt(e.target.value, 10) || 1), + }) + } + inputProps={{ + min: 1, + style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' }, + }} + sx={{ width: 70 }} + /> + + this.setState({ optionMandated: e.target.checked })} + sx={{ p: 0.5 }} + /> + } + label={ + + Pflicht + + } + sx={{ ml: 0 }} + /> + + + + + + + + ); + } + + renderStandardView(selectableAttributes, assignedAttributeIds) { + const { busy, activeCreatorMode, selectedAttributeId, attributes } = this.state; + return ( + + + + Typ: + + + Standard-Artikel + + + + {activeCreatorMode === 'father' ? ( + + {selectableAttributes.length === 0 ? ( + + Alle vorhandenen Merkmale sind diesem Artikel bereits zugewiesen. + + ) : ( + + + Merkmal für Varianten + + + + )} + + + + + + + ) : ( + + + + + + )} + + ); + } + render() { const { article: articleData } = this.props; const { attributes, assignedAttributes, + allArticles, busy, - isActivating, + activeCreatorMode, + activeListTab, selectedAttributeId, selectedChildValueId, newChildText, isCustomChildValue, + stuecklisteTargetArticle, + stuecklisteCount, + optionTargetArticle, + optionCount, + optionMandated, removeTargetChild, + confirmClearType, } = this.state; if (!articleData) return null; @@ -284,6 +1362,12 @@ export default class ArticleViewLists extends PureComponent { const isFather = Boolean(articleData.isFather); const children = Array.isArray(articleData.children) ? articleData.children : []; + const stuecklisteItems = Array.isArray(articleData.articlelist) ? articleData.articlelist : []; + const isStueckliste = articleData.articlelistId != null || stuecklisteItems.length > 0; + + const optionsItems = Array.isArray(articleData.options) ? articleData.options : []; + const hasOptions = articleData.optionslistId != null || optionsItems.length > 0; + // Attributes assigned to this article that cannot be selected for father mode const assignedAttributeIds = new Set( (assignedAttributes || []).map((a) => a.attributeId) @@ -292,6 +1376,9 @@ export default class ArticleViewLists extends PureComponent { (a) => !assignedAttributeIds.has(a.id) ); + // Filter available articles for Autocomplete (cannot select self) + const selectableOtherArticles = allArticles.filter((a) => a.sku !== articleData.sku); + // Find father attribute definition & values const fatherAttrDef = isFather ? attributes.find((a) => a.id === articleData.fatherAttributeNameId) @@ -341,7 +1428,7 @@ export default class ArticleViewLists extends PureComponent { ) : null} - {/* Father Mode or Activation (supported on both root and child articles) */} + {/* ── Mode 1: Father Article ──────────────────────────────────────── */} {isFather ? ( @@ -550,135 +1637,70 @@ export default class ArticleViewLists extends PureComponent { )} - ) : ( - /* Case 3: Regular Article (neither father nor child) */ - - - - Vaterartikel: - - - nein - + ) : isStueckliste || hasOptions || activeCreatorMode === 'stueckliste' || activeCreatorMode === 'options' ? ( + /* ── Combined Compound Manager (Stückliste & Optionen) ─────────── */ + + {/* Sub-tabs: Stückliste & Optionen */} + + + - {!isActivating ? ( - - ) : ( - - {selectableAttributes.length === 0 ? ( - - Alle vorhandenen Merkmale sind diesem Artikel bereits zugewiesen. - - ) : ( - - - Merkmal für Varianten - - - - )} - - - - - - - )} - - )} - - {/* Counts Line at Bottom */} - {(articleData.optionsCount != null || articleData.articleListCount != null) && ( - - {articleData.optionsCount != null && ( - - Optionen: {articleData.optionsCount} - - )} - {articleData.articleListCount != null && ( - - Stücklisten: {articleData.articleListCount} - - )} + {activeListTab === 'stueckliste' + ? isStueckliste + ? this.renderStuecklisteContent(stuecklisteItems, selectableOtherArticles) + : this.renderStuecklisteCreator(selectableOtherArticles, hasOptions) + : hasOptions + ? this.renderOptionsContent(optionsItems, selectableOtherArticles) + : this.renderOptionsCreator(selectableOtherArticles, isStueckliste)} + ) : ( + this.renderStandardView(selectableAttributes, assignedAttributeIds) )} @@ -724,6 +1746,48 @@ export default class ArticleViewLists extends PureComponent { + + {/* Clear Stückliste / Optionen Confirmation Dialog */} + this.setState({ confirmClearType: null })} + maxWidth="xs" + fullWidth + > + + {confirmClearType === 'stueckliste' ? 'Stückliste auflösen?' : 'Optionen entfernen?'} + + + + {confirmClearType === 'stueckliste' + ? (hasOptions + ? 'Möchten Sie alle Komponenten aus dieser Stückliste entfernen? Die Optionen bleiben dabei erhalten.' + : 'Möchten Sie alle Komponenten aus dieser Stückliste entfernen und den Artikel wieder als Standard-Artikel führen?') + : (isStueckliste + ? 'Möchten Sie alle Optionen von diesem Artikel entfernen? Die Stückliste bleibt dabei erhalten.' + : 'Möchten Sie alle Optionen von diesem Artikel entfernen und ihn wieder als Standard-Artikel führen?')} + + + + + + + ); } diff --git a/src/components/article/boxes/ArticleViewPrice.jsx b/src/components/article/boxes/ArticleViewPrice.jsx index 4429ea7..261ef19 100644 --- a/src/components/article/boxes/ArticleViewPrice.jsx +++ b/src/components/article/boxes/ArticleViewPrice.jsx @@ -3,7 +3,7 @@ import Box from '@mui/material/Box'; import InputAdornment from '@mui/material/InputAdornment'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; -import { EditablePanelTile } from 'dashgui/panels'; +import { EditablePanelTile, PanelTile } from 'dashgui/panels'; import ArticlePriceTiersDialog from '../ArticlePriceTiersDialog'; import ArticleUnitEditorDialog from '../ArticleUnitEditorDialog'; import { StorageManagerContext } from '../../../api/storageManagerContext'; @@ -485,6 +485,53 @@ export default class ArticleViewPrice extends PureComponent { render() { const { article: articleData } = this.props; + + if (articleData?.isFather) { + return ( + + + + Preis + + + + Kein Preis + + + Preise werden auf Variantenebene gepflegt + + + + + ); + } + const { company, tiersDialogOpen, unitEditorOpen } = this.state; const priceValue = priceDraftValue(articleData, company); const basePrice = primaryPrice(articleData); diff --git a/src/components/article/boxes/ArticleViewStock.jsx b/src/components/article/boxes/ArticleViewStock.jsx index 01c1c74..e43afcd 100644 --- a/src/components/article/boxes/ArticleViewStock.jsx +++ b/src/components/article/boxes/ArticleViewStock.jsx @@ -31,8 +31,9 @@ export default class ArticleViewStock extends PureComponent { render() { const { article: articleData } = this.props; const { adjustOpen } = this.state; + const isFather = Boolean(articleData?.isFather); const isTrackStock = - articleData?.track_stock !== false && articleData?.track_stock !== 0; + !isFather && articleData?.track_stock !== false && articleData?.track_stock !== 0; const rawWarehouses = Array.isArray(articleData?.warehouses) ? articleData.warehouses @@ -60,12 +61,16 @@ export default class ArticleViewStock extends PureComponent { - {isTrackStock ? ( + {isFather ? ( + + + Kein Bestand + + + Bestand wird auf Variantenebene geführt + + + ) : isTrackStock ? ( - + {!isFather ? ( + + ) : null} ); } diff --git a/src/components/article/tabs/ArticleSupplierTab.jsx b/src/components/article/tabs/ArticleSupplierTab.jsx index b4567a1..e9ad60f 100644 --- a/src/components/article/tabs/ArticleSupplierTab.jsx +++ b/src/components/article/tabs/ArticleSupplierTab.jsx @@ -197,6 +197,33 @@ export default class ArticleSupplierTab extends PureComponent { busyId, } = this.state; + if (this.props.article?.isFather) { + return ( + + + + Lieferanten sind für Vaterartikel deaktiviert + + + Lieferanten, Einkaufspreise und Lieferanten-Artikelnummern werden für jede Variante separat gepflegt. + + + ); + } + if (loading) { return ( diff --git a/src/components/tiles/ArticleTile.jsx b/src/components/tiles/ArticleTile.jsx index 2f6b2d3..7044c6b 100644 --- a/src/components/tiles/ArticleTile.jsx +++ b/src/components/tiles/ArticleTile.jsx @@ -53,6 +53,33 @@ export class ArticleTile extends Tile { ); } + renderExtra() { + const childCount = Number(this.props.article?.childCount) || 0; + if (childCount <= 0) return null; + return ( + + {childCount} + + ); + } + render() { if (!this.hasPhoto()) { return super.render(); @@ -87,6 +114,7 @@ export class ArticleTile extends Tile { }} /> + {this.renderExtra()}