v1.27a: strikte Ablesedatum-Validierung (datumGueltig, kein Monatsueberlauf)

This commit is contained in:
2026-10-04 17:29:20 +02:00
parent 3753cec87c
commit 0ab8fb0aaf
3 changed files with 27 additions and 4 deletions
+5 -1
View File
@@ -195,7 +195,11 @@ Default heute); stand_am + erster Verlaufseintrag (erstbefund) nutzen das Datum.
Frontend ZaehlerDialog: Haus-Textfeld entfernt – Haus folgt der Wohneinheit,
nur bei „Allgemeinzähler (ohne Wohnung)" erscheint eine Haus-Auswahl
(buildings-Select, Anzeige b.adresse). Ablesedatum (type=date, Default heute,
Pflicht) nur bei Neuanlage; Frontend wandelt YYYY-MM-DD → TT.MM.JJJJ.
Pflicht) nur bei Neuanlage; Frontend wandelt YYYY-MM-DD → TT.MM.JJJJ. WICHTIG: parseDatum ist ein
lenient Parser (Überlauf 32.13. → Feb Folgejahr!) – für Benutzereingaben
zusätzlich zaehlerVerlauf.datumGueltig (Exaktheits-Roundtrip, nur
dd.mm.yyyy/yyyy-mm-dd) – eingesetzt in POST /api/meters und
POST /api/meters/:id/readings.
**Erledigt 04.10. (v1.26):** Haus-Formular ohne Namensfeld, Adresse in Einzelfelder.
buildings um strasse/hausnummer/plz/ort/lage/wohnflaeche/grundstuecksflaeche
+2 -2
View File
@@ -558,7 +558,7 @@ app.post('/api/meters', auth, adminOnly, async (req, res, next) => {
const standZahl = Number(stand)
if (!Number.isFinite(standZahl) || standZahl < 0) return res.status(400).json({ fehler: 'Ungültiger Zählerstand.' })
const datum = String(standAm || '').trim() || deutschesDatum()
if (!zaehlerVerlauf.parseDatum(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
if (!zaehlerVerlauf.datumGueltig(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
// Wohnung angegeben → Haus aus der Wohnung ableiten; sonst muss das Haus im Body stehen
let einheitId = unitId ? String(unitId) : null
let haus = String(building || '').trim()
@@ -657,7 +657,7 @@ app.post('/api/meters/:id/readings', auth, async (req, res, next) => {
const standZahl = Number(stand)
if (!Number.isFinite(standZahl) || standZahl < 0) return res.status(400).json({ fehler: 'Ungültiger Zählerstand.' })
const datum = String(standAm || '').trim() || deutschesDatum()
if (!zaehlerVerlauf.parseDatum(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
if (!zaehlerVerlauf.datumGueltig(datum)) return res.status(400).json({ fehler: 'Ungültiges Ablesedatum.' })
const { eintrag, anomalie, grund } = await zaehlerVerlauf.standEintragen({
meter: m, stand: standZahl, standAm: datum,
erfasstVon: req.user.name, quelle: req.user.rolle === 'verwaltung' ? 'manuel' : 'mieter',
+20 -1
View File
@@ -29,6 +29,25 @@ function parseDatum(s) {
return Number.isNaN(fallback.getTime()) ? null : fallback
}
/** Strikte Prüfung für Benutzereingaben: nur dd.mm.yyyy / yyyy-mm-dd und das
* Datum muss exakt existieren (kein Überlauf wie 32.13. → Feb des Folgejahres). */
function datumGueltig(s) {
const str = String(s || '').trim()
const d = parseDatum(str)
if (!d) return false
const iso = /^(\d{4})-(\d{2})-(\d{2})$/.exec(str)
if (iso) {
return d.getUTCFullYear() === Number(iso[1]) && d.getUTCMonth() === Number(iso[2]) - 1 &&
d.getUTCDate() === Number(iso[3])
}
const de = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/.exec(str)
if (de) {
return d.getUTCFullYear() === Number(de[3]) && d.getUTCMonth() === Number(de[2]) - 1 &&
d.getUTCDate() === Number(de[1])
}
return false
}
const tageZwischen = (a, b) => Math.round((b - a) / 86400000)
const runde = (n, stellen = 1) => Math.round(n * 10 ** stellen) / 10 ** stellen
@@ -171,4 +190,4 @@ async function erstbefundFehlende() {
if (rows.length) console.log(`[api] Zählerverlauf: ${rows.length} Erstbefund(e) ergänzt`)
}
module.exports = { pruefeAnomalie, verlaufLaden, standEintragen, erstbefundFehlende, mapReading, parseDatum }
module.exports = { pruefeAnomalie, verlaufLaden, standEintragen, erstbefundFehlende, mapReading, parseDatum, datumGueltig }