diff --git a/server/routes/team/customers.js b/server/routes/team/customers.js index 7be8a0c..debb5c3 100644 --- a/server/routes/team/customers.js +++ b/server/routes/team/customers.js @@ -56,16 +56,20 @@ router.get('/:id/abo', async (req, res) => { // 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 +// (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, ledger: null }) + 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, ledger: null }) - const l = await getLedger(req.params.id, { active: ACTIVE_STATUSES.has(sub.status) }) + 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, diff --git a/test/teamStundenkonto.test.js b/test/teamStundenkonto.test.js index 19b701a..c525c95 100644 --- a/test/teamStundenkonto.test.js +++ b/test/teamStundenkonto.test.js @@ -4,7 +4,8 @@ import { startAppwriteMock } from './support/appwriteMock.js' import { startFakeStripe } from './support/fakeStripe.js' import { useTestEnv } from './support/testEnv.js' -// Team-Endpunkt Stundenkonto fuers Ticketsystem: Produkt, Guthaben, Vorschuss, Mehraufwand +// Team-Endpunkt Stundenkonto fuers Ticketsystem: Produkt, Guthaben, Vorschuss, Mehraufwand und active (Abo laeuft; E-Fix W1: WOMS zeigt bei beendetem Abo +// "verfaellt, wird nicht berechnet" statt "wird mit den naechsten Gutschriften verrechnet") const aw = await startAppwriteMock() const st = await startFakeStripe() useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url }) @@ -24,13 +25,17 @@ test('Wartung: Guthaben, Vorschuss und Mehraufwand aus dem Stundenkonto', async aw.seed('workorders', { $id: 'wo_t', customerId: 'c3', woid: '100401', topic: 'Einrichtung', einrichtungAbrechnung: 'abo' }) aw.seed('worksheets', { $id: 'ws_t', workorderId: 'wo_t', wsid: 'ws_t', countsToPlan: true, totalTime: 90, startDate: '10.09.2026' }) const res = await rawGet('/api/team/customers/c3/stundenkonto', auth) - assert.deepEqual([res.status, res.body], [200, { produkt: 'wartung', ledger: { balanceMinutes: 0, vorschussMinutes: 30, overageMinutes: 0, nextExpiry: null } }]) + assert.deepEqual([res.status, res.body], [200, { produkt: 'wartung', active: true, ledger: { balanceMinutes: 0, vorschussMinutes: 30, overageMinutes: 0, nextExpiry: null } }]) }) test('Hosting: kein Konto; ohne Abo null; beendetes Wartungs-Abo behält den Vorschuss, Guthaben 0; ohne JWT 401', async () => { - assert.deepEqual((await rawGet('/api/team/customers/c1/stundenkonto', auth)).body, { produkt: 'hosting', ledger: null }) // c1: Hosting aus seedFixtures - assert.deepEqual((await rawGet('/api/team/customers/c4/stundenkonto', auth)).body, { produkt: null, ledger: null }) + assert.deepEqual((await rawGet('/api/team/customers/c1/stundenkonto', auth)).body, { produkt: 'hosting', active: true, ledger: null }) // c1: Hosting aus seedFixtures + assert.deepEqual((await rawGet('/api/team/customers/c4/stundenkonto', auth)).body, { produkt: null, active: false, ledger: null }) await updateDocument('subscriptions', 'sub_t', { status: 'canceled' }) // aw.docs() liefert Kopien: ueber die API aendern const ende = (await rawGet('/api/team/customers/c3/stundenkonto', auth)).body - assert.deepEqual([ende.produkt, ende.ledger.vorschussMinutes, ende.ledger.balanceMinutes], ['wartung', 30, 0]) + assert.deepEqual([ende.produkt, ende.active, ende.ledger.vorschussMinutes, ende.ledger.balanceMinutes], ['wartung', false, 30, 0]) + await updateDocument('subscriptions', 'sub_t', { status: 'active', cancelAtPeriodEnd: true }) // gekündigt, aber bis zum Periodenende noch aktiv: der Vorschuss geht noch in die Gutschriften + assert.equal((await rawGet('/api/team/customers/c3/stundenkonto', auth)).body.active, true) + await updateDocument('subscriptions', 'sub_t', { status: 'past_due', cancelAtPeriodEnd: false }) // offene Zahlung: das Abo läuft noch (ACTIVE_STATUSES) + assert.equal((await rawGet('/api/team/customers/c3/stundenkonto', auth)).body.active, true) assert.equal((await rawGet('/api/team/customers/c3/stundenkonto')).status, 401) })