Kundenansicht P5: POST /api/funktionen/:key/einschalten (nur neue Ansicht)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f5b43c55a9
commit
e30318a470
3 files changed
+431
-15
No files matched your search
@@ -34,6 +34,14 @@ export const EINSCHALTEN_TEXTE = Object.freeze({
|
||||
nichtPruefbar: 'Gerade nicht prüfbar – bitte versuch es gleich noch einmal.',
|
||||
})
|
||||
|
||||
/** Berliner Kalendertag eines Zeitpunkts (ISO) als TT.MM.JJJJ; kein Zeitpunkt ist ein TypeError. */
|
||||
export function datumDe(zeitpunkt) {
|
||||
const ms = Date.parse(zeitpunkt)
|
||||
if (Number.isNaN(ms)) throw new TypeError(`datumDe: kein Zeitpunkt: ${String(zeitpunkt)}`)
|
||||
const [jahr, monat, tag] = berlinDay(new Date(ms)).split('-')
|
||||
return `${tag}.${monat}.${jahr}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Kundentext zu einem Abo-Grund. Bei beginnt_spaeter steht der Berliner Tag von `ab` (ISO) als TT.MM.JJJJ im Text,
|
||||
* z. B. "Dein Abo beginnt am 03.11.2026. Ab dann kannst du einschalten."
|
||||
@@ -41,11 +49,7 @@ export const EINSCHALTEN_TEXTE = Object.freeze({
|
||||
export function aboFehlerText(grund, ab = null) {
|
||||
if (!Object.hasOwn(EINSCHALTEN_TEXTE.abo, grund)) throw new TypeError(`aboFehlerText: unbekannter Grund ${String(grund)}`)
|
||||
const text = EINSCHALTEN_TEXTE.abo[grund]
|
||||
if (grund !== 'beginnt_spaeter') return text
|
||||
const ms = Date.parse(ab)
|
||||
if (Number.isNaN(ms)) throw new TypeError('aboFehlerText: beginnt_spaeter braucht ab als Zeitpunkt')
|
||||
const [jahr, monat, tag] = berlinDay(new Date(ms)).split('-')
|
||||
return text.replace('{datum}', `${tag}.${monat}.${jahr}`)
|
||||
return grund === 'beginnt_spaeter' ? text.replace('{datum}', datumDe(ab)) : text
|
||||
}
|
||||
|
||||
/** 409 abo_fehlt: body.grund, body.wechsel (Hosting-Kunde: Wechsel ins Wartungs-Abo anbieten) und bei beginnt_spaeter body.ab. */
|
||||
@@ -54,8 +58,11 @@ const aboFehlt = (grund, ab) =>
|
||||
|
||||
const konflikt = (code, text) => bestellFehler(409, text, code)
|
||||
|
||||
/** Abo frisch lesen und gegen die Funktion pruefen (immer ohne Zwischenspeicher); Lesefehler sind 503, nie stillschweigend erlaubt. */
|
||||
async function pruefeAbo(customerId, key, now) {
|
||||
/**
|
||||
* Abo frisch lesen und gegen die Funktion pruefen (immer ohne Zwischenspeicher): 409 abo_fehlt oder nichts. Lesefehler sind 503,
|
||||
* nie stillschweigend erlaubt. Auch fuer die Vorpruefungen der Route.
|
||||
*/
|
||||
export async function pruefeAboDeckung(customerId, key, now = new Date()) {
|
||||
let ergebnis
|
||||
try {
|
||||
ergebnis = await aboDeckt(customerId, key, { now })
|
||||
@@ -69,8 +76,11 @@ async function pruefeAbo(customerId, key, now) {
|
||||
/** setup ohne Ticket und durch Abo oder Projekt eingeschaltet: der Dienst heilt (nur das Ticket), es entsteht nichts Neues. */
|
||||
const heilbar = (doc) => doc?.state === 'setup' && !doc.ticketId && ['abo', 'projekt'].includes(unlockedByVon(doc))
|
||||
|
||||
/** Zustand des Dokuments pruefen: wirft den Konflikt mit Code; Heilung und ein frisches oder ausgeschaltetes Dokument sind frei. */
|
||||
function pruefeZustand(doc) {
|
||||
/**
|
||||
* Zustand des Dokuments pruefen: wirft den 409 mit Code (bestellung_laeuft, abgeschaltet, bereits_eingeschaltet); ein fehlendes oder
|
||||
* ausgeschaltetes (off) Dokument und die Heilung (setup ohne Ticket, durch Abo oder Projekt) sind frei. Auch fuer die Vorpruefungen der Route.
|
||||
*/
|
||||
export function pruefeEinschaltZustand(doc) {
|
||||
if (!doc || doc.state === 'off') return
|
||||
if (doc.state === 'ordered') throw konflikt('bestellung_laeuft', EINSCHALTEN_TEXTE.laeuft)
|
||||
if (doc.state === 'disabled') throw konflikt('abgeschaltet', EINSCHALTEN_TEXTE.abgeschaltet)
|
||||
@@ -118,8 +128,8 @@ export async function einschalten({ customerId, projectId, key, by, abrechnung,
|
||||
// liefert dieselben Codes wie unten (nur fuer den Kunden, das Team schaltet wie bisher)
|
||||
if (key === 'zeiten') {
|
||||
const vorpruefung = async () => {
|
||||
await pruefeAbo(customerId, key, now)
|
||||
pruefeZustand(await getFeatureDoc(customerId, projectId, key))
|
||||
await pruefeAboDeckung(customerId, key, now)
|
||||
pruefeEinschaltZustand(await getFeatureDoc(customerId, projectId, key))
|
||||
}
|
||||
const { doc } = await einschaltenZeiten({ customerId, projectId, by, staff, nachweis, now, abrechnung, vorpruefung })
|
||||
return { doc, ticketWoid: await ticketWoid(doc.ticketId) }
|
||||
@@ -127,11 +137,11 @@ export async function einschalten({ customerId, projectId, key, by, abrechnung,
|
||||
|
||||
const doc = await withLocks([`feature-order:${customerId}:${projectId}`], async () => {
|
||||
// 1 Abo frisch (nie aus einem Zwischenspeicher); fuer das Team entfaellt es
|
||||
if (by === 'kunde') await pruefeAbo(customerId, key, now)
|
||||
if (by === 'kunde') await pruefeAboDeckung(customerId, key, now)
|
||||
|
||||
// 2 Zustand: Heilung (setup ohne Ticket, durch Abo oder Projekt) legt nur das Ticket an
|
||||
const bisher = await getFeatureDoc(customerId, projectId, key)
|
||||
pruefeZustand(bisher)
|
||||
pruefeEinschaltZustand(bisher)
|
||||
if (heilbar(bisher)) {
|
||||
const gespeichert = Object.hasOwn(ABRECHNUNGS_ZEILEN, bisher.einrichtungAbrechnung) ? bisher.einrichtungAbrechnung : abrechnung
|
||||
return legeTicketAn(bisher, { key, feature, project, customerId, abrechnung: gespeichert })
|
||||
|
||||
Reference in new issue
Block a user