import React, { Component } from 'react'; import { Box, Typography, Button, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, TextField, Select, MenuItem, FormControl, InputLabel, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Alert, Chip } from '@mui/material'; import { Add as AddIcon, Delete as DeleteIcon, Edit as EditIcon, Save as SaveIcon, Cancel as CancelIcon } from '@mui/icons-material'; import AuthService from '../services/AuthService'; class AccountingItemsManager extends Component { constructor(props) { super(props); this.state = { accountingItems: [], kontos: [], bus: [], loading: true, editingItem: null, showCreateDialog: false, showCreateKontoDialog: false, jtlKontierung: null, newItem: { umsatz_brutto: '', soll_haben_kz: 'S', konto: '', bu: '', rechnungsnummer: '', buchungstext: '' }, newKonto: { konto: '', name: '' }, error: null, saving: false }; this.authService = new AuthService(); } componentDidMount() { this.loadData(); this.loadJtlKontierung(); } loadData = async () => { try { // Load accounting items for this transaction await this.loadAccountingItems(); // Load Konto and BU options await Promise.all([ this.loadKontos(), this.loadBUs() ]); this.setState({ loading: false }); } catch (error) { console.error('Error loading data:', error); this.setState({ error: 'Fehler beim Laden der Daten', loading: false }); } }; loadJtlKontierung = async () => { try { const { transaction } = this.props; if (!transaction || !transaction.jtlId) { this.setState({ jtlKontierung: undefined }); return; } const response = await this.authService.apiCall(`/data/jtl-kontierung/${transaction.jtlId}`); if (!response) return; if (response.ok) { const data = await response.json(); this.setState({ jtlKontierung: data }); } else { const err = await response.json(); console.error('Failed to load JTL Kontierung:', err); this.setState({ jtlKontierung: undefined }); } } catch (e) { console.error('Error loading JTL Kontierung:', e); this.setState({ jtlKontierung: undefined }); } } loadAccountingItems = async () => { const { transaction } = this.props; if (!transaction?.id) return; try { const response = await this.authService.apiCall(`/data/accounting-items/${transaction.id}`); if (response && response.ok) { const items = await response.json(); this.setState({ accountingItems: items }); } } catch (error) { console.error('Error loading accounting items:', error); } }; loadKontos = async () => { try { const response = await this.authService.apiCall('/data/kontos'); if (response && response.ok) { const kontos = await response.json(); this.setState({ kontos }); } } catch (error) { console.error('Error loading kontos:', error); } }; loadBUs = async () => { try { const response = await this.authService.apiCall('/data/bus'); if (response && response.ok) { const bus = await response.json(); this.setState({ bus }); } } catch (error) { console.error('Error loading BUs:', error); } }; handleCreateItem = () => { const { transaction } = this.props; this.setState({ showCreateDialog: true, newItem: { umsatz_brutto: Math.abs(transaction.numericAmount || 0).toString(), soll_haben_kz: (transaction.numericAmount || 0) >= 0 ? 'H' : 'S', konto: '', bu: '', rechnungsnummer: '', buchungstext: transaction.description || '' } }); }; handleSaveItem = async () => { const { transaction } = this.props; const { newItem } = this.state; if (!newItem.umsatz_brutto || !newItem.konto) { this.setState({ error: 'Betrag und Konto sind erforderlich' }); return; } this.setState({ saving: true, error: null }); try { const itemData = { ...newItem, transaction_id: transaction.isFromCSV ? null : transaction.id, csv_transaction_id: transaction.isFromCSV ? transaction.id : null, buchungsdatum: transaction.parsed_date || new Date().toISOString().split('T')[0] }; const response = await this.authService.apiCall('/data/accounting-items', { method: 'POST', body: JSON.stringify(itemData) }); if (response && response.ok) { await this.loadAccountingItems(); this.setState({ showCreateDialog: false, saving: false, newItem: { umsatz_brutto: '', soll_haben_kz: 'S', konto: '', bu: '', rechnungsnummer: '', buchungstext: '' } }); } else { const errorData = await response.json(); this.setState({ error: errorData.error || 'Fehler beim Speichern', saving: false }); } } catch (error) { console.error('Error saving accounting item:', error); this.setState({ error: 'Fehler beim Speichern', saving: false }); } }; handleCreateKonto = async () => { const { newKonto } = this.state; if (!newKonto.konto || !newKonto.name) { this.setState({ error: 'Konto-Nummer und Name sind erforderlich' }); return; } this.setState({ saving: true, error: null }); try { const response = await this.authService.apiCall('/data/kontos', { method: 'POST', body: JSON.stringify(newKonto) }); if (response && response.ok) { await this.loadKontos(); this.setState({ showCreateKontoDialog: false, saving: false, newKonto: { konto: '', name: '' } }); } else { const errorData = await response.json(); this.setState({ error: errorData.error || 'Fehler beim Erstellen des Kontos', saving: false }); } } catch (error) { console.error('Error creating konto:', error); this.setState({ error: 'Fehler beim Erstellen des Kontos', saving: false }); } }; handleDeleteItem = async (itemId) => { if (!window.confirm('Buchungsposten wirklich löschen?')) return; try { const response = await this.authService.apiCall(`/data/accounting-items/${itemId}`, { method: 'DELETE' }); if (response && response.ok) { await this.loadAccountingItems(); } } catch (error) { console.error('Error deleting accounting item:', error); this.setState({ error: 'Fehler beim Löschen' }); } }; calculateTotal = () => { return this.state.accountingItems.reduce((sum, item) => { const amount = parseFloat(item.umsatz_brutto) || 0; return sum + (item.soll_haben_kz === 'S' ? amount : -amount); }, 0); }; render() { const { transaction } = this.props; const { accountingItems, kontos, bus, loading, showCreateDialog, showCreateKontoDialog, newItem, newKonto, error, saving } = this.state; if (loading) { return Lade Buchungsdaten...; } const transactionAmount = transaction.numericAmount || 0; const currentTotal = this.calculateTotal(); const isBalanced = Math.abs(currentTotal - Math.abs(transactionAmount)) < 0.01; return ( Buchungsposten {error && ( {error} )} Transaktionsbetrag: {Math.abs(transactionAmount).toFixed(2)} € Summe Buchungsposten: {Math.abs(currentTotal).toFixed(2)} € {transaction?.jtlId && ( Debug: tUmsatzKontierung.data {this.state.jtlKontierung === undefined ? 'undefined' : this.state.jtlKontierung === null ? 'null' : typeof this.state.jtlKontierung === 'object' ? JSON.stringify(this.state.jtlKontierung, null, 2) : String(this.state.jtlKontierung)} )} Betrag S/H Konto BU Buchungstext Aktionen {accountingItems.map((item) => ( {parseFloat(item.umsatz_brutto).toFixed(2)} € {item.konto} - {item.konto_name} {item.bu ? `${item.bu} - ${item.bu_name}` : '-'} {item.buchungstext || '-'} this.handleDeleteItem(item.id)} color="error" > ))} {accountingItems.length === 0 && ( Keine Buchungsposten vorhanden )}
{/* Create Item Dialog */} this.setState({ showCreateDialog: false })} maxWidth="sm" fullWidth> Neuen Buchungsposten erstellen this.setState({ newItem: { ...newItem, umsatz_brutto: e.target.value } })} required fullWidth /> Soll/Haben Konto BU (Steuercode) this.setState({ newItem: { ...newItem, buchungstext: e.target.value } })} fullWidth multiline rows={2} /> {/* Create Konto Dialog */} this.setState({ showCreateKontoDialog: false })} maxWidth="xs" fullWidth> Neues Konto erstellen this.setState({ newKonto: { ...newKonto, konto: e.target.value } })} required fullWidth /> this.setState({ newKonto: { ...newKonto, name: e.target.value } })} required fullWidth />
); } } export default AccountingItemsManager;