Compare commits

..

48 commits

Author SHA1 Message Date
3860ec51f2
refactor: reduce nesting depth in process_batch function
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-16 15:15:37 +01:00
40cdcbe453
fix: address notification handling review feedback
1. Fix misleading comment in async create_member path
2. Use skip_lock?: true in test case for create_member
3. Fix generate_cycles_for_all_members/1
2025-12-16 15:15:37 +01:00
4997493139
refactor: implement proper notification handling via after_action hooks
Refactor notification handling according to Ash best practices
2025-12-16 15:15:36 +01:00
f7c33bfc7d
fix: resolve notification handling and maintain after_action for cycle regeneration 2025-12-16 15:15:36 +01:00
6a91f7c711
fix: correct return_notifications? logic to prevent missed notifications
Fix the logic for return_notifications? in create_cycles
2025-12-16 15:15:36 +01:00
b517594141
refactor: reduce nesting depth in regenerate_cycles_on_type_change
Split the function into smaller, focused functions to reduce nesting depth
2025-12-16 15:15:36 +01:00
358b1bfdb1
fix: address code review points for cycle regeneration
1. Fix critical notifications bug
2. Fix today inconsistency
3. Add advisory lock around deletion
4. Improve helper function documentation
5. Improve error message UX
2025-12-16 15:15:35 +01:00
b8791cbcfe
refactor: reduce complexity of with_advisory_lock function
Split the complex with_advisory_lock function into smaller, focused
functions to improve readability and reduce cyclomatic complexity
2025-12-16 15:15:35 +01:00
5b66d49fcd
fix: prevent deadlocks by detecting existing transactions 2025-12-16 15:15:35 +01:00
83cf6d7503
test: update test to reflect nil assignment prevention 2025-12-16 15:15:35 +01:00
6ab7028275
fix: remove unused variable warning in ValidateSameInterval 2025-12-16 15:15:35 +01:00
4cdbf5ed4f
docs: update architecture docs for atomic cycle regeneration 2025-12-16 15:15:34 +01:00
b690d168c4
test: add monthly interval tests for cycle calculations 2025-12-16 15:15:34 +01:00
2704e0887e
test: remove Process.sleep from type change integration tests 2025-12-16 15:15:34 +01:00
cb37f0c360
fix: prevent nil assignment for membership_fee_type_id
Reject attempts to set membership_fee_type_id to nil when a current type
exists.
2025-12-16 15:15:34 +01:00
355cba6bbc
fix: implement fail-closed behavior in ValidateSameInterval
Change validation to fail closed instead of fail open when types cannot
be loaded. This prevents inconsistent data states and provides clearer
error messages to users.
2025-12-16 15:15:34 +01:00
3733ad9d89
fix: make cycle regeneration atomic on type change
Make cycle regeneration synchronous in the same transaction as the member
update to ensure atomicity.
2025-12-16 15:15:33 +01:00
4384086245
refactor: reduce nesting depth and improve code readability
- Replace Enum.map |> Enum.join with Enum.map_join for efficiency
- Extract helper functions to reduce nesting depth from 4 to 2
- Rename is_current_cycle? to current_cycle? following Elixir conventions
2025-12-16 15:15:33 +01:00
2d1d650c28
feat: regenerate cycles when membership fee type changes (same interval)
- Implemented regenerate_cycles_on_type_change helper in Member resource
- Cycles that haven't ended yet (cycle_end >= today) are deleted and regenerated
- Paid and suspended cycles remain unchanged (not deleted)
- CycleGenerator reloads member with new membership_fee_type_id
- Adjusted tests to work with current cycles only (no future cycles)
- All integration tests passing

