Shop Review B I-6, I-7: Archiv der bestaetigten Rechtstexte, G09 ohne Link-Ersatz
I-6: Neue Collection shopRechtstexteArchiv (ID r + 35 Hex aus sha256(shopId + hash)); das Bestaetigen legt die Fassung samt Versand-/Zahlungstext und Gewaehrleistungstext ab, G09 zeigt die Fassung zum rechtstexteHash der Bestellung (Rueckfall mit Warnung auf die aktuell bestaetigte), loescheShopDaten loescht das Archiv mit. I-7: Grenzen AGB 20000, Belehrung 12000, Formular 3000 plus Pruefung der maskierten Laenge beim Speichern; G09 ersetzt die AGB nie mehr durch einen Link, sondern scheitert (Marker bleibt offen, wird wiederholt). Test mit Texten in Hoechstlaenge gegen die Grenzen der Warteschlange. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f4f360a21
commit
08d837d690
17 files changed
+288
-65
No files matched your search
@@ -7,6 +7,7 @@ import { pruefeNow, zeitMs } from '../termine/util.js'
|
||||
import { datumKurz, uhrzeit } from '../termine/zeit.js'
|
||||
import { euro } from './geld.js'
|
||||
import { oeffentlicheRechtstexte } from './kontext.js'
|
||||
import { holeArchivierteRechtstexte } from './archiv.js'
|
||||
import { gewaehrleistungsHinweis } from './rechtstexte.js'
|
||||
|
||||
/**
|
||||
@@ -162,16 +163,17 @@ const datumVon = (now, ...zeitpunkte) => {
|
||||
* @param {{ doc?: object, shop: object }} ctx
|
||||
* @param {object|null} order
|
||||
* @param {Date} [now] Ersatzdatum, wenn die Bestellung keinen Zeitpunkt hat
|
||||
* @param {{ archiv?: object|null }} [optionen] archiv: archivierte Rechtstexte der Bestellung (archiv.js); ohne sie die aktuell bestaetigten
|
||||
* @returns {Record<string, string>}
|
||||
*/
|
||||
export function kaeuferWerte(ctx, order, now = new Date()) {
|
||||
export function kaeuferWerte(ctx, order, now = new Date(), { archiv = null } = {}) {
|
||||
if (!istObjekt(ctx?.shop)) throw new TypeError('kaeuferWerte: ctx.shop fehlt')
|
||||
const snap = schnappschuss(order)
|
||||
const e = einstellungenVon(ctx)
|
||||
const auszug = istObjekt(snap.einstellungenAuszug) ? snap.einstellungenAuszug : {}
|
||||
const anbieter = anbieterVon(ctx, snap)
|
||||
const abholung = { ...(e.abholung ?? {}), ...(istObjekt(auszug.abholung) ? auszug.abholung : {}) }
|
||||
const rechtstexte = oeffentlicheRechtstexte(ctx) ?? {}
|
||||
const rechtstexte = (istObjekt(archiv) ? archiv : oeffentlicheRechtstexte(ctx)) ?? {}
|
||||
const summen = istObjekt(snap.summen) ? snap.summen : snap
|
||||
const texte = istObjekt(summen.texte) ? summen.texte : {}
|
||||
const cent = (wert) => (Number.isInteger(wert) ? euro(wert) : LEER)
|
||||
@@ -204,7 +206,7 @@ export function kaeuferWerte(ctx, order, now = new Date()) {
|
||||
agb: oderLeer(rechtstexte.agb),
|
||||
widerrufsbelehrung: oderLeer(rechtstexte.widerrufsbelehrung),
|
||||
widerrufsformular: oderLeer(rechtstexte.widerrufsformular),
|
||||
gewaehrleistung: gewaehrleistungsHinweis(),
|
||||
gewaehrleistung: hatText(rechtstexte.gewaehrleistung) ? rechtstexte.gewaehrleistung.trim() : gewaehrleistungsHinweis(),
|
||||
// fuer G11 (Standard, wenn der Haendler keinen Hinweis hinterlegt hat)
|
||||
hinweis: text(abholung.hinweis) || ABHOLUNG_HINWEIS,
|
||||
}
|
||||
@@ -219,8 +221,8 @@ export function kaeuferWerte(ctx, order, now = new Date()) {
|
||||
* G12 name, bestellnummer (Eingaben des Kaeufers; ohne Angabe die der Bestellung), eingang (Date | ISO, Pflicht), nachricht
|
||||
* G13 betragCent (Pflicht, ganze Cent), grund
|
||||
*/
|
||||
function kaeuferMail(art, ctx, order, extra, now) {
|
||||
const alle = kaeuferWerte(ctx, order, now)
|
||||
function kaeuferMail(art, ctx, order, extra, now, archiv = null) {
|
||||
const alle = kaeuferWerte(ctx, order, now, { archiv })
|
||||
const blocks = {}
|
||||
switch (art) {
|
||||
case 'G09':
|
||||
@@ -257,38 +259,45 @@ function kaeuferMail(art, ctx, order, extra, now) {
|
||||
return { values, blocks: Object.fromEntries(Object.entries(blocks).filter(([name]) => vorlage.blocks.includes(name))) }
|
||||
}
|
||||
|
||||
/** Archivierte Rechtstexte zum Hash der Bestellung (G09); null (mit Warnung) fuer Bestellungen ohne Archiveintrag. Datenbankfehler werfen. */
|
||||
async function archivFuer(ctx, order) {
|
||||
const hash = text(schnappschuss(order).rechtstexteHash)
|
||||
if (hash === '') return null
|
||||
const shopId = ctx?.shop?.id || ctx?.doc?.$id
|
||||
const archiv = await holeArchivierteRechtstexte(shopId, hash)
|
||||
if (!archiv) console.warn(`[shop-mails] G09: Rechtstexte der Bestellung ${String(order?.$id).slice(0, 36)} fehlen im Archiv, die aktuell bestätigte Fassung wird verwendet`)
|
||||
return archiv
|
||||
}
|
||||
|
||||
const zuLang = (mail) => mail.html.length > MAX_HTML || mail.text.length > MAX_TEXT
|
||||
|
||||
// ---------------------------------------------------------------- Einreihen
|
||||
|
||||
/**
|
||||
* Reiht eine Kaeufermail (G09-G14) in die Warteschlange ein. Empfaenger ist extra.an, sonst order.kaeuferEmail. bookingId ist
|
||||
* order.$id, sonst extra.bookingId (z. B. die ID des Widerrufs bei G12), sonst "-". Ist G09 zu lang fuer die Warteschlange
|
||||
* (AGB, Widerrufsbelehrung und Formular koennen zusammen sehr lang sein), ersetzt der Server den AGB-Text durch einen Satz mit
|
||||
* Link auf die AGB im Shop; Widerrufsbelehrung, Formular und Gewaehrleistungshinweis bleiben immer vollstaendig.
|
||||
* order.$id, sonst extra.bookingId (z. B. die ID des Widerrufs bei G12), sonst "-". G09 enthaelt die Rechtstexte in der Fassung der
|
||||
* Bestellung (archiv.js, Hash bestellung.rechtstexteHash); fehlt sie im Archiv, gilt mit Warnung die aktuell bestaetigte. Ist die
|
||||
* Mail zu lang fuer die Warteschlange, scheitert sie (ok: false), statt Texte durch einen Link zu ersetzen.
|
||||
* @param {'G09'|'G10'|'G11'|'G12'|'G13'|'G14'} art
|
||||
* @param {{ doc: object, shop: object }} ctx Shop-Kontext (kontext.js)
|
||||
* @param {object|null} order shopOrders-Dokument (bei G12 ohne passende Bestellung null)
|
||||
* @param {object} [extra] siehe kaeuferMail
|
||||
* @param {Date} [now]
|
||||
* @returns {Promise<{ ok: true, eintrag: object, gekuerzt: boolean } | { ok: false, fehler: string }>}
|
||||
* @returns {Promise<{ ok: true, eintrag: object } | { ok: false, fehler: string }>}
|
||||
*/
|
||||
export async function anKaeufer(art, ctx, order, extra = {}, now = new Date()) {
|
||||
try {
|
||||
if (!GAST_ARTEN.includes(art)) throw new TypeError(`${String(art).slice(0, 20)} ist keine Käufermail`)
|
||||
pruefeNow(now, 'anKaeufer')
|
||||
extra = extra ?? {}
|
||||
const { values, blocks } = kaeuferMail(art, ctx, order, extra, now)
|
||||
const archiv = art === 'G09' ? await archivFuer(ctx, order) : null
|
||||
const { values, blocks } = kaeuferMail(art, ctx, order, extra, now, archiv)
|
||||
const an = hatText(extra.an) ? extra.an.trim() : text(order?.kaeuferEmail)
|
||||
if (an === '') throw new TypeError('Empfänger fehlt')
|
||||
|
||||
let gekuerzt = false
|
||||
let mail = renderMail(art, values, blocks)
|
||||
if (art === 'G09' && zuLang(mail)) {
|
||||
values.agb = `Die vollständigen AGB finden Sie unter ${shopSeite(ctx)}?ansicht=agb`
|
||||
mail = renderMail(art, values, blocks)
|
||||
gekuerzt = true
|
||||
}
|
||||
// Nie ein Link statt der Texte: die Bestaetigung muss den Vertragsinhalt selbst enthalten (§ 312f Abs. 2 BGB). Passt die Mail nicht
|
||||
// in die Warteschlange, scheitert sie hier; der Aufrufer behaelt den Marker offen, zaehlt den Versuch und wiederholt.
|
||||
const mail = renderMail(art, values, blocks)
|
||||
if (zuLang(mail)) throw new TypeError('Die Mail ist zu lang für die Warteschlange')
|
||||
|
||||
const e = einstellungenVon(ctx)
|
||||
@@ -306,7 +315,7 @@ export async function anKaeufer(art, ctx, order, extra = {}, now = new Date()) {
|
||||
},
|
||||
now,
|
||||
)
|
||||
return { ok: true, eintrag, gekuerzt }
|
||||
return { ok: true, eintrag }
|
||||
} catch (err) {
|
||||
console.warn(`[shop-mails] ${String(art).slice(0, 20)} nicht eingereiht: ${err?.name}: ${String(err?.message).slice(0, 120)}`)
|
||||
return { ok: false, fehler: 'Die E-Mail an den Käufer konnte nicht eingereiht werden.' }
|
||||
|
||||
Reference in new issue
Block a user