Files
buchhaltung/backend/routes/customers.js
T
root 0f8475ce87 Backend: Neue API-Endpunkte hinzugefügt
- /api/dashboard/* - Dashboard Übersicht, Cashflow, Steuer-Preview
- /api/customers/* - Kunden CRUD API
- /api/invoices/* - Rechnungen CRUD API + Zahlungen

Server.js angepasst um neue Routen einzubinden.
2026-05-07 13:11:10 +00:00

207 lines
6.1 KiB
JavaScript

const express = require('express');
const { Pool } = require('pg');
const { v4: uuidv4 } = require('uuid');
const router = express.Router();
// Database pool
const pool = new Pool({
host: process.env.DB_HOST || 'db',
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME || 'steuer',
user: process.env.DB_USER || 'app',
password: process.env.DB_PASSWORD || 'app123',
});
// GET /api/customers - Alle Kunden
router.get('/', async (req, res) => {
try {
const { search, limit = 100 } = req.query;
let query = `
SELECT k.*,
(SELECT COUNT(*) FROM rechnungen WHERE kunde = k.name) as rechnungen_count,
(SELECT COALESCE(SUM(betrag), 0) FROM rechnungen WHERE kunde = k.name AND status = 'bezahlt') as total_umsatz
FROM kunden k
`;
const values = [];
if (search) {
query += ` WHERE k.name ILIKE $1 OR k.email ILIKE $1 OR k.adresse ILIKE $1`;
values.push(`%${search}%`);
}
query += ` ORDER BY k.name ASC LIMIT $${values.length + 1}`;
values.push(limit);
const result = await pool.query(query, values);
res.json({
success: true,
customers: result.rows.map(row => ({
...row,
rechnungen_count: parseInt(row.rechnungen_count) || 0,
total_umsatz: parseFloat(row.total_umsatz) || 0
}))
});
} catch (error) {
console.error('Customers List Error:', error);
res.status(500).json({ success: false, error: error.message });
}
});
// GET /api/customers/:id - Einzelner Kunde
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
// Kunde + Rechnungen
const [kundeResult, rechnungenResult] = await Promise.all([
pool.query('SELECT * FROM kunden WHERE id = $1', [id]),
pool.query(`
SELECT * FROM rechnungen
WHERE kunde = (SELECT name FROM kunden WHERE id = $1)
ORDER BY datum DESC
`, [id])
]);
if (kundeResult.rows.length === 0) {
return res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
}
const kunde = kundeResult.rows[0];
const rechnungen = rechnungenResult.rows;
// Zusammenfassung
const totalUmsatz = rechnungen.reduce((sum, r) => sum + parseFloat(r.betrag || 0), 0);
const paidUmsatz = rechnungen
.filter(r => r.status === 'bezahlt')
.reduce((sum, r) => sum + parseFloat(r.betrag || 0), 0);
const openUmsatz = rechnungen
.filter(r => r.status === 'offen')
.reduce((sum, r) => sum + parseFloat(r.betrag || 0), 0);
res.json({
success: true,
customer: {
...kunde,
rechnungen,
summary: {
total_rechnungen: rechnungen.length,
total_umsatz: totalUmsatz,
paid_umsatz: paidUmsatz,
open_umsatz: openUmsatz
}
}
});
} catch (error) {
console.error('Customer Detail Error:', error);
res.status(500).json({ success: false, error: error.message });
}
});
// POST /api/customers - Kunde erstellen
router.post('/', async (req, res) => {
try {
const { name, adresse, plz, ort, email, telefon, notizen } = req.body;
if (!name) {
return res.status(400).json({ success: false, error: 'Name ist erforderlich' });
}
const query = `
INSERT INTO kunden (id, name, adresse, plz, ort, email, telefon, notizen, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW())
RETURNING *
`;
const values = [uuidv4(), name, adresse || null, plz || null, ort || null, email || null, telefon || null, notizen || null];
const result = await pool.query(query, values);
res.status(201).json({
success: true,
customer: result.rows[0]
});
} catch (error) {
console.error('Customer Create Error:', error);
if (error.code === '23505') {
return res.status(409).json({ success: false, error: 'Kunde mit diesem Namen existiert bereits' });
}
res.status(500).json({ success: false, error: error.message });
}
});
// PUT /api/customers/:id - Kunde aktualisieren
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { name, adresse, plz, ort, email, telefon, notizen } = req.body;
const query = `
UPDATE kunden
SET
name = COALESCE($1, name),
adresse = COALESCE($2, adresse),
plz = COALESCE($3, plz),
ort = COALESCE($4, ort),
email = COALESCE($5, email),
telefon = COALESCE($6, telefon),
notizen = COALESCE($7, notizen),
updated_at = NOW()
WHERE id = $8
RETURNING *
`;
const values = [name, adresse, plz, ort, email, telefon, notizen, id];
const result = await pool.query(query, values);
if (result.rows.length === 0) {
return res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
}
res.json({
success: true,
customer: result.rows[0]
});
} catch (error) {
console.error('Customer Update Error:', error);
res.status(500).json({ success: false, error: error.message });
}
});
// DELETE /api/customers/:id - Kunde löschen
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
// Prüfe ob Kunde Rechnungen hat
const checkResult = await pool.query(`
SELECT COUNT(*) as count FROM rechnungen
WHERE kunde = (SELECT name FROM kunden WHERE id = $1)
`, [id]);
if (parseInt(checkResult.rows[0].count) > 0) {
return res.status(400).json({
success: false,
error: 'Kunde kann nicht gelöscht werden - es existieren Rechnungen'
});
}
const result = await pool.query('DELETE FROM kunden WHERE id = $1 RETURNING *', [id]);
if (result.rows.length === 0) {
return res.status(404).json({ success: false, error: 'Kunde nicht gefunden' });
}
res.json({
success: true,
message: 'Kunde erfolgreich gelöscht',
deleted: result.rows[0]
});
} catch (error) {
console.error('Customer Delete Error:', error);
res.status(500).json({ success: false, error: error.message });
}
});
module.exports = router;