(Dieser öffentliche ENTWURF wurde zuletzt am 5. März geändert, um den aktuellen Stand widerzuspiegeln. Sobald dieser Hinweis verschwindet, gilt die vorliegende Version als final und zitierbar.)

Im hypothetischen Verein ist es passiert: Der amtierende Kassierer möchte von seinem bisherigen System auf TreasureDB umsteigen, oder bei der Mitgliederversammlung wurde das Amt auf jemand anderen übertragen, der diese Software verwenden möchte. Wie auch immer – mit der von den Mitgliedern beschlossenen Entlastung des Kassenwarts ist der neue/bisherige Kassenwart frei, die Methodik oder das System zu verwenden, mit dem er glaubt am besten zurechtzukommen. Das muss natürlich nicht TreasureDB sein, aber in diesem Fall hat die Lektüre dieses Artikels wenig Sinn.

Dieser Artikel ist Teil einer Serie. Die Serie beginnt mit TreasureDB: ein elektronisches, transparentes Vereinskassenbuch (1). Dort sind auch die anderen Teile verlinkt.

Voraussetzungen

Bevor migriert werden kann, müssen einige Dinge geprüft werden.

Lohnt sich das eigentlich?

Bevor du das Programm installierst, unterziehe das Vorhaben gerne einer Aufwandsschätzung, um späteren Frust auf beiden Seiten zu vermeiden. Lies diese Artikelserie (das ist schon mal nicht wenig), sowie die README-Datei zum Projekt.

Und wie viel Zeit verbringst du – kritisch betrachtet – mit deinem bisherigen System? Wie viel steckst du insbesondere in die Vorbereitung des Kassenberichts? Wie lange dauert eine Kassenprüfung, und ist sie nervenaufreibend? Wie viel Zeit steckt der Vorstand in das Verständnis deiner Aufbereitung, wenn du mal vorübergehend ausfällst oder du für bestimmte Dinge, die die Kasse betreffen (z.B. Einladung der Mitglieder zur jährlichen Versammlung, jeweils mit Aufstellung der Zahlungen und Rückstände pro Mitglied) keine ausreichenden Rechte hast? Wenn die bisher verwendete Software kostenpflichtig ist, ziehe auch diese Kosten in Betracht.

Je stärker das bisherige System einen pragmatischen Ansatz verfolgt, umso aufwendiger ist der Umstieg auf TreasureDB. Wenn die Daten erst migriert sind, dürfte sich dieser Aufwand dafür aber stark reduzieren, da das Programm dem Kassierer die Eingaben zweckorientiert aufbereitet.

Support versus Datenschutz

Auch der Bedarf an Unterstützung ist zu beachten, konkret der Aufwand um Daten für etwaige Supportanfragen vorzubereiten, um sie trotz der Weitergabe zu schützen. Besonders in der Testphase des Programms kann es nötig sein, dass der Kassierer mit mir, dem Entwickler der Software, am Beispiel von Daten kommunizieren muss, damit ich ihm bei Problemen helfen kann.

Schicke tunlichst keine echten Daten. Damit würdest du bzw. würde der Verein Beschwerden oder gar Klagen Dritter im Sinne des Datenschutzgesetzes riskieren.

Ich (oder die Community, die es vielleicht einmal gibt) kann auch am Beispiel fingierter oder pseudonymisierter Daten helfen. In der Distribution ist ein kleines Skript namens pseudonymizr enthalten. Mittels einer Mappingdatei, die als Argument angegeben wird und les- und schreibbar sein sollte, werden speziell markierte Stellen im Eingabestrom pseudonymisiert bzw. bei Angabe des Flags -r depseudonymisiert und ausgegeben. Diese spezielle Markierung ist „X{…}“, wobei X ein beliebiger Buchstabe sein kann; diese Markierung bleibt in der pseudonymisierten Version bestehen.

Sind die Daten vollständig?

Wann ist der Zeitpunkt X anzusetzen, was ist der Stichtag, von dem ausgehend alle Aus- und Eingänge mit TreasureDB zu erfassen sind? Dies muss spätestens der Tag der letzten Entlastung sein.

Der erste Kontoauszug, der erstmalig Buchungen vom Stichtag und spätere enthält, muss vorliegen, genauso lückenlos alle späteren Auszüge. Am besten wäre es, wenn die Kontoauszüge bereits in elektronischer, maschinell verarbeitbarer Form vorliegen. Wenn nicht schon geschehen, müssen diese Daten in einer Tabellenkalkulation importiert werden. Andernfalls bleibt wohl nichts anderes übrig, als die Kontoauszüge händisch oder per automatischer Zeichenerkennung (OCR) abzutippen.

