This commit is contained in:
seb
2026-09-30 09:47:47 +02:00
parent d8f4727f32
commit 2f2906b8b5
11 changed files with 1479 additions and 162 deletions

View File

@@ -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 {
<Tab
key={row.id}
value={row.id}
disabled={Boolean(row.disabled)}
aria-label={row.label}
icon={
<>
@@ -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',
},
}}
/>
);

View File

@@ -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) {

File diff suppressed because it is too large Load Diff

View File

@@ -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 (
<PanelTile
wide={1}
minHeight={SECOND_ROW_MIN_HEIGHT}
color="#e2e8f0"
contentAlignItems="flex-start"
>
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }}>
<Typography
variant="caption"
color="text.secondary"
sx={{
textTransform: 'uppercase',
letterSpacing: 0.6,
fontWeight: 600,
display: 'block',
}}
>
Preis
</Typography>
<Box sx={{ my: 0.75 }}>
<Typography
component="div"
sx={{
fontSize: '1.25rem',
fontWeight: 700,
lineHeight: 1.2,
color: 'text.secondary',
}}
>
Kein Preis
</Typography>
<Typography
variant="caption"
color="text.secondary"
sx={{ display: 'block', mt: 0.25 }}
>
Preise werden auf Variantenebene gepflegt
</Typography>
</Box>
</Box>
</PanelTile>
);
}
const { company, tiersDialogOpen, unitEditorOpen } = this.state;
const priceValue = priceDraftValue(articleData, company);
const basePrice = primaryPrice(articleData);

View File

@@ -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 {
<PanelTile
wide={1}
minHeight={SECOND_ROW_MIN_HEIGHT}
color="#bbf7d0"
action={{
label: 'Bestand anpassen',
icon: EditOutlinedIcon,
onClick: this.handleOpenAdjust,
}}
color={isFather ? '#e2e8f0' : '#bbf7d0'}
action={
isFather
? null
: {
label: 'Bestand anpassen',
icon: EditOutlinedIcon,
onClick: this.handleOpenAdjust,
}
}
>
<Box
sx={{
@@ -90,7 +95,28 @@ export default class ArticleViewStock extends PureComponent {
Bestand
</Typography>
{isTrackStock ? (
{isFather ? (
<Box sx={{ my: 0.75 }}>
<Typography
component="div"
sx={{
fontSize: '1.25rem',
fontWeight: 700,
lineHeight: 1.2,
color: 'text.secondary',
}}
>
Kein Bestand
</Typography>
<Typography
variant="caption"
color="text.secondary"
sx={{ display: 'block', mt: 0.25 }}
>
Bestand wird auf Variantenebene geführt
</Typography>
</Box>
) : isTrackStock ? (
<Tooltip title="Klicken, um Bestand anzupassen" arrow placement="top-start">
<Box
onClick={this.handleOpenAdjust}
@@ -251,11 +277,13 @@ export default class ArticleViewStock extends PureComponent {
</Box>
</PanelTile>
<ArticleStockAdjustDialog
open={adjustOpen}
onClose={this.handleCloseAdjust}
article={articleData}
/>
{!isFather ? (
<ArticleStockAdjustDialog
open={adjustOpen}
onClose={this.handleCloseAdjust}
article={articleData}
/>
) : null}
</>
);
}

View File

@@ -197,6 +197,33 @@ export default class ArticleSupplierTab extends PureComponent {
busyId,
} = this.state;
if (this.props.article?.isFather) {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
py: 6,
px: 2,
borderRadius: 2,
border: '1px dashed rgba(0, 0, 0, 0.15)',
bgcolor: 'rgba(0, 0, 0, 0.01)',
textAlign: 'center',
}}
>
<LocalShippingOutlinedIcon sx={{ fontSize: 48, mb: 1.5, color: 'text.secondary', opacity: 0.4 }} />
<Typography variant="subtitle2" sx={{ fontWeight: 600, mb: 0.5, color: 'text.secondary' }}>
Lieferanten sind für Vaterartikel deaktiviert
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ maxWidth: 400 }}>
Lieferanten, Einkaufspreise und Lieferanten-Artikelnummern werden für jede Variante separat gepflegt.
</Typography>
</Box>
);
}
if (loading) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', py: 6 }}>