Repository navigation
Conversation
- Request money from multiple users at once (atomic, one transaction per user) - Split by factors, percentages or absolute amounts; unset shares are calculated automatically and can be reset per person - Optionally include yourself in the split (your share isn't requested) - Equal card heights on the split page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017yaFTPNEHGx3LenyDccnGs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWPHvWv8YsGKLcJ2gfSart
…cksichtigen Neues Feld ordersStartsAt (Standard 72h) an der Vorlage inkl. Migration. Der Cron legt den Gruppenkauf jetzt an, sobald die nächste Deadline höchstens ordersStartsAt Stunden entfernt ist, statt fest 3 Tage vorher. Im Formular wird der Wert als Zahl übergeben und die Wochentage beginnen mit Montag. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QpZB9UHK22rJeuAYwvXVoe
Nutzer aus der LDAP-Zeit heißen noch "vorname.nachname", weil NextAuth den Namen bestehender Nutzer nie aktualisiert. Beim Keycloak-Login und einmal beim Serverstart (instrumentation.ts) wird ein solcher Name durch den Keycloak-Anzeigenamen ersetzt. Andere Namen bleiben unverändert. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HpKohmJY768aFwQmP9xahY
NextAuth nutzt das Keycloak-Profil nur beim Anlegen eines Nutzers, daher wurde LABEATS_ADMIN bei späteren Logins weder vergeben noch entzogen. Der jwt-Callback gleicht is_admin jetzt bei jedem Keycloak-Login mit den Gruppen ab; ohne groups-Claim bleibt der gespeicherte Wert unverändert. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015C7vjctgvrGP2e4jWisqEg
…chalter Die normale Ansicht ist wieder das einfache Formular (Betrag, ein Nutzer, Anmerkung). Über den Schalter "Einzeln / Gruppe" neben der Überschrift lässt sich die Aufteilung auf mehrere Nutzer einblenden. Betrag und Anmerkung bleiben beim Umschalten erhalten. Gruppenansicht aufgeräumt: Aufteilungsart erst sichtbar, wenn Nutzer gewählt sind; Anteile werden beim Moduswechsel auch bei negativen automatischen Anteilen umgerechnet; negative automatische Anteile sind mit einem Warnsymbol markiert; keine negativen Eingaben, Prozent höchstens 100; automatische Werte als Platzhalter mit max. 4 Nachkommastellen; feste Plätze für Zurücksetzen- und Entfernen-Knopf. Die Split-Seite nimmt jetzt immer die volle Breite ein, damit sie beim Umschalten nicht springt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uopa4yzVNAMzdHG5cNhdv
type="number" akzeptiert nur den Dezimaltrenner der Browsersprache und leert das Feld beim anderen. Neue Komponente DecimalInput (Textfeld mit Zifferntastatur): nimmt "," und "." an, ignoriert Buchstaben und Werte außerhalb von min/max und zeigt höchstens 4 Nachkommastellen. Eingesetzt bei Geld senden/einfordern (inkl. Anteile), Verrechnungskonten, Gruppenbestellung abrechnen, Artikelpreis und Kategorie-Aufpreis (über react-hook-form Controller). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uopa4yzVNAMzdHG5cNhdv
Einzelartikel in einer offenen Gruppenbestellung werden nur noch vorgemerkt (Transaktionstyp 4) und lassen sich bis zum Schließen der Bestellung stornieren. Beim Abrechnen zahlt der Besteller Preis plus Kategorie-Aufschlag, der Artikelpreis geht an den Empfänger der Abrechnung. Abrechnung stornieren erstattet die Einzelartikel und merkt sie wieder vor; ältere, schon beim Kauf gebuchte Käufe werden dabei vom Verrechnungskonto zurückgeholt. Offene Vormerkungen zählen beim Kontostand-Check für Käufe, Überweisungen und Wünsche mit. Die gewählte Menge im Kaufdialog wird jetzt auch bei Gruppenbestellungen übernommen, und "Gezahlt von" steht auf einer Höhe mit dem Abrechnen-Knopf. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017VymTx4jHy14YS6vBSrUXp
Die Session ist ein JWT-Cookie und lässt sich nur über eine Prüfung ungültig machen. next.config.mjs schreibt beim Build einen Zeitstempel als SESSION_BUILD_ID in den Code, beim Login landet er als build_id im Token. Passt die Kennung nicht zum laufenden Build, wirft der jwt-Callback und NextAuth löscht das Cookie; der Proxy lässt solche Token ebenfalls nicht mehr durch. Ein Neustart mit demselben Image meldet niemanden ab. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RtF3pTQLHfrUvE1MY8YWHv
Ein Artikel kann optional einen Pfandbetrag haben (neues Feld deposit, im Artikel-Formular über das DecimalInput einstellbar). Auf der Karte im Kaufen-Tab steht dann rechts neben dem Preis ein "+ Pfand"-Knopf, der denselben Bestätigungsdialog mit Mengenauswahl wie der normale Kauf öffnet. Jedes Stück wird als eigener normaler Kauf (Typ 0) nur über den Pfandbetrag gebucht, ohne Kategorie-Aufschläge, auf das Verrechnungskonto des Artikels. Der Kontostand-Check berücksichtigt vorgemerkte Käufe, stornieren geht wie bei normalen Käufen. In der Inventarübersicht der Admins steht der Pfand als eigene Spalte. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LnGACsiy32iHUzXayUeCwW
"Alle auswählen" und "Alle abwählen" haben jetzt beide den Rahmen-Stil. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LnGACsiy32iHUzXayUeCwW
Ein Klick auf Name, Preis, Pfand oder Konto sortiert aufsteigend, ein weiterer Klick absteigend; ein Pfeil zeigt die Richtung. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LnGACsiy32iHUzXayUeCwW
…chnung - Einkäufer: "Ich gehe einkaufen" / austragen solange offen, wird beim Schließen automatisch gesetzt und bei der Abrechnung auf die zahlende Person gesetzt; Anzeige unter dem Titel (auch in der History) - Kategorie-Filter: Admins und Einkäufer wählen eine Kategorie, das Bestellfenster zeigt nur passende Artikel - Abrechnung: vorgemerkte Einzel-Artikel mit anpassbarem Preis, Kosten-Toggle "Gesamt / nur Gruppen-Artikel" (Gesamt zieht die Einzel-Artikel ab), Abrechnung hinter eigenem Knopf - Abrechnung stornieren: Admins immer, die abrechnende Person 2h lang - einheitliche Darstellung (Trennlinien Gruppen-/Einzel-Artikel, Titel und Datum, zentrierte Karten, Icons) - Gruppen-Artikel können nach dem Schließen nicht mehr bestellt werden Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017VymTx4jHy14YS6vBSrUXp
Im Profil stehen jetzt alle Kennzahlen als einheitliche Kacheln: Ranking, Gesamtausgaben, Beschaffung, Wochen- und Monatsschnitt, bezahlter Pfand und die Top 3 Lieblingsartikel mit Anzahl und Durchschnitt pro Woche. Neue Admin-Seite "Statistik" (nur adminProcedure, nur Summen, nichts pro Person): Umsatz 7/30 Tage mit Durchschnitt pro Woche und Monat, aktive Nutzer, Pfand der letzten 30 Tage, offene Guthaben, Summe der überzogenen Konten sowie die meistverkauften Artikel im Kaufen-Tab und in Gruppenbestellungen. Gezählt werden nur abgebuchte Käufe (Typ 0, nicht storniert); vorgemerkte Gruppenkäufe zählen erst nach dem Abschluss. Pfand zählt bei den persönlichen Ausgaben mit, beim Admin-Umsatz nicht. Durchschnitte laufen über die letzten 12 Wochen, ab dem späteren Zeitpunkt aus erstem Kauf (bzw. erstem Verkauf des Artikels) und Fensterbeginn. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K4Xj8GGcCUjPtYNFPFf7y1
Wenn jemand anderes Geld von einer Person einzieht, zeigt der Header am Konto-Tab (mobil am Menü-Symbol) einen roten Punkt. Er verschwindet, sobald die Konto-Seite die Einzüge angezeigt hat. Der Gesehen-Stand liegt pro Nutzer in der Datenbank (User.retractionsSeenAt), die Migration markiert bestehende Einzüge als gesehen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2FVn2YVSYKZece2KDGvaz
- user.getCtx entfernt: gab ohne Login das LDAP-Bind-Passwort zurück - Undo bucht Artikelpreis/Pfand wieder vom Verrechnungskonto ab - Undo, Überweisen, Gruppenbestellung schließen/zurücknehmen gegen Doppelausführung abgesichert - Gebühren ohne Ziel-Verrechnungskonto werden abgelehnt statt verloren - Rücknahme von Gruppen-Artikeln nutzt die Kategorien der Buchung - Keine negativen Preise beim Gruppen-Abschluss, ganzzahlige Kaufmenge - sendMoneyProcurement als Korrektur-Werkzeug kommentiert - LDAP-Nutzerdaten nicht mehr ins Log, Cron-Fehler werden geloggt - Ungenutzte Funktionen, Debug-Logs und doppelten Endpunkt entfernt - getStats zählt per count(), eslint ignoriert .next/ - npm audit fix Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHpZzB3cmDemfMC9RyptpY
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait...
|
Kacheln auf Kaufen, Gruppenbestellungen, Konto und Co. nutzen jetzt denselben Verlauf wie Geld senden/einfordern, mit feinem Rauschen gegen Banding. Über dem Header zeigt der Overscroll-Bereich die Header-Farbe. Außerdem kompaktere Such- und Filterleiste auf der Kaufen-Seite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WSPEf53xnfN1DksTwuXVEM
13Bytes
left a comment
There was a problem hiding this comment.
Hey,
danke für die Erweiterungen!
Ein paar Dinge:
- Category fees can disappear before settlement — P1
The new flow delays charging a reserved item until the group order is settled. To calculate its fees later, settlement reads the categories associated with that purchase.
However, the category relationship can point to only one purchase. Buying another item with the same category moves that relationship to the newer purchase.
For example:
- Alice reserves an item with a €0.50 category fee.
- Bob reserves another item using that category.
- The category association moves from Alice’s reservation to Bob’s.
- At settlement, Alice’s reservation no longer has the category information needed to charge her fee.
An ordinary purchase can also displace the association. The result is missing charges and too little money credited to the clearing account. Previously, fees were calculated immediately, before a later purchase could change the relationship.
Suggested fix: save the applicable fees and their destination accounts separately for every reservation, so subsequent purchases cannot change them.
- A reservation can arrive after the order has settled — P2
Checking whether an order is open and creating the reservation happen separately. Another request can close and settle the order between those steps.
For example:
- Alice clicks to reserve an item; the server checks that the order is open.
- The shopper closes and settles the order.
- Alice’s request resumes and creates the reservation without checking again.
That reservation missed settlement, but cancellation is now blocked because the order is closed. Its amount can remain reserved against Alice’s available balance, reducing what she can spend or transfer.
Suggested fix: make checking the order’s state and creating the reservation one coordinated operation that cannot race with closure or settlement.
- Editing a category can empty an order’s item list — P2
Editing a category creates a replacement category with a new ID and moves its items to that replacement. An existing group order’s filter still points to the old ID.
For example, an order is filtered to “Drinks.” An admin edits that category. All drinks now belong to its replacement, while the order continues filtering by the old category. Consequently, no items match, and the old category also disappears from the selector.
This disrupts ordering until the shopper or an admin resets the filter; it does not delete the items.
Suggested fix: update affected order filters when replacing a category, or have filters refer to a category identity that remains stable across edits.
| name String | ||
| categories Category[] | ||
| price Float | ||
| deposit Float @default(0) // optional deposit (Pfand) in €, bought separately |
There was a problem hiding this comment.
Do we need a dedicated deposit or would a bottle deposit item be good enough?
| revertedById String? | ||
| shopperId String? | ||
| // only show items of this category when ordering (set by admins or the shopper) | ||
| filterCategory Category? @relation("groupOrderFilter", fields: [filterCategoryId], references: [id], onDelete: SetNull) |
There was a problem hiding this comment.
would it make sense to add a default to the GroupOrderTemplate as well?
| setFilterCategory(groupOrder.id, e.target.value || null) | ||
| } | ||
| > | ||
| <option value="">Alle Kategorien</option> |
| <span className="font-medium">Gesamt von {participants.length} Nutzer(n)</span> | ||
| <span className="text-lg font-bold text-warning">{requestedAmount.toFixed(2)}€</span> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
can we harmonize/combine this component with the one that handels the group order multi-user money split?