Phase 4 completed: Cycle regeneration on type change
2025-12-16 15:15:33 +01:00
cd915531c2
feat: add validation for same-interval membership fee type changes 2025-12-16 15:15:33 +01:00
673e90d179
feat: add cycle status calculations to Member resource 2025-12-16 15:15:32 +01:00
86b6816d9b
feat: add status management actions to MembershipFeeCycle 2025-12-16 15:15:32 +01:00
651f518215
Merge branch 'main' into feature/278_membership_fee_settings
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-16 15:12:51 +01:00
f39fd49af3 feat: add 4 example membership fee types to seed script
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-12 19:10:02 +01:00
b3016f0c81
chore: update gettext 2025-12-12 19:08:03 +01:00
569d88ccdf docs: document require_atomic? false in MembershipFeeType actions 2025-12-12 19:06:08 +01:00
8cbd481709 refactor: migrate MembershipFeeSettingsLive to AshPhoenix.Form 2025-12-12 19:05:41 +01:00
9e441213be feat: improve error handling in settings validation for default_membership_fee_type_id 2025-12-12 19:02:22 +01:00
c445b78157 feat: prevent deletion of membership fee type when used as default in settings 2025-12-12 19:01:18 +01:00
af7ae2b722 refactor: use Enum.map_join instead of Enum.map |> Enum.join
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-12 18:16:55 +01:00
5bdd769699 fix: improve accessibility - WCAG 2 AA contrast and select label
Some checks failed
continuous-integration/drone/push Build is failing
2025-12-12 18:09:15 +01:00
96729cb2f4 refactor: replace ContributionSettingsLive mockup with MembershipFeeSettingsLive in navigation 2025-12-12 18:05:19 +01:00
98b908abdb i18n: add German translations for membership fee settings 2025-12-12 18:00:38 +01:00
da1fd3da73 feat: implement full CRUD for membership fee types with settings UI
- Add interval immutability and deletion prevention validations
- Add settings validation for default_membership_fee_type_id
- Create MembershipFeeSettingsLive for admin UI with form handling
- Add comprehensive test coverage (unit, integration, settings)
2025-12-12 17:52:52 +01:00
82897d5cd3 refactor: improve cycle generation code quality and documentation
All checks were successful
continuous-integration/drone/push Build is passing
- Remove Process.sleep calls from integration tests (tests run synchronously in SQL sandbox)
- Improve error handling: membership_fee_type_not_found now returns changeset error instead of just logging
- Clarify partial_failure documentation: successful_cycles are not persisted on rollback
- Update documentation: joined_at → join_date, left_at → exit_date
- Document PostgreSQL advisory locks per member (not whole table lock)
- Document gap handling: explicitly deleted cycles are not recreated
2025-12-12 17:41:22 +01:00
e6ac5d1ab1 fix: handle Ash notifications in CycleGenerator transactions
All checks were successful
continuous-integration/drone/push Build is passing
- Use return_notifications?: true when creating cycles within transaction
- Collect notifications and send them after transaction commits
- Prevents 'Missed notifications' warnings in test output
- Notifications are now properly sent via Ash.Notifier.notify/1
2025-12-12 16:53:57 +01:00
f7fc1f4897 test: fix date dependencies in cycle generator tests
- Add create_member_with_cycles helper that uses fixed 'today' date
- Update tests to use explicit 'today:' option instead of Date.utc_today()
- Prevents test failures when current date changes (e.g., in 2026+)
- Tests now explicitly delete and regenerate cycles with fixed dates
- Ensures consistent test behavior regardless of execution date
2025-12-12 16:41:44 +01:00
0b986db635 fix: CycleGenerator generates from last cycle, not filling gaps
- Change algorithm to start from last existing cycle instead of start_date
- Deleted cycles (gaps) are no longer automatically filled
- Add test to verify gaps remain unfilled
- Update documentation to clarify gap handling behavior
2025-12-12 16:33:39 +01:00
272a8a8afc test: make CycleGenerator tests more robust
- Replace weak assertions (>= 0, if length > 0) with concrete expectations
- Remove unnecessary Process.sleep calls (tests run synchronously)
- Add get_member_cycles helper for direct cycle verification
- Tests now validate actual generated cycles instead of relying on async behavior
2025-12-12 16:25:41 +01:00
cf8a1fa30d feat: improve error handling in CycleGenerator
- Handle Task crashes in async_stream with {:exit, reason}
- Return {:error, {:partial_failure, successes, errors}} when some cycles fail
- Previously returned {:ok, successful} even on partial failures
- Improves debuggability and allows callers to handle partial failures
2025-12-12 16:23:12 +01:00
a99f56969d feat: include inactive members in batch cycle generation
- Remove exit_date filter from generate_cycles_for_all_members query
- Inactive members now get cycles generated up to their exit_date
- Add tests for inactive member processing and exit_date boundary
- Document exit_date == cycle_start behavior (cycle still generated)
2025-12-12 16:21:36 +01:00
b693ab1e26 refactor: improve SetMembershipFeeStartDate change module
- Add warning logging for unexpected errors (not missing prerequisites)
- Use CalendarCycles.interval() type instead of generic atom()
- Update moduledoc to reflect actual usage (no where clause needed)
2025-12-12 16:16:11 +01:00
2e8c9eeccb feat: add error logging in after_action cycle generation hooks
- Log warnings when cycle generation fails in Member create/update
- Extract generate_fn to reduce code duplication
- Improves debuggability of silent failures
2025-12-12 16:09:37 +01:00
7e4d3e0a60 refactor: use sql_sandbox config instead of env for sync/async
- Replace Application.get_env(:mv, :env) with :sql_sandbox config
- Remove redundant :env config from test.exs
- More explicit and less error-prone for test environment detection
2025-12-12 16:05:39 +01:00
162d06da21
feat: implement automatic cycle generation for members
All checks were successful
continuous-integration/drone/push Build is passing
- Add CycleGenerator module with advisory lock mechanism
- Add SetMembershipFeeStartDate change for auto-calculation
- Extend Settings with include_joining_cycle and default_membership_fee_type_id
- Add scheduled job skeleton for future Oban integration
2025-12-11 21:26:58 +01:00
ecddf55331
docs: fix CalendarCycles documentation to match actual implementation
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-11 20:21:20 +01:00
b257c9897f
refactor: improve CalendarCycles API and tests based on code review
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-11 20:08:19 +01:00
822d06ed54
feat: implement calendar-based cycle calculation functions
All checks were successful
continuous-integration/drone/push Build is passing
Add CalendarCycles module with functions for all interval types.
Includes comprehensive tests for edge cases.
2025-12-11 19:45:01 +01:00
3 changed files with 66 additions and 73 deletions

