diff --git a/client/src/components/admin/KreditorTable.js b/client/src/components/admin/KreditorTable.js index 0968922..5c309c7 100644 --- a/client/src/components/admin/KreditorTable.js +++ b/client/src/components/admin/KreditorTable.js @@ -196,7 +196,7 @@ class KreditorTable extends Component { }, 100); try { - const response = await this.authService.apiCall(`/admin/kreditoren/${kreditor.id}`, { + const response = await this.authService.apiCall(`/admin/kreditoren/${itemToDelete.id}`, { method: 'DELETE', }); diff --git a/client/src/components/cellRenderers/DocumentRenderer.js b/client/src/components/cellRenderers/DocumentRenderer.js index 157e642..befebf3 100644 --- a/client/src/components/cellRenderers/DocumentRenderer.js +++ b/client/src/components/cellRenderers/DocumentRenderer.js @@ -350,7 +350,15 @@ const DocumentRenderer = (params) => { aria-describedby="document-dialog-content" > - {hasDocuments ? `Dokumente (${totalCount})` : 'Dokumentinformationen'} + {(() => { + const beschreibung = params?.data?.description || ''; + const beschreibungTrimmed = beschreibung ? String(beschreibung).slice(0, 120) : ''; + if (hasDocuments) { + return `Dokumente (${totalCount})${beschreibungTrimmed ? ' — ' + beschreibungTrimmed : ''}`; + } + // No documents: still include description next to "Dokumentinformationen" + return `Dokumentinformationen${beschreibungTrimmed ? ' — ' + beschreibungTrimmed : ''}`; + })()} {error && ( @@ -367,13 +375,13 @@ const DocumentRenderer = (params) => { sx={{ color: !params.data['Kontonummer/IBAN'] ? 'text.secondary' - : params.data.hasKreditor + : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking) ? 'success.main' : 'warning.main', '&.Mui-selected': { color: !params.data['Kontonummer/IBAN'] ? 'text.secondary' - : params.data.hasKreditor + : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking) ? 'success.main' : 'warning.main', } diff --git a/client/src/services/KreditorService.js b/client/src/services/KreditorService.js index a8ba4e6..59baa6c 100644 --- a/client/src/services/KreditorService.js +++ b/client/src/services/KreditorService.js @@ -1,5 +1,6 @@ class KreditorService { constructor() { + // API is mounted under /api (see src/index.js). Keep consistent with AuthService. this.baseURL = '/api'; } @@ -45,7 +46,8 @@ class KreditorService { async getAllKreditors() { try { - const response = await fetch(`${this.baseURL}/data/kreditors`, { + const url = `${this.baseURL}/data/kreditors`; + const response = await fetch(url, { method: 'GET', headers: await this.getAuthHeaders(), }); @@ -53,19 +55,23 @@ class KreditorService { return await this.handleResponse(response); } catch (error) { console.error('Error fetching kreditors:', error); - - // Handle network errors if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut'); } - throw error; } } + // Convenience: find kreditor by kreditorId (string compare) + async findKreditorByCode(kreditorId) { + const all = await this.getAllKreditors(); + return (all || []).find(k => String(k.kreditorId).trim() === String(kreditorId).trim()); + } + async getKreditorById(id) { try { - const response = await fetch(`${this.baseURL}/data/kreditors/${id}`, { + const url = `${this.baseURL}/data/kreditors/${id}`; + const response = await fetch(url, { method: 'GET', headers: await this.getAuthHeaders(), }); @@ -73,19 +79,18 @@ class KreditorService { return await this.handleResponse(response); } catch (error) { console.error('Error fetching kreditor:', error); - - // Handle network errors if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut'); } - throw error; } } async createKreditor(kreditorData) { try { - const response = await fetch(`${this.baseURL}/data/kreditors`, { + const url = `${this.baseURL}/data/kreditors`; + console.debug('[KreditorService] POST', url, kreditorData); + const response = await fetch(url, { method: 'POST', headers: await this.getAuthHeaders(), body: JSON.stringify(kreditorData), @@ -93,20 +98,23 @@ class KreditorService { return await this.handleResponse(response); } catch (error) { + // Map unique constraint errors to a clearer duplicate message when possible + const msg = (error && error.message) || ''; + if (/unique|bereits vorhanden|already exists|kreditor/i.test(msg)) { + throw new Error('Kreditor bereits vorhanden'); + } console.error('Error creating kreditor:', error); - - // Handle network errors if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut'); } - throw error; } } async updateKreditor(id, kreditorData) { try { - const response = await fetch(`${this.baseURL}/data/kreditors/${id}`, { + const url = `${this.baseURL}/data/kreditors/${id}`; + const response = await fetch(url, { method: 'PUT', headers: await this.getAuthHeaders(), body: JSON.stringify(kreditorData), @@ -115,19 +123,17 @@ class KreditorService { return await this.handleResponse(response); } catch (error) { console.error('Error updating kreditor:', error); - - // Handle network errors if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut'); } - throw error; } } async deleteKreditor(id) { try { - const response = await fetch(`${this.baseURL}/data/kreditors/${id}`, { + const url = `${this.baseURL}/data/kreditors/${id}`; + const response = await fetch(url, { method: 'DELETE', headers: await this.getAuthHeaders(), }); @@ -135,12 +141,9 @@ class KreditorService { return await this.handleResponse(response); } catch (error) { console.error('Error deleting kreditor:', error); - - // Handle network errors if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut'); } - throw error; } } diff --git a/src/routes/admin.js b/src/routes/admin.js index fe0d0fe..1a93a11 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -270,4 +270,4 @@ router.delete('/buchungsschluessel/:id', authenticateToken, async (req, res) => } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/routes/data/bankingTransactions.js b/src/routes/data/bankingTransactions.js index 20460b8..6344277 100644 --- a/src/routes/data/bankingTransactions.js +++ b/src/routes/data/bankingTransactions.js @@ -143,8 +143,8 @@ router.get('/assignable-kreditors', authenticateToken, async (req, res) => { const query = ` SELECT id, name, kreditorId - FROM fibdash.Kreditor - WHERE is_banking = 0 + FROM fibdash.Kreditor + WHERE (is_banking = 0 OR is_banking IS NULL) ORDER BY name `; diff --git a/src/routes/data/kreditors.js b/src/routes/data/kreditors.js index 91398ef..0adb5df 100644 --- a/src/routes/data/kreditors.js +++ b/src/routes/data/kreditors.js @@ -58,8 +58,9 @@ router.post('/kreditors', authenticateToken, async (req, res) => { return res.status(400).json({ error: 'Name and kreditorId are required' }); } - if (!isBanking && (!iban || iban.trim() === '')) { - return res.status(400).json({ error: 'IBAN is required (except for banking accounts)' }); + // Business rule: IBAN is required for banking kreditors (proxies), not required for real kreditors + if (isBanking && (!iban || iban.trim() === '')) { + return res.status(400).json({ error: 'IBAN is required for banking kreditors' }); } if (iban && iban.trim() !== '') { @@ -108,8 +109,9 @@ router.put('/kreditors/:id', authenticateToken, async (req, res) => { return res.status(400).json({ error: 'Name and kreditorId are required' }); } - if (!isBanking && (!iban || iban.trim() === '')) { - return res.status(400).json({ error: 'IBAN is required (except for banking accounts)' }); + // Business rule: IBAN is required for banking kreditors (proxies), not required for real kreditors + if (isBanking && (!iban || iban.trim() === '')) { + return res.status(400).json({ error: 'IBAN is required for banking kreditors' }); } const checkQuery = `SELECT id FROM fibdash.Kreditor WHERE id = @id`;