From 45203284ee693a35f48e92d292b22e1ad283e660 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 02:52:38 +0000 Subject: [PATCH] Abo-Status eines Kunden fuer alle Mitarbeiter im Team-Eingang Co-Authored-By: Claude Opus 5.5 --- server/routes/team/customers.js | 26 ++++++++++++++++++++++++++ server/services/takeoverCredits.js | 2 +- test/teamRoutes.test.js | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/server/routes/team/customers.js b/server/routes/team/customers.js index 9185aac..0871c13 100644 --- a/server/routes/team/customers.js +++ b/server/routes/team/customers.js @@ -2,6 +2,9 @@ import { Router } from 'express' import { config } from '../../config.js' import { listViewLog } from '../../services/customerViewLog.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 @@ -27,6 +30,29 @@ router.get('/:id/protokoll', async (req, res) => { } }) +// 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' }) + } +}) + // Kundenansicht oeffnen: Einmal-Link (60 s), den das Ticketsystem in einem neuen Tab aufruft router.post('/:id/kundenansicht', async (req, res) => { try { diff --git a/server/services/takeoverCredits.js b/server/services/takeoverCredits.js index d25293e..8b25ff4 100644 --- a/server/services/takeoverCredits.js +++ b/server/services/takeoverCredits.js @@ -347,7 +347,7 @@ function withHistory(doc, entry) { return json } -const serviceStartOf = (sub) => sub?.trialEnd || sub?.startedAt || null +export const serviceStartOf = (sub) => sub?.trialEnd || sub?.startedAt || null const isActiveSub = (sub) => Boolean(sub && ACTIVE_STATUSES.has(sub.status)) // Produkt des Abos: Altbestand ohne productKey und unbekannte Schluessel gelten als Wartungs-Abo const productKeyOf = (sub) => (PRODUCT_KEYS.includes(sub?.productKey) ? sub.productKey : 'wartung') diff --git a/test/teamRoutes.test.js b/test/teamRoutes.test.js index 17c5b9f..80d34b3 100644 --- a/test/teamRoutes.test.js +++ b/test/teamRoutes.test.js @@ -67,3 +67,21 @@ test('alle Team-Routen ohne JWT -> 401', async () => { assert.equal(res.status, 401, `${method} ${p}`) } }) + +// Etappe 3: Abo-Status fuer alle Mitarbeiter (Kunden-Reiter "Abo & Angebote", Stunden-Pruefung am Ticket) +test('abo: Mitarbeiter ohne Admin-Label sieht den Abo-Status', async () => { + aw.seed('subscriptions', { + $id: 'sub_t', customerId: 'c3', status: 'active', productKey: 'wartung', storageUnits: 1, + startedAt: '2026-09-01T10:00:00.000Z', trialEnd: '2026-09-15T00:00:00.000Z', currentPeriodEnd: '2026-11-01T00:00:00.000Z', cancelAt: null, + }) + const res = await rawGet('/api/team/customers/c3/abo', auth) + assert.equal(res.status, 200) + assert.deepEqual(res.body, { + productKey: 'wartung', active: true, status: 'active', storageUnits: 1, startedAt: '2026-09-01T10:00:00.000Z', + serviceStart: '2026-09-15T00:00:00.000Z', currentPeriodEnd: '2026-11-01T00:00:00.000Z', cancelAt: null, + }) + assert.deepEqual((await rawGet('/api/team/customers/c4/abo', auth)).body, { + productKey: null, active: false, status: null, storageUnits: 0, startedAt: null, serviceStart: null, currentPeriodEnd: null, cancelAt: null, + }) + assert.equal((await rawGet('/api/team/customers/c3/abo')).status, 401) +})