View file

@ -1326,26 +1326,6 @@ msgstr "Textfeld"
msgid "Yes/No-Selection"
msgstr "Ja/Nein-Auswahl"
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr "Jeder Zahlungs-Zustand"
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses"
msgstr "E-Mail-Adressen kopieren"
#: lib/mv_web/live/custom_field_live/form_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field"
msgstr "Benutzerdefiniertes Feld speichern"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field Value"
msgstr "Benutzerdefinierten Feldwert speichern"
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Configure global settings for membership fees."
@ -1422,6 +1402,26 @@ msgstr "Jährliches Intervall Beitrittszeitraum nicht einbezogen"
msgid "Yearly Interval - Joining Cycle Included"
msgstr "Jährliches Intervall Beitrittszeitraum einbezogen"
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr "Jeder Zahlungs-Zustand"
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses"
msgstr "E-Mail-Adressen kopieren"
#: lib/mv_web/live/custom_field_live/form_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field"
msgstr "Benutzerdefiniertes Feld speichern"
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field Value"
msgstr "Benutzerdefinierten Feldwert speichern"
#~ #: lib/mv_web/live/custom_field_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Auto-generated identifier (immutable)"

View file

@ -1327,26 +1327,6 @@ msgstr ""
msgid "Yes/No-Selection"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr ""
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Copy email addresses"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Save Custom Field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Save Custom Field Value"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format
msgid "Configure global settings for membership fees."
@ -1422,3 +1402,23 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Yearly Interval - Joining Cycle Included"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr ""
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format
msgid "Copy email addresses"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#, elixir-autogen, elixir-format
msgid "Save Custom Field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format
msgid "Save Custom Field Value"
msgstr ""

View file

@ -1327,26 +1327,6 @@ msgstr ""
msgid "Yes/No-Selection"
msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr ""
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field Value"
msgstr ""
#: lib/mv_web/live/membership_fee_settings_live.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Configure global settings for membership fees."
@ -1423,12 +1403,27 @@ msgstr ""
msgid "Yearly Interval - Joining Cycle Included"
msgstr ""
#~ #: lib/mv_web/live/custom_field_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Auto-generated identifier (immutable)"
#~ msgstr ""
#: lib/mv_web/live/components/payment_filter_component.ex
#, elixir-autogen, elixir-format
msgid "All payment statuses"
msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
#: lib/mv_web/live/member_live/index.html.heex
#, elixir-autogen, elixir-format, fuzzy
msgid "Copy email addresses"
msgstr ""
#: lib/mv_web/live/custom_field_live/form_component.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field"
msgstr ""
#: lib/mv_web/live/custom_field_value_live/form.ex
#, elixir-autogen, elixir-format, fuzzy
msgid "Save Custom Field Value"
msgstr ""
#~ #: lib/mv_web/live/custom_field_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Configure global settings for membership contributions."
#~ msgstr ""
@ -1439,22 +1434,18 @@ msgstr ""
#~ msgid "Contribution Settings"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #: lib/mv_web/live/member_live/form.ex
#~ #: lib/mv_web/live/member_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Contribution start"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #: lib/mv_web/live/member_live/index.html.heex
#~ #, elixir-autogen, elixir-format
#~ msgid "Copy emails"
#~ msgstr ""
#~ #: lib/mv_web/live/member_live/form.ex
#~ #: lib/mv_web/live/member_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Custom Field Values"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Default Contribution Type"
@ -1470,11 +1461,13 @@ msgstr ""
#~ msgid "Failed to save settings. Please check the errors below."
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #: lib/mv_web/live/user_live/index.html.heex
#~ #: lib/mv_web/live/user_live/show.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Generated periods"
#~ msgstr ""
#~ #: lib/mv_web/live/contribution_settings_live.ex
#~ #: lib/mv_web/live/custom_field_live/form_component.ex
#~ #, elixir-autogen, elixir-format
#~ msgid "Immutable"