dimesum

Startseite / Blog / Geld

Geld

Warum das Ändern einer Ausgabe die Aufteilung neu setzt

· 8 Min. Lesezeit ·

Die Standardwerte einer Änderung sind Geldfehler, deshalb macht Dimesum participants und split_type bei jedem Ausgaben-PATCH erforderlich, neben der base_version, die eine veraltete Änderung ablehnt statt sie zusammenzuführen.

Ein Tippfehler zu korrigieren sollte nicht ändern, wer Geld schuldet. In Dimesum gibt das Bearbeiten einer geteilten Ausgabe die gesamte Aufteilung neu an. participants und split_type sind bei jedem PATCH erforderlich, und eine Änderung, die eines von beiden weglässt, kommt mit 400 zurück, statt einen Standardwert einzusetzen. Die Standardwerte beim Anlegen sind alle aktuellen Mitglieder und eine gleichmäßige Aufteilung, die bei einer Änderung Geldfehler sind.

Zwei Fehlerfälle machen es konkret. Ein Mitbewohner, der im August eingezogen ist, wird in das Abendessen vom Juli hineingezogen, weil „alle aktuellen Mitglieder“ zum Zeitpunkt der Änderung ausgewertet wird und nicht zum Zeitpunkt, als die Ausgabe erfasst wurde. Eine bewusste 70/30-Mietaufteilung wird auf 50/50 nivelliert, weil ein fehlendes split_type EQUAL bedeutet. Keiner der beiden Fälle löst einen Fehler aus, und beide bewegen echtes Geld.

Die Haltung

Die Standardwerte einer Änderung sind Geldfehler. Ein Standardwert beim Anlegen rät über die Gruppe, die der Autor gerade vor sich sieht. Eine Änderung trifft später ein, gegen eine Gruppe, die sich verändert hat, und dieselbe Vermutung schreibt stillschweigend um, wer was schuldet.

Standardwerte beim Anlegen beschreiben eine neue Ausgabe, nicht eine alte

Beim Anlegen sind die Standardwerte ehrlich. Der Autor sieht die Gruppe so, wie sie besteht, und eine gleichmäßige Aufteilung auf alle ist der Normalfall, also füllt Dimesum beides aus. Die Ausgabe speichert ihre Eingaben statt nur ihre Ergebnisse: splits.percent_bp, splits.weight und splits.exact_minor bewahren, was der Nutzer eingegeben hat, sodass eine spätere Änderung sie wieder öffnen kann.

Eine Änderung ist ein anderer Vorgang. Dieselbe Ausgabe kann Wochen später korrigiert werden, nachdem ein neuer Mitbewohner eingezogen ist oder ein Platzhaltermitglied beansprucht wurde. Die Mitgliedschaft ist ein bewegliches Ziel, die Aufteilung nicht. Die Standardwerte beim Anlegen erneut zu verwenden verlangt von der heutigen Gruppe, eine Frage zu beantworten, die die alte Ausgabe bereits beantwortet hat.

Zwei Versionen derselben Änderung: geerbte Standardwerte ändern jeden Anteil, während eine neu angegebene Aufteilung nur die Beschreibung ändert ÄNDERUNG ERBT DIE STANDARDWERTE ÄNDERUNG GIBT DIE AUFTEILUNG NEU AN Miete, 20.000 Euro, Juli Miete, 20.000 Euro, Juli v1 Asha 70%, 14.000 Bhavna 30%, 6.000 v1 Asha 70%, 14.000 Bhavna 30%, 6.000 Chetan zieht im August in die WG ein. Chetan zieht im August in die WG ein. PATCH korrigiert einen Tippfehler, sendet keine Aufteilung. PATCH korrigiert einen Tippfehler, sendet die Aufteilung: participants: Asha, Bhavna split_type: PERCENT 7000 / 3000 v2 Asha 6.666,67 Bhavna 6.666,67 Chetan 6.666,66 v2 Asha 70%, 14.000 Bhavna 30%, 6.000 Chetan schuldet einen Monat, den er nie gewohnt hat. Nur die Beschreibung hat sich geändert.
Dieselbe Änderung um ein Zeichen, zweimal. Das Erben der Standardwerte beim Anlegen teilt 20.000 Euro erneut auf drei auf und belastet ein Mitglied, das einen Monat später eingetreten ist; das Neuangeben der Aufteilung lässt jeden Anteil unberührt.

