feat: Vorgaenge als Fallakte mit Genehmigungskette und Vorlagen
Jeder Vorgang ist eine kleine Akte: Kopfdaten, Aufgabenliste zum Abhaken, Genehmigungsstufen und ein Zeitstrahl, der Dokumente, Kontaktprotokolle, Fristen, Buchungen und Statuswechsel zusammenfuehrt. Die abnahmerelevante Regel: Ein genehmigungspflichtiger Vorgang laesst sich nicht auf VOLLZOGEN setzen, solange kein Beschlussdokument hinterlegt ist. Geprueft wird in darfStatusWechseln() UND in der Server Action - nicht nur im UI, wo das Auswahlfeld die Stufe ohnehin gar nicht erst anbietet. Hintergrund ist § 1858 BGB: die Genehmigung muss vor dem Rechtsgeschaeft vorliegen, nicht danach. Neun Vorlagen mit durchdachter Aufgabenreihenfolge. Bei Wohnungs- aufloesung und Hausverkauf erzwingen sie die Kette Mitteilung -> Genehmigung -> Rechtsgeschaeft; ein Test prueft das. Kanban ohne Drag-and-Drop: Statuswechsel laufen ueber Auswahlfeld und Knopf. Am Handy ist das verlaesslicher, und am Schreibtisch kostet es einen Klick mehr statt einer verlorenen Karte. Zeitzonen: alsTag() bildet den oertlichen Kalendertag auf Mitternacht UTC ab. Ohne das rutscht in Deutschland jede Faelligkeit einen Tag zurueck. 33 Tests. Etappe: 2 Phase: Alltag
This commit is contained in:
@@ -746,15 +746,16 @@ export type VorlagenErgebnis = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Tagesanteil eines Datums, auf Mitternacht UTC normiert.
|
||||
* Bildet den örtlichen Kalendertag auf Mitternacht UTC ab.
|
||||
*
|
||||
* Fristen und Fälligkeiten sind in der Datenbank @db.Date - ohne diese
|
||||
* Normierung verschiebt die Zeitzone eine Frist um einen Tag.
|
||||
* Fristen und Fälligkeiten sind in der Datenbank @db.Date. Wer hier den
|
||||
* Zeitstempel einfach abschneidet, verschiebt in Deutschland jede Frist um
|
||||
* einen Tag nach hinten - der 22.08. um 01:00 Uhr Ortszeit ist in UTC noch
|
||||
* der 21.08. Deshalb wird der Tag gelesen, wie ihn der Kalender an der Wand
|
||||
* zeigt, und dann als Mitternacht UTC abgelegt.
|
||||
*/
|
||||
export function alsTag(datum: Date): Date {
|
||||
return new Date(
|
||||
Date.UTC(datum.getUTCFullYear(), datum.getUTCMonth(), datum.getUTCDate(), 0, 0, 0, 0),
|
||||
)
|
||||
return new Date(Date.UTC(datum.getFullYear(), datum.getMonth(), datum.getDate(), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
export function vorlageAnwenden(schluessel: string, start: Date = new Date()): VorlagenErgebnis {
|
||||
@@ -763,22 +764,22 @@ export function vorlageAnwenden(schluessel: string, start: Date = new Date()): V
|
||||
throw new Error(`Unbekannte Vorgangs-Vorlage: ${schluessel}`)
|
||||
}
|
||||
|
||||
const basis = alsTag(start)
|
||||
|
||||
// Gerechnet wird auf dem oertlichen Datum (date-fns ist dabei
|
||||
// sommerzeitfest), erst das Ergebnis wird auf Mitternacht UTC normiert.
|
||||
return {
|
||||
vorlage,
|
||||
aufgaben: vorlage.aufgaben.map((aufgabe, index) => ({
|
||||
titel: aufgabe.titel,
|
||||
beschreibung: aufgabe.beschreibung ?? null,
|
||||
faelligAm:
|
||||
aufgabe.tageBisFaellig === undefined ? null : alsTag(addDays(basis, aufgabe.tageBisFaellig)),
|
||||
aufgabe.tageBisFaellig === undefined ? null : alsTag(addDays(start, aufgabe.tageBisFaellig)),
|
||||
// Die Reihenfolge ist der fachliche Kern der Vorlage - sie wird
|
||||
// festgeschrieben, damit spaeteres Sortieren sie nicht zerstoert.
|
||||
reihenfolge: index,
|
||||
})),
|
||||
fristen: (vorlage.fristen ?? []).map((frist) => ({
|
||||
titel: frist.titel,
|
||||
datum: alsTag(addDays(basis, frist.tageBisFaellig)),
|
||||
datum: alsTag(addDays(start, frist.tageBisFaellig)),
|
||||
rechtsgrundlage: frist.rechtsgrundlage ?? null,
|
||||
})),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user