Kontoauszüge haben meist folgende tabellarische Spalten, und alle diese Daten sollten so originalgetreu wie möglich elektronisch vorliegen.

  1. Buchungsdatum
  2. Datum der Wertstellung (evtl.). Welches Datum in TreasureDB erfasst wird, bleibt dem Kassierer überlassen.
  3. Verwendungszweck, stets im originalen Wortlaut aufzubewahren und zu übertragen.
  4. Sollbetrag, wenn es sich bei einer Buchung um eine Sollbuchung handelt, also eine Auszahlung.
  5. Habenbetrag, wenn es sich um eine Habenbuchung handelt, etwa einen Mitgliedsbeitrag.

Kategorien

Zunächst sind in die Datenbank Kategorien von Guthaben und Belastungen einzutragen. Derzeit muss das via SQL erfolgen:

$ echo 'INSERT INTO Category("label")' \
       ' VALUES ("Mitgliedsbeitrag", "Spende", ...);' | \
       sqlite3 trsr.db

Die Kategorien dienen in der HTTP-Schnittstelle zur Filterung von Ansichten.

Anlegen der Konten

Hierzu kann die HTTP-Schnittstelle verwendet werden, alternativ ist SQL möglich:

$ sqlite3 trsr.db <<'SQL'
INSERT INTO Account(ID, name, type, altId, IBAN) VALUES
    ("main", "Vereinskonto", "", 1, ""),
    ("abs", "Abschreibungen", "", 2, NULL),
    ("flow", "Florian Heß", "Member", 123, NULL),
    ... -- weitere Mitgliedskonten
    ;
SQL

Die ID ist das Kürzel des Kontos, das nicht numerisch sein muss, und auch nicht sein sollte, damit die Gefahr bei Vertippern, die u.U. ohne Fehlermeldung akzeptiert werden, gering ist. Nach dem Typ werden Konten nicht nur in der Gesamtansicht gruppiert (HTTP-Schnittstelle). Man kann anhand des Typs Konten zwecks Sammelbelastungen eingrenzen, z.B. für monatliche Mitgliedsbeiträge. Die altId ist die ID pro Typ. Im Falle von Mitgliedern sollte es die ID lt. Mitgliedertabelle sein, die nicht in der Vereinskasse liegen muss (aber kann, TreasureDB wird sie dann geflissentlich ignorieren).

Anreicherung

Alleine nutzen die Kontoauszugsdaten noch nicht. Allenfalls wird TreasureDB daraus den Kontoauszug rekonstruieren können, aber darüberhinaus wird es keinen Sinn haben.

Im Vorfeld müssen die obigen Spalten in einem Texteditor mit den entsprechenden Funktionen der Tabellenkalkulation umsortiert werden, da die Importroutine eine bestimmte Abfolge erwartet.

  1. Buchungs- oder Wertstellungsdatum
  2. Kürzel des assoziierten Kontos. Es ist sehr wichtig, sich hier nicht zu vertun. Um Vertipper zu vermeiden, kommt dir TreasureDB entgegen, in dem nicht nur einfache numerische IDs hier erlaubt sind, die sich leicht vertippen ließen.
  3. Sollbetrag.
  4. Habenbetrag.
  5. Verwendungszweck. Siehe nächsten Abschnitt.
  6. Am Schluss im Feld „Verwendungszweck“ kann die Kategorie dieser Buchung in Form von „-C[…]“ angegeben werden. Bildlich soll diese Notation übrigens an ein Hängeetikett erinnern. Es braucht nur der Anfang eines Labels eingegeben werden, solange dieser eindeutig ist.

Verwendungszweck von Sollbuchungen (Import)

Im Falle einer Sollbuchung stets eingeleitet mit „Rechnungs-ID:“, jeweils ersetzt mit der tatsächlichen ID. Der Doppelpunkt ist wichtig, und die Rechnungs-ID darf keine Leerzeichen enthalten.

Diese ist auch da erforderlich, wo eigentlich keine mit dem jeweiligen  Geschäftspartner vereinbart ist. Es empfiehlt sich, vorab ein Schema für alle virtuellen Rechnungen zu festzulegen, z.B. „MB1612-Mitgliedskürzel“ für Mitgliedsbeiträge für den Dezember 2016. Auch, wenn die Rechnungsnummer im originalen Verwendungszweck enthalten ist, muss sie den Verwendungszweck dennoch einleiten. Das gilt nur für den Import. In der Datenbank wird der originale Verwendungszweck ohne diese ID stehen.

