Geviertstriche aus Text, Kommentaren und Doku entfernen
467 Stellen. Nicht durch einen anderen Strich ersetzt, sondern durch das
Satzzeichen, das an der jeweiligen Stelle richtig ist: Doppelpunkt, wo eine
Erlaeuterung folgt; Komma, wo der Satz weiterlaeuft; Punkt, wo zwei
Hauptsaetze aneinanderstiessen. Ein Halbgeviertstrich waere nur derselbe
Griff mit kuerzerem Strich gewesen.
Regelbasiert ersetzt und danach nachgeprueft, weil eine Regel allein nicht
reicht. Gefunden und einzeln korrigiert:
- Eingeschobene Saetze mit Strichpaar. Nur der oeffnende Strich passte ins
Muster, der schliessende blieb als "—," stehen. Drei Stellen, jetzt mit
Klammern oder als eigener Satz.
- Doppelte Doppelpunkte, wo beide Striche eines Paars zu Doppelpunkten wurden
("Globale Regeln: Schrift, Groessen, Abstaende: kommen aus HiveGrid").
Sieben Stellen in Doku und Oberflaeche, jetzt in Klammern.
- Pfade, denen ein Komma statt eines Doppelpunkts folgte.
- Ausgerichtete Kommentartabellen, in denen der Strich eine Spalte war.
Nicht angefasst: drei Geviertstriche, die keine Prosa sind, sondern
Tabellenplatzhalter fuer "kein Wert" (Trichter, Auftraege, Kunden). Dort ist
der Strich die Angabe selbst; ihn zu entfernen liesse leere Zellen zurueck.
tsc, lint, build, verify:carbon und verify:layout laufen sauber.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
This commit is contained in:
1 parent
e998231fed
commit
6a0b36ddcc
75 files changed
+471
-471
No files matched your search
@@ -2,7 +2,7 @@
|
||||
* Kennzahlen-Berechnungen.
|
||||
*
|
||||
* Alle Funktionen sind pure functions über dem gefilterten Datensatz
|
||||
* (Anforderung INFRA-06) — die Entwicklung kann sie unverändert übernehmen
|
||||
* (Anforderung INFRA-06): die Entwicklung kann sie unverändert übernehmen
|
||||
* und lediglich die Datenquelle austauschen.
|
||||
*
|
||||
* Die Definition jeder Kennzahl steht in docs/KPI-KATALOG.md.
|
||||
@@ -46,7 +46,7 @@ export function umsatz(rechnungen: Rechnung[], zeitraum: ZeitraumPreset): number
|
||||
.reduce((summe, r) => summe + r.betrag, 0);
|
||||
}
|
||||
|
||||
/** Gleiche Berechnung für die Vorperiode — Basis der Trend-Badges. */
|
||||
/** Gleiche Berechnung für die Vorperiode: Basis der Trend-Badges. */
|
||||
export function umsatzVorperiode(rechnungen: Rechnung[], zeitraum: ZeitraumPreset): number {
|
||||
const fenster = vorperiode(zeitraum);
|
||||
return rechnungen
|
||||
@@ -54,7 +54,7 @@ export function umsatzVorperiode(rechnungen: Rechnung[], zeitraum: ZeitraumPrese
|
||||
.reduce((summe, r) => summe + r.betrag, 0);
|
||||
}
|
||||
|
||||
/** Beschriftung eines Buckets — eine Stelle fuer alle drei Zeitreihen. */
|
||||
/** Beschriftung eines Buckets, eine Stelle fuer alle drei Zeitreihen. */
|
||||
function bucketLabel(key: string, art: "tag" | "woche" | "monat"): string {
|
||||
if (art === "monat") return formatMonatKurz(key);
|
||||
if (art === "woche") return formatWoche(key);
|
||||
@@ -171,7 +171,7 @@ export function zahlungsdauer(rechnungen: Rechnung[], zeitraum: ZeitraumPreset):
|
||||
* Zusammensetzung des OFFENEN Bestands.
|
||||
*
|
||||
* Nicht zu verwechseln mit rechnungenNachStatus(): dort steht der kumulierte
|
||||
* Umsatz aus zwei Jahren gegen den heutigen Forderungsbestand — dabei entfallen
|
||||
* Umsatz aus zwei Jahren gegen den heutigen Forderungsbestand, dabei entfallen
|
||||
* über 90 % auf "Bezahlt", und der Ring zeigt nichts mehr. Der offene Bestand
|
||||
* ist ein echtes Ganzes: alles, was heute noch aussteht.
|
||||
*/
|
||||
@@ -205,13 +205,13 @@ export function auftraegeImZeitraum(auftraege: Auftrag[], zeitraum: ZeitraumPres
|
||||
return auftraege.filter((a) => imFenster(a.angelegtAm, fenster));
|
||||
}
|
||||
|
||||
/** "Aktiv" = beauftragt oder in Arbeit — das, was das Team gerade bindet. */
|
||||
/** "Aktiv" = beauftragt oder in Arbeit: das, was das Team gerade bindet. */
|
||||
export function aktiveAuftraege(auftraege: Auftrag[]): Auftrag[] {
|
||||
return auftraege.filter((a) => a.status === "beauftragt" || a.status === "in_arbeit");
|
||||
}
|
||||
|
||||
export function ueberfaelligeAuftraege(auftraege: Auftrag[]): Auftrag[] {
|
||||
// Einen Termin gibt es erst ab der Beauftragung — eine offene Anfrage kann
|
||||
// Einen Termin gibt es erst ab der Beauftragung, eine offene Anfrage kann
|
||||
// nicht "über Termin" sein.
|
||||
return auftraege
|
||||
.filter(
|
||||
@@ -264,7 +264,7 @@ export function umsatzNachGewerk(
|
||||
* Verlorene Aufträge zählen nur bis zu ihrer letzten erreichten Stufe.
|
||||
*/
|
||||
/**
|
||||
* Vertriebstrichter — bewusst nur über das Angebotsgeschäft.
|
||||
* Vertriebstrichter: bewusst nur über das Angebotsgeschäft.
|
||||
*
|
||||
* Wartung, Notdienst und E-Check laufen ohne Angebot direkt in die Ausführung.
|
||||
* Zählte man sie mit, würde der Trichter unten breiter als oben und die
|
||||
@@ -335,7 +335,7 @@ export function neueKunden(kunden: Kunde[], zeitraum: ZeitraumPreset): number {
|
||||
return kunden.filter((k) => imFenster(k.angelegtAm, fenster)).length;
|
||||
}
|
||||
|
||||
/** Gleiches Fenster davor — Basis fuer den Trend an der Neukunden-Karte. */
|
||||
/** Gleiches Fenster davor: Basis fuer den Trend an der Neukunden-Karte. */
|
||||
export function neueKundenVorperiode(kunden: Kunde[], zeitraum: ZeitraumPreset): number {
|
||||
const fenster = vorperiode(zeitraum);
|
||||
return kunden.filter((k) => imFenster(k.angelegtAm, fenster)).length;
|
||||
@@ -455,7 +455,7 @@ export interface AlertEintrag {
|
||||
text: string;
|
||||
detail: string;
|
||||
art: "kritisch" | "warnung" | "hinweis";
|
||||
/** Betrag, um den es geht — macht die Zeilen untereinander vergleichbar. */
|
||||
/** Betrag, um den es geht: macht die Zeilen untereinander vergleichbar. */
|
||||
betrag?: number;
|
||||
/**
|
||||
* Die Handlung, nicht die Beobachtung. Eine Liste ohne Verb ist ein Bericht;
|
||||
@@ -538,7 +538,7 @@ export function handlungsbedarf(daten: Datensatz): AlertEintrag[] {
|
||||
*
|
||||
* Bezugsgröße sind ausschließlich förderfähige Leistungen (Wärmepumpe, PV,
|
||||
* Speicher, Lüftung). Über alle Aufträge gerechnet läge die Quote im
|
||||
* einstelligen Bereich und wäre wertlos — eine Badwartung war nie förderfähig.
|
||||
* einstelligen Bereich und wäre wertlos, eine Badwartung war nie förderfähig.
|
||||
*/
|
||||
export function foerderQuote(auftraege: Auftrag[]): {
|
||||
quote: number;
|
||||
|
||||
@@ -26,7 +26,7 @@ export const FILTER_RELEVANZ: Record<SeitenId, FilterFeld[]> = {
|
||||
|
||||
/**
|
||||
* Setzt alle für die Seite irrelevanten Filter zurück, ohne sie im
|
||||
* globalen State zu löschen — wechselt man die Seite, sind sie wieder da.
|
||||
* globalen State zu löschen: wechselt man die Seite, sind sie wieder da.
|
||||
*/
|
||||
export function wirksameFilter(filter: FilterState, seite: SeitenId): FilterState {
|
||||
const erlaubt = new Set(FILTER_RELEVANZ[seite]);
|
||||
@@ -41,7 +41,7 @@ export function wirksameFilter(filter: FilterState, seite: SeitenId): FilterStat
|
||||
}
|
||||
|
||||
/**
|
||||
* Wendet die Dimensionsfilter an — bewusst OHNE Zeitfilter.
|
||||
* Wendet die Dimensionsfilter an: bewusst OHNE Zeitfilter.
|
||||
*
|
||||
* Grund: Kennzahlen brauchen unterschiedliche Zeitbezüge (Umsatz im Zeitraum,
|
||||
* offene Rechnungen dagegen stichtagsbezogen). Der Zeitraum wird deshalb
|
||||
@@ -77,7 +77,7 @@ export function wendeFilterAn(daten: Datensatz, filter: FilterState): Datensatz
|
||||
return { mitarbeiter, kunden, auftraege, rechnungen, zeiten };
|
||||
}
|
||||
|
||||
/** Anzahl aktiver Einschränkungen — für das Badge am Filter-Button. */
|
||||
/** Anzahl aktiver Einschränkungen, für das Badge am Filter-Button. */
|
||||
export function anzahlAktiverFilter(filter: FilterState, seite: SeitenId): number {
|
||||
const wirksam = wirksameFilter(filter, seite);
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Der angemeldete Benutzer — im Prototyp eine Attrappe.
|
||||
* Der angemeldete Benutzer, im Prototyp eine Attrappe.
|
||||
*
|
||||
* In HiveGrid Cloud kommt das aus der Sitzung (`requireMember`) und steht dem
|
||||
* Loader zur Verfügung, bevor die Seite rendert. Hier ist es eine Konstante,
|
||||
@@ -16,7 +16,7 @@ export const NUTZER = {
|
||||
* Für die Anrede. In eckigen Klammern und nicht als erfundener Name:
|
||||
* „Guten Tag, Dejan" sieht in einer Abnahme aus wie ein fest verdrahteter
|
||||
* Wert, und irgendwann fragt jemand, warum dort immer derselbe Name steht.
|
||||
* „[Vorname]" sagt stattdessen, was an dieser Stelle eingesetzt wird — beim
|
||||
* „[Vorname]" sagt stattdessen, was an dieser Stelle eingesetzt wird: beim
|
||||
* Einbau kommt er aus der Sitzung.
|
||||
*/
|
||||
vorname: "[Vorname]",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Bewusst deterministisch (fester Seed): Die Zahlen bleiben über Reloads,
|
||||
* Screenshots und Abstimmungsrunden identisch. Für die echte Software wird
|
||||
* diese Datei durch Entity-Builder-Queries ersetzt — das Interface `Datensatz`
|
||||
* diese Datei durch Entity-Builder-Queries ersetzt, das Interface `Datensatz`
|
||||
* bleibt gleich.
|
||||
*
|
||||
* Betriebsprofil (fiktiv):
|
||||
@@ -32,7 +32,7 @@ import { HEUTE, addTage, toISO } from "../utils/dateUtils";
|
||||
|
||||
/* ------------------------------------------------------ Zufall mit Seed */
|
||||
|
||||
/** mulberry32 — kleiner, schneller PRNG mit reproduzierbarer Sequenz. */
|
||||
/** mulberry32: kleiner, schneller PRNG mit reproduzierbarer Sequenz. */
|
||||
function prng(seed: number) {
|
||||
let a = seed;
|
||||
return function random(): number {
|
||||
@@ -177,7 +177,7 @@ function baueKunden(anzahl: number): Kunde[] {
|
||||
umsatzGesamt: 0,
|
||||
auftraegeGesamt: 0,
|
||||
// Die meisten zahlen nahe am Zahlungsziel; ein kleiner Teil schleppt.
|
||||
// Gleichverteilt wäre jeder zweite Kunde auffällig — das wäre kein Betrieb,
|
||||
// Gleichverteilt wäre jeder zweite Kunde auffällig, das wäre kein Betrieb,
|
||||
// sondern ein Inkassofall.
|
||||
zahlungsmoralTage: gewichtet([
|
||||
{ wert: zahl(5, 14), gewicht: 42 },
|
||||
@@ -217,7 +217,7 @@ const WERT_SPANNEN: Record<string, [number, number]> = {
|
||||
"E-Check": [140, 390],
|
||||
};
|
||||
|
||||
/** Bezugsgröße der Förderquote — zentral in types.ts definiert. */
|
||||
/** Bezugsgröße der Förderquote: zentral in types.ts definiert. */
|
||||
const FOERDERFAEHIG = FOERDERFAEHIGE_KATEGORIEN;
|
||||
|
||||
/**
|
||||
@@ -225,12 +225,12 @@ const FOERDERFAEHIG = FOERDERFAEHIGE_KATEGORIEN;
|
||||
*
|
||||
* Die Verteilung bildet einen realistischen Trichter ab:
|
||||
* von 100 Anfragen werden rund 55 zum Angebot und rund 24 zum Auftrag.
|
||||
* Verlorene Vorgänge merken sich, auf welcher Stufe sie abgesprungen sind —
|
||||
* Verlorene Vorgänge merken sich, auf welcher Stufe sie abgesprungen sind,
|
||||
* sonst zeigt der Trichter oben zu wenig und unten zu viel.
|
||||
*/
|
||||
function ergebnisFuerAlter(
|
||||
tageAlt: number,
|
||||
/** Große Vorhaben werden angeboten und oft verglichen — kleine kaum. */
|
||||
/** Große Vorhaben werden angeboten und oft verglichen: kleine kaum. */
|
||||
grossauftrag: boolean
|
||||
): {
|
||||
status: AuftragStatus;
|
||||
@@ -297,7 +297,7 @@ function ergebnisFuerAlter(
|
||||
|
||||
/**
|
||||
* Leistungsart innerhalb eines Gewerks.
|
||||
* Wartung, Notdienst und E-Check sind im Alltag die häufigsten Aufträge —
|
||||
* Wartung, Notdienst und E-Check sind im Alltag die häufigsten Aufträge,
|
||||
* gleichverteilt zu ziehen würde den Durchschnittswert massiv verzerren.
|
||||
*/
|
||||
function kategorieFuer(gewerk: Gewerk): string {
|
||||
@@ -451,7 +451,7 @@ function baueRechnungen(auftraege: Auftrag[]): Rechnung[] {
|
||||
let bezahltAm: string | null = null;
|
||||
|
||||
if (gestellt > HEUTE) {
|
||||
// Rechnung existiert noch nicht — z. B. Schlussrechnung eines laufenden Auftrags.
|
||||
// Rechnung existiert noch nicht: z. B. Schlussrechnung eines laufenden Auftrags.
|
||||
status = "entwurf";
|
||||
} else if (ueberfaelligTage < 0) {
|
||||
// Noch innerhalb des Zahlungsziels; ein Teil zahlt sofort.
|
||||
@@ -473,7 +473,7 @@ function baueRechnungen(auftraege: Auftrag[]): Rechnung[] {
|
||||
status = "ueberfaellig";
|
||||
}
|
||||
} else if (ueberfaelligTage <= 120) {
|
||||
// Mahnstufe erreicht — der Großteil zahlt spätestens jetzt.
|
||||
// Mahnstufe erreicht, der Großteil zahlt spätestens jetzt.
|
||||
if (rnd() < 0.94) {
|
||||
status = "bezahlt";
|
||||
bezahltAm = toISO(addTage(faellig, zahl(-8, 70)));
|
||||
@@ -489,7 +489,7 @@ function baueRechnungen(auftraege: Auftrag[]): Rechnung[] {
|
||||
status = "ueberfaellig";
|
||||
}
|
||||
} else {
|
||||
// Älter als sieben Monate: eingebracht oder ausgebucht — in beiden
|
||||
// Älter als sieben Monate: eingebracht oder ausgebucht, in beiden
|
||||
// Fällen steht die Forderung nicht mehr in der offenen Liste.
|
||||
status = "bezahlt";
|
||||
bezahltAm = toISO(addTage(faellig, zahl(-8, 150)));
|
||||
|
||||
@@ -18,7 +18,7 @@ export const GEWERKE = [
|
||||
] as const;
|
||||
export type Gewerk = (typeof GEWERKE)[number];
|
||||
|
||||
/** Leistungsarten je Gewerk — Grundlage für "Umsatz nach Kategorie". */
|
||||
/** Leistungsarten je Gewerk: Grundlage für "Umsatz nach Kategorie". */
|
||||
export const KATEGORIEN: Record<Gewerk, string[]> = {
|
||||
Heizung: ["Wärmepumpe", "Gastherme", "Heizkörper", "Wartung Heizung"],
|
||||
Sanitär: ["Bad-Sanierung", "Rohrleitungen", "Armaturen", "Notdienst"],
|
||||
@@ -65,7 +65,7 @@ export interface Kunde {
|
||||
ort: string;
|
||||
angelegtAm: string;
|
||||
letzterAuftragAm: string | null;
|
||||
/** Aggregiert aus Rechnungen — wird in seed.ts nachberechnet. */
|
||||
/** Aggregiert aus Rechnungen: wird in seed.ts nachberechnet. */
|
||||
umsatzGesamt: number;
|
||||
auftraegeGesamt: number;
|
||||
zahlungsmoralTage: number;
|
||||
@@ -94,7 +94,7 @@ export const AUFTRAG_STATUS_LABEL: Record<AuftragStatus, string> = {
|
||||
|
||||
/**
|
||||
* Leistungsarten, für die BAFA-/KfW-Förderung in Frage kommt.
|
||||
* Bezugsgröße der Förderquote — sonst rechnet man Wartungsaufträge
|
||||
* Bezugsgröße der Förderquote, sonst rechnet man Wartungsaufträge
|
||||
* in eine Quote hinein, die für sie gar nicht gilt.
|
||||
*/
|
||||
export const FOERDERFAEHIGE_KATEGORIEN = new Set([
|
||||
@@ -137,7 +137,7 @@ export interface Auftrag {
|
||||
/**
|
||||
* Bei verlorenen Vorgängen: die zuletzt erreichte Trichterstufe.
|
||||
* Ohne dieses Feld müsste der Trichter raten, wo ein Vorgang abgesprungen
|
||||
* ist — und würde die oberen Stufen systematisch zu klein zeigen.
|
||||
* ist, und würde die oberen Stufen systematisch zu klein zeigen.
|
||||
*/
|
||||
verlorenAb: AuftragStatus | null;
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user