import { Component } from 'react'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; import Divider from '@mui/material/Divider'; import FormControl from '@mui/material/FormControl'; import FormLabel from '@mui/material/FormLabel'; import IconButton from '@mui/material/IconButton'; import InputAdornment from '@mui/material/InputAdornment'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; import ListItemText from '@mui/material/ListItemText'; import Stack from '@mui/material/Stack'; import Switch from '@mui/material/Switch'; import TextField from '@mui/material/TextField'; import ToggleButton from '@mui/material/ToggleButton'; import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined'; import PointOfSaleIcon from '@mui/icons-material/PointOfSale'; import { enqueueSnackbar } from 'notistack'; import BackendError from './BackendError'; import SingleLineTextEdit from './SingleLineTextEdit'; import { StorageManagerContext } from '../api/storageManager'; import { requireDefined, requireObject } from '../utils/apiShape'; import { ISO639_OPTIONS, parseTaxRate } from '../utils/companyForm'; import { backendErrorFrom, isBackendFailure } from '../utils/backendError'; import { notifyError } from '../utils/notify'; export default class CompanySettingsDialog extends Component { static contextType = StorageManagerContext; constructor(props) { super(props); this.state = { busy: false, error: null, name: '', iso639: 'de', taxrate: '', reducedtaxrate: '', newTerminalName: '', creatingTerminal: false, enableAllForNew: true, terminals: [], }; this.handleSubmit = this.handleSubmit.bind(this); this.syncFormFromCompany = this.syncFormFromCompany.bind(this); this.handleCreateTerminal = this.handleCreateTerminal.bind(this); this.handleDeleteTerminal = this.handleDeleteTerminal.bind(this); this.loadCompany = this.loadCompany.bind(this); this.loadTerminals = this.loadTerminals.bind(this); } componentDidUpdate(prevProps) { const { open } = this.props; if (open && !prevProps.open) { this.setState({ error: null }); void this.loadCompany(); void this.loadTerminals(); } } syncFormFromCompany(companyRow) { this.setState({ name: companyRow.name, iso639: companyRow.iso639, taxrate: companyRow.taxrate != null ? String(companyRow.taxrate) : '', reducedtaxrate: companyRow.reducedtaxrate != null ? String(companyRow.reducedtaxrate) : '', }); } async loadCompany() { try { const session = await this.context.authMePromise; const companyId = requireDefined(session.user?.selectedCompanyId, 'selectedCompanyId'); const companies = await this.context.companyPromise; const companyRow = requireDefined( companies.find((c) => c.id === companyId), 'company', ); this.syncFormFromCompany(companyRow); } catch (err) { notifyError(err); } } async loadTerminals() { try { const terminals = await this.context.posterminalPromise; this.setState({ terminals }); } catch (err) { notifyError(err); } } async handleCreateTerminal() { const { newTerminalName, enableAllForNew } = this.state; const trimmed = newTerminalName.trim(); this.setState({ creatingTerminal: true }); try { const data = await this.context.posterminal.upsert({ name: trimmed }); requireObject(data, 'PUT /api/posterminal'); requireObject(data.posterminal, 'posterminal'); requireDefined(data.posterminal.id, 'posterminal.id'); if (enableAllForNew) { await this.context.posterminal.setOnAll(data.posterminal.id); await this.context.updateCategoryTree(); } const terminals = await this.context.updatePosterminal(); this.setState({ newTerminalName: '', terminals }); } catch (err) { notifyError(err); } finally { this.setState({ creatingTerminal: false }); } } async handleDeleteTerminal(terminal) { try { await this.context.posterminal.delete(terminal.id); const terminals = await this.context.updatePosterminal(); this.setState({ terminals }); } catch (err) { notifyError(err); } } async handleSubmit(event) { event.preventDefault(); const { onClose } = this.props; const { name, iso639, taxrate, reducedtaxrate } = this.state; const parsedTaxrate = parseTaxRate(taxrate); const parsedReducedtaxrate = parseTaxRate(reducedtaxrate); const canSave = name.trim() !== '' && ISO639_OPTIONS.includes(iso639) && parsedTaxrate != null && parsedReducedtaxrate != null; if (!canSave) { this.setState({ error: 'Bitte alle Pflichtfelder ausfüllen.' }); return; } this.setState({ busy: true, error: null }); try { const session = await this.context.authMePromise; const companyId = requireDefined(session.user?.selectedCompanyId, 'selectedCompanyId'); await this.context.company.update(companyId, { name: name.trim(), iso639, taxrate: parsedTaxrate, reducedtaxrate: parsedReducedtaxrate, }); await this.context.updateCompany(); enqueueSnackbar('Unternehmen gespeichert.', { variant: 'success' }); onClose(); } catch (err) { this.setState({ error: backendErrorFrom(err) }); } finally { this.setState({ busy: false }); } } render() { const { open, onClose } = this.props; const { busy, error, name, iso639, taxrate, reducedtaxrate } = this.state; const parsedTaxrate = parseTaxRate(taxrate); const parsedReducedtaxrate = parseTaxRate(reducedtaxrate); const canSave = name.trim() !== '' && ISO639_OPTIONS.includes(iso639) && parsedTaxrate != null && parsedReducedtaxrate != null; return ( Unternehmen bearbeiten {error ? ( isBackendFailure(error) ? ( ) : ( {typeof error === 'string' ? error : error.message} ) ) : null} this.setState({ name: nextName })} autoComplete="off" autoFocus /> Sprache { if (value) this.setState({ iso639: value }); }} > Deutsch English this.setState({ taxrate: e.target.value })} error={taxrate !== '' && parsedTaxrate == null} slotProps={{ input: { endAdornment: %, }, htmlInput: { min: 0, max: 100, step: 0.01, required: true }, }} /> this.setState({ reducedtaxrate: e.target.value })} error={reducedtaxrate !== '' && parsedReducedtaxrate == null} slotProps={{ input: { endAdornment: %, }, htmlInput: { min: 0, max: 100, step: 0.01, required: true }, }} /> {this.state.terminals.map((terminal) => ( this.handleDeleteTerminal(terminal)} disabled={busy || this.state.creatingTerminal} > ))} this.setState({ newTerminalName: e.target.value })} disabled={busy || this.state.creatingTerminal} autoComplete="off" onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); this.handleCreateTerminal(); } }} sx={{ flex: 1 }} /> this.setState({ enableAllForNew: e.target.checked })} disabled={busy || this.state.creatingTerminal} /> ); } }