Handelt es sich bei der Sollbuchung um eine externe Auszahlung an ein Vereinsmitglied und soll sie auch mit dessen Konto assoziiert werden, steht diese Ausgangsbuchung normalerweise einer internen Verrechnung zwischen Haupt- und Mitgliedskonto gegenüber. Zum jetzigen Zeitpunkt der Migrationsvorbereitung wird diese noch nicht existieren, weshalb auch die schlichte Rechnungsnummer zunächst in Ordnung wäre. Aber vorsorglich, empfehle ich, sollte die Rechnungs-ID in diesem Fall um ein Anhängsel erweitert werden, etwa um „[Ausz.]“. Die entsprechende interne Verrechnung kann dann mit der Rechnungsnummer versehen werden, ohne dieses Anhängsel.

Natürlich müssen alle Rechnungs-IDs eindeutig sein. Das kann es erfordern, insbesondere bei einfach oder gar nicht strukturierten, schlicht laufenden Nummern, sie um voran- oder nachgestellte Zusätze zu erweitern – am besten ebenfalls nach einem vorab festgelegten Schema.

Erste Zeile

Die erste Zeile sollte der Anfangssaldo sein. Im CSV-Format könnte sie wie folgt aussehen. Beachte, dass der Betrag hier als Centangabe interpretiert wird, da er keinen Punkt enthält:

2016-06-14,main,,123400,"Anfangssaldo ab Entl. Kassenwart
A. B. Christiansen, MV 2016"

Migration

Jetzt gehts ans Eingemachte. Speichern wir unsere Tabelle zunächst als CSV (komma-separierte Tabellenzeilen) oder TSV (tab-separierte Tabellenzeilen). Abhängig von der verwendeten Tabellenkalkulation kann auch der Weg über Markieren, Kopieren und Einfügen gewählt werden. LibreOffice etwa gibt TSV in die Textkonsole aus, damit würde es auch gehen.

Wichtig: Die Beträge müssen entweder in Cent angegeben, also Ganzzahlen ohne Währungszeichen sein, oder als Trennzeichen muss Leerzeichen, Tabulator oder Senkrechtstrich eingestellt werden. Komma wird nur dann unterstützt, wenn es nicht zugleich auch Cent von Euro trennt. Der Verwendungszweck muss mit Anführungszeichen umfasst sein, wo er Zeilenumbrüche enthält. Ggf. sind entsprechende Einstellungen in der Tabellenkalkulation vorzunehmen.

$ export TRSRDB_SQLITE_FILE=trsr.db
$ ./trsr charge ${Eingabedatei}

Gibst du keine Eingabedatei(en) an, wartet das Programm auf einen Eingabedatenstrom. Schließe dann deine Eingabe mit Strg-D ab.

Hier ein Beispiel. Wir haben eine Datei kontoauszug.csv mit folgendem Inhalt erstellt:

2016-10-01,Club,,1452.00 €,Anfangssaldo -C[Übertrag]
2016-10-14;flow;;"72,00 €";Mitgliedsgebühr flow 2016 -C[Mitgliedsbeitrag]
2016-10-28|rose||72,00 €|"Mitgliedsgebühr, D. Rosenthal, 2016 -C[Mitgl]"
2016-11-05 Club 380,00€ "" AMZ2016/45674-01: Neuer Kühlschrank -C[Kauf]
2016-11-13 john 36,00 € J. Albrecht Juli bis Dezember -C[Mitgl]
2016-12-05 Club €18,00
 BF-2016:
 Kontogebühren -C[Kontofüh]

In jeder Zeile habe ich das Format ein bisschen abgewandelt, um die Flexibilität von trsr zu demonstrieren. Verfüttern wir unsere Eingabedatei an das Programm:

$ ./trsr charge < Vortrag/kontoauszug.csv 
Created 1, transferred 0 of 145200.
Created 2, transferred 0 of 7200.
Created 3, transferred 0 of 7200.
Created AMZ2016/45674-01, transferred 0 of 38000.
Created 4, transferred 0 of 3600.
Created BF-2016, transferred 0 of 1800.
$ ./trsr status
Get balance status ...
ID      availbl earned spent   promise arrears even_until
Club     145200      0      0       0   39800
alex          0      0      0       0       0
flow       7200      0      0       0       0
john       3600      0      0       0       0
rose       7200      0      0       0       0

