import { Injectable } from '@nestjs/common'; import { DEFAULT_WELCOME_MAIL_TEXTS, type WelcomeMailTexts } from '@tessera/shared'; import { PrismaService } from '../prisma/prisma.service'; import { forTenant } from '../prisma/prisma-tenant.extension'; /** * WelcomeMailTemplateService — eigene Vorlage der Willkommensmail * (Administrator → Willkommensmail). * * Hoechstens eine Vorlage je Mandant (Tabelle "WelcomeMailTemplate", * `tenantId` eindeutig). Gespeichert werden nur die sechs Texte; Kopf, * Zugangsdaten, Knoepfe und Fusszeile bleiben fest in * `welcome-mail.template.ts`. Fehlt die Vorlage, gelten * `DEFAULT_WELCOME_MAIL_TEXTS` aus `@tessera/shared`. Die zwei * Anmeldehinweise kamen spaeter hinzu (Spalten nullable): fehlt ein Wert in * einer aelteren Vorlage, gilt fuer ihn der Standardtext. * * Mandantenbindung: jeder Zugriff laeuft ueber `forTenant(prisma, tenantId)`. * Beim Versand ist `tenantId` der Mandant des ZIEL-Benutzers * (`WelcomeMailService.send`), in der Verwaltung der Mandant des * angemeldeten Administrators (`WelcomeMailTemplateController`). Die * Platzhalter-Pruefung liegt im DTO (400 bei unbekanntem Platzhalter). */ /** Antwort von GET /welcome-mail-template. */ export interface WelcomeMailTemplateState { /** `true` = eigene Vorlage gespeichert, `false` = Standardtexte. */ custom: boolean; /** Wirksame Texte (eigene Vorlage oder Standard) — Vorbelegung im Formular. */ texts: WelcomeMailTexts; /** Standardtexte fuer "Auf Standard zuruecksetzen". */ defaults: WelcomeMailTexts; updatedAt: Date | null; updatedBy: string | null; } const TEXT_SELECT = { subject: true, heading: true, intro: true, loginHintDirectory: true, loginHintLocal: true, closing: true, updatedAt: true, updatedBy: true, } as const; @Injectable() export class WelcomeMailTemplateService { constructor(private readonly prisma: PrismaService) {} async getState(tenantId: string): Promise { const tenantPrisma = forTenant(this.prisma, tenantId); const row = await tenantPrisma.welcomeMailTemplate.findUnique({ where: { tenantId }, select: TEXT_SELECT, }); const defaults = { ...DEFAULT_WELCOME_MAIL_TEXTS }; if (!row) { return { custom: false, texts: defaults, defaults, updatedAt: null, updatedBy: null }; } return { custom: true, texts: { subject: row.subject, heading: row.heading, intro: row.intro, loginHintDirectory: row.loginHintDirectory?.trim() ? row.loginHintDirectory : defaults.loginHintDirectory, loginHintLocal: row.loginHintLocal?.trim() ? row.loginHintLocal : defaults.loginHintLocal, closing: row.closing, }, defaults, updatedAt: row.updatedAt, updatedBy: row.updatedBy, }; } /** Texte fuer den Versand: eigene Vorlage des Mandanten, sonst `null` (= Standard). */ async getCustomTexts(tenantId: string): Promise { const state = await this.getState(tenantId); return state.custom ? state.texts : null; } /** Name des Mandanten fuer `{{firma}}`; leer, wenn der Mandant fehlt. */ async getTenantName(tenantId: string): Promise { const tenantPrisma = forTenant(this.prisma, tenantId); const tenant = await tenantPrisma.tenant.findUnique({ where: { id: tenantId }, select: { name: true }, }); return tenant?.name ?? ''; } async save( tenantId: string, texts: WelcomeMailTexts, updatedBy: string, ): Promise { const tenantPrisma = forTenant(this.prisma, tenantId); const data = { subject: texts.subject, heading: texts.heading, intro: texts.intro, loginHintDirectory: texts.loginHintDirectory, loginHintLocal: texts.loginHintLocal, closing: texts.closing, updatedBy, }; await tenantPrisma.welcomeMailTemplate.upsert({ where: { tenantId }, create: { tenantId, ...data }, update: data, }); return this.getState(tenantId); } /** "Auf Standard zuruecksetzen": loescht die eigene Vorlage (idempotent). */ async reset(tenantId: string): Promise { const tenantPrisma = forTenant(this.prisma, tenantId); await tenantPrisma.welcomeMailTemplate.deleteMany({ where: { tenantId } }); return this.getState(tenantId); } }