Die Weigerung zu raten ist hier nicht neu. Eine Ausgabe mit mehreren Zahlern muss ihre Zahler ebenfalls neu angeben. soleStoredPayer verwendet den gespeicherten Zahler nur dann erneut, wenn die Ausgabe genau einen hat, sodass eine Änderung, die zu zwei Zahlern schweigt, abgelehnt statt neu zugeordnet wird. Eine Änderung, die nichts über die Zahler sagt, behält den eigenen Zahler der Ausgabe bei, niemals die Person, die die Änderung vornimmt.

Die API lehnt eine Änderung ab, die ihre Aufteilung nicht neu angibt

Die Prüfung am Gateway läuft, bevor irgendein Betrag berechnet wird. Wenn participants leer ist oder split_type fehlt, kommt die Anfrage mit 400 zurück, mit dem Code invalid_expense und der Meldung „an edit must restate the split: participants and split_type are required“. Der Expense-Service wiederholt die Regel in validateAmend, sodass ein Aufrufer, der den Service auf anderem Weg erreicht, auf dieselbe Ablehnung trifft.

Was ein Anlegen sendet gegenüber dem, was eine Änderung neu angeben muss, bei POST und PATCH /v1/groups/{id}/expenses.
FeldBeim AnlegenBei einer ÄnderungWas der Standardwert kosten würde
participantsOptional. Standard sind alle aktuellen MitgliederErforderlichEin später eingetretenes Mitglied gerät in eine alte Ausgabe
split_typeOptional. Standard ist EQUALErforderlichEine 70/30-Aufteilung wird auf 50/50 nivelliert
payersOptional. Standard ist der AutorWeggelassen behält den alleinigen Zahler der Ausgabe; zwei Zahler müssen neu angegeben werdenDer Bearbeiter wird zum Zahler und kehrt um, wer wem schuldet
base_versionWird nicht gesendetErforderlich und muss der aktuellen Version entsprechenEine veraltete Änderung überschreibt eine Änderung, die ihr Autor nie gelesen hat
revision_idClient-UUIDv7, der IdempotenzschlüsselGleich, eine pro VersionEine wiederholte Änderung belastet die Gruppe doppelt
currencyPro Ausgabe angegebenMuss übereinstimmen; eine Änderung wird abgelehntEine Saldenzeile hält eine Währung pro Mitglied

Die Währung gehört zur selben Familie von Ablehnungen. Eine Änderung kann eine Ausgabe nicht umdenominieren, weil eine Saldenzeile eine Währung pro Mitglied hält. Die Schreibseite weist die Änderung zurück, und das append-only Ledger parkt eine solche Korrektur, falls sie es je erreicht. Die Ablehnung an der Tür verhindert, dass die beiden Hälften sich widersprechen.

Eine veraltete Änderung wird für ihren Autor abgelehnt, niemals zusammengeführt

base_version ist die andere Hälfte des Vertrags. Jeder PATCH trägt die Version, die sein Autor gelesen hat, und checkTransition vergleicht sie mit der Zeile, die die Transaktion gerade gesperrt hat. Sind sie gleich, wird die Änderung bei Version plus eins angewendet. Sind sie verschieden, erhält der Aufrufer HTTP 409 mit dem Code stale_version.

Das Zusammenführen ist die verlockende Alternative, und es ist falsch. Zwei Änderungen an einer Ausgabe sind zwei vollständige Aussagen darüber, was die Rechnung bedeutet. Sie zusammenzuführen erzeugt eine dritte Aussage, die niemand geschrieben hat, mit Anteilen, die keiner der beiden Autoren wiedererkennen würde. Die Ablehnung gibt den Konflikt an die eine Person zurück, die ihn lösen kann.

