This commit is contained in:
sebseb7
2025-08-02 08:26:08 +02:00
parent 5c416c77f0
commit da435d2e66
6 changed files with 44 additions and 31 deletions

View File

@@ -196,7 +196,7 @@ class KreditorTable extends Component {
}, 100); }, 100);
try { try {
const response = await this.authService.apiCall(`/admin/kreditoren/${kreditor.id}`, { const response = await this.authService.apiCall(`/admin/kreditoren/${itemToDelete.id}`, {
method: 'DELETE', method: 'DELETE',
}); });

View File

@@ -350,7 +350,15 @@ const DocumentRenderer = (params) => {
aria-describedby="document-dialog-content" aria-describedby="document-dialog-content"
> >
<DialogTitle id="document-dialog-title"> <DialogTitle id="document-dialog-title">
{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 : ''}`;
})()}
</DialogTitle> </DialogTitle>
<DialogContent sx={{ p: 0 }} id="document-dialog-content"> <DialogContent sx={{ p: 0 }} id="document-dialog-content">
{error && ( {error && (
@@ -367,13 +375,13 @@ const DocumentRenderer = (params) => {
sx={{ sx={{
color: !params.data['Kontonummer/IBAN'] color: !params.data['Kontonummer/IBAN']
? 'text.secondary' ? 'text.secondary'
: params.data.hasKreditor : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking)
? 'success.main' ? 'success.main'
: 'warning.main', : 'warning.main',
'&.Mui-selected': { '&.Mui-selected': {
color: !params.data['Kontonummer/IBAN'] color: !params.data['Kontonummer/IBAN']
? 'text.secondary' ? 'text.secondary'
: params.data.hasKreditor : (params.data.hasKreditor && params.data.kreditor && !params.data.kreditor.is_banking)
? 'success.main' ? 'success.main'
: 'warning.main', : 'warning.main',
} }

View File

@@ -1,5 +1,6 @@
class KreditorService { class KreditorService {
constructor() { constructor() {
// API is mounted under /api (see src/index.js). Keep consistent with AuthService.
this.baseURL = '/api'; this.baseURL = '/api';
} }
@@ -45,7 +46,8 @@ class KreditorService {
async getAllKreditors() { async getAllKreditors() {
try { try {
const response = await fetch(`${this.baseURL}/data/kreditors`, { const url = `${this.baseURL}/data/kreditors`;
const response = await fetch(url, {
method: 'GET', method: 'GET',
headers: await this.getAuthHeaders(), headers: await this.getAuthHeaders(),
}); });
@@ -53,19 +55,23 @@ class KreditorService {
return await this.handleResponse(response); return await this.handleResponse(response);
} catch (error) { } catch (error) {
console.error('Error fetching kreditors:', error); console.error('Error fetching kreditors:', error);
// Handle network errors
if (error instanceof TypeError && error.message.includes('fetch')) { 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 new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut');
} }
throw error; 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) { async getKreditorById(id) {
try { try {
const response = await fetch(`${this.baseURL}/data/kreditors/${id}`, { const url = `${this.baseURL}/data/kreditors/${id}`;
const response = await fetch(url, {
method: 'GET', method: 'GET',
headers: await this.getAuthHeaders(), headers: await this.getAuthHeaders(),
}); });
@@ -73,19 +79,18 @@ class KreditorService {
return await this.handleResponse(response); return await this.handleResponse(response);
} catch (error) { } catch (error) {
console.error('Error fetching kreditor:', error); console.error('Error fetching kreditor:', error);
// Handle network errors
if (error instanceof TypeError && error.message.includes('fetch')) { 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 new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut');
} }
throw error; throw error;
} }
} }
async createKreditor(kreditorData) { async createKreditor(kreditorData) {
try { 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', method: 'POST',
headers: await this.getAuthHeaders(), headers: await this.getAuthHeaders(),
body: JSON.stringify(kreditorData), body: JSON.stringify(kreditorData),
@@ -93,20 +98,23 @@ class KreditorService {
return await this.handleResponse(response); return await this.handleResponse(response);
} catch (error) { } 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); console.error('Error creating kreditor:', error);
// Handle network errors
if (error instanceof TypeError && error.message.includes('fetch')) { 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 new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut');
} }
throw error; throw error;
} }
} }
async updateKreditor(id, kreditorData) { async updateKreditor(id, kreditorData) {
try { try {
const response = await fetch(`${this.baseURL}/data/kreditors/${id}`, { const url = `${this.baseURL}/data/kreditors/${id}`;
const response = await fetch(url, {
method: 'PUT', method: 'PUT',
headers: await this.getAuthHeaders(), headers: await this.getAuthHeaders(),
body: JSON.stringify(kreditorData), body: JSON.stringify(kreditorData),
@@ -115,19 +123,17 @@ class KreditorService {
return await this.handleResponse(response); return await this.handleResponse(response);
} catch (error) { } catch (error) {
console.error('Error updating kreditor:', error); console.error('Error updating kreditor:', error);
// Handle network errors
if (error instanceof TypeError && error.message.includes('fetch')) { 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 new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut');
} }
throw error; throw error;
} }
} }
async deleteKreditor(id) { async deleteKreditor(id) {
try { try {
const response = await fetch(`${this.baseURL}/data/kreditors/${id}`, { const url = `${this.baseURL}/data/kreditors/${id}`;
const response = await fetch(url, {
method: 'DELETE', method: 'DELETE',
headers: await this.getAuthHeaders(), headers: await this.getAuthHeaders(),
}); });
@@ -135,12 +141,9 @@ class KreditorService {
return await this.handleResponse(response); return await this.handleResponse(response);
} catch (error) { } catch (error) {
console.error('Error deleting kreditor:', error); console.error('Error deleting kreditor:', error);
// Handle network errors
if (error instanceof TypeError && error.message.includes('fetch')) { 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 new Error('FibDash Service nicht erreichbar - Prüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut');
} }
throw error; throw error;
} }
} }

View File

@@ -270,4 +270,4 @@ router.delete('/buchungsschluessel/:id', authenticateToken, async (req, res) =>
} }
}); });
module.exports = router; module.exports = router;

View File

@@ -143,8 +143,8 @@ router.get('/assignable-kreditors', authenticateToken, async (req, res) => {
const query = ` const query = `
SELECT id, name, kreditorId SELECT id, name, kreditorId
FROM fibdash.Kreditor FROM fibdash.Kreditor
WHERE is_banking = 0 WHERE (is_banking = 0 OR is_banking IS NULL)
ORDER BY name ORDER BY name
`; `;

View File

@@ -58,8 +58,9 @@ router.post('/kreditors', authenticateToken, async (req, res) => {
return res.status(400).json({ error: 'Name and kreditorId are required' }); return res.status(400).json({ error: 'Name and kreditorId are required' });
} }
if (!isBanking && (!iban || iban.trim() === '')) { // Business rule: IBAN is required for banking kreditors (proxies), not required for real kreditors
return res.status(400).json({ error: 'IBAN is required (except for banking accounts)' }); if (isBanking && (!iban || iban.trim() === '')) {
return res.status(400).json({ error: 'IBAN is required for banking kreditors' });
} }
if (iban && iban.trim() !== '') { 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' }); return res.status(400).json({ error: 'Name and kreditorId are required' });
} }
if (!isBanking && (!iban || iban.trim() === '')) { // Business rule: IBAN is required for banking kreditors (proxies), not required for real kreditors
return res.status(400).json({ error: 'IBAN is required (except for banking accounts)' }); 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`; const checkQuery = `SELECT id FROM fibdash.Kreditor WHERE id = @id`;