Übernahmegutschrift und Leitfaden: für beide Abos (Entwurf)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Fable 5.1 committed 2026-09-27 20:23:44 +00:00
1 parent 0098899ac6
commit a2c1f0d81c
6 files changed
+73 -28

No files matched your search

+12 -9
View File
@@ -6,6 +6,7 @@ import { domainToASCII, domainToUnicode } from 'node:url'
import { config } from '../config.js'
import { createDocument, getDocument, listDocuments, Query, updateDocument } from './appwriteAdmin.js'
import { sendAdminNotice, sendCustomerMail } from './mailer.js'
import { getProduct } from './products.js'
import { ACTIVE_STATUSES, getCurrentSubscription, getStripe, isStripeConfigured } from './stripeBilling.js'
/**
@@ -45,7 +46,7 @@ export const REQUIRED_CHECKS = [
{ key: 'recipient', label: 'Rechnung ist auf den Kunden ausgestellt (Name bzw. Firma stimmt)' },
{ key: 'service', label: 'Leistung und Domain auf der Rechnung stimmen mit dem Antrag überein' },
{ key: 'period', label: 'Betrag und bezahlter Zeitraum stimmen mit der Rechnung überein' },
{ key: 'replaced', label: 'Die Leistung wird durch das Wartungs-Abo ersetzt (Umzug zu WEBklar)' },
{ key: 'replaced', label: 'Die Leistung wird durch das Abo ersetzt (Umzug zu WEBklar)' },
]
const DAY_MS = 24 * 60 * 60 * 1000
@@ -387,7 +388,7 @@ export function customerView(doc) {
export async function createTakeoverRequest({ customer, input, uploads, consentIp }) {
const sub = await getCurrentSubscription(customer.$id)
if (!isActiveSub(sub)) {
throw httpError(409, 'Die Übernahmegutschrift können Sie beantragen, sobald Ihr Wartungs-Abo läuft.')
throw httpError(409, 'Die Übernahmegutschrift können Sie beantragen, sobald Ihr Abo läuft.')
}
const own = await listTakeoverRequests({ customerId: customer.$id })
const open = own.filter((r) => r.status === STATUS.open || r.status === STATUS.question)
@@ -618,14 +619,14 @@ export async function runAutoChecks(doc, { allRequests, subscription, calc }) {
}
if (!subscription) {
checks.push({ key: 'abo', status: 'warn', label: 'Kunde hat kein Wartungs-Abo – eine Gutschrift kann nicht verrechnet werden' })
checks.push({ key: 'abo', status: 'warn', label: 'Kunde hat kein Abo – eine Gutschrift kann nicht verrechnet werden' })
} else if (!isActiveSub(subscription)) {
checks.push({ key: 'abo', status: 'warn', label: `Wartungs-Abo ist beendet (Status ${subscription.status}) – keine Verrechnung möglich` })
checks.push({ key: 'abo', status: 'warn', label: `Abo ist beendet (Status ${subscription.status}) – keine Verrechnung möglich` })
} else {
checks.push({
key: 'abo',
status: subscription.cancelAtPeriodEnd ? 'info' : 'ok',
label: `Wartungs-Abo läuft, Leistungsbeginn ${fmtDate(serviceStartOf(subscription))}${subscription.cancelAtPeriodEnd ? `, gekündigt zum ${fmtDate(subscription.cancelAt || subscription.currentPeriodEnd)}` : ''}`,
label: `Abo läuft, Leistungsbeginn ${fmtDate(serviceStartOf(subscription))}${subscription.cancelAtPeriodEnd ? `, gekündigt zum ${fmtDate(subscription.cancelAt || subscription.currentPeriodEnd)}` : ''}`,
})
}
@@ -637,12 +638,14 @@ export async function runAutoChecks(doc, { allRequests, subscription, calc }) {
)
}
// Plausibilitaet gegen die Monatsgebuehr des Abos des Kunden; Abos ohne productKey (Altbestand) gelten als Wartungs-Abo
const monthly = doc.invoiceAmountCents / Math.max(1, doc.billingMonths)
if (monthly > config.stripe.priceAmountCents) {
const aboCents = getProduct(subscription?.productKey || 'wartung').amountCents
if (monthly > aboCents) {
checks.push({
key: 'amount',
status: 'info',
label: `Beim bisherigen Anbieter ${fmtEuro(monthly)} pro Monat – mehr als das Abo. Enthält die Rechnung weitere Leistungen?`,
label: `Beim bisherigen Anbieter ${fmtEuro(monthly)} pro Monat – mehr als die Monatsgebühr des Abos (${fmtEuro(aboCents)}). Enthält die Rechnung weitere Leistungen?`,
})
}
@@ -768,7 +771,7 @@ export async function applyTakeoverCredit(doc) {
if (!isStripeConfigured()) return { applied: false, doc, reason: 'Stripe ist nicht eingerichtet.' }
const sub = await getCurrentSubscription(doc.customerId)
if (!isActiveSub(sub) || !sub.stripeCustomerId) {
return { applied: false, doc, reason: 'Der Kunde hat kein laufendes Wartungs-Abo.' }
return { applied: false, doc, reason: 'Der Kunde hat kein laufendes Abo.' }
}
try {
const txn =
@@ -873,7 +876,7 @@ export async function decideTakeoverRequest(id, body, admin) {
const verified = readInvoiceFigures(body, doc)
const sub = await getCurrentSubscription(doc.customerId)
if (!isActiveSub(sub)) {
throw httpError(409, 'Der Kunde hat kein laufendes Wartungs-Abo – die Gutschrift kann nicht verrechnet werden.')
throw httpError(409, 'Der Kunde hat kein laufendes Abo – die Gutschrift kann nicht verrechnet werden.')
}
const corrections = [