Zwei Autoren lesen Version 3; die erste Änderung bucht Version 4 und die zweite wird mit 409 stale_version abgelehnt Ausgabe, Version 3 Autor A PATCH base_version: 3 200 OK, Version 4 Autor B PATCH base_version: 3 409 stale_version Ledger, eine Transaktion: EXPENSE_REVERSAL von v3 EXPENSE v4 Autor B liest Version 4 erneut, wendet die Änderung erneut an, sendet base_version: 4
Optimistische Nebenläufigkeit bei einer Ausgabe. Die unterlegene Änderung wird an ihren Autor zurückgegeben, zusammen mit der Version, auf der sie neu aufgebaut werden muss. Die erfolgreiche Änderung erreicht das Ledger als Stornierung plus erneute Buchung, niemals als Aktualisierung.

Idempotenz und Nebenläufigkeit werden bewusst getrennt gehalten. Das Einfügen der Revision läuft vor der Versionsprüfung, weil eine wiederholte Änderung die base_version trägt, die sie ursprünglich gelesen hat und die nun veraltet ist. Eine Wiederholung muss als Wiederholung gelesen werden und nicht als Konflikt, also antwortet revision_id zuerst und gibt das gespeicherte Ergebnis zurück.

Die Antwort trägt bereits, was die nächste Änderung braucht

Mehr Felder bei einem PATCH zu verlangen ist nur fair, wenn ein Client sie günstig bekommen kann. Jede Ausgabenantwort trägt version, sodass ein Client, der gerade eine Ausgabe geschrieben hat, sie ohne zweites Lesen bearbeiten kann. Die Anlege-Antwort, die Änderungsantwort und jede Listenzeile tragen dasselbe Feld.

Für den Client, der die Ausgabe nicht gerade geschrieben hat, gibt GET /v1/groups/{id}/expenses/{id} die Version, die berechneten Anteile und die dahinterliegenden Eingaben zurück. Die Eingaben kommen unter denselben Namen zurück, die ein PATCH akzeptiert: participants, split_type, percents, weights, shares, items, pools. Ein Client liest eine Form und sendet sie mit Änderungen zurück, statt für eine Rechnung zwischen zwei Vokabularen zu übersetzen.

Die GET-Antwort und die PATCH-Anfrage verwenden dieselben Feldnamen, wobei version in base_version umbenannt wird GET LIEFERT PATCH AKZEPTIERT version base_version participants participants split_type split_type percents, weights, shares percents, weights, shares items, pools items, pools umbenannt
Ein Vokabular, zwei Richtungen. Nur das Feld version ändert über den Hin- und Rückweg seinen Namen, sodass ein Bearbeitungsclient nie eine Übersetzungsschicht zwischen dem, was er liest, und dem, was er schreibt, pflegt.

Die Symmetrie ist am wichtigsten für Aufteilungen, die sich nicht rekonstruieren lassen. Eine PERCENT-Aufteilung oder eine aufgeschlüsselte Rechnung lässt sich nicht allein aus ihren aufgelösten Anteilen neu angeben, weil bereits gerundet wurde und die Basispunkte und Einzelposten weg sind. Revisions-Snapshots tragen die Eingaben ebenfalls, sodass das Verlaufsblatt zeigen kann, was in jeder früheren Version aufgeschlüsselt war.

Jetzt erforderlich, weil eine Anforderung später nicht hinzugefügt werden kann

Ein Feld am ersten Tag zu verlangen ist eine Entscheidung über die Zukunft und nicht über heute. Ein erforderliches Feld später zu lockern ist abwärtskompatibel: Clients senden es bereits, und der Server beginnt, Anfragen ohne es zu akzeptieren. Eine Anforderung später hinzuzufügen bricht jeden Client, der sich auf den alten Standardwert verlassen hat.

Also wird die Richtung einmal früh gewählt. Dimesum verlangt participants, split_type und base_version bei einer Änderung, solange die Zahl der Clients noch klein genug für Änderungen ist. Falls je ein sicherer Standardwert für Änderungen gefunden wird, werden die Felder optional und nichts bereits Ausgeliefertes hört auf zu funktionieren.

