Varför en ändrad utgift måste ange delningen på nytt
En ändrings standardvärden är pengabuggar, så Dimesum gör participants och split_type obligatoriska på varje PATCH av en utgift, vid sidan av base_version som avvisar en föråldrad ändring i stället för att slå ihop den.
Att rätta ett stavfel ska inte ändra vem som är skyldig pengar. I Dimesum anger en ändring av en delad utgift hela delningen på nytt. participants och split_type är obligatoriska på varje PATCH, och en ändring som utelämnar något av dem kommer tillbaka 400 i stället för att fylla i ett standardvärde. Standardvärdena vid skapande är alla nuvarande medlemmar och en lika delning, vilket vid en ändring är pengabuggar.
Två fel gör saken konkret. En inneboende som gick med i augusti dras in i julis middag, eftersom "alla nuvarande medlemmar" utvärderas när ändringen landar, inte när utgiften skrevs. En medveten 70/30-delning av hyran plattas ut till 50/50, eftersom ett saknat split_type betyder EQUAL. Inget av felen ger ett felmeddelande, och båda flyttar riktiga pengar.
En ändrings standardvärden är pengabuggar. Ett standardvärde vid skapande gissar om gruppen som författaren ser framför sig just nu. En ändring kommer senare, mot en grupp som har rört sig, och samma gissning skriver tyst om vad människor är skyldiga.
Standardvärden vid skapande beskriver en ny utgift, inte en gammal
Vid skapandet är standardvärdena ärliga. Författaren ser gruppen som den ser ut, och en lika delning mellan alla är det vanliga fallet, så Dimesum fyller i båda. Utgiften lagrar sina indata snarare än enbart sina resultat: splits.percent_bp, splits.weight och splits.exact_minor behåller det som användaren skrev, så att en senare ändring kan öppna den igen.
En ändring är en annan handling. Samma utgift kan rättas flera veckor senare, när en ny inneboende har gått med eller en spökmedlem har gjorts anspråk på. Medlemskapet är ett rörligt mål och delningen är det inte. Att återanvända standardvärdena vid skapande ber dagens grupp att svara på en fråga som den gamla utgiften redan besvarade.
Vägran att gissa är inte något nytt här. En utgift med flera betalare måste ange sina betalare på nytt också. soleStoredPayer återanvänder den lagrade betalaren bara när utgiften har exakt en, så en ändring som förblir tyst om två betalare avvisas i stället för att tilldelas om. En ändring som inte säger något om betalare behåller utgiftens egen betalare, aldrig personen som gör ändringen.
API:et avvisar en ändring som inte anger sin delning på nytt
Kontrollen i gatewayen körs innan några pengar beräknas. När participants är tom eller split_type är blank kommer förfrågan tillbaka 400 med koden invalid_expense och meddelandet "an edit must restate the split: participants and split_type are required". Utgiftstjänsten upprepar regeln i validateAmend, så en anropare som når tjänsten på ett annat sätt möter samma vägran.
| Fält | Vid skapande | Vid en ändring | Vad standardvärdet skulle kosta |
|---|---|---|---|
participants | Valfritt. Standard är alla nuvarande medlemmar | Obligatoriskt | En medlem som gick med efteråt hamnar i en gammal utgift |
split_type | Valfritt. Standard är EQUAL | Obligatoriskt | En 70/30-delning plattas ut till 50/50 |
payers | Valfritt. Standard är författaren | Utelämnad behåller utgiftens enda betalare; två betalare måste anges på nytt | Den som ändrar blir betalare, vilket vänder på vem som är skyldig vem |
base_version | Skickas inte | Obligatoriskt, och måste vara lika med den aktuella versionen | En föråldrad ändring skriver över en ändring som dess författare aldrig läste |
revision_id | Klientens UUIDv7, idempotensnyckeln | Samma, en per version | En omförsökt ändring debiterar gruppen två gånger |
currency | Anges per utgift | Måste stämma; en ändring avvisas | En saldorad håller en valuta per medlem |
Valuta hör till samma familj av vägranden. En ändring kan inte omvandla en utgift till en annan valuta, eftersom en saldorad håller en valuta per medlem. Skrivsidan avvisar ändringen, och den append-only-bokföringen parkerar ett sådant tillägg om det någonsin når den. Att vägra vid dörren håller de två halvorna från att vara oense.
En föråldrad ändring avvisas åt sin författare, aldrig sammanslagen
base_version är den andra halvan av kontraktet. Varje PATCH bär med sig den version som dess författare läste, och checkTransition jämför den mot raden som transaktionen just låste. Lika, och ändringen tillämpas på version plus ett. Olika, och anroparen får HTTP 409 med koden stale_version.
Att slå ihop är det frestande alternativet, och det är fel. Två ändringar av en utgift är två fullständiga utsagor om vad räkningen betyder. Att slå ihop dem ger en tredje utsaga som ingen skrev, med andelar som ingen av författarna skulle känna igen. Vägran lämnar konflikten tillbaka till den enda person som kan lösa den.
Idempotens och samtidighet hålls medvetet isär. Insättningen av revisionen körs före versionskontrollen, eftersom en omförsökt ändring bär med sig den basversion den ursprungligen läste, som nu är föråldrad. Ett omtag måste läsas som ett omtag snarare än en konflikt, så revision_id svarar först och returnerar det lagrade resultatet.
Svaret bär redan med sig det som nästa ändring behöver
Att kräva fler fält på en PATCH är rimligt bara om en klient kan få tag på dem billigt. Varje svar för en utgift bär med sig version, så en klient som just skrev en utgift kan ändra den utan en andra läsning. Svaret vid skapande, svaret vid ändring och varje rad i listan bär med sig samma fält.
För klienten som inte just skrev utgiften returnerar GET /v1/groups/{id}/expenses/{id} versionen, de beräknade andelarna och indata bakom dem. Indata kommer tillbaka under samma namn som en PATCH accepterar: participants, split_type, percents, weights, shares, items, pools. En klient läser en form och skickar tillbaka den med ändringar, i stället för att översätta mellan två vokabulär för en räkning.
Symmetrin betyder mest för delningar som inte kan rekonstrueras. En PERCENT-delning eller en specificerad räkning kan inte anges på nytt utifrån enbart sina uträknade andelar, eftersom avrundning redan har tillämpats och baspunkterna och radposterna är borta. Revisionsögonblicksbilder bär med sig indata också, så historikbladet kan visa vad som specificerades på vilken tidigare version som helst.
Obligatoriskt nu, eftersom ett krav inte kan läggas till senare
Att kräva ett fält på dag ett är ett beslut om framtiden snarare än om i dag. Att luckra upp ett obligatoriskt fält senare är bakåtkompatibelt: klienterna skickar det redan, och servern börjar acceptera förfrågningar utan det. Att lägga till ett krav senare bryter varje klient som förlitade sig på det gamla standardvärdet.
Så riktningen väljs en gång, tidigt. Dimesum kräver participants, split_type och base_version på en ändring medan antalet klienter fortfarande är litet nog att ändra. Om ett säkert standardvärde för ändringar någonsin hittas blir fälten valfria och inget som redan levererats slutar fungera.
Låt en ändring ange vad den betyder
Standardvärden hör till skapandet, där författaren kan se gruppen de går med på. Vid en ändring är samma standardvärden en gissning om en grupp som sedan har rört sig. Ditt nästa steg: öppna din egen läsändpunkt och kontrollera att den lämnar tillbaka delningens indata under exakt de fältnamn som din skrivändpunkt accepterar. En klient som måste översätta mellan de två kommer till slut att översätta en av dem fel.
Vanliga frågor
Varför måste jag ange participants och split_type när jag ändrar en delad utgift?
Dimesum kräver båda fälten eftersom standardvärdena vid skapande är fel för en ändring. Vid skapande utgår Dimesum från alla nuvarande medlemmar och en lika delning, vilket stämmer med gruppen som författaren ser framför sig. En ändring kan komma flera veckor senare, efter att någon har gått med. Att återanvända de standardvärdena skulle dra in en ny inneboende i en gammal middag och platta ut en medveten 70/30-delning av hyran tillbaka till 50/50, utan att något fel visas.
Vad händer om två personer ändrar samma utgift samtidigt?
Den andra ändringen avvisas med HTTP 409 och felkoden stale_version. Varje PATCH bär med sig base_version, den version som författaren läste, och ändringsvägen jämför den mot den låsta raden. En avvikelse betyder att utgiften har rört sig, så ändringen kommer tillbaka för att författaren ska tillämpa den på nytt mot den version de nu kan se. Ingenting slås ihop.
Uppdateras bokföringsraderna direkt när jag ändrar en utgift?
Nej, att ändra en utgift uppdaterar aldrig en bokföringsrad i Dimesum. En ändring bokför två verifikat i en transaktion: en EXPENSE_REVERSAL som upphäver den gamla versionen ben för ben, sedan ett EXPENSE-verifikat för den nya versionen. UPDATE och DELETE är återkallade från bokföringens egen databasroll, så en mutation är omöjlig även för buggig kod. Du ser en markering om att utgiften ändrats och ett historikblad.
Behöver jag göra ett extra API-anrop innan jag ändrar en delad utgift?
Nej, en klient som just skrev utgiften har redan den version som en ändring behöver. Varje svar för en utgift bär med sig version, vilket är det som nästa PATCH skickar som base_version. En klient som inte skrev utgiften anropar GET /v1/groups/{id}/expenses/{id}, som returnerar versionen plus delningsuppgifterna under samma fältnamn som en PATCH accepterar.
Varför kräva participants och split_type nu i stället för att lägga till dem senare?
Att kräva ett fält på dag ett är omvändbart, medan att lägga till ett senare inte är det. Att luckra upp ett obligatoriskt fält senare är bakåtkompatibelt: klienterna skickar det redan, och servern börjar acceptera förfrågningar utan det. Att lägga till ett krav senare bryter varje klient som förlitade sig på det gamla standardvärdet, och i ett penning-API är felet tyst tills någons saldo är fel.
Populära inlägg
- Journalen som håller delade utgifter exakta8 min läsning
- Sex pengabuggar i utgiftsdelning med flera valutor8 min läsning
- Gör upp: reglera gruppens utgifter med färre överföringar5 min läsning
- Dela notan när en rätt inte delades9 min läsning
- Dela hyran rättvist med rumskompisar5 min läsning