Tagesjob: Abo-Abgleich vor der Löschung, auch bei TERMINE_ENABLED=off mit bezahlten Dokumenten
taeglicherJob ruft gleicheAboAb(now) nach den Löschfristen und vor der Löschung
abgeschalteter Funktionen auf (Schritt „Abo-Abgleich“, ein Fehler dort stoppt weder
Löschung noch Rest; die Fehler des Abgleichs zählen in fehler). Das Ergebnis trägt
aboAbgleich, tagesbericht hängt „Abo-Abgleich N geprüft / N abgeschaltet / N wieder ein /
N Mails / N ausgenommen“ vor „N Fehler“ an.
fuehreAus nimmt trotzAus: Im Modus off läuft der Tagesjob nur, wenn es ein vom Kauf
freigeschaltetes Termine-Dokument gibt (eine Abfrage, limit 1; bei einem Lesefehler
Warnung und { aus: true }). Dann läuft ein eingeschränkter Lauf (eingeschraenkt: true,
gleiche Überlappungssperre): nur Abo-Abgleich und Löschung abgeschalteter Funktionen,
mit eigener Berichtszeile und den Feldern, die meldeTagesjob und die Statusroute lesen.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1c74c89feb
commit
91e7f7d8a3
2 files changed
+540
-66
No files matched your search
+157
-50
@@ -1,7 +1,8 @@
|
||||
import { config } from '../../config.js'
|
||||
import { Query, deleteDocument, getDocument, listAllDocuments } from '../appwriteAdmin.js'
|
||||
import { Query, deleteDocument, getDocument, listAllDocuments, listDocuments } from '../appwriteAdmin.js'
|
||||
import { withLocks } from '../keyedLock.js'
|
||||
import { gibFreiSicher, iso, vertragAm } from './ablauf.js'
|
||||
import { gleicheAboAb } from './aboAbgleich.js'
|
||||
import { meldeTagesjob, pruefeBetrieb, zaehleAktiveBetriebe } from './betrieb.js'
|
||||
import {
|
||||
abgelaufeneHaltungen,
|
||||
@@ -33,12 +34,19 @@ import { gibZellenFrei, raeumeVerwaisteZellen } from './zellen.js'
|
||||
* Anmeldepause => Log und Team-Mail, betrieb.js)
|
||||
* fuenfMinutenJob alle 5 Minuten Erinnerungen (G05); bestaetigt 1 Stunde nach Ende => erledigt; Anfragen mit Beginn in
|
||||
* der Vergangenheit => abgelehnt (vom System, Zellen frei, keine Mail)
|
||||
* taeglicherJob taeglich Loeschfristen (11), festgefahrene und alte Mails, abgeschaltete Funktionen, verwaiste Zellen;
|
||||
* zuletzt die aktiven Betriebe zaehlen und den Lauf melden (Fehler, Loeschdeckel in Folge,
|
||||
* ab 20 Betrieben => Log und Team-Mail, meldeTagesjob in betrieb.js)
|
||||
* taeglicherJob taeglich Loeschfristen (11), Abo-Abgleich (aboAbgleich.js: der Terminplaner endet mit dem Wartungs-Abo),
|
||||
* abgeschaltete Funktionen, festgefahrene und alte Mails, verwaiste Zellen; der Abgleich laeuft VOR
|
||||
* der Loeschung abgeschalteter Funktionen, damit ein Terminplaner, dessen Abo heute zurueckkehrt,
|
||||
* nicht mehr geloescht wird; zuletzt die aktiven Betriebe zaehlen und den Lauf melden (Fehler,
|
||||
* Loeschdeckel in Folge, ab 20 Betrieben => Log und Team-Mail, meldeTagesjob in betrieb.js)
|
||||
*
|
||||
* Gemeinsame Regeln:
|
||||
* - Modus off: jeder Job kehrt sofort zurueck, ohne Abfrage, mit { aus: true }.
|
||||
* - Modus off: Minuten- und Fuenf-Minuten-Job kehren sofort zurueck, ohne Abfrage, mit { aus: true }. Der Tagesjob stellt eine einzige
|
||||
* Frage (Guard: gibt es ein vom Kauf freigeschaltetes Termine-Dokument, unlockedByPurchase true?); ohne bezahltes Dokument oder bei
|
||||
* einem Lesefehler (Warnung) kehrt auch er mit { aus: true } zurueck. Mit bezahltem Dokument laeuft ein eingeschraenkter Tagesjob
|
||||
* (`eingeschraenkt: true`): nur der Abo-Abgleich und die Loeschung abgeschalteter Funktionen, damit ein beendetes Abo auch bei
|
||||
* TERMINE_ENABLED=off zum Abschalten und zur Loeschung nach 30 Tagen fuehrt. Loeschfristen der Buchungen, Warteschlange und Zellen
|
||||
* bleiben aus.
|
||||
* - Keine ueberlappenden Laeufe: laeuft ein Job noch, wird der naechste Takt uebersprungen ({ uebersprungen: true }, einmal im
|
||||
* Log je Ueberlappung). Die Sperre gilt je `job:teil` (fuehreAus mit { teil }): haengt beim Minutenjob der Verfall, versendet der
|
||||
* naechste Takt trotzdem die Warteschlange und umgekehrt; uebersprungen gilt der Takt erst, wenn beide Teile uebersprungen sind
|
||||
@@ -107,10 +115,24 @@ async function schritt(lauf, was, fn, ersatz) {
|
||||
* Rahmen jedes Jobs: Schalter, Ueberlappung, Laufdaten. => Ergebnis von arbeit(lauf) | { aus: true } | { uebersprungen: true }
|
||||
* `teil` (Name eines Teiljobs, z. B. „Verfall“) gibt dem Teil eine eigene Ueberlappungssperre `job:teil` und nennt ihn in der
|
||||
* Warnung (`job (teil)`); `lauf.job` bleibt der Jobname (Fehlerlog, Warnung bei fehlender Collection).
|
||||
* `trotzAus` (async, => wahr/falsch): im Modus off laeuft der Job nur, wenn es wahr liefert; dann steht `lauf.eingeschraenkt` auf true
|
||||
* und der Job beschraenkt sich auf das, was auch bei abgeschaltetem Terminplaner gelten muss. Wirft es, wird gewarnt (ohne Kennungen)
|
||||
* und { aus: true } geliefert; ohne `trotzAus` gilt Modus off wie bisher (sofort { aus: true }, ohne Abfrage). Die Ueberlappungssperre
|
||||
* gilt unveraendert je Job (bzw. `job:teil`), der eingeschraenkte Lauf teilt sie mit dem vollen.
|
||||
*/
|
||||
async function fuehreAus(job, now, arbeit, { teil = null } = {}) {
|
||||
async function fuehreAus(job, now, arbeit, { teil = null, trotzAus = null } = {}) {
|
||||
const nowMs = pruefeNow(now, job)
|
||||
if (termineAus()) return { aus: true }
|
||||
let eingeschraenkt = false
|
||||
if (termineAus()) {
|
||||
if (trotzAus === null) return { aus: true }
|
||||
try {
|
||||
if (!(await trotzAus())) return { aus: true }
|
||||
} catch (err) {
|
||||
console.warn(`[termine-jobs] ${job}: Modus off, die Prüfung auf bezahlte Terminplaner ist fehlgeschlagen, der Job bleibt aus: ${fehlerText(err)}`)
|
||||
return { aus: true }
|
||||
}
|
||||
eingeschraenkt = true
|
||||
}
|
||||
const sperre = teil === null ? job : `${job}:${teil}`
|
||||
if (laufend.has(sperre)) {
|
||||
if (!ueberlappGemeldet.has(sperre)) {
|
||||
@@ -121,7 +143,7 @@ async function fuehreAus(job, now, arbeit, { teil = null } = {}) {
|
||||
}
|
||||
laufend.add(sperre)
|
||||
try {
|
||||
return await arbeit({ job, nowMs, fehler: 0, gedeckelt: 0 })
|
||||
return await arbeit({ job, nowMs, fehler: 0, gedeckelt: 0, eingeschraenkt })
|
||||
} finally {
|
||||
laufend.delete(sperre)
|
||||
ueberlappGemeldet.delete(sperre)
|
||||
@@ -634,51 +656,92 @@ export function loescheAbgeschalteteFunktionen(now) {
|
||||
/** Summe aller Loeschzaehler eines Tagesjobs: Buchungen aus den Loeschfristen und aus abgeschalteten Funktionen (Zellen, Outbox und Mails zaehlen nicht). */
|
||||
const buchungenGeloescht = (loeschfristen, abgeschaltet) => Object.values(loeschfristen).reduce((summe, n) => summe + n, 0) + abgeschaltet.buchungen
|
||||
|
||||
/** Zaehler des Abo-Abgleichs, wenn der Schritt selbst scheitert (gleicheAboAb wirft nie; der Fehler steht dann schon in lauf.fehler). */
|
||||
const aboErsatz = () => ({ geprueft: 0, abgeschaltet: 0, wiederEin: 0, mails: 0, ausgenommen: 0, fehler: 0 })
|
||||
|
||||
/**
|
||||
* Guard fuer den Modus off (fuehreAus: trotzAus): gibt es ein vom Kauf freigeschaltetes Termine-Dokument? Eine einzige Abfrage
|
||||
* (limit 1) ohne Kunden- oder Gastdaten; ein Lesefehler wirft und haelt den Tagesjob aus.
|
||||
*/
|
||||
async function gibtBezahlteTerminplaner() {
|
||||
const treffer = await listDocuments(config.collections.portalFeatures, [
|
||||
Query.equal('featureKey', 'termine'),
|
||||
Query.equal('unlockedByPurchase', true),
|
||||
Query.limit(1),
|
||||
])
|
||||
return treffer.length > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Abo-Abgleich (aboAbgleich.js) als Schritt des Tagesjobs: ein Fehler stoppt weder die Loeschung noch den Rest. Die Fehler des
|
||||
* Abgleichs (Lese- und Versandfehler, schon im Log) kommen in lauf.fehler. => Zaehler des Abgleichs
|
||||
*/
|
||||
async function aboAbgleichSchritt(lauf, now) {
|
||||
const abgleich = await schritt(lauf, 'Abo-Abgleich', () => gleicheAboAb(now), aboErsatz())
|
||||
lauf.fehler += abgleich.fehler
|
||||
return abgleich
|
||||
}
|
||||
|
||||
/**
|
||||
* Taeglich: festgefahrene Mails beenden (vor dem Aufraeumen, damit sie 48 Stunden lang als Zustellfehler sichtbar bleiben),
|
||||
* Loeschfristen, abgeschaltete Funktionen, alte Warteschlangen-Eintraege, verwaiste Zellen; zuletzt die aktiven Betriebe zaehlen
|
||||
* (ein nicht lesbarer Zaehler ergibt null und einen Fehler) und den Lauf ans Team melden (meldeTagesjob: Fehler, Loeschdeckel an
|
||||
* 3 Tagen in Folge, ab 20 Betrieben; wirft nie). Bricht der Job unerwartet ab, erfaehrt das Team es ebenfalls, und der Fehler geht
|
||||
* unveraendert an den Aufrufer.
|
||||
* `geloescht` = Summe aller Loeschzaehler (Buchungen aus Loeschfristen und abgeschalteten Funktionen), `aktiveBetriebe` = Zahl oder null.
|
||||
* Loeschfristen, Abo-Abgleich (der Terminplaner endet mit dem Wartungs-Abo; VOR der Loeschung, damit ein Betrieb, dessen Abo heute
|
||||
* zurueckkehrt, nicht mehr geloescht wird), abgeschaltete Funktionen, alte Warteschlangen-Eintraege, verwaiste Zellen; zuletzt die
|
||||
* aktiven Betriebe zaehlen (ein nicht lesbarer Zaehler ergibt null und einen Fehler) und den Lauf ans Team melden (meldeTagesjob:
|
||||
* Fehler, Loeschdeckel an 3 Tagen in Folge, ab 20 Betrieben; wirft nie). Bricht der Job unerwartet ab, erfaehrt das Team es ebenfalls,
|
||||
* und der Fehler geht unveraendert an den Aufrufer.
|
||||
* `geloescht` = Summe aller Loeschzaehler (Buchungen aus Loeschfristen und abgeschalteten Funktionen), `aktiveBetriebe` = Zahl oder null,
|
||||
* `aboAbgleich` = Zaehler des Abgleichs { geprueft, abgeschaltet, wiederEin, mails, ausgenommen, fehler }.
|
||||
* Modus off: ohne vom Kauf freigeschaltetes Termine-Dokument { aus: true } nach genau einer Abfrage (Guard). Mit einem bezahlten
|
||||
* Dokument laeuft der eingeschraenkte Tagesjob nur mit Abo-Abgleich und Loeschung abgeschalteter Funktionen (Loeschfristen der
|
||||
* Buchungen, Warteschlange und Zellen bleiben aus; ob die uebrigen Schritte dort auch laufen sollen, ist offen). Sein Ergebnis traegt
|
||||
* dieselben Felder, die meldeTagesjob und die Statusroute lesen: { eingeschraenkt: true, aboAbgleich, abgeschaltet, gedeckelt
|
||||
* { loeschfristen: 0, abgeschaltet, zellen: 0 }, geloescht, aktiveBetriebe: null, fehler }. Beide Laeufe teilen die Ueberlappungssperre.
|
||||
* @param {Date} now
|
||||
* @returns {Promise<object | { aus: true } | { uebersprungen: true }>}
|
||||
*/
|
||||
export async function taeglicherJob(now) {
|
||||
let aktuellerLauf = null // gesetzt, sobald der Rahmen den Job startet (fuer die Meldung eines Abbruchs)
|
||||
try {
|
||||
return await fuehreAus('taeglicherJob', now, async (lauf) => {
|
||||
aktuellerLauf = lauf
|
||||
const festgefahren = await schritt(lauf, 'Festgefahrene Mails', () => beendeFestgefahrene(now), 0)
|
||||
const { gedeckelt: loeschfristenGedeckelt, ...loeschung } = await loeschfristen(lauf, now)
|
||||
const { gedeckelt: abgeschaltetGedeckelt, ...abgeschaltet } = await schritt(lauf, 'Abgeschaltete Funktionen', () => loescheAbgeschaltete(lauf), {
|
||||
funktionen: 0,
|
||||
buchungen: 0,
|
||||
gedeckelt: 0,
|
||||
})
|
||||
const aufgeraeumt = await schritt(lauf, 'Warteschlange aufräumen', () => raeumeAuf(now), 0)
|
||||
const zellenErgebnis = await schritt(lauf, 'Verwaiste Zellen', () => raeumeVerwaisteZellen(now), { geloescht: 0, gedeckelt: false })
|
||||
if (zellenErgebnis.gedeckelt) {
|
||||
console.warn(`[termine-jobs] ${lauf.job}: Zellen-Sweep endet am Deckel von ${config.termine.zellenDeckel} je Lauf, der Rest folgt an den nächsten Tagen`)
|
||||
}
|
||||
const aktiveBetriebe = await schritt(lauf, 'Aktive Betriebe', () => zaehleAktiveBetriebe(), null)
|
||||
// gedeckelt: Schritte bzw. Funktionen, die am Deckel endeten (zellen: 1, wenn der Zellen-Sweep daran endete); getrennt vom Loeschzaehler, damit die Summe im Tagesbericht stimmt
|
||||
const gedeckelt = { loeschfristen: loeschfristenGedeckelt, abgeschaltet: abgeschaltetGedeckelt, zellen: zellenErgebnis.gedeckelt ? 1 : 0 }
|
||||
const ergebnis = {
|
||||
festgefahren,
|
||||
loeschfristen: loeschung,
|
||||
gedeckelt,
|
||||
abgeschaltet,
|
||||
aufgeraeumt,
|
||||
zellen: zellenErgebnis.geloescht,
|
||||
geloescht: buchungenGeloescht(loeschung, abgeschaltet),
|
||||
aktiveBetriebe,
|
||||
fehler: lauf.fehler,
|
||||
}
|
||||
// Zuletzt: die Meldung sieht den ganzen Lauf; sie wirft nie, ein Fehler dort stuende nur im Log
|
||||
await schritt(lauf, 'Tagesjob-Meldung', () => meldeTagesjob(ergebnis, now), [])
|
||||
return ergebnis
|
||||
})
|
||||
return await fuehreAus(
|
||||
'taeglicherJob',
|
||||
now,
|
||||
async (lauf) => {
|
||||
aktuellerLauf = lauf
|
||||
if (lauf.eingeschraenkt) return eingeschraenkterTagesjob(lauf, now)
|
||||
const festgefahren = await schritt(lauf, 'Festgefahrene Mails', () => beendeFestgefahrene(now), 0)
|
||||
const { gedeckelt: loeschfristenGedeckelt, ...loeschung } = await loeschfristen(lauf, now)
|
||||
const aboAbgleich = await aboAbgleichSchritt(lauf, now)
|
||||
const { gedeckelt: abgeschaltetGedeckelt, ...abgeschaltet } = await schritt(lauf, 'Abgeschaltete Funktionen', () => loescheAbgeschaltete(lauf), {
|
||||
funktionen: 0,
|
||||
buchungen: 0,
|
||||
gedeckelt: 0,
|
||||
})
|
||||
const aufgeraeumt = await schritt(lauf, 'Warteschlange aufräumen', () => raeumeAuf(now), 0)
|
||||
const zellenErgebnis = await schritt(lauf, 'Verwaiste Zellen', () => raeumeVerwaisteZellen(now), { geloescht: 0, gedeckelt: false })
|
||||
if (zellenErgebnis.gedeckelt) {
|
||||
console.warn(`[termine-jobs] ${lauf.job}: Zellen-Sweep endet am Deckel von ${config.termine.zellenDeckel} je Lauf, der Rest folgt an den nächsten Tagen`)
|
||||
}
|
||||
const aktiveBetriebe = await schritt(lauf, 'Aktive Betriebe', () => zaehleAktiveBetriebe(), null)
|
||||
// gedeckelt: Schritte bzw. Funktionen, die am Deckel endeten (zellen: 1, wenn der Zellen-Sweep daran endete); getrennt vom Loeschzaehler, damit die Summe im Tagesbericht stimmt
|
||||
const gedeckelt = { loeschfristen: loeschfristenGedeckelt, abgeschaltet: abgeschaltetGedeckelt, zellen: zellenErgebnis.gedeckelt ? 1 : 0 }
|
||||
const ergebnis = {
|
||||
festgefahren,
|
||||
loeschfristen: loeschung,
|
||||
gedeckelt,
|
||||
abgeschaltet,
|
||||
aboAbgleich,
|
||||
aufgeraeumt,
|
||||
zellen: zellenErgebnis.geloescht,
|
||||
geloescht: buchungenGeloescht(loeschung, abgeschaltet),
|
||||
aktiveBetriebe,
|
||||
fehler: lauf.fehler,
|
||||
}
|
||||
// Zuletzt: die Meldung sieht den ganzen Lauf; sie wirft nie, ein Fehler dort stuende nur im Log
|
||||
await schritt(lauf, 'Tagesjob-Meldung', () => meldeTagesjob(ergebnis, now), [])
|
||||
return ergebnis
|
||||
},
|
||||
{ trotzAus: gibtBezahlteTerminplaner },
|
||||
)
|
||||
} catch (err) {
|
||||
// Abbruch ausserhalb der Schritte: das Team erfaehrt es; der Fehler (Text im Log des Aufrufers) geht unveraendert weiter
|
||||
if (aktuellerLauf) await schritt(aktuellerLauf, 'Tagesjob-Meldung', () => meldeTagesjob({ abbruch: true, fehler: aktuellerLauf.fehler + 1 }, now), [])
|
||||
@@ -686,26 +749,70 @@ export async function taeglicherJob(now) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Der eingeschraenkte Tagesjob (Modus off, es gibt bezahlte Terminplaner): erst der Abo-Abgleich, dann die Loeschung abgeschalteter
|
||||
* Funktionen (gleiche Reihenfolge wie im vollen Lauf). Mehr nicht: keine Loeschfristen der Buchungen, keine Warteschlange, keine
|
||||
* Zellen, keine Zaehlung der aktiven Betriebe (`aktiveBetriebe: null`). Das Ergebnis traegt auch die Felder, die meldeTagesjob liest,
|
||||
* und wird wie im vollen Lauf gemeldet (Fehler gehen als Vorfall ans Team).
|
||||
*/
|
||||
async function eingeschraenkterTagesjob(lauf, now) {
|
||||
const aboAbgleich = await aboAbgleichSchritt(lauf, now)
|
||||
const { gedeckelt: abgeschaltetGedeckelt, ...abgeschaltet } = await schritt(lauf, 'Abgeschaltete Funktionen', () => loescheAbgeschaltete(lauf), {
|
||||
funktionen: 0,
|
||||
buchungen: 0,
|
||||
gedeckelt: 0,
|
||||
})
|
||||
const ergebnis = {
|
||||
eingeschraenkt: true,
|
||||
aboAbgleich,
|
||||
abgeschaltet,
|
||||
gedeckelt: { loeschfristen: 0, abgeschaltet: abgeschaltetGedeckelt, zellen: 0 },
|
||||
geloescht: abgeschaltet.buchungen,
|
||||
aktiveBetriebe: null,
|
||||
fehler: lauf.fehler,
|
||||
}
|
||||
await schritt(lauf, 'Tagesjob-Meldung', () => meldeTagesjob(ergebnis, now), [])
|
||||
return ergebnis
|
||||
}
|
||||
|
||||
/** Zahl fuer die Berichtszeile: ganze Zahl ab 0, alles andere (fehlt, Text) zaehlt als 0. */
|
||||
const zahl = (wert) => (Number.isInteger(wert) && wert > 0 ? wert : 0)
|
||||
|
||||
/**
|
||||
* Eine Zeile fuers Log nach dem Tagesjob: nur Zahlen, keine personenbezogenen Daten und keine Buchungs- oder Funktions-IDs
|
||||
* (Spur der unwiderruflichen Loeschungen). Ganz am Ende steht „n aktive Betriebe“ (fehlt, wenn die Zahl unbekannt ist).
|
||||
* null, wenn der Job nicht gelaufen ist (Modus off, uebersprungen).
|
||||
* (Spur der unwiderruflichen Loeschungen). Reihenfolge der Segmente (verbindlich): `…, N Mails nicht zugestellt, [Löschläufe am
|
||||
* Deckel: n, Rest folgt an den nächsten Tagen, ]Abo-Abgleich g geprüft / a abgeschaltet / w wieder ein / m Mails / x ausgenommen,
|
||||
* N Fehler, n aktive Betriebe`; „n aktive Betriebe“ steht ganz am Ende und fehlt, wenn die Zahl unbekannt ist, „Abo-Abgleich“ fehlt,
|
||||
* wenn das Ergebnis keine Zaehler dafuer traegt. Der eingeschraenkte Lauf (Modus off) hat eine eigene Zeile „[termine] Tagesjob (Modus
|
||||
* off, nur Abo-Abgleich und Löschung): …“ ohne Zellen, Outbox, Mails und Betriebe.
|
||||
* null, wenn der Job nicht gelaufen ist (Modus off ohne bezahlte Terminplaner, uebersprungen).
|
||||
* @param {object} ergebnis Rueckgabe von taeglicherJob
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export function tagesbericht(ergebnis) {
|
||||
if (!ergebnis || ergebnis.aus || ergebnis.uebersprungen) return null
|
||||
const l = ergebnis.loeschfristen
|
||||
const a = ergebnis.abgeschaltet
|
||||
const gesamt = buchungenGeloescht(l, a)
|
||||
const amDeckel = Object.values(ergebnis.gedeckelt ?? {}).reduce((summe, n) => summe + n, 0)
|
||||
const deckel = amDeckel > 0 ? `Löschläufe am Deckel: ${amDeckel}, Rest folgt an den nächsten Tagen, ` : ''
|
||||
const abo = ergebnis.aboAbgleich
|
||||
const aboSegment =
|
||||
abo === null || typeof abo !== 'object'
|
||||
? ''
|
||||
: `Abo-Abgleich ${zahl(abo.geprueft)} geprüft / ${zahl(abo.abgeschaltet)} abgeschaltet / ${zahl(abo.wiederEin)} wieder ein / ${zahl(abo.mails)} Mails / ${zahl(abo.ausgenommen)} ausgenommen, `
|
||||
if (ergebnis.eingeschraenkt) {
|
||||
return (
|
||||
`[termine] Tagesjob (Modus off, nur Abo-Abgleich und Löschung): ${a.buchungen} Buchungen gelöscht (abgeschaltete Funktionen ${a.buchungen}), ` +
|
||||
`${a.funktionen} Funktionen bereinigt, ${deckel}${aboSegment}${ergebnis.fehler} Fehler`
|
||||
)
|
||||
}
|
||||
const l = ergebnis.loeschfristen
|
||||
const gesamt = buchungenGeloescht(l, a)
|
||||
const betriebe = Number.isInteger(ergebnis.aktiveBetriebe) && ergebnis.aktiveBetriebe >= 0 ? `, ${ergebnis.aktiveBetriebe} aktive Betriebe` : ''
|
||||
return (
|
||||
`[termine] Tagesjob: ${gesamt} Buchungen gelöscht (verfallen ${l.verfallen}, abgesagt ${l.abgesagt}, abgelehnt ${l.abgelehnt}, ` +
|
||||
`erledigt ${l.erledigt}, nichterschienen ${l.nichterschienen}, abgeschaltete Funktionen ${a.buchungen}), ` +
|
||||
`${a.funktionen} Funktionen bereinigt, ${ergebnis.zellen} Zellen, ${ergebnis.aufgeraeumt} Outbox, ` +
|
||||
`${ergebnis.festgefahren} Mails nicht zugestellt, ${deckel}${ergebnis.fehler} Fehler${betriebe}`
|
||||
`${ergebnis.festgefahren} Mails nicht zugestellt, ${deckel}${aboSegment}${ergebnis.fehler} Fehler${betriebe}`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user