import { Router } from 'express' import { config } from '../../config.js' import { listViewLog } from '../../services/customerViewLog.js' import { getLedger } from '../../services/hourLedger.js' import { getPortalAccessStatus } from '../../services/portalAccessStatus.js' import { PRODUCT_KEYS } from '../../services/products.js' import { ACTIVE_STATUSES, getCurrentSubscription } from '../../services/stripeBilling.js' import { serviceStartOf } from '../../services/takeoverCredits.js' import { createStaffPreviewToken } from '../../services/staffPreviewTokens.js' // Kunden im Team-Eingang (/api/team/customers), requireStaffJwt sitzt in routes/team/index.js const router = Router() // Portal-Zugang und letzter Login (Karte "Portal-Zugang" im Ticketsystem) router.get('/:id/portal', async (req, res) => { try { const { customer, hasAccess, lastLoginAt } = await getPortalAccessStatus(req.params.id) if (!customer) return res.status(404).json({ error: 'Kunde nicht gefunden' }) return res.json({ hasAccess, lastLoginAt }) } catch (err) { return res.status(500).json({ error: err.message || 'Portal-Zugang konnte nicht geladen werden' }) } }) // Protokoll der Kundenansicht (Reiter "Protokoll"), neueste zuerst router.get('/:id/protokoll', async (req, res) => { try { return res.json({ entries: await listViewLog(req.params.id) }) } catch (err) { return res.status(500).json({ error: err.message || 'Protokoll konnte nicht geladen werden' }) } }) // Abo-Status (Kunden-Reiter "Abo & Angebote", Stunden-Pruefung am Ticket): productKey/active wie die Admin-Route // /api/admin/customers/:id/abo, dazu Laufzeit und Speicher; nur der Appwrite-Spiegel, kein Stripe router.get('/:id/abo', async (req, res) => { try { const sub = await getCurrentSubscription(req.params.id) if (!sub) { return res.json({ productKey: null, active: false, status: null, storageUnits: 0, startedAt: null, serviceStart: null, currentPeriodEnd: null, cancelAt: null }) } return res.json({ productKey: PRODUCT_KEYS.includes(sub.productKey) ? sub.productKey : 'wartung', active: ACTIVE_STATUSES.has(sub.status), status: sub.status || null, storageUnits: Number(sub.storageUnits) || 0, startedAt: sub.startedAt || null, serviceStart: serviceStartOf(sub), currentPeriodEnd: sub.currentPeriodEnd || null, cancelAt: sub.cancelAt || null, }) } catch (err) { return res.status(err.status || 500).json({ error: err.message || 'Abo konnte nicht geladen werden' }) } }) // Stundenkonto fuers Ticketsystem (Abo-Modell der Funktionen): Produkt wie /:id/abo, dazu Guthaben, Vorschuss // (Einrichtung ohne Deckung) und Mehraufwand. Nur das Wartungs-Abo hat ein Konto, auch nach dem Ende // (dann Guthaben 0, der Vorschuss bleibt stehen); Hosting und kein Abo ergeben ledger null. // active (E-Fix W1): das Abo laeuft (Status active, trialing oder past_due, auch mit vorgemerkter Kuendigung); ohne Abo false. WOMS zeigt // bei active false zum Vorschuss "verfaellt, wird nicht berechnet" (V8, E17) statt "wird mit den naechsten Gutschriften verrechnet". router.get('/:id/stundenkonto', async (req, res) => { try { const sub = await getCurrentSubscription(req.params.id) if (!sub) return res.json({ produkt: null, active: false, ledger: null }) const active = ACTIVE_STATUSES.has(sub.status) const produkt = PRODUCT_KEYS.includes(sub.productKey) ? sub.productKey : 'wartung' if (produkt !== 'wartung') return res.json({ produkt, active, ledger: null }) const l = await getLedger(req.params.id, { active }) return res.json({ produkt, active, ledger: { balanceMinutes: l.balanceMinutes, vorschussMinutes: l.vorschussMinutes, overageMinutes: l.overageMinutes, nextExpiry: l.nextExpiry, }, }) } catch (err) { return res.status(500).json({ error: err.message || 'Stundenkonto konnte nicht geladen werden' }) } }) // Kundenansicht oeffnen: Einmal-Link (60 s), den das Ticketsystem in einem neuen Tab aufruft router.post('/:id/kundenansicht', async (req, res) => { try { const { customer, hasAccess } = await getPortalAccessStatus(req.params.id) if (!customer) return res.status(404).json({ error: 'Kunde nicht gefunden' }) if (!hasAccess) return res.status(409).json({ code: 'no_portal_access', error: 'Kein Portal-Zugang' }) const token = createStaffPreviewToken({ purpose: 'kundenansicht', customerId: customer.$id, staff: req.staff }) return res.json({ openUrl: `${config.publicBaseUrl}/api/auth/kundenansicht?token=${encodeURIComponent(token)}` }) } catch (err) { return res.status(500).json({ error: err.message || 'Kundenansicht konnte nicht geƶffnet werden' }) } }) export default router