Öffnungszeiten: Behalten nur ohne Abo anbieten (kein_abo)
Behalten (30 €) gab es bei jedem inklusive false. nicht_aktiv und gekuendigt heißen aber, dass das Wartungs-Abo läuft (Server: 409 Abo läuft wieder). Jetzt gilt an allen fünf Stellen (Kachel, Karte, Behalten-Seite, Weg aus der Kachel, Abgeschaltet-Karte) dasselbe Prädikat: inklusive false UND grund kein_abo; sonst „Abo läuft wieder“ ohne Bezahlknopf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7bb918390a
commit
cee44d6852
5 files changed
+166
-20
No files matched your search
+21
-5
@@ -267,6 +267,17 @@ function zeitenFrist(aboEndeAt) {
|
||||
return new Date(Date.UTC(y, m - 1, d + 30)).toLocaleDateString('de-DE', { timeZone: 'UTC', day: '2-digit', month: '2-digit', year: 'numeric' })
|
||||
}
|
||||
|
||||
// Behalten (30 €) ist nur bezahlbar, wenn das Wartungs-Abo wirklich beendet ist: inklusive false UND grund kein_abo (Z12, R20, R25). nicht_aktiv und
|
||||
// gekuendigt heissen, dass das Abo laeuft (Probezeit/Zahlung offen bzw. Kuendigung vorgemerkt): 'abo', nie ein Bezahlknopf. Doppelt zu behaltenLage in
|
||||
// zeiten-bestellen.js (funktionen.js importiert dort nichts); beide pinnen die Tests. 'bezahlen' | 'abo' | 'inklusive' | 'unklar' (Abo nicht lesbar)
|
||||
function zeitenBehaltenLage(angebot) {
|
||||
if (angebot.inklusive === true) return 'inklusive'
|
||||
if (angebot.inklusive !== false) return 'unklar'
|
||||
if (angebot.grund === 'kein_abo') return 'bezahlen'
|
||||
return angebot.grund === 'nicht_aktiv' || angebot.grund === 'gekuendigt' ? 'abo' : 'unklar'
|
||||
}
|
||||
const ZEITEN_ABO_WIEDER = 'Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten.'
|
||||
|
||||
// 'JJJJ-MM-TT' als TT.MM.JJJJ, sonst ''
|
||||
const zeitenTag = (iso) => {
|
||||
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(typeof iso === 'string' ? iso : '')
|
||||
@@ -296,11 +307,14 @@ function zeitenTile(projects, angebot = {}) {
|
||||
}
|
||||
if (target.zeitenLeer === true) extra += `<p>${aktiv ? 'Trag jetzt deine Zeiten ein.' : 'Trag jetzt deine Zeiten ein – erst dann kann das Team die Tabelle einbauen.'}</p>`
|
||||
action = knopf('Zeiten bearbeiten')
|
||||
// Wartungs-Abo beendet (aboEndeAt): fuer einmalig 30 € behalten; nur wenn sicher ist, dass das Abo nicht wieder laeuft (inklusive false)
|
||||
const frist = f.aboEndeAt && angebot.inklusive === false ? zeitenFrist(f.aboEndeAt) : ''
|
||||
// Wartungs-Abo beendet (aboEndeAt): fuer einmalig 30 € behalten, aber nur ohne Abo (kein_abo); laeuft es (nicht_aktiv, gekuendigt), steht der Hinweis ohne Knopf da
|
||||
const lage = f.aboEndeAt ? zeitenBehaltenLage(angebot) : ''
|
||||
const frist = lage === 'bezahlen' ? zeitenFrist(f.aboEndeAt) : ''
|
||||
if (frist) {
|
||||
extra += `<p class="fn-count">Dein Wartungs-Abo ist beendet – behalte die Funktion bis ${frist} für einmalig ${esc(preis)}.</p>`
|
||||
action += knopf(`Für ${esc(preis)} behalten`, { weg: 'behalten', klasse: 'kb-btn-outline' })
|
||||
} else if (lage === 'abo') {
|
||||
extra += `<p class="fn-count">${ZEITEN_ABO_WIEDER}</p>`
|
||||
}
|
||||
} else if (f.state === 'ordered') {
|
||||
pill = ['Zahlung offen', 'setup']
|
||||
@@ -313,9 +327,11 @@ function zeitenTile(projects, angebot = {}) {
|
||||
const daten = aboEnde && target.zeitenLeer === false && !(typeof f.loeschungAm === 'string' && f.loeschungAm < berlinIsoDay())
|
||||
extra = `<p>${aboEnde ? 'Öffnungszeiten live ist abgeschaltet, weil dein Wartungs-Abo beendet ist.' : ZEITEN_ABGESCHALTET}</p>`
|
||||
if (daten && zeitenTag(f.loeschungAm)) extra += `<p class="fn-count">Deine Eingaben bleiben bis ${zeitenTag(f.loeschungAm)} gespeichert.</p>`
|
||||
if (daten && angebot.inklusive === true) extra += '<p class="fn-count">Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein (meist innerhalb eines Tages).</p>'
|
||||
else if (daten && angebot.inklusive !== false) extra += `<p class="fn-count">${ZEITEN_NICHT_PRUEFBAR}</p>`
|
||||
else action = daten ? knopf(`Für ${esc(preis)} wieder einschalten`, { weg: 'behalten' }) : chat
|
||||
const lage = daten ? zeitenBehaltenLage(angebot) : ''
|
||||
if (!daten) action = chat
|
||||
else if (lage === 'bezahlen') action = knopf(`Für ${esc(preis)} wieder einschalten`, { weg: 'behalten' })
|
||||
else if (lage === 'unklar') extra += `<p class="fn-count">${ZEITEN_NICHT_PRUEFBAR}</p>`
|
||||
else extra += '<p class="fn-count">Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein (meist innerhalb eines Tages).</p>'
|
||||
} else if (angebot.inklusive === true) {
|
||||
extra = '<p>Im Wartungs-Abo enthalten.</p>'
|
||||
action = knopf('Einschalten')
|
||||
|
||||
@@ -47,6 +47,19 @@ const text = (wert) => (typeof wert === 'string' ? wert : '')
|
||||
const istConsent = (wert) => wert !== null && typeof wert === 'object' && typeof wert.version === 'string' && typeof wert.knopf === 'string' && Array.isArray(wert.items)
|
||||
const chatKnopf = () => h('button', { type: 'button', class: 'kb-btn-outline', 'data-view': 'chat' }, 'Frage dazu? Chat öffnen')
|
||||
|
||||
/**
|
||||
* Wo steht das Wartungs-Abo, wenn das Abo-Ende erkannt ist (Behalten)? 'bezahlen': kein Abo (inklusive false UND grund kein_abo), nur dann gibt es
|
||||
* Behalten gegen 30 €. 'abo': das Abo laeuft (grund nicht_aktiv = Probezeit/Zahlung offen, gekuendigt = Kuendigung vorgemerkt; der Server antwortet
|
||||
* beim Behalten dort mit 409 „Abo läuft wieder“): nie ein Bezahlknopf (Z12, R20, R25). 'inklusive': enthalten (inklusive true). 'unklar': Abo nicht
|
||||
* lesbar oder unbekannter Grund (fail-closed, kein Bezahlknopf auf Verdacht). Doppelt zu zeitenBehaltenLage in funktionen.js (die Kachel importiert nichts).
|
||||
*/
|
||||
export function behaltenLage(angebot) {
|
||||
if (angebot?.inklusive === true) return 'inklusive'
|
||||
if (angebot?.inklusive !== false) return 'unklar'
|
||||
if (angebot.grund === 'kein_abo') return 'bezahlen'
|
||||
return angebot.grund === 'nicht_aktiv' || angebot.grund === 'gekuendigt' ? 'abo' : 'unklar'
|
||||
}
|
||||
|
||||
/** Adresse einer Unterlage: https oder /rechtliches/ (sicherLink) oder eine Datei unter /vertragsunterlagen/ dieses Portals, sonst ''. */
|
||||
function unterlagenLink(url) {
|
||||
const sicher = sicherLink(url)
|
||||
@@ -105,7 +118,11 @@ export function renderZeitenBestellen(root, { ctx = {}, angebot = {}, projekt =
|
||||
if (angebot.inklusive === null || angebot.inklusive === undefined) {
|
||||
return zeige(h('div', { class: 'kb-panel tm-info zt-hinweis', 'data-hinweis': 'nicht-pruefbar', role: 'alert' }, h('p', null, NICHT_PRUEFBAR), nochmal ? h('button', { type: 'button', class: 'kb-btn-outline', onclick: nochmal }, 'Noch einmal versuchen') : null, zurueck))
|
||||
}
|
||||
if (modus === 'behalten' && angebot.inklusive === true) {
|
||||
if (modus === 'behalten' && behaltenLage(angebot) !== 'bezahlen') {
|
||||
// das Abo laeuft (wieder) oder ist nicht sicher lesbar: nichts zu bezahlen, nie ein Bezahlknopf auf Verdacht
|
||||
if (behaltenLage(angebot) === 'unklar') {
|
||||
return zeige(h('div', { class: 'kb-panel tm-info zt-hinweis', 'data-hinweis': 'nicht-pruefbar', role: 'alert' }, h('p', null, NICHT_PRUEFBAR), nochmal ? h('button', { type: 'button', class: 'kb-btn-outline', onclick: nochmal }, 'Noch einmal versuchen') : null, zurueck))
|
||||
}
|
||||
return zeige(h('div', { class: 'kb-panel tm-info zt-hinweis', 'data-hinweis': 'abo-laeuft' }, h('h3', null, 'Dein Wartungs-Abo läuft wieder'), h('p', null, 'Öffnungszeiten live ist darin enthalten – du musst nichts bezahlen.'), zurueck))
|
||||
}
|
||||
if (modus === 'behalten') variante = 'behalten'
|
||||
@@ -265,7 +282,7 @@ function dankeText(danke, feature) {
|
||||
* projekt = Eintrag aus GET /api/funktionen (projectId, zeitenLeer), feature = projekt.features.zeiten (state, einbau, ticketWoid, orderedAt,
|
||||
* invoiceUrl, aboEndeAt, disabledReason, loeschungAm), angebot = zeiten (inklusive, preis), kundenansicht = Sitzung in der Kundenansicht,
|
||||
* danke = 'bestellt' | 'behalten' | 'eingeschaltet' (Rueckkehr von Stripe oder Einschalten), meldung = Text am Knopf.
|
||||
* behalten() oeffnet die Behalten-Seite (Knopf „Für 30 € behalten“ nur, wenn sicher ist, dass das Abo nicht wieder laeuft: inklusive false);
|
||||
* behalten() oeffnet die Behalten-Seite (Knopf „Für 30 € behalten“ nur ohne Abo: behaltenLage 'bezahlen', also inklusive false UND grund kein_abo; laeuft das Abo, steht nur der Hinweis);
|
||||
* weiter({ bestellen, meldung }) wird gerufen, wenn „Zahlung fortsetzen“ scheitert (bestellen: es gibt keine offene Zahlungsseite mehr, die Ansicht
|
||||
* zeigt wieder die Bestellseite).
|
||||
*/
|
||||
@@ -273,7 +290,6 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
const state = text(feature?.state) || 'off'
|
||||
const [pilleText, pilleArt] = PILLE[state] || PILLE.setup
|
||||
const preis = text(angebot?.preis) || '30 €'
|
||||
const inklusive = angebot?.inklusive
|
||||
const leer = projekt?.zeitenLeer === true
|
||||
const ticket = feature?.ticketWoid
|
||||
const hatTicket = ticket !== null && ticket !== undefined && String(ticket) !== ''
|
||||
@@ -309,17 +325,21 @@ export function zeitenStatusKarte({ projekt, feature, angebot, kundenansicht, da
|
||||
|
||||
// Behalten (30 €): in Einrichtung/aktiv nach dem erkannten Abo-Ende, abgeschaltet solange die Eingaben noch da sind
|
||||
const behaltenKnopf = (beschriftung) => h('button', { type: 'button', class: 'kb-btn-outline', 'data-aktion': 'behalten', disabled: sperre, onclick: () => { if (typeof behalten === 'function') behalten() } }, beschriftung)
|
||||
const behaltenBox = frist && inklusive === false
|
||||
? h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'behalten' },
|
||||
const lage = behaltenLage(angebot)
|
||||
let behaltenBox = null
|
||||
if (frist && lage === 'bezahlen') {
|
||||
behaltenBox = h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'behalten' },
|
||||
h('span', null, `Dein Wartungs-Abo ist beendet – behalte die Funktion bis ${frist} für einmalig ${preis}.`),
|
||||
behaltenKnopf(`Für ${preis} behalten`))
|
||||
: null
|
||||
} else if (frist && lage === 'abo') {
|
||||
behaltenBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-laeuft' }, h('span', null, 'Dein Wartungs-Abo läuft wieder – Öffnungszeiten live ist darin enthalten.'))
|
||||
}
|
||||
let abgeschaltetBox = null
|
||||
if (daten && inklusive === false) {
|
||||
if (daten && lage === 'bezahlen') {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-warn', role: 'note', 'data-hinweis': 'wieder-einschalten' },
|
||||
h('span', null, `Deine Eingaben bleiben${tagText(feature?.loeschungAm) ? ` bis ${tagText(feature.loeschungAm)}` : ''} gespeichert. Für einmalig ${preis} schaltest du die Funktion wieder ein.`),
|
||||
behaltenKnopf(`Für ${preis} wieder einschalten`))
|
||||
} else if (daten && inklusive === true) {
|
||||
} else if (daten && (lage === 'inklusive' || lage === 'abo')) {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'abo-laeuft' }, h('span', null, 'Dein Wartungs-Abo läuft wieder – wir schalten Öffnungszeiten live automatisch wieder ein (meist innerhalb eines Tages).'))
|
||||
} else if (daten) {
|
||||
abgeschaltetBox = h('div', { class: 'tm-box tm-box-info', role: 'note', 'data-hinweis': 'nicht-pruefbar' }, h('span', null, NICHT_PRUEFBAR))
|
||||
|
||||
+9
-6
@@ -17,7 +17,7 @@
|
||||
Kundenansicht (window.KbView.id() gesetzt): Einschalten, Bestellen und Behalten sind gesperrt, der Server antwortet dort ohnehin mit 403. */
|
||||
|
||||
import { h, infoKarte, request } from './termine-dom.js'
|
||||
import { renderZeitenBestellen, zeitenStatusKarte } from './zeiten-bestellen.js'
|
||||
import { behaltenLage, renderZeitenBestellen, zeitenStatusKarte } from './zeiten-bestellen.js'
|
||||
|
||||
// Auswahl bleibt beim Wechsel zwischen Funktionen und Zeiten erhalten
|
||||
const auswahl = { projectId: '' }
|
||||
@@ -44,10 +44,13 @@ function pickProjekt(projects) {
|
||||
|| projects[0]
|
||||
}
|
||||
|
||||
/** Darf der Kunde die Funktion jetzt behalten (30 €)? Nach dem erkannten Abo-Ende in Einrichtung/aktiv, abgeschaltet nur mit gespeicherten Eingaben. */
|
||||
function behaltenMoeglich(projekt) {
|
||||
/**
|
||||
* Darf der Kunde die Funktion jetzt behalten (30 €)? Nach dem erkannten Abo-Ende in Einrichtung/aktiv, abgeschaltet nur mit gespeicherten Eingaben,
|
||||
* und nur ohne Abo (behaltenLage 'bezahlen': inklusive false UND grund kein_abo); laeuft das Abo (nicht_aktiv, gekuendigt, inklusive), gibt es nichts zu bezahlen.
|
||||
*/
|
||||
function behaltenMoeglich(projekt, angebot) {
|
||||
const feature = projekt?.features?.zeiten
|
||||
if (!feature) return false
|
||||
if (!feature || behaltenLage(angebot) !== 'bezahlen') return false
|
||||
if (feature.state === 'setup' || feature.state === 'active') return Boolean(feature.aboEndeAt)
|
||||
return feature.state === 'disabled' && feature.disabledReason === 'abo_ende' && projekt.zeitenLeer === false
|
||||
}
|
||||
@@ -86,7 +89,7 @@ export async function initZeiten(root, { projectId } = {}) {
|
||||
const key = stand(s.projekt, s.angebot)
|
||||
const editorDa = root.dataset.zeitenKey === key && root.querySelector('.zt-editor') && S?.root === root
|
||||
if (editorDa && !rueckkehr.bestellt && !rueckkehr.behalten) {
|
||||
if (wunschWeg === 'behalten' && behaltenMoeglich(S.projekt)) zeigeBehalten(S)
|
||||
if (wunschWeg === 'behalten' && behaltenMoeglich(S.projekt, S.angebot)) zeigeBehalten(S)
|
||||
return
|
||||
}
|
||||
S = s
|
||||
@@ -199,7 +202,7 @@ function zeichneProjekt(s, { weg = '', meldung = '', bestellformular = false } =
|
||||
return
|
||||
}
|
||||
kopf.replaceChildren(statusKarte())
|
||||
if (weg === 'behalten' && behaltenMoeglich(projekt)) zeigeBehalten(s)
|
||||
if (weg === 'behalten' && behaltenMoeglich(projekt, s.angebot)) zeigeBehalten(s)
|
||||
}
|
||||
|
||||
/** Behalten-Seite (30 €) statt der Statuskarte; der Editor darunter bleibt, nur versteckt, damit nichts verloren geht. „Zurück“ zeigt wieder die Karte. */
|
||||
|
||||
Reference in new issue
Block a user