Stand v0.24.0 (August 2026) · Pro-Addon buchhaltung · App apps/buchhaltung (Tabellen-Prefix bh_) · Konzept: docs/konzept-buchhaltung.md
Doppelte Vereinsbuchhaltung für Laien: KMU-Kontenrahmen, einfache Buchungsmasken, Bank-Abgleich (camt), Belege, Budget und die fertige Jahresrechnung für die GV. Das früher geplante Addon Nr. 22 «Zahlungs-Matching» ist hier als Matching-Center aufgegangen (aus dem Store entfernt).
| Prinzip | Umsetzung |
|---|---|
| Revisionssicherheit | Buchungen sind nach dem Buchen UNVERÄNDERLICH; Korrektur nur über Storno-Gegenbuchung (reverses-FK). Buchungsnummern {FY-Label}-{seq:05d} lückenlos pro Geschäftsjahr (select_for_update). |
| Perioden-Sperre | Jede Buchung liegt in genau einem OFFENEN Geschäftsjahr. Abschluss sperrt und erzeugt automatisch den Vortrag ins Folgejahr; Wiedereröffnen storniert den Vortrag (kein Nummern-Loch), erneuter Abschluss erzeugt ihn frisch. |
| Kontenplan | Schweizer KMU-Kontenrahmen 1:1 als Seed (kontenrahmen.py, ~68 Konten, Klassen 1–8) + gekennzeichnete Vereins-Unterkonten (3410 Mitgliederbeiträge, 3420 Spenden, 3430 Sponsoring, 3440/4410 Anlässe, 1091 PSP-Verrechnungskonto). Pro Verein kopiert, frei erweiterbar; Systemkonten (1000/1020/1100/2970/3805) geschützt; Konten mit Buchungen nur deaktivierbar. |
| Zwei Dimensionen | Sachkonto = WAS, Kostenstelle = WOFÜR (optional, eigene Auswertung). |
| CHF-only | Währungsfeld vorhanden, UI einspurig. |
FiscalYear (inkl. MwSt-Konfig pro Jahr) · LedgerAccount (mit financial_kind ''/bank/cash/psp + IBAN für camt-Zuordnung) · CostCenter · JournalEntry/JournalLine (Soll=Haben vom Service erzwungen) · BusinessEvent (generische Geschäftsvorgangs-Schnittstelle — Andock-Punkt für Ticketing/Shop/Spesen; Idempotenz via Unique source/event/ref) · BankTransaction/BankImportBatch (Dedupe über AcctSvcrRef bzw. Hash) · Budget/BudgetLine · BhSettings (Standardkonten + Spesen-Regeln) · ExpenseRequest/ExpenseItem/PayoutAccount (Spesen, v0.24.0). Belege: M2M bh_entry_document auf Datei-Browser-Assets + AssetUsage (usage_type buchung) → Lösch-Schutz.
Soll-Stellung (automatisch): Rechnungsversand bucht «Forderungen (1100) an Ertragskonto» (Auflösung Automation → Profil → Standard), Storno der Rechnung storniert die Buchung. Alle Hooks (apps/buchhaltung/integration.py) sind idempotent und defensiv — Fehler lassen den Vorgang mit Notiz in «Zu kontieren» liegen, brechen Faktura aber NIE ab.
Kernidee: die jüngste Buchung eines offenen Jahres darf physisch gelöscht werden — ihre Nummer wird wieder frei, die Lückenlosigkeit bleibt. Kein freies Editieren/Löschen beliebiger Buchungen (bewusster Entscheid).
correct_entry).vat_relevant-Ertragskonten (Mitgliederbeiträge/Spenden default ausgenommen).Was kann es: Im Journal steht bei jeder Buchung, die aus einer Rechnung stammt (Soll-Stellung, Zahlung, Ausbuchung, Storno), ein Link «📄 Rechnung 2026-0007» zum PDF und «↗» in die Rechnungen-Liste; die Journal-Suche findet auch Buchungsnummern. Umgekehrt zeigt das Rechnungs-Detail den Block «Buchhaltung» mit allen Buchungen und springt per Klick ins Journal; ist ein Vorgang noch «zu kontieren», steht das dort. Revisoren sehen im Portal zu jeder Buchung das Rechnungs-PDF. Jeder Bericht (Bilanz, Erfolgsrechnung, Kontoblatt, Offene Posten) und das Journal hatten «⬇ Excel»; seit v0.27.9 liegen alle Exporte gesammelt unter Einstellungen › Export (siehe unten), die Knöpfe verweisen dorthin. Beträge sind echte Zahlen, Summen rechnen in Excel. Neu in den Berichten: Offene Posten per Stichtag — welche Rechnungen waren am Stichtag versendet und nicht vollständig bezahlt, mit Fälligkeit, Tagen überfällig, Mahnstufe und Total; Zahlungen nach dem Stichtag zählen nicht, die Liste stimmt also rückwirkend per Bilanzstichtag.
Warum so: Das Rechnungs-PDF wird nicht als Beleg kopiert, sondern jederzeit identisch aus dem Snapshot erzeugt — ein Link genügt und es gibt keine veralteten Kopien. Excel statt CSV, weil Treuhänder und Revisoren damit arbeiten und CSV mit Umlauten und Trennzeichen Ärger macht. Die Offene-Posten-Liste rechnet aus den Rechnungen, nicht aus Konto 1100, damit sie je Rechnung Fälligkeit und Mahnstufe zeigt und auch bei unvollständiger Buchhaltung stimmt.
Was kann es: Ein eigenes Register in den Einstellungen (nach Spesen) listet jede Buchhaltungs-Liste mit ihrer Eingrenzung und einem Export-Knopf: Journal, Kontoblatt (Konto wählen), Bilanz, Erfolgsrechnung, MwSt, Kostenstellen-Bericht, Budget (je Geschäftsjahr, einmal oben gewählt), Offene Posten (Stichtag), Kontenplan, Kostenstellen, Zahlungsabgleich, Zu kontieren, Spesen (je Status) — und mit aktivem Rechnungs-Addon zusätzlich Rechnungen (plus Blatt «Zahlungen», Filter Status und Jahr) und Artikel. Der Knopf öffnet den gleichen Export-Dialog wie bei Personen und Mitgliedern (Excel oder CSV); Blatt-Aufbau und Schlusszeilen («Saldo …», «Total Aktiven», «Jahresergebnis», «Total offen per …») sind wie beim bisherigen Excel-Export, dazu ein Blatt «Info» und ein Eintrag im Berechtigungs-Protokoll. Spesen werden bewusst ohne Auszahlungs-IBAN exportiert. Details und Warum: Listen-Export.
Warum so: Finanz-Exporte brauchen Parameter (Jahr, Konto, Stichtag, Status), die auf der jeweiligen Seite nicht alle sichtbar sind; ein Register mit allen Listen zeigt auf einen Blick, was es gibt und wie man es eingrenzt. Die alten Excel-Adressen (export/<liste>.xlsx) bleiben gültig, damit gespeicherte Links weiter funktionieren.
Was kann es: Material mit Haken «Buchhaltung» erscheint im Register Anlagen: je Geschäftsjahr Buchwert Anfang, Abschreibung, Buchwert Ende, Aufwand im Jahr und Status. Abschreiben per Mehrfachauswahl und Operation (CHF oder Prozent vom Vorjahr übernehmen, alle x Prozent vom Anschaffungswert oder Buchwert, alle CHF x), «Abschreibungen buchen» erzeugt eine Sammelbuchung 6800 an Anlagekonto per Jahresende, «Buchung zurücknehmen» storniert sie, «Ausbuchen» verbucht den Restbuchwert bei Abgang. Buchungszeilen können einem Material zugeordnet werden (Aufwand je Material). Der Jahresabschluss fragt nach, wenn erfasste Abschreibungen noch nicht gebucht sind. Anlagespiegel im Export-Register. Details: Addon: Inventar.
Warum so: Abschreibungen sind in Vereinen Jahresentscheide des Kassiers, keine Automatik. Eine Sammelbuchung je Jahr hält das Journal lesbar; die Haben-Zeilen tragen das Material, damit das Kontoblatt des Anlagekontos je Gerät nachvollziehbar bleibt.
Was kann es: Im Spesen-Register wählt der Kassier die verbuchten Anträge, die Bankverbindung und das Ausführungsdatum und klickt «Zahlungsdatei erstellen». CluHu erzeugt eine Datei nach Swiss Payment Standards (pain.001), eine Überweisung je Person, mit Verwendungszweck «Spesen Nr. …». Die Datei wird im E-Banking hochgeladen und dort freigegeben, mehr Tippen gibt es nicht. Mit der Datei gelten die Anträge als ausbezahlt: Buchung Verbindlichkeit an Bank per Ausführungsdatum, Meldung an die Person. Lehnt die Bank die Datei ab, macht «↶ Auszahlung» je Antrag alles rückgängig. Fehlt bei einem Antrag die IBAN oder ist sie ausländisch, wird die Datei nicht erstellt, die Meldung nennt den Antrag. Alle Dateien liegen löschgeschützt im Datei-Browser unter «Buchhaltung Zahlungsdateien» und in der Karte «Zahlungsdateien» mit Download.
Warum so: Absender ist immer die Konto-IBAN der Bankverbindung, nie die QR-IBAN, weil eine QR-IBAN nur für Zahlungseingänge gilt. Die Bank wird aus der IBAN abgeleitet, ein BIC ist nicht nötig. Nur CHF auf CH/LI-IBAN, weil Spesen im Verein praktisch nie ins Ausland gehen und jede Zusatzvariante die Datei komplizierter macht. Die Datei ist nach Standard gebaut, aber nicht gegen eine Bank getestet: Vor dem ersten produktiven Einsatz eine Datei in der Prüffunktion des E-Bankings hochladen.
Beim Import einer camt.053/054-Datei wird das Finanzkonto an der IBAN in der Datei erkannt; die manuelle Wahl bleibt als Fallback. Trägt kein Finanzkonto diese IBAN, sagt die Meldung, wo sie einzutragen ist (Einstellungen › Konten). Gehört die Datei laut IBAN zu einem anderen Finanzkonto als dem gewählten, wird der Import abgelehnt — auch wenn das gewählte Konto keine IBAN hat. Der Zahlungsabgleich zeigt bei mehreren Bankkonten eine Konto-Spalte und einen Konto-Filter. Seit v0.27.4 tragen Finanzkonten keine eigene IBAN mehr, sondern zeigen auf eine Bankverbindung des Vereins (Karte in Einstellungen › Konten, dieselbe wie bei den Rechnungen); die Erkennung beim Import läuft über diese Bankverbindung.
Was kann es: Mitglieder mit der Rollen-Freigabe portal.spesen reichen im Portal Auslagen ein — Titel, Datum, mehrere Positionen, optional Kilometergeld (km × Ansatz), Quittungsfoto und die IBAN für die Auszahlung (wird für den nächsten Antrag gespeichert). Danach läuft die Freigabe über zwei fest definierte Personen pro Stufe (pflegbar unter Berechtigungen › Freigaben › Spesen oder Buchhaltung › Einstellungen › Spesen — seit v0.26.0 dieselbe Komponente an beiden Orten): Die 1. Freigabe prüft jeden Antrag sachlich — eine der beiden Personen genügt, entschieden wird eingeloggt im Clu-Portal unter «Aufgaben». Optional gibt es eine 2. Freigabestufe ab einer Betragslimite (z.B. Präsidium ab CHF 200; Limite 0 = immer). Erst NACH der finalen Freigabe übernimmt der Kassier: Er kontiert und verbucht im Clu-Core (Tab «Spesen», je Position Aufwandkonto/Kostenstelle, mit Budget-Hinweis) und löst die Auszahlung aus — einzeln, als Sammel-Auszahlung pro Person oder direkt aus dem Matching-Center, wenn die Belastung im Kontoauszug auftaucht. Findet jemand einen Mangel (Beleg fehlt, Total falsch), geht der Antrag «zur Korrektur zurück» an die Person, die ihn im Portal anpasst und erneut einreicht — die Freigabe startet dann von vorn. Benachrichtigungen laufen über «🔔 Meldungen» und per E-Mail; der Antrag zeigt beiden Seiten jederzeit, auf welcher Stufe er liegt.
Die Mail-Kadenz ist bewusst gestuft: Die Aufgabe erscheint sofort bei beiden Personen der Stufe im Portal, die Mail geht aber nur an die primäre Person — die Stellvertretung wird erst nach X Tagen ohne Entscheid nachgemailt (Default 3, einstellbar), und nach der doppelten Frist erinnert das System einmalig beide und meldet den Admins «Antrag hängt» (täglicher Lauf 05:25). So versandet nichts, ohne dass jede Spese zwei Postfächer flutet.
Portal-Formular (v0.27.6): Das Spesenformular im Portal hat dieselbe Feldgestaltung wie die übrigen Portal-Seiten (Beschriftung über dem Feld, Belege per Button als Chips, Kontoinhaber mit dem Namen der Person vorbelegt). IBAN und Kontoinhaber werden in einer eigenen Tabelle je Person gespeichert und sind nur im eigenen Formular und im Spesen-Detail der Buchhaltung sichtbar, nicht in Mitgliederlisten oder Exporten. Eine Einreichfrist gibt es standardmässig nicht mehr (0 = keine); wer eine will, trägt unter Einstellungen › Spesen die Tage ein, dann prüft das System und zeigt die Frist im Formular.
Einrichtungs-Checkliste im Spesen-Tab (v0.25.1): Solange die Freigabe-Personen fehlen oder die Rolle «Spesen einreichen» keiner Rolle zugewiesen ist, zeigt der leere Tab nicht einfach «Keine Spesen», sondern die zwei offenen Schritte mit Direktsprung zu den Einstellungen bzw. zu den Berechtigungen — eine leere Liste wäre sonst nicht von «bereit, aber noch nichts eingereicht» unterscheidbar. Sind beide Punkte erledigt, steht dort «Bereit — Mitglieder mit der Rollen-Freigabe können im Portal einreichen».
Warum so gebaut — und warum nicht anders:
Bewusst nicht gebaut: Doppel-Unterschriften pro Stufe, ressort-/kostenstellenabhängige Freigabe-Personen, Magic-Link-Freigabe, Vorschüsse, Kreditkarten-Abrechnungen, Fremdwährungen, eigenes Spesen-Reporting (Kostenstellen-Auswertung deckt es ab), pain.001-Zahlungsdatei für den Massenversand ins E-Banking (späteres Paket).
Rollen-Freigabe portal.revision (🧾 im Rollen-Dialog): read-only Bilanz/ER/Journal/Beleg-Download unter /portal/revision/ — ohne CluCore-Zugriff (Revisor sieht keine Mitgliederdaten). Beleg-Auslieferung nur für Dateien, die an Buchungen des Vereins hängen.
Zugriff V1: Admin (Owner = Admin seit v0.26.0; wie Faktura); Kassier-Teilrechte später mit dem Rollen-Editor. Offen (Andock-Punkte vorbereitet): Stripe/TWINT-Abrechnungs-Importer, effektive MwSt-Methode, Zahlungsdatei für andere Auszahlungen als Spesen (z.B. Lieferanten). camt-Light für «nur Rechnungen» ist seit v0.27.1 im Rechnungs-Addon.
Was kann es. Das Register «Übersicht» zeigt Liquidität, Ertrag, Aufwand und Jahresergebnis (Gewinn/Verlust als Icon plus Text, nicht nur Farbe), einen Budget-Meter, klickbare Kacheln «Zu erledigen» (Zu kontieren, Zahlungsabgleich, Spesen, überfällige Rechnungen), Konten mit Saldo oder Bewegung, zwei Grafiken (Ertrag/Aufwand je Monat, Liquidität per Monatsende), die fünf grössten Budgetposten und die letzten Buchungen (v0.26.13). Ab 1280 px zwei Zonen: links Grafiken, rechts Aufgaben, Konten, Buchungen (v0.26.14). Die Rechnungen-Seite bekam Kennzahl-Kacheln als Filter, Suche, Sortierung, «überfällig seit n Tagen», Mahnstufe, Teilzahlungs-Meter, «Ausstehend nach Alter» und eine Vorschau beim Erfassen (v0.26.13).
Bündel Buchhaltung ⊃ Rechnungen (v0.26.14, User-Entscheid). Rechnungen gilt als aktiv, solange die Buchhaltung aktiv ist, ohne eigene Aktivierung. Rechnungen lässt sich bei aktiver Buchhaltung nicht deaktivieren; wird die Buchhaltung deaktiviert, fällt Rechnungen auf seine eigene Aktivierung zurück, der Dialog erklärt das. Der Store zeigt «Aktiv · über Buchhaltung», die Seitenleiste keinen eigenen Menüpunkt; Rechnungen ist ein Register der Buchhaltung zwischen Journal und Zu kontieren, mit Badge für Überfällige, die Rechnungsseite trägt die Buchhaltungs-Registerleiste. Bei aktiver Buchhaltung wandern die Rechnungs-Kennzahlen als Block ins Dashboard (Kacheln sind Deep-Links mit Status-Filter), unter Rechnungen bleibt das Register «Liste» mit Suche und Tabelle (v0.26.15). Ohne Buchhaltung bleibt alles wie bisher.
Warum so. Zwei Addons bleiben (Basis/Pro), aber wer die Buchhaltung hat, braucht die Rechnungen immer; eine getrennte Aktivierung wäre ein Stolperstein (Einschätzung). Eine Kennzahl steht an einem Ort: die Zeile «Offene Rechnungen» in der Kontenliste entfiel, beide Seiten rechnen aus derselben Funktion. Die Aktiv-Prüfung ist zentral (addon_state) statt fünf kopierter Abfragen. ⚠️ Eine alte Abfrage prüfte dennoch die eigene Zeile, das Register fehlte bei Bündel-Aktivierung (behoben v0.28.30): jede neue Addon-Prüfung muss über addon_state laufen.