Zu jeder Eingabezeile wird die ID vom Guthaben (Zahl) bzw. von der Belastung (eingebene Zeichenfolge vor dem Doppelpunkt) ausgegeben. Außerdem wird der Betrag in Cent angegeben, und wie viel davon bereits transferiert wurde.

Hinweise für zukünftige Stapelbuchungen

Hier ein kleiner Einblick, was zukünftig die Arbeit mit trsr charge einfacher macht. Wer es später lesen möchte, kann zum Abschnitt „Bewegung! Belastung!“ scrollen.

Sofort-Transfers

Von der Möglichkeit solcher Soforttransfers haben wir hier noch keinen Gebrauch gemacht, es kann aber  in Zukunft Arbeit sparen, wenn wieder eine Reihe von Buchungen vorab in einem Editor präpariert wurden und eingespeist werden sollen.

„>> *“ am Ende des Verwendungszwecks von Habenbuchungen transferriert die Beträge auf alle Belastungen, aufsteigend sortiert nach deren Buchungsdatum, soweit welche mit dem gleichen Konto assoziiert sind. Statt des Sternchens kann auch eine oder mehrere durch Komma getrennte Rechnungs-IDs offener Belastungen angegeben werden.

Analog dazu können auch Belastungen bereits bei Buchung aus angegebenen Guthaben getilgt werden. Dann müssen aber „<<“ verwendet werden. Alternativ zu den doppelten spitzen Klammern kann auch ein Tabulator eingegeben werden.

Interne Verrechnungen

Bei Belastungen, die keine Auszahlungen sind, kann die Referenz eines Zielguthabens in der Habenspalte angegeben werden. Die Referenz ist entweder die numerische ID des Guthabens, oder ein für diese vergebener Name, der mit einem Buchstaben beginnen muss und nur Buchstaben und Ziffern enthalten darf. Der Name ist für die Sitzung gültig, das heißt, mit dem Beenden von trsr verfällt er. Angegeben wird ein Name in der Sollspalte einer Habenbuchung. Er kann dann übrigens auch bei Soforttransfers bei späterhin gebuchten Belastungen angegeben werden.

Oft verwendete Namen können auch schon beim Aufruf von trsr charge definiert werden. Auf der Kommandozeile …

$ ./trsr charge --target-credit Mb=1 # oder kürzer: --tcr Mb=1
...
2017-04-11;Club;79900;;EBAY4354332/17: Beamer ... << Mb
...
$

Durch Benennung von Guthaben vorab kann der Benutzer das Risiko von falschen Referenzierungen erheblich mindern.

Belastung! Bewegung!

Nun, da wir Mitgliedskonten mit Guthaben versehen haben, und wahrscheinlich das Hauptkonto mit allerlei Belastungen wie Raummieten etc., wird TreasureDB immer noch nicht seinen Sinn und Zweck ausspielen können.

Es fehlen die internen Verrechnungen. Die setzen sich zusammen aus Belastungen und Bewegungen. Letztere sind die Verknüpfung zwischen einer Belastung und einem Guthaben, die beide dem gleichen Konto zugeordnet sein müssen, und einen Zeitstempel tragen.

Mit einer Belastung, die keine Auszahlung nach außen ist, muss ein Zielguthaben verknüpft werden. Für alle Mitgliedsbeiträge empfiehlt sich ein und dasselbe Zielguthaben zu verwenden. Du definierst es mit folgendem Kommando:

$ ./trsr charge -a main -v 0 -p "Mitgliedsbeiträge"

Das -v 0 ist entscheidend. Damit legst du fest, dass das Guthaben initial keinen Betrag besitzt.

Vom Stichtag X (Anfangssaldo) ausgehend muss anhand der Unterlagen des entlasteten Kassenwarts ermittelt werden, welches Mitglied aktuell welche Rückstände bis zum aktuellen Datum hat. Dabei gilt die satzungsgemäße Abrechnungseinheit, z.B. Monat. Für jeden Zeitraum (Monat/Jahr) musst du also ermitteln, welche Mitglieder noch keine Mitgliedsgebühr für den jeweiligen Monat entrichtet haben. trsr teilst du dies mit dem folgenden Kommando mit:

$ ./trsr ct --bill-id="MB1601-@{flow,john,dada,...}" -v 600 \
  -p "Mitgliedsbeitrag normal für Januar 2016"

Im Anschluss an dieses Kommando wird interaktiv abgefragt, welche offenen Belastungen von welchen verfügbaren Guthaben beglichen werden soll.