Branch
feature/split-multiple-users, 18 commits againstmain(54 files, +2914 / −993).Request money: split between several users
Amount fields: dot and comma
DecimalInputcomponent replacestype="number": accepts both dot and comma as decimal separator; at most 4 decimal places are shownKeycloak / LDAP
LABEATS_ADMIN) are synced with the user's groups on every Keycloak login; without agroupsclaim the stored value is keptSign-out after update
SESSION_BUILD_ID; sessions from older builds are rejected, so every container update signs all users out once. Restarting the same image signs nobody out.Group orders
ordersStartsAt, default 72 h)Deposit
Statistics
Notification dot
Minor UI fixes
Cleanup and booking bugs
user.getCtx, which returned the LDAP bind password without loginnpm audit fixDeployment notes
prisma migrate deployin the entrypoint:add_orders_starts_at_to_template: new column, default 72add_item_deposit: new column, default 0add_grouporder_shopper_and_filter: rebuilds theGroupOrdertable and backfills the shopper of closed orders with the recipient of the settlement.add_user_retractions_seen_at: marks all existing money collections as seen so nobody sees a stale red dot after the updatesrc/instrumentation.tsrenames old LDAP usernames once at server start. This is transitional code and can be removed (together withsyncKeycloakNamesinsrc/server/syncKeycloakNames.ts) once all accounts have been migrated. Renaming on login is not affected.prisma migrate deployin the container log after deploying.Summary by CodeRabbit