Funktionen: Warnung der Aufbauphase, Abo-Beginn mit Datum, Texte für abgeschaltet und bereits eingeschaltet
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
06166221ec
commit
1742fb254d
4 files changed
+116
-10
No files matched your search
@@ -67,6 +67,8 @@ export function createFinanceRequest({ baseUrl, createJWT, fetchImpl = (...args)
|
|||||||
if (typeof info.code === 'string') err.code = info.code
|
if (typeof info.code === 'string') err.code = info.code
|
||||||
// Funktion einschalten ohne passendes Abo (409 abo_fehlt): der Grund (kein_abo, falsches_abo, gekuendigt, nicht_aktiv …) bestimmt den Text
|
// Funktion einschalten ohne passendes Abo (409 abo_fehlt): der Grund (kein_abo, falsches_abo, gekuendigt, nicht_aktiv …) bestimmt den Text
|
||||||
if (typeof info.grund === 'string') err.grund = info.grund
|
if (typeof info.grund === 'string') err.grund = info.grund
|
||||||
|
// Bei grund beginnt_spaeter nennt das Portal den Beginn des Abos (ab, ISO-Zeitpunkt); featureFehlerText macht daraus das Datum
|
||||||
|
if (typeof info.ab === 'string') err.ab = info.ab
|
||||||
if (info.fehler && typeof info.fehler === 'object') err.fehler = info.fehler
|
if (info.fehler && typeof info.fehler === 'object') err.fehler = info.fehler
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-10
@@ -312,8 +312,12 @@ export const fragtAbrechnung = (key, action, feature) =>
|
|||||||
export const abrechnungBereit = ({ key, action, feature, abrechnung }) =>
|
export const abrechnungBereit = ({ key, action, feature, abrechnung }) =>
|
||||||
!fragtAbrechnung(key, action, feature) || ABRECHNUNG_OPTIONEN.some((o) => o.value === abrechnung)
|
!fragtAbrechnung(key, action, feature) || ABRECHNUNG_OPTIONEN.some((o) => o.value === abrechnung)
|
||||||
|
|
||||||
/** Text zu 409 abo_fehlt (Portal: grund = kein_abo | falsches_abo | gekuendigt | nicht_aktiv); unbekannter oder fehlender Grund -> allgemeiner Satz. */
|
/**
|
||||||
export function aboFehltText(grund, key) {
|
* Text zu 409 abo_fehlt (Portal: grund = kein_abo | falsches_abo | gekuendigt | nicht_aktiv | beginnt_spaeter); unbekannter oder fehlender Grund
|
||||||
|
* -> allgemeiner Satz. ab (ISO-Zeitpunkt, nur bei beginnt_spaeter) nennt das Datum als Berliner Kalendertag; ohne brauchbares Datum bleibt es
|
||||||
|
* beim allgemeinen Satz.
|
||||||
|
*/
|
||||||
|
export function aboFehltText(grund, key, ab) {
|
||||||
const name = FUNKTIONEN.find((f) => f.key === key)?.label || key
|
const name = FUNKTIONEN.find((f) => f.key === key)?.label || key
|
||||||
switch (grund) {
|
switch (grund) {
|
||||||
case 'kein_abo':
|
case 'kein_abo':
|
||||||
@@ -324,13 +328,34 @@ export function aboFehltText(grund, key) {
|
|||||||
return `${name}: Das Abo des Kunden ist gekündigt. Eingeschaltet wird nicht, solange die Kündigung vorgemerkt ist.`
|
return `${name}: Das Abo des Kunden ist gekündigt. Eingeschaltet wird nicht, solange die Kündigung vorgemerkt ist.`
|
||||||
case 'nicht_aktiv':
|
case 'nicht_aktiv':
|
||||||
return `${name}: Das Abo des Kunden läuft nicht (Status nicht aktiv). Bitte zuerst das Abo klären.`
|
return `${name}: Das Abo des Kunden läuft nicht (Status nicht aktiv). Bitte zuerst das Abo klären.`
|
||||||
|
case 'beginnt_spaeter':
|
||||||
|
if (typeof ab === 'string' && ab !== '' && !Number.isNaN(Date.parse(ab))) {
|
||||||
|
return `${name}: Das Abo beginnt am ${fmtBerlin(ab)}. Ab dann kann die Funktion eingeschaltet werden.`
|
||||||
|
}
|
||||||
|
return `${name}: Das passende Abo fehlt.`
|
||||||
default:
|
default:
|
||||||
return `${name}: Das passende Abo fehlt.`
|
return `${name}: Das passende Abo fehlt.`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fehlertext zu einer Aktion: bei abo_fehlt der Text zum Grund, sonst die Meldung des Fehlers, ohne beides "Speichern fehlgeschlagen.". */
|
/**
|
||||||
export const featureFehlerText = (err, key) => (err?.code === 'abo_fehlt' ? aboFehltText(err.grund, key) : err?.message || 'Speichern fehlgeschlagen.')
|
* Fehlertext zu einer Aktion. 409 abo_fehlt: der Text zum Grund (bei beginnt_spaeter mit Datum). 409 abgeschaltet und bereits_eingeschaltet
|
||||||
|
* (Team-Aktivierung im Abo-Modell): eigene Texte fuers Team, nie der Kundentext des Portals. Sonst die Meldung des Fehlers, ohne beides
|
||||||
|
* "Speichern fehlgeschlagen.".
|
||||||
|
*/
|
||||||
|
export function featureFehlerText(err, key) {
|
||||||
|
const name = FUNKTIONEN.find((f) => f.key === key)?.label || key
|
||||||
|
switch (err?.code) {
|
||||||
|
case 'abo_fehlt':
|
||||||
|
return aboFehltText(err.grund, key, err.ab)
|
||||||
|
case 'abgeschaltet':
|
||||||
|
return `${name}: Die Funktion wurde für dieses Projekt abgeschaltet (der Grund steht in der Zeile). Sie lässt sich hier nicht wieder einschalten.`
|
||||||
|
case 'bereits_eingeschaltet':
|
||||||
|
return `${name}: Die Funktion ist für dieses Projekt schon eingeschaltet. Bitte die Seite neu laden.`
|
||||||
|
default:
|
||||||
|
return err?.message || 'Speichern fehlgeschlagen.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rumpf fuer POST /funktionen/:customerId/:key; Datum und Weg der Textform nur beim Aktivieren und nur, wenn beide da sind,
|
* Rumpf fuer POST /funktionen/:customerId/:key; Datum und Weg der Textform nur beim Aktivieren und nur, wenn beide da sind,
|
||||||
@@ -356,13 +381,21 @@ export const K16_WARNUNG_OHNE_UNTERLAGEN = 'Eingeschaltet, aber die E-Mail „be
|
|||||||
const K16_OHNE_UNTERLAGEN = ['zeiten']
|
const K16_OHNE_UNTERLAGEN = ['zeiten']
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Statuszeile nach dem Speichern einer Aktion: Warnung statt "Gespeichert.", wenn die Antwort warnung: true traegt. Der Wortlaut der
|
* Aufbauphase (Abo-Modell): das Team hat "Im Projekt enthalten" gewaehlt, obwohl der Kunde noch nie ein Abo hatte. Das Portal schaltet ein und
|
||||||
* Warnung folgt der Funktion (key): Terminplaner und Webshop nennen Leistungsbeschreibung und AVV, Oeffnungszeiten live keinen AVV.
|
* antwortet mit 200 und warnung: 'abo_fehlt' (dazu grund: 'kein_abo'). Nicht zu verwechseln mit warnung: true (K16 ging nicht raus).
|
||||||
*/
|
*/
|
||||||
export const speicherStatus = (antwort, key) =>
|
export const ABO_FEHLT_WARNUNG = 'Eingeschaltet. Achtung: Diese Funktion läuft nur mit Wartungs-Abo. Bitte das Abo vor dem Livegang vereinbaren.'
|
||||||
antwort?.warnung === true
|
|
||||||
? { ok: false, text: K16_OHNE_UNTERLAGEN.includes(key) ? K16_WARNUNG_OHNE_UNTERLAGEN : K16_WARNUNG }
|
/**
|
||||||
: { ok: true, text: 'Gespeichert.' }
|
* Statuszeile nach dem Speichern einer Aktion: Warnung statt "Gespeichert.", wenn die Antwort warnung: true (K16 nicht gesendet) oder
|
||||||
|
* warnung: 'abo_fehlt' (Aufbauphase ohne Abo) traegt. Der Wortlaut der K16-Warnung folgt der Funktion (key): Terminplaner und Webshop nennen
|
||||||
|
* Leistungsbeschreibung und AVV, Oeffnungszeiten live keinen AVV; die Abo-Warnung gilt fuer alle Funktionen gleich.
|
||||||
|
*/
|
||||||
|
export function speicherStatus(antwort, key) {
|
||||||
|
if (antwort?.warnung === true) return { ok: false, text: K16_OHNE_UNTERLAGEN.includes(key) ? K16_WARNUNG_OHNE_UNTERLAGEN : K16_WARNUNG }
|
||||||
|
if (antwort?.warnung === 'abo_fehlt') return { ok: false, text: ABO_FEHLT_WARNUNG }
|
||||||
|
return { ok: true, text: 'Gespeichert.' }
|
||||||
|
}
|
||||||
|
|
||||||
// --- Webshop: Anfrage des Kunden und Stand des Shops (Portal: GET /shop/:customerId) ---
|
// --- Webshop: Anfrage des Kunden und Stand des Shops (Portal: GET /shop/:customerId) ---
|
||||||
|
|
||||||
|
|||||||
@@ -151,3 +151,17 @@ test('Der Grund einer Abo-Sperre bleibt am Fehler (409 abo_fehlt mit grund)', as
|
|||||||
assert.deepEqual([a.status, a.code, a.grund], [409, 'abo_fehlt', 'kein_abo'])
|
assert.deepEqual([a.status, a.code, a.grund], [409, 'abo_fehlt', 'kein_abo'])
|
||||||
assert.equal((await fails(api('/funktionen/c1/news', { method: 'POST', body: {} }))).grund, undefined)
|
assert.equal((await fails(api('/funktionen/c1/news', { method: 'POST', body: {} }))).grund, undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Das Datum eines späteren Abo-Beginns bleibt am Fehler (409 abo_fehlt, beginnt_spaeter mit ab)', async () => {
|
||||||
|
const { api } = client({
|
||||||
|
responses: [
|
||||||
|
reply(409, { error: 'x', code: 'abo_fehlt', grund: 'beginnt_spaeter', ab: '2026-11-03T08:00:00.000Z' }),
|
||||||
|
reply(409, { error: 'y', code: 'abo_fehlt', grund: 'beginnt_spaeter', ab: 5 }),
|
||||||
|
reply(409, { error: 'z', code: 'abo_fehlt', grund: 'beginnt_spaeter' }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
const a = await fails(api('/funktionen/c6/news', { method: 'POST', body: {} }))
|
||||||
|
assert.deepEqual([a.status, a.code, a.grund, a.ab], [409, 'abo_fehlt', 'beginnt_spaeter', '2026-11-03T08:00:00.000Z'])
|
||||||
|
assert.equal((await fails(api('/funktionen/c6/news', { method: 'POST', body: {} }))).ab, undefined) // kein Text
|
||||||
|
assert.equal((await fails(api('/funktionen/c6/news', { method: 'POST', body: {} }))).ab, undefined) // fehlt
|
||||||
|
})
|
||||||
@@ -37,6 +37,7 @@ const {
|
|||||||
speicherStatus,
|
speicherStatus,
|
||||||
K16_WARNUNG,
|
K16_WARNUNG,
|
||||||
K16_WARNUNG_OHNE_UNTERLAGEN,
|
K16_WARNUNG_OHNE_UNTERLAGEN,
|
||||||
|
ABO_FEHLT_WARNUNG,
|
||||||
warenartLabel,
|
warenartLabel,
|
||||||
artikelzahlLabel,
|
artikelzahlLabel,
|
||||||
anfrageStatus,
|
anfrageStatus,
|
||||||
@@ -896,3 +897,59 @@ test('aboFehltText und featureFehlerText: jeder Grund, unbekannter Grund, andere
|
|||||||
assert.equal(featureFehlerText(fehler('kein_abo', 'andere'), 'news'), 'Text des Portals')
|
assert.equal(featureFehlerText(fehler('kein_abo', 'andere'), 'news'), 'Text des Portals')
|
||||||
assert.equal(featureFehlerText(null, 'news'), 'Speichern fehlgeschlagen.')
|
assert.equal(featureFehlerText(null, 'news'), 'Speichern fehlgeschlagen.')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('aboFehltText: beginnt_spaeter nennt den Berliner Tag des Beginns, ohne brauchbares Datum bleibt der allgemeine Satz', () => {
|
||||||
|
assert.equal(aboFehltText('beginnt_spaeter', 'termine', '2026-11-03T08:00:00.000Z'), 'Terminplaner: Das Abo beginnt am 03.11.2026. Ab dann kann die Funktion eingeschaltet werden.')
|
||||||
|
// Berliner Kalendertag, nicht UTC: 23:30 UTC am 3.11. ist in Berlin schon der 4.11.
|
||||||
|
assert.equal(aboFehltText('beginnt_spaeter', 'news', '2026-11-03T23:30:00.000Z'), 'News: Das Abo beginnt am 04.11.2026. Ab dann kann die Funktion eingeschaltet werden.')
|
||||||
|
for (const ab of [undefined, null, '', 'kein Datum', 5, {}]) assert.equal(aboFehltText('beginnt_spaeter', 'news', ab), 'News: Das passende Abo fehlt.', String(ab))
|
||||||
|
// das Datum gehört nur zu beginnt_spaeter
|
||||||
|
for (const grund of ['kein_abo', 'falsches_abo', 'gekuendigt', 'nicht_aktiv', 'unbekannt']) {
|
||||||
|
assert.equal(aboFehltText(grund, 'news', '2026-11-03T08:00:00.000Z'), aboFehltText(grund, 'news'), grund)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('featureFehlerText: beginnt_spaeter trägt das Datum des Fehlers, abgeschaltet und bereits_eingeschaltet haben eigene Texte fürs Team', () => {
|
||||||
|
const fehler = (code, extra = {}) => Object.assign(new Error('Text des Portals (in der Du-Form des Kunden)'), { status: 409, code, ...extra })
|
||||||
|
assert.equal(
|
||||||
|
featureFehlerText(fehler('abo_fehlt', { grund: 'beginnt_spaeter', ab: '2026-11-03T08:00:00.000Z' }), 'shop'),
|
||||||
|
'Webshop: Das Abo beginnt am 03.11.2026. Ab dann kann die Funktion eingeschaltet werden.'
|
||||||
|
)
|
||||||
|
assert.equal(featureFehlerText(fehler('abo_fehlt', { grund: 'beginnt_spaeter' }), 'shop'), 'Webshop: Das passende Abo fehlt.')
|
||||||
|
assert.equal(
|
||||||
|
featureFehlerText(fehler('abgeschaltet'), 'news'),
|
||||||
|
'News: Die Funktion wurde für dieses Projekt abgeschaltet (der Grund steht in der Zeile). Sie lässt sich hier nicht wieder einschalten.'
|
||||||
|
)
|
||||||
|
assert.equal(
|
||||||
|
featureFehlerText(fehler('bereits_eingeschaltet'), 'zeiten'),
|
||||||
|
'Öffnungszeiten live: Die Funktion ist für dieses Projekt schon eingeschaltet. Bitte die Seite neu laden.'
|
||||||
|
)
|
||||||
|
assert.equal(featureFehlerText(fehler('abgeschaltet'), 'unbekannt'), 'unbekannt: Die Funktion wurde für dieses Projekt abgeschaltet (der Grund steht in der Zeile). Sie lässt sich hier nicht wieder einschalten.')
|
||||||
|
// nie der Kundentext des Portals ("du", "Schreib uns im Chat"), und kein Preis
|
||||||
|
for (const code of ['abgeschaltet', 'bereits_eingeschaltet']) {
|
||||||
|
const text = featureFehlerText(fehler(code), 'termine')
|
||||||
|
assert.doesNotMatch(text, /\bdu\b|\bdein|Schreib|Portal|€/i, code)
|
||||||
|
}
|
||||||
|
// ein anderer Code oder ein Fehler ohne Code zeigt wie bisher die Meldung des Portals
|
||||||
|
assert.equal(featureFehlerText(fehler('bestellung_laeuft'), 'news'), 'Text des Portals (in der Du-Form des Kunden)')
|
||||||
|
assert.equal(featureFehlerText(Object.assign(new Error('Netz weg'), { status: 0 }), 'news'), 'Netz weg')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('speicherStatus: warnung "abo_fehlt" (Aufbauphase, noch kein Abo) ist eine eigene Warnung und wird nicht mit der K16-Warnung verwechselt', () => {
|
||||||
|
const aufbau = { projects: [], warnung: 'abo_fehlt', grund: 'kein_abo' }
|
||||||
|
for (const key of [undefined, 'news', 'termine', 'shop', 'zeiten', 'unterkunft']) {
|
||||||
|
assert.deepEqual(speicherStatus(aufbau, key), { ok: false, text: ABO_FEHLT_WARNUNG }, String(key))
|
||||||
|
}
|
||||||
|
assert.equal(ABO_FEHLT_WARNUNG, 'Eingeschaltet. Achtung: Diese Funktion läuft nur mit Wartungs-Abo. Bitte das Abo vor dem Livegang vereinbaren.')
|
||||||
|
assert.doesNotMatch(ABO_FEHLT_WARNUNG, /K16|AVV|Unterlagen|€/)
|
||||||
|
assert.notEqual(ABO_FEHLT_WARNUNG, K16_WARNUNG)
|
||||||
|
assert.notEqual(ABO_FEHLT_WARNUNG, K16_WARNUNG_OHNE_UNTERLAGEN)
|
||||||
|
// die alte boolesche Warnung (K16 ging nicht raus) behält ihren Text, mit und ohne Grund
|
||||||
|
assert.deepEqual(speicherStatus({ projects: [], warnung: true }, 'termine'), { ok: false, text: K16_WARNUNG })
|
||||||
|
assert.deepEqual(speicherStatus({ projects: [], warnung: true }, 'zeiten'), { ok: false, text: K16_WARNUNG_OHNE_UNTERLAGEN })
|
||||||
|
assert.deepEqual(speicherStatus({ projects: [], warnung: true, grund: 'kein_abo' }, 'shop'), { ok: false, text: K16_WARNUNG })
|
||||||
|
// ohne Warnung (oder mit einem Wert, den es nicht gibt) bleibt es bei "Gespeichert."
|
||||||
|
for (const antwort of [{ projects: [] }, { warnung: false }, { warnung: 'unbekannt' }, { warnung: 'abo_fehlt_nicht' }, undefined, null]) {
|
||||||
|
assert.deepEqual(speicherStatus(antwort, 'news'), { ok: true, text: 'Gespeichert.' }, JSON.stringify(antwort))
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in new issue
Block a user