diff --git a/public/unterkunft-buchung.js b/public/unterkunft-buchung.js index c027ed0..a0e963e 100644 --- a/public/unterkunft-buchung.js +++ b/public/unterkunft-buchung.js @@ -30,6 +30,7 @@ kleinunternehmer: 'Gemäß § 19 UStG wird keine Umsatzsteuer berechnet.', laden: 'Die Buchung konnte nicht geladen werden. Bitte versuchen Sie es später erneut.', senden: 'Die Buchung konnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.', + belegt: 'Der Zeitraum ist leider inzwischen belegt. Bitte fragen Sie einen anderen Zeitraum an.', knopf: 'Zahlungspflichtig buchen', } @@ -91,6 +92,11 @@ return [a.firma, a.inhaber].filter(Boolean).join(', ') + (zeile ? ', ' + zeile : '') } + function zahlartenText(d, wahl) { + var z = (d.zahlarten || []).map(function (x) { return x === 'online' ? 'online (Karte, über Stripe)' : 'vor Ort' }) + return 'Zahlarten: ' + z.join(' oder ') + (wahl ? ' (gewählt: ' + (wahl === 'online' ? 'online' : 'vor Ort') + ')' : '') + } + /* ------------------------------------------------------------- Pflichtangaben --- */ function pflichtangaben(d, wahl) { var s = d.summen || {} @@ -99,14 +105,14 @@ var kontakt = [a.email, a.telefon].filter(Boolean).join(', ') var u = d.unterkunft || {} var posten = h('ul', { 'data-tag': 'posten' }, (d.posten || []).map(function (x) { - return h('li', {}, [h('span', { text: x.text }), h('span', { text: euro(x.menge * x.einzelCent) })]) + var bez = x.menge !== 1 ? x.text + ' (' + x.menge + ' × ' + euro(x.einzelCent) + ')' : x.text + return h('li', {}, [h('span', { text: bez }), h('span', { text: euro(x.menge * x.einzelCent) })]) })) var gesamt = 'Gesamtpreis: ' + euro(s.bruttoCent || 0) var ust = [] if (d.steuerart === 'kleinunternehmer') ust.push(TXT.kleinunternehmer) else (s.ust || []).filter(function (z) { return z.satz > 0 }).forEach(function (z) { ust.push(z.satz + ' % USt ' + euro(z.steuerCent)) }) var preisText = d.steuerart === 'kleinunternehmer' ? gesamt + '. ' + TXT.kleinunternehmer : gesamt + (ust.length ? ' inkl. ' + ust.join(' und ') : ' inkl. USt') - var zahlarten = (d.zahlarten || []).map(function (z) { return z === 'online' ? 'online (Karte, über Stripe)' : 'vor Ort' }) var ds = a.datenschutzUrl && /^https:\/\//.test(a.datenschutzUrl) ? a.datenschutzUrl : '' var datenschutz = [p('Datenschutz: ' + (a.firma || 'Der Anbieter') + ' verarbeitet Ihre Angaben zur Durchführung der Buchung und für die Rechnung (Art. 6 Abs. 1 lit. b und c DSGVO). Näheres steht in der Datenschutzerklärung des Anbieters.')] if (ds) datenschutz.push(h('a', { class: 'uk-link', href: ds, target: '_blank', rel: 'noopener noreferrer', text: 'Datenschutzerklärung des Anbieters' })) @@ -117,7 +123,7 @@ p('Personen: ' + d.personen, 'uk-personen'), posten, p(preisText, 'uk-gesamt'), - p('Zahlarten: ' + zahlarten.join(' oder ') + (wahl ? ' (gewählt: ' + (wahl === 'online' ? 'online' : 'vor Ort') + ')' : ''), 'uk-zahlarten'), + p(zahlartenText(d, wahl), 'uk-zahlarten'), p('Stornobedingungen: ' + (d.stornobedingungen || ''), 'uk-storno'), p(TXT.widerruf, 'uk-widerruf'), h('div', { class: 'uk-datenschutz' }, datenschutz), @@ -131,7 +137,7 @@ var g = d.gast || {} var felder = {} function feld(name, bez, wert, extra) { - var input = h('input', Object.assign({ type: 'text', name: name, autocomplete: (extra && extra.ac) || 'off', maxlength: 120 }, {})) + var input = h('input', { type: 'text', name: name, autocomplete: (extra && extra.ac) || 'off', maxlength: 120 }) input.value = wert || '' felder[name] = input var fehler = h('span', { class: 'uk-feldfehler', 'data-fehler': name, hidden: true }) @@ -161,10 +167,16 @@ fLand.wurzel, h('h2', { text: 'Zahlart' }), h('div', { 'data-tag': 'zahlart' }, radios), - pflichtangaben(d), + pflichtangaben(d, gewaehlt), knopf, meldung, ]) + radios.forEach(function (l) { + l.querySelector('input').addEventListener('change', function () { + var z = form.querySelector('.uk-zahlarten') + if (z) z.textContent = zahlartenText(d, waehl()) + }) + }) function waehl() { var r = radios.map(function (l) { return l.querySelector('input') }).find(function (i) { return i.checked }); return r ? r.value : gewaehlt } var sendet = false function fehlerAnzeigen(felderFehler, text) { @@ -196,10 +208,12 @@ bestaetigt(Object.assign({}, d, { status: 'gebucht', zahlart: zahlart, bezahlt: false, rechnungNummer: r.body.rechnungNummer })) return } + if (r.status === 404) return nurText('Buchung', TXT.ungueltig) + if (r.status === 410 || r.body.fehler === 'frist_abgelaufen' || r.body.fehler === 'abgelaufen') return nurText('Buchung', TXT.abgelaufen) + if (r.body.fehler === 'zeitraum_belegt') return nurText('Buchung', TXT.belegt) + if (r.status === 409 || r.ok) { laden(); return } sendet = false knopf.removeAttribute('disabled') - if (r.status === 404) return nurText('Buchung', TXT.ungueltig) - if (r.body.fehler === 'abgelaufen') return nurText('Buchung', TXT.abgelaufen) fehlerAnzeigen(r.body.felder, r.status === 400 && r.body.felder ? 'Bitte prüfen Sie Ihre Angaben.' : TXT.senden) }, function () { sendet = false @@ -217,7 +231,7 @@ if (d.rechnungNummer) kinder.push(p('Rechnung ' + d.rechnungNummer)) kinder.push(h('a', { class: 'uk-link', 'data-tag': 'rechnung', href: basis + '/rechnung.pdf' + tokenQuery + '&art=rechnung', text: 'Rechnung herunterladen (PDF)' })) var online = (d.zahlarten || []).indexOf('online') >= 0 - if (d.bezahlt) kinder.push(p('Die Zahlung ist eingegangen. Vielen Dank!')) + if (d.bezahlt || d.status === 'bezahlt') kinder.push(p('Bezahlt – vielen Dank.')) else if (d.zahlart === 'online' && online) { var meldung = h('div', { class: 'uk-fehler', role: 'alert', hidden: true }) var knopf = h('button', { type: 'button', class: 'uk-knopf', 'data-tag': 'online-zahlen', text: 'Jetzt online bezahlen' }) @@ -241,8 +255,9 @@ /* ----------------------------------------------------------------------- Start --- */ function darstellen(d) { akzent(d) + if (d.unterkunft && d.unterkunft.name) document.title = 'Buchung – ' + d.unterkunft.name if (d.status === 'freigegeben') zeige(formular(d), hinweisAbbruch()) - else if (d.status === 'gebucht') bestaetigt(d) + else if (d.status === 'gebucht' || d.status === 'bezahlt') bestaetigt(d) else if (d.status === 'abgelaufen') nurText('Buchung', TXT.abgelaufen) else if (d.status === 'abgelehnt') nurText('Buchung', 'Diese Anfrage wurde leider abgelehnt.') else if (d.status === 'storniert') nurText('Buchung', 'Diese Buchung wurde storniert.') @@ -250,9 +265,12 @@ } if (!token || !id) { nurText('Buchung', TXT.ungueltig); return } - api('', 'GET').then(function (r) { + function laden() { + return api('', 'GET').then(function (r) { if (r.status === 404 || r.status === 401 || r.status === 403) return nurText('Buchung', TXT.ungueltig) if (!r.ok) return nurText('Buchung', TXT.laden) darstellen(r.body) }, function () { nurText('Buchung', TXT.laden) }) + } + laden() })() diff --git a/public/unterkunft-seite.html b/public/unterkunft-seite.html index 3f263ec..f7987e1 100644 --- a/public/unterkunft-seite.html +++ b/public/unterkunft-seite.html @@ -4,7 +4,8 @@ -Unterkunft + +Buchung diff --git a/server/routes/unterkunftSeite.js b/server/routes/unterkunftSeite.js index 7273d25..8f6f526 100644 --- a/server/routes/unterkunftSeite.js +++ b/server/routes/unterkunftSeite.js @@ -45,7 +45,7 @@ function seite(res, next, siteKey, ansicht) { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store', 'X-Content-Type-Options': 'nosniff', - 'Referrer-Policy': 'strict-origin-when-cross-origin', + 'Referrer-Policy': 'no-referrer', 'Content-Security-Policy': CSP, 'X-Robots-Tag': 'noindex', }) diff --git a/test/unterkunftSeite.test.js b/test/unterkunftSeite.test.js index 8c07d8b..bafcddd 100644 --- a/test/unterkunftSeite.test.js +++ b/test/unterkunftSeite.test.js @@ -32,6 +32,8 @@ test('Widget-Seite und Buchungsseite: 200, Schluessel escaped, Schutz-Header', a assert.match(r.text, /