Phase 9: Kassenverwaltung (#14)
This commit was merged in pull request #14.
This commit is contained in:
@@ -32,7 +32,7 @@ Anmelden mit `SEED_ADMIN_EMAIL` und `SEED_ADMIN_PASSWORT` aus der `.env`
|
||||
| Phase 6 — Kartenmodule | fertig |
|
||||
| Phase 7 — Benachrichtigungen und Termine | fertig |
|
||||
| Phase 8 — Lokale KI | fertig |
|
||||
| Phase 9 — Kassenverwaltung | offen |
|
||||
| Phase 9 — Kassenverwaltung | fertig |
|
||||
|
||||
Vollständige Planung mit Aufwänden: [docs/ROADMAP.md](docs/ROADMAP.md) und
|
||||
[docs/AUFWANDSSCHAETZUNG.md](docs/AUFWANDSSCHAETZUNG.md).
|
||||
@@ -1155,6 +1155,156 @@ Fehlermeldung der Datenbank durchzureichen, die niemand versteht.
|
||||
|
||||
---
|
||||
|
||||
## Phase 9 — Kassenverwaltung
|
||||
|
||||
Kassenbuch, Mitgliedsbeiträge, Rechnungen an Dritte, Kassenprüfung und die
|
||||
Zahlen für die Jahreshauptversammlung.
|
||||
|
||||
Eine Feuerwehrkasse wird von einem Ehrenamtlichen geführt und von zwei anderen
|
||||
geprüft. Was die Prüfung wert ist, hängt einzig daran, dass sich das Geprüfte
|
||||
hinterher nicht mehr ändern lässt. Deshalb ist der zentrale Punkt dieser Phase
|
||||
kein Formular, sondern eine Sperre.
|
||||
|
||||
### Drei Festlegungen
|
||||
|
||||
**Eine festgeschriebene Buchung ist unveränderbar.** Nicht, weil ein Zweig im
|
||||
Router es so vorsieht, sondern weil die Datenbank es ablehnt — ein vergessener
|
||||
Zustandscheck, ein Skript, ein Import würden sonst reichen. Korrigiert wird
|
||||
über eine **Gegenbuchung** mit Verweis: beide Buchungen bleiben stehen, und im
|
||||
Kassenbuch ist zu sehen, dass etwas berichtigt wurde. Genau das will ein
|
||||
Kassenprüfer sehen; eine verschwundene Buchung kann er nicht prüfen.
|
||||
|
||||
**Der Kassenabschluss schließt seinen Zeitraum.** Danach wird keine Buchung
|
||||
mit einem Datum darin mehr angenommen — auch keine nachträglich
|
||||
hineindatierte. Ohne diese zweite Hälfte stimmte der geprüfte Bestand nach
|
||||
einem Datumswechsel nicht mehr, und niemand hätte es bemerkt. Wer nach der
|
||||
Prüfung noch eine Quittung findet, bucht sie mit dem heutigen Datum.
|
||||
|
||||
**Beträge sind `DECIMAL(12,2)`, gerechnet wird mit `Prisma.Decimal`.**
|
||||
`0.1 + 0.2` ergibt in JavaScript 0.30000000000000004. In einer Kasse ist das
|
||||
kein Rundungsfehler, sondern eine Differenz in der Prüfung, die niemand
|
||||
erklären kann.
|
||||
|
||||
### Das Kassenbuch
|
||||
|
||||
Drei Kontenarten: Barkasse, Bankkonto, Rücklage. Der **Bestand wird gerechnet,
|
||||
nie gespeichert** — Anfangsbestand plus die Wirkung aller Buchungen. Ein
|
||||
gespeicherter Bestand neben einer nachgetragenen Buchung wäre falsch, ohne
|
||||
falsch auszusehen, und genau danach sucht ein Kassenprüfer.
|
||||
|
||||
Der Betrag einer Buchung ist immer positiv; die Richtung steht daneben. Eine
|
||||
negative Ausgabe wäre eine Einnahme in Verkleidung und in keiner Auswertung
|
||||
als solche zu erkennen — die Datenbank nimmt sie deshalb nicht an.
|
||||
|
||||
Die Buchungsmaske schlägt eine Kategorie vor: zuerst aus den **eigenen
|
||||
früheren Buchungen** der Wehr, erst danach aus einer kurzen Stichwortliste.
|
||||
Der Nutzen liegt nicht in den gesparten Sekunden, sondern darin, dass dieselbe
|
||||
Sache immer unter derselben Kategorie landet — erst das macht die
|
||||
Jahresauswertung brauchbar. Unter der Hälfte gemeinsamer Wörter schweigt der
|
||||
Vorschlag: ein falscher wird übernommen, ohne gelesen zu werden.
|
||||
|
||||
Ein **gegengezeichneter Beleg** aus Phase 8 wird mit einem Klick zur Buchung:
|
||||
Konto und Kategorie wählen, fertig. Betrag, Datum und Händler stehen schon
|
||||
fest, und das Foto hängt danach an der Buchung — zu jeder Ausgabe ein Beleg,
|
||||
an der Buchung und nicht in einem Ordner daneben. Ein Entwurf lässt sich
|
||||
nicht buchen: er ist ein von einem Modell ausgefülltes Formular.
|
||||
|
||||
### Kontoauszug einlesen
|
||||
|
||||
Das Online-Banking bietet beides zum Herunterladen an, meist unter
|
||||
„Umsatzexport“:
|
||||
|
||||
| Format | Datei | Was gelesen wird |
|
||||
|---|---|---|
|
||||
| CAMT.053 | `.xml`, `.camt` | Datum, Richtung, Betrag, Verwendungszweck, Gegenseite, Bankkennung |
|
||||
| MT940 | `.txt`, `.sta` | dasselbe, aus den Feldern `:61:` und `:86:` |
|
||||
|
||||
Erkannt wird am Inhalt, nicht an der Dateiendung. Es entstehen **Entwürfe,
|
||||
keine fertigen Buchungen** — durchsehen muss sie der Kassier.
|
||||
|
||||
Jeder Umsatz trägt eine Kennung: die der Bank, sonst eine aus Datum, Betrag
|
||||
und Zweck gebildete. Derselbe Auszug zweimal eingelesen wird dadurch als
|
||||
solcher erkannt und in der Liste als „steht schon in der Kasse“ ausgewiesen.
|
||||
Ohne das wäre es ein doppelt gebuchter Monat, und der fiele erst beim
|
||||
Kassenabschluss auf.
|
||||
|
||||
Was nicht gelesen werden konnte, steht mit Grund daneben. Ein Parser, der
|
||||
stillschweigend drei Umsätze verschluckt, ist schlimmer als einer, der gar
|
||||
nicht erst liest.
|
||||
|
||||
### Mitgliedsbeiträge
|
||||
|
||||
Sätze gelten ab einem Jahr und je Abteilung; ein Satz ohne Abteilung gilt für
|
||||
alle übrigen. Rückwirkend geändert wird nichts — sonst stimmten die Beiträge
|
||||
vergangener Jahre nicht mehr, und die sind bereits geprüft.
|
||||
|
||||
Der **Beitragslauf** zeigt vorher, was entstehen wird: wie viele Beiträge,
|
||||
welche Summe, und wer keinen passenden Satz hat. Der letzte Teil ist der
|
||||
interessante — das sind die Leute, für die niemand entschieden hat, was sie
|
||||
zahlen. Der Lauf ist wiederholbar; im Februar noch einmal gestartet legt er
|
||||
nur die Neuen an.
|
||||
|
||||
Ein Zahlungseingang erzeugt zugleich die Buchung. Ein Häkchen ohne Geld in der
|
||||
Kasse wäre eine Behauptung. Ein Erlass braucht eine Begründung — die Datenbank
|
||||
lehnt einen ohne ab, weil er in der Kassenprüfung sonst eine offene Frage ist.
|
||||
|
||||
Die Rückstandsliste geht über alle Jahre. Sie ist die einzige Ansicht, in der
|
||||
auffällt, wer seit drei Jahren offen steht.
|
||||
|
||||
### Rechnungen an Dritte
|
||||
|
||||
Kostenersatz nach Art. 28 BayFwG, Vermietungen, Veranstaltungen. Die Nummer
|
||||
vergibt FLORIAN fortlaufend je Jahr und **lückenlos**: eine fehlende Nummer
|
||||
muss in der Kassenprüfung erklärt werden.
|
||||
|
||||
Der Betrag ist die Summe der Positionen, gerechnet und nicht eingegeben. Ein
|
||||
eingetippter Gesamtbetrag neben Positionen, die etwas anderes ergeben, ist der
|
||||
Fehler, den niemand bemerkt, bis der Empfänger anruft. Die Menge hat drei
|
||||
Nachkommastellen — Einsatzstunden werden viertelstündlich abgerechnet, und
|
||||
0,25 × 47,50 muss auf den Cent stimmen.
|
||||
|
||||
Bis zum Versenden ist eine Rechnung ein Entwurf, danach unveränderbar: was
|
||||
beim Empfänger liegt, muss dem entsprechen, was hier steht. Korrigiert wird
|
||||
über Storno und eine neue Rechnung; die stornierte bleibt mit Grund stehen.
|
||||
|
||||
### Kassenprüfung
|
||||
|
||||
In der Prüfmaske steht links der Bestand laut Buch und rechts der gezählte.
|
||||
In dieser Reihenfolge, und die gezählten Felder fangen leer an — wer erst das
|
||||
Buch sieht und dann zählt, zählt, bis es passt.
|
||||
|
||||
Ohne Prüfer und Prüfdatum lässt sich nichts festschreiben: ohne beides ist es
|
||||
kein Abschluss, sondern eine Notiz. Eine Differenz braucht eine Erläuterung,
|
||||
sonst steht in fünf Jahren „stimmt nicht“ im Bericht und niemand weiß mehr,
|
||||
warum.
|
||||
|
||||
Der Kassenprüfbericht entsteht als PDF mit vier Unterschriftsfeldern und wird
|
||||
im Hintergrund erzeugt.
|
||||
|
||||
### Auswertungen
|
||||
|
||||
Einnahmen und Ausgaben nach Kategorie und Monat, mit dem Vorjahr daneben —
|
||||
eine Zahl ohne Vergleich sagt nichts. Der Export ist eine CSV-Datei mit
|
||||
Semikolon und BOM: das ist, was Excel in Deutschland ohne Rückfrage richtig
|
||||
öffnet. Eine Datei, die beim Doppelklick als eine Spalte voller Semikolons
|
||||
erscheint, wird zurückgeschickt.
|
||||
|
||||
### Wer was sieht
|
||||
|
||||
| Recht | Wer | Was |
|
||||
|---|---|---|
|
||||
| `kasse.lesen` | Kassier, Kommandant, Vorstand | Buchungen und Auswertungen ansehen |
|
||||
| `kasse.bearbeiten` | Kassier | Buchungen, Beiträge, Rechnungen erfassen |
|
||||
| `kasse.abschluss` | Kassier, Kassenprüfer | Kassenabschluss anlegen und festschreiben |
|
||||
|
||||
Der Gerätewart sieht keine Buchungen, der Kassier keine Atemschutzdaten. Die
|
||||
Trennung liegt in `lib/authz.ts`, nicht in den Komponenten.
|
||||
|
||||
**Keine zusätzlichen Umgebungsvariablen.** Die Kasse braucht nur, was ohnehin
|
||||
läuft: Postgres, MinIO für die Belege und Gotenberg für die PDFs.
|
||||
|
||||
---
|
||||
|
||||
## Befehle
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user