14 KiB
| name | overview | todos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Code Review Fixes - Membership Fee Features | Umsetzung der validen Code Review Punkte aus beiden Reviews mit Priorisierung nach Kritikalität. Fokus auf Transaktionssicherheit, Code-Qualität, Performance und UX-Verbesserungen. |
|
Code Review Fixes - Membership Fee Features
Kritische Probleme (Müssen vor Merge behoben werden)
1. after_action mit Task.start - Transaktionsprobleme
Dateien: lib/membership/member.ex (Zeilen 142, 279)
Problem: Task.start/1 wird innerhalb von after_action Hooks verwendet. after_action läuft innerhalb der DB-Transaktion, daher:
- Tasks sehen möglicherweise noch nicht committed state
- Tasks werden auch bei Rollback gestartet
- Keine Supervision → Memory Leaks möglich
Lösung:
after_transactionHook verwenden (Ash Best Practice)Task.Supervisorzum Supervision Tree hinzufügen (lib/mv/application.ex)Task.Supervisor.async_nolink/3stattTask.start/1verwenden
Betroffene Stellen:
- Member Creation (Zeile 116-164)
- Join/Exit Date Change (Zeile 250-301)
2. Code-Duplikation in Cycle-Generation-Logik
Datei: lib/membership/member.ex
Problem: Cycle-Generation-Logik ist dreimal dupliziert (Create, Type Change, Date Change)
Lösung: Extrahiere in private Funktion handle_cycle_generation/2
Wichtige Probleme (Sollten behoben werden)
3. join_date Validierung entfernt, aber Dokumentation behauptet Gegenteil
Datei: lib/membership/member.ex (Zeile 27, 516-518)
Problem: Dokumentation sagt "join_date not in future", aber Validierung fehlt
Lösung: Dokumentation anpassen
4. load_cycles_for_members overpromises
Datei: lib/mv_web/member_live/index/membership_fee_status.ex (Zeile 36-40)
Problem: Dokumentation sagt "Only loads the relevant cycle per member" und "Filters cycles at database level", aber lädt alle Cycles
Lösung: echte Filterung implementieren (z.B. nur letzte 2 Intervalle)
5. get_current_cycle nondeterministisch
Datei: lib/mv_web/helpers/membership_fee_helpers.ex (Zeile 178-182)
Problem: List.first() ohne explizite Sortierung → Ergebnis hängt von Reihenfolge ab
Lösung: Vor List.first() nach cycle_start sortieren (desc)
6. N+1 Query durch get_member_count
Datei: lib/mv_web/live/membership_fee_type_live/index.ex (Zeile 134-140)
Problem: get_member_count/1 wird pro Row aufgerufen → N+1 Query
Lösung: Aggregate auf MembershipFeeType definieren oder einmalig vorab laden
7. assign_new kann stale werden
Datei: lib/mv_web/live/member_live/show/membership_fees_component.ex (Zeile 402-403)
Problem: assign_new(:cycles, ...) und assign_new(:available_fee_types, ...) werden nur gesetzt, wenn Assign noch nicht existiert
Lösung: In update/2 immer assign(:cycles, cycles) / assign(:available_fee_types, available_fee_types) setzen
8. @regenerating wird nie auf true gesetzt
Datei: lib/mv_web/live/member_live/show/membership_fees_component.ex (Zeile 526-561)
Problem: regenerating wird nur auf false gesetzt, nie auf true → Button/Spinner werden nie disabled
Lösung: Direkt beim Event-Start socket |> assign(:regenerating, true) setzen
9. Create-cycle parsing: invalid amount zeigt falsche Fehlermeldung
Datei: lib/mv_web/live/member_live/show/membership_fees_component.ex (Zeile 748-812)
Problem: Decimal.parse/1 gibt :error zurück, aber with behandelt es als :error → landet in "Invalid date format" Branch
Lösung: Explizit {:error, :invalid_amount} zurückgeben:
amount = case Decimal.parse(amount_str) do
{d, _} -> {:ok, d}
:error -> {:error, :invalid_amount}
end
10. Delete all cycles: nicht atomar
Datei: lib/mv_web/live/member_live/show/membership_fees_component.ex (Zeile 666-714)
Problem: Enum.map(cycles, &Ash.destroy/1) → nicht atomar, teilweise gelöscht möglich
Lösung: Bulk Delete Query verwenden (Ash bulk destroy oder Query-basiert)
11. Fehlerbehandlung bei async Tasks
Datei: lib/membership/member.ex
Problem: Bei Fehlern in async Tasks wird nur geloggt, aber der Benutzer erhält keine Rückmeldung. Die Member-Aktion wird als erfolgreich zurückgegeben, auch wenn die Cycle-Generierung fehlschlägt. Keine Retry-Logik oder Monitoring.
Lösung:
- Für kritische Fälle: synchron ausführen oder Retry-Mechanismus implementieren
- Für nicht-kritische Fälle: Event-System für spätere Benachrichtigung
- Strukturierte Error-Logs mit Context
- Optional: Error-Tracking (Sentry, etc.)
12. format_currency Robustheit
Datei: lib/mv_web/helpers/membership_fee_helpers.ex (Zeilen 27-51)
Problem: Die Funktion verwendet String-Manipulation für Formatierung. Edge Cases könnten problematisch sein (z.B. sehr große Zahlen, negative Werte).
Lösung:
Number.Currencyoder ähnliche Bibliothek verwenden- Oder: Robusteres Pattern Matching für Edge Cases
- Tests für Edge Cases hinzufügen (negative Zahlen, sehr große Zahlen)
13. Fehlende Typespecs
Datei: lib/membership/member/changes/set_default_membership_fee_type.ex
Problem: Keine @spec für die change/3 Funktion.
Lösung: Typespecs hinzufügen für bessere Dokumentation und Dialyzer-Support.
14. Potenzielle Race Condition
Datei: lib/membership/member.ex (Zeile 250-301)
Problem: Wenn join_date und exit_date gleichzeitig geändert werden, könnte die Cycle-Generierung zweimal ausgelöst werden (einmal pro Änderung).
Lösung: Prüfen, ob Ash dies bereits verhindert, oder Logik anpassen (z.B. beide Änderungen in einem Hook zusammenfassen).
15. Magic Numbers/Strings
Problem: Application.get_env(:mv, :sql_sandbox, false) wird mehrfach verwendet.
Lösung: Extrahiere in Konstante oder Helper-Funktion (z.B. Mv.Config.sql_sandbox?/0).
Mittlere Probleme (Nice-to-have)
16. Inconsistent use of domain
Datei: lib/mv_web/live/member_live/show/membership_fees_component.ex (Zeile 819-821)
Problem: Einige Actions verwenden domain: MembershipFees, andere nicht
Lösung: Konsistent domain überall verwenden
17. Tests: create_cycle/3 löscht jedes Mal alle Cycles
Datei: test/mv_web/member_live/index/membership_fee_status_test.exs (Zeile 45-52)
Problem: Helper löscht vor jedem Create alle Cycles → Tests prüfen nicht, was sie denken
Lösung: Cleanup nur einmal im Setup oder gezielt nur auto-generierte löschen
18. Tests/Design: Date.utc_today() macht Tests flaky
Problem: Tests hängen von Date.utc_today() ab → nicht deterministisch
Lösung: today Parameter durchgeben (z.B. get_cycle_status_for_member(member, show_current, today \\ Date.utc_today()))
19. UI/Locale: input type="number" + Decimal/Komma
Problem: type="number" funktioniert nicht zuverlässig mit Komma als Dezimaltrenner
Lösung: type="text" + inputmode="decimal" + serverseitig "," → "." normalisieren
20. Delete-all-Confirmation: String-Vergleich ist fragil
Datei: lib/mv_web/live/member_live/show/membership_fees_component.ex (Zeile 296-298)
Problem: String-Vergleich gegen gettext("Yes") und "Yes" → fragil bei Whitespace/Locale
Lösung: String.trim() + case-insensitive Vergleich oder "type DELETE" Pattern
21. MembershipFeeType Form: Warning-State kann hängen bleiben
Datei: lib/mv_web/live/membership_fee_type_live/form.ex (Zeile 367-378)
Problem: Bei Decimal.parse(:error) wird nur socket zurückgegeben → Warning kann stehen bleiben
Lösung: Bei :error explizit hide_amount_warning(socket) aufrufen
22. UI/UX: Toggle ist doppelt vorhanden
Datei: lib/mv_web/live/member_live/index.html.heex (Zeile 45-72, 284-296)
Problem: Toggle-Button sowohl in Toolbar als auch im Spalten-Header
Lösung: Toggle im Spalten-Header entfernen (nur in Toolbar behalten)
23. Konsistenz: format_currency vs Inputs
Problem: format_currency formatiert deutsch (Komma), aber Inputs erwarten Punkt
Lösung: Inputs ebenfalls auf Komma ausrichten oder serverseitig normalisieren
Implementierungsreihenfolge
- Kritisch: after_action → after_transaction + Task.Supervisor
- Kritisch: Code-Duplikation reduzieren
- Wichtig: join_date Validierung/Dokumentation
- Wichtig: load_cycles_for_members Dokumentation/Implementierung
- Wichtig: get_current_cycle Sortierung
- Wichtig: N+1 Query beheben
- Wichtig: assign_new → assign
- Wichtig: @regenerating auf true setzen
- Wichtig: Create-cycle parsing Fix
- Wichtig: Delete all cycles atomar
- Wichtig: Fehlerbehandlung bei async Tasks
- Wichtig: format_currency Robustheit
- Wichtig: Fehlende Typespecs
- Wichtig: Potenzielle Race Condition prüfen/beheben
- Wichtig: Magic Numbers/Strings extrahieren
- Mittel: Domain-Konsistenz
- Mittel: Test-Helper Fix
- Mittel: Date.utc_today() Parameter
- Mittel: UI/Locale Fixes
- Mittel: String-Vergleich robuster
- Mittel: Warning-State Fix
- Mittel: Toggle entfernen
- Mittel: Format-Konsistenz