Lassen Sie eine Änderung neu angeben, was sie bedeutet

Standardwerte gehören zum Anlegen, wo der Autor die Gruppe sehen kann, der er zustimmt. Bei einer Änderung sind dieselben Standardwerte eine Vermutung über eine Gruppe, die sich seitdem verändert hat. Ihr nächster Schritt: Öffnen Sie Ihren eigenen Lese-Endpunkt und prüfen Sie, dass er die Aufteilungseingaben unter genau den Feldnamen zurückgibt, die Ihr Schreib-Endpunkt akzeptiert. Ein Client, der zwischen beiden übersetzen muss, wird irgendwann eine davon falsch übersetzen.

Häufige Fragen

Warum verlangt das Bearbeiten einer geteilten Ausgabe participants und split_type?

Dimesum verlangt beide Felder, weil die Standardwerte beim Anlegen für eine Änderung falsch sind. Beim Anlegen setzt Dimesum standardmäßig alle aktuellen Mitglieder und eine gleichmäßige Aufteilung, was zur Gruppe passt, die der Autor gerade vor sich hat. Eine Änderung kann Wochen später eintreffen, nachdem jemand beigetreten ist. Diese Standardwerte erneut zu verwenden würde einen neuen Mitbewohner in ein altes Abendessen ziehen und eine bewusste 70/30-Mietaufteilung stillschweigend auf 50/50 zurücksetzen, ohne dass ein Fehler angezeigt wird.

Was passiert, wenn zwei Personen dieselbe Ausgabe gleichzeitig bearbeiten?

Die zweite Änderung wird mit HTTP 409 und dem Fehlercode stale_version abgelehnt. Jeder PATCH trägt base_version, die Version, die sein Autor gelesen hat, und der Änderungspfad vergleicht sie mit der gesperrten Zeile. Eine Abweichung bedeutet, dass sich die Ausgabe bewegt hat, also kommt die Änderung an ihren Autor zurück, damit er sie gegen die Version, die er nun sehen kann, erneut anwendet. Nichts wird zusammengeführt.

Werden beim Bearbeiten einer Ausgabe die Ledger-Zeilen direkt geändert?

Nein, das Bearbeiten einer Ausgabe aktualisiert in Dimesum niemals eine Ledger-Zeile. Eine Änderung bucht zwei Journale in einer Transaktion: eine EXPENSE_REVERSAL, die die alte Version Buchung für Buchung aufhebt, und dann ein EXPENSE-Journal für die neue Version. UPDATE und DELETE sind der eigenen Datenbankrolle des Ledgers entzogen, sodass eine Mutation selbst bei fehlerhaftem Code unmöglich ist. Sie sehen ein Bearbeitet-Kennzeichen und ein Verlaufsblatt.

Brauche ich vor dem Bearbeiten einer geteilten Ausgabe einen zweiten API-Aufruf?

Nein, ein Client, der die Ausgabe gerade geschrieben hat, hält bereits die Version, die eine Änderung braucht. Jede Ausgabenantwort trägt version, und genau das sendet der nächste PATCH als base_version. Ein Client, der die Ausgabe nicht geschrieben hat, ruft GET /v1/groups/{id}/expenses/{id} auf, was die Version plus die Aufteilungseingaben unter denselben Feldnamen zurückgibt, die ein PATCH akzeptiert.

Warum participants und split_type jetzt verlangen statt später ergänzen?

Ein Feld am ersten Tag zu verlangen ist umkehrbar, eines später hinzuzufügen nicht. Ein erforderliches Feld später zu lockern ist abwärtskompatibel: Clients senden es bereits, und der Server beginnt, Anfragen ohne es zu akzeptieren. Eine Anforderung später hinzuzufügen bricht jeden Client, der sich auf den alten Standardwert verlassen hat, und in einer Geld-API bleibt der Bruch unbemerkt, bis der Saldo von jemandem falsch ist.