Webshop E4: Mails (G09-G14 an Käufer, K25/K26 an Händler), shop/mails.js, Mailversand der Warteschlange auch bei Terminplaner off

Käufermails laufen über die Warteschlange des Terminplaners (G09/G12 mit Vorrang),
G09 wird bei Überlänge um den AGB-Text gekürzt (Link statt Text). Der Mailteil des
Minutenjobs und die Aufräumschritte des Tagesjobs laufen auch bei TERMINE_ENABLED=off,
solange SHOP_ENABLED nicht off ist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-05 08:56:40 +00:00
1 parent 38bd56fe8b
commit 0ebd2e6663
15 files changed
+1570 -26

No files matched your search

+87
View File
@@ -3016,3 +3016,90 @@ test('server/index.js: die drei Jobs laufen alle 60 s, alle 5 min und täglich (
}
}
})
// ---------------------------------------------------------------- Webshop: die Mail-Warteschlange gehört auch dem Shop (E4)
const SHOP_MODUS_VORHER = config.shop.mode
afterEach(() => {
config.shop.mode = SHOP_MODUS_VORHER
})
test('Webshop aktiv, Terminplaner off: der Minutenjob sendet die Warteschlange, der Verfall und alles andere bleibt aus', async () => {
await richteFunktionEin()
await legeBuchung('b_abgelaufen', { status: 'gehalten', heldUntil: iso(plus(JETZT, -5 * MIN)) })
legeEintrag('o_faellig', { art: 'G09' })
legeEintrag('o_spaeter', { art: 'G09', naechsterVersuch: iso(plus(JETZT, 10 * MIN)) })
config.termine.mode = 'off'
config.shop.mode = 'pilot'
assert.deepEqual(await minutenJob(JETZT), { eingeschraenkt: true, verfallen: 0, faellig: 1, gesendet: 1, fehler: 0 })
assert.equal(sink.sent.length, 1)
assert.equal(aw.docs('termineOutbox').get('o_faellig').status, 'gesendet')
assert.equal(aw.docs('termineOutbox').get('o_spaeter').status, 'wartet')
assert.equal(status('b_abgelaufen'), 'gehalten', 'der Verfall der Haltungen bleibt aus')
// Shop aus, Terminplaner off: wie bisher nichts
config.shop.mode = 'off'
legeEintrag('o_nochmal', { art: 'G09' })
assert.deepEqual(await minutenJob(JETZT), { aus: true })
assert.equal(aw.docs('termineOutbox').get('o_nochmal').status, 'wartet')
})
test('Webshop aktiv, Terminplaner off und ohne bezahlte Terminplaner: der Tagesjob beendet festgefahrene Mails und räumt die Warteschlange auf, sonst nichts', async () => {
await richteFunktionEin() // aktiv, nicht bezahlt
await legeLoeschFall('b_verfallen', 'verfallen', 1)
legeEintrag('o_festgefahren', { art: 'G09', createdAt: iso(plus(JETZT, -3 * TAG)) })
legeEintrag('o_gesendet', { art: 'G09', status: 'gesendet', an: '', html: '', text: '', naechsterVersuch: iso(plus(JETZT, -MIN)) })
legeEintrag('o_neu', { art: 'G09' })
config.termine.mode = 'off'
config.shop.mode = 'on'
const erg = await taeglicherJob(JETZT)
assert.deepEqual(erg, {
eingeschraenkt: true,
aboAbgleich: { geprueft: 0, abgeschaltet: 0, wiederEin: 0, mails: 0, ausgenommen: 0, fehler: 0 },
abgeschaltet: { funktionen: 0, buchungen: 0 },
gedeckelt: { loeschfristen: 0, abgeschaltet: 0, zellen: 0 },
geloescht: 0,
aktiveBetriebe: null,
festgefahren: 1,
aufgeraeumt: 1,
fehler: 0,
})
assert.equal(aw.docs('termineOutbox').get('o_festgefahren').status, 'fehler')
assert.equal(aw.docs('termineOutbox').get('o_gesendet'), undefined, 'gesendeter Eintrag gelöscht')
assert.equal(aw.docs('termineOutbox').get('o_neu').status, 'wartet')
assert.ok(buchung('b_verfallen'), 'keine Löschfristen der Buchungen')
assert.equal(
tagesbericht(erg),
'[termine] Tagesjob (Modus off, nur Abo-Abgleich und Löschung sowie Mail-Warteschlange): 0 Buchungen gelöscht (abgeschaltete Funktionen 0), 0 Funktionen bereinigt, 1 Outbox, 1 Mails nicht zugestellt, Abo-Abgleich 0 geprüft / 0 abgeschaltet / 0 wieder ein / 0 Mails / 0 ausgenommen, 0 Fehler',
)
})
test('Webshop aktiv, Terminplaner off, bezahlter Terminplaner: Abo-Abgleich und Löschung laufen wie bisher, dazu die Warteschlange', async () => {
await richteFunktionEin(F1, BEZAHLT)
wartung('active')
legeEintrag('o_festgefahren', { art: 'G09', createdAt: iso(plus(JETZT, -3 * TAG)) })
config.termine.mode = 'off'
config.shop.mode = 'pilot'
const erg = await taeglicherJob(JETZT)
assert.equal(erg.eingeschraenkt, true)
assert.equal(erg.aboAbgleich.geprueft, 1)
assert.equal(erg.festgefahren, 1)
assert.equal(erg.aufgeraeumt, 0)
assert.equal(aw.docs('termineOutbox').get('o_festgefahren').status, 'fehler')
})
test('Webshop aktiv, Terminplaner off: schlägt die Guard-Abfrage fehl, läuft nur die Warteschlange (Warnung ohne Kennungen)', async () => {
legeEintrag('o_festgefahren', { art: 'G09', createdAt: iso(plus(JETZT, -3 * TAG)) })
config.termine.mode = 'off'
config.shop.mode = 'on'
aw.failNext({ collection: 'portalFeatures', method: 'GET', status: 500, message: 'Nachbau kaputt' })
const erg = await taeglicherJob(JETZT)
assert.equal(erg.eingeschraenkt, true)
assert.equal(erg.festgefahren, 1)
assert.equal(warnLog().filter((zeile) => /taeglicherJob/.test(zeile) && /Nachbau kaputt/.test(zeile)).length, 1)
assert.doesNotMatch(warnLog().join('\n'), /\bb_|\bf_|\bp_|\bc_|@/)
})