Geburtstag-Management-Modal mit Reset + Send + Auto-Flag
Neuer Cake-Button neben dem Geburtsdatum in den Stammdaten öffnet ein Modal mit drei Funktionen: 1. **Gruß-Marker zurücksetzen** (lastBirthdayGreetingYear → null) - Für Debugging oder als Fallback, wenn der Kunde den Gruß erneut sehen soll - Mit Bestätigungsdialog 2. **Geburtstagsgruß jetzt senden** (Email / WhatsApp / Telegram / Signal) - Email: direkt via System-SMTP mit HTML-Template (Du/Sie-abhängig) - WhatsApp/Telegram/Signal: öffnet vorbefülltes Fenster mit Gruß-Text - Text beachtet Du/Sie-Verhältnis (pronomen, possessiv, etc.) - Mit Bestätigungsdialog 3. **Automatisch senden** – neue Einstellung am Customer - autoBirthdayGreeting (Boolean) + autoBirthdayChannel (String) - Für späteren Cron-basierten Automatik-Versand vorbereitet Backend: - birthday.service.ts: resetBirthdayGreeting, buildBirthdayGreetingText, getBirthdayGreetingData - birthday.controller.ts: resetBirthdayGreeting, sendBirthdayGreeting - Routes: POST /birthdays/:customerId/reset + /send - Audit-Log bei beiden Aktionen Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { Response } from 'express';
|
||||
import { AuthRequest } from '../types/index.js';
|
||||
import * as birthdayService from '../services/birthday.service.js';
|
||||
import { sendEmail, SmtpCredentials } from '../services/smtpService.js';
|
||||
import { getSystemEmailCredentials } from '../services/emailProvider/emailProviderService.js';
|
||||
import { createAuditLog } from '../services/audit.service.js';
|
||||
|
||||
/**
|
||||
* Admin/Mitarbeiter: Kommende und vergangene Geburtstage
|
||||
@@ -54,3 +57,125 @@ export async function acknowledgeMyBirthday(req: AuthRequest, res: Response) {
|
||||
res.status(500).json({ success: false, error: 'Fehler beim Speichern' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin: Geburtstagsgruß-Marker für einen Kunden zurücksetzen (Debug / Re-Trigger).
|
||||
*/
|
||||
export async function resetBirthdayGreeting(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const customerId = parseInt(req.params.customerId);
|
||||
await birthdayService.resetBirthdayGreeting(customerId);
|
||||
|
||||
await createAuditLog({
|
||||
userId: req.user?.userId,
|
||||
userEmail: req.user?.email || 'unknown',
|
||||
action: 'UPDATE',
|
||||
resourceType: 'Customer',
|
||||
resourceId: customerId.toString(),
|
||||
resourceLabel: `Geburtstagsgruß-Marker zurückgesetzt`,
|
||||
endpoint: req.path,
|
||||
httpMethod: req.method,
|
||||
ipAddress: req.socket.remoteAddress || 'unknown',
|
||||
dataSubjectId: customerId,
|
||||
});
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Zurücksetzen:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Fehler beim Zurücksetzen',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin: Geburtstagsgruß manuell senden (Email oder Link für WhatsApp/Telegram/Signal).
|
||||
*/
|
||||
export async function sendBirthdayGreeting(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const customerId = parseInt(req.params.customerId);
|
||||
const { channel } = req.body; // 'email', 'whatsapp', 'telegram', 'signal'
|
||||
|
||||
if (!['email', 'whatsapp', 'telegram', 'signal'].includes(channel)) {
|
||||
return res.status(400).json({ success: false, error: 'Ungültiger Kanal' });
|
||||
}
|
||||
|
||||
const data = await birthdayService.getBirthdayGreetingData(customerId);
|
||||
if (!data) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Kunde hat kein Geburtsdatum hinterlegt',
|
||||
});
|
||||
}
|
||||
|
||||
const { subject, plain, html } = birthdayService.buildBirthdayGreetingText(data, data.age);
|
||||
|
||||
if (channel === 'email') {
|
||||
if (!data.email) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Kunde hat keine E-Mail-Adresse hinterlegt',
|
||||
});
|
||||
}
|
||||
|
||||
const systemEmail = await getSystemEmailCredentials();
|
||||
if (!systemEmail) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Keine System-E-Mail konfiguriert. Bitte in den Email-Provider-Einstellungen hinterlegen.',
|
||||
});
|
||||
}
|
||||
|
||||
const credentials: SmtpCredentials = {
|
||||
host: systemEmail.smtpServer,
|
||||
port: systemEmail.smtpPort,
|
||||
user: systemEmail.emailAddress,
|
||||
password: systemEmail.password,
|
||||
encryption: systemEmail.smtpEncryption,
|
||||
allowSelfSignedCerts: systemEmail.allowSelfSignedCerts,
|
||||
};
|
||||
|
||||
const result = await sendEmail(credentials, systemEmail.emailAddress, {
|
||||
to: data.email,
|
||||
subject,
|
||||
html,
|
||||
}, {
|
||||
context: 'birthday-greeting',
|
||||
customerId,
|
||||
triggeredBy: req.user?.email,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: `E-Mail-Versand fehlgeschlagen: ${result.error}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await createAuditLog({
|
||||
userId: req.user?.userId,
|
||||
userEmail: req.user?.email || 'unknown',
|
||||
action: 'CREATE',
|
||||
resourceType: 'Customer',
|
||||
resourceId: customerId.toString(),
|
||||
resourceLabel: `Geburtstagsgruß gesendet (${channel})`,
|
||||
endpoint: req.path,
|
||||
httpMethod: req.method,
|
||||
ipAddress: req.socket.remoteAddress || 'unknown',
|
||||
dataSubjectId: customerId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: { channel, messageText: plain },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Senden des Geburtstagsgrußes:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Fehler beim Senden',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user