Cycle Management & Member Integration closes #279 #294

Open
moritz wants to merge 45 commits from feature/279_cycle_management into main
Owner

Description of the implemented changes

The changes were:

  • Bugfixing
  • New Feature
  • Breaking Change
  • Refactoring

Definition of Done

Code Quality

  • No new technical depths
  • Linting passed
  • Documentation is added were needed

Accessibility

  • New elements are properly defined with html-tags
  • Colour contrast follows WCAG criteria
  • Aria labels are added when needed
  • Everything is accessible by keyboard
  • Tab-Order is comprehensible
  • All interactive elements have a visible focus

Testing

  • Tests for new code are written
  • All tests pass
  • axe-core dev tools show no critical or major issues
## Description of the implemented changes The changes were: - [ ] Bugfixing - [x] New Feature - [ ] Breaking Change - [ ] Refactoring ## Definition of Done ### Code Quality - [x] No new technical depths - [x] Linting passed - [x] Documentation is added were needed ### Accessibility - [ ] New elements are properly defined with html-tags - [ ] Colour contrast follows WCAG criteria - [ ] Aria labels are added when needed - [ ] Everything is accessible by keyboard - [ ] Tab-Order is comprehensible - [ ] All interactive elements have a visible focus ### Testing - [x] Tests for new code are written - [x] All tests pass - [ ] axe-core dev tools show no critical or major issues
moritz self-assigned this 2025-12-15 11:38:39 +01:00
moritz added 31 commits 2025-12-15 11:38:41 +01:00
fix(membership-fees): add DB constraints for enum and decimal precision
All checks were successful
continuous-integration/drone/push Build is passing
ebbf347e42
feat: implement calendar-based cycle calculation functions
All checks were successful
continuous-integration/drone/push Build is passing
822d06ed54
Add CalendarCycles module with functions for all interval types.
Includes comprehensive tests for edge cases.
refactor: improve CalendarCycles API and tests based on code review
All checks were successful
continuous-integration/drone/push Build is passing
b257c9897f
docs: fix CalendarCycles documentation to match actual implementation
All checks were successful
continuous-integration/drone/push Build is passing
ecddf55331
feat: implement automatic cycle generation for members
All checks were successful
continuous-integration/drone/push Build is passing
162d06da21
- 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
- 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
- Log warnings when cycle generation fails in Member create/update
- Extract generate_fn to reduce code duplication
- Improves debuggability of silent failures
- 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)
- 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)
- 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
- 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
- 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
- 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
fix: handle Ash notifications in CycleGenerator transactions
All checks were successful
continuous-integration/drone/push Build is passing
e6ac5d1ab1
- 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
refactor: improve cycle generation code quality and documentation
All checks were successful
continuous-integration/drone/push Build is passing
82897d5cd3
- 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
- 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)
fix: improve accessibility - WCAG 2 AA contrast and select label
Some checks failed
continuous-integration/drone/push Build is failing
5bdd769699
refactor: use Enum.map_join instead of Enum.map |> Enum.join
All checks were successful
continuous-integration/drone/push Build is passing
af7ae2b722
feat: add 4 example membership fee types to seed script
All checks were successful
continuous-integration/drone/push Build is passing
f39fd49af3
feat: regenerate cycles when membership fee type changes (same interval)
Some checks failed
continuous-integration/drone/push Build is failing
cad8cac9a8
- 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
moritz force-pushed feature/279_cycle_management from cad8cac9a8 to 06324d77c5 2025-12-15 11:39:37 +01:00 Compare
moritz changed title from Cycle Management & Member Integration to Cycle Management & Member Integration closes #279 2025-12-15 11:40:14 +01:00
moritz added 1 commit 2025-12-15 11:50:26 +01:00
refactor: reduce nesting depth and improve code readability
All checks were successful
continuous-integration/drone/push Build is passing
e9c53cc520
- 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
moritz added 10 commits 2025-12-15 12:41:38 +01:00
Make cycle regeneration synchronous in the same transaction as the member
update to ensure atomicity.
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.
Reject attempts to set membership_fee_type_id to nil when a current type
exists.
refactor: reduce complexity of with_advisory_lock function
All checks were successful
continuous-integration/drone/push Build is passing
34aabaec47
Split the complex with_advisory_lock function into smaller, focused
functions to improve readability and reduce cyclomatic complexity:

- with_advisory_lock_in_transaction: Handles case when already in transaction
- with_advisory_lock_new_transaction: Handles case when starting new transaction
- execute_within_transaction: Executes function within transaction
- normalize_fun_result: Normalizes function results to consistent format
- handle_transaction_result: Handles transaction results and sends notifications

This fixes Credo warnings:
- Function body nested too deep (was 3, now max 2)
- Function too complex (was 11, now max 9)
moritz force-pushed feature/279_cycle_management from 34aabaec47 to 65d1561803 2025-12-15 13:38:43 +01:00 Compare
All checks were successful
continuous-integration/drone/push Build is passing
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feature/279_cycle_management:feature/279_cycle_management
git checkout feature/279_cycle_management

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git checkout main
git merge --no-ff feature/279_cycle_management
git checkout feature/279_cycle_management
git rebase main
git checkout main
git merge --ff-only feature/279_cycle_management
git checkout feature/279_cycle_management
git rebase main
git checkout main
git merge --no-ff feature/279_cycle_management
git checkout main
git merge --squash feature/279_cycle_management
git checkout main
git merge --ff-only feature/279_cycle_management
git checkout main
git merge feature/279_cycle_management
git push origin main
Sign in to join this conversation.
No description provided.