Skip to content

Fix manual assignment precedence over subscriptions#20

Merged
rameerez merged 2 commits intomainfrom
fix/manual-assignment-precedence
Feb 15, 2026
Merged

Fix manual assignment precedence over subscriptions#20
rameerez merged 2 commits intomainfrom
fix/manual-assignment-precedence

Conversation

@rameerez
Copy link
Copy Markdown
Owner

Summary

  • Admin overrides (manual assignments) now take precedence over subscription-based plans
  • Previously, Pay subscriptions were checked first, which meant manual assignments were ignored for users with active subscriptions - defeating the purpose of admin overrides

New Resolution Order

  1. Manual assignment (admin override) - FIRST
  2. Pay subscription
  3. Default plan

Test plan

  • Updated test test_manual_assignment_overrides_pay_subscription to verify new behavior
  • Updated integration test test_manual_plan_assignment_override_workflow
  • All 481 tests pass

🤖 Generated with Claude Code

Admin overrides (manual assignments) should take precedence over
subscription-based plans. The previous order checked Pay subscriptions
first, which meant manual assignments were ignored for users with
active subscriptions - defeating the purpose of admin overrides.

New resolution order:
1. Manual assignment (admin override) - FIRST
2. Pay subscription
3. Default plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Feb 15, 2026

Code Review - PR #20: Fix manual assignment precedence over subscriptions

Summary

This PR changes the plan resolution order to prioritize manual assignments (admin overrides) over Pay subscriptions. The change is logical and well-implemented, but there's a critical documentation issue that needs to be addressed.


✅ Positives

1. Core Logic Change is Sound

  • The new precedence order makes intuitive sense: admin overrides should take precedence over automated subscription-based plans
  • This is a common use case for:
    • Testing features with specific plans
    • Giving employees/partners special access
    • Handling edge cases requiring manual intervention
    • Demos and trials

2. Clean Implementation

  • The code change is minimal and focused (just reordering the checks in plan_resolver.rb:10-43)
  • No new code complexity introduced
  • Debug logging remains consistent and helpful
  • Maintains backward compatibility with existing assign_plan_manually! and remove_manual_assignment! methods

3. Comprehensive Test Updates

  • Both unit tests (test/services/plan_resolver_test.rb) and integration tests (test/integration/complete_workflow_test.rb) were updated
  • Test names clearly describe the new behavior: test_manual_assignment_overrides_pay_subscription
  • Test assertions match the new precedence order
  • PR description states all 481 tests pass

4. Good PR Documentation

  • Clear summary explaining the motivation
  • Explicit listing of the new resolution order
  • Checklist showing tests were updated and pass

⚠️ Critical Issue: Documentation Out of Sync

The README.md is now incorrect and contradicts the code:

Line 167 states:

How it's used: PlanResolver checks Pay → manual assignment → default plan

This describes the OLD order (before this PR). After this PR, the order is:

Manual assignment → Pay → default plan

Impact: This will confuse users who read the documentation to understand plan resolution order. The README is the primary source of truth for gem behavior.

Required Action: Update README.md:167 to reflect the new order:

- How it's used: `PlanResolver` checks Pay → manual assignment → default plan. You can call `assign_pricing_plan!` and `remove_pricing_plan!` on the plan_owner.
+ How it's used: `PlanResolver` checks manual assignment → Pay → default plan. Manual assignments (admin overrides) take precedence over subscription-based plans. You can call `assign_pricing_plan!` and `remove_pricing_plan!` on the plan_owner.

🤔 Considerations

1. Breaking Change Implications
This is a behavior-breaking change for existing users who may have relied on the old precedence order. Consider:

  • Is this a major version bump? (semantic versioning)
  • Should this be mentioned in a CHANGELOG or migration guide?
  • Are there users who might have worked around the old behavior in unexpected ways?

Example scenario where this could break things:

  • A user has an active subscription to the :pro plan
  • Admin manually assigns :free plan (perhaps mistakenly, or for testing)
  • Before: User still gets :pro (subscription wins)
  • After: User gets :free (manual assignment wins)

This new behavior is arguably more correct (admin intent should override automation), but it's worth noting in release notes.

2. Potential Edge Case: Stale Manual Assignments
If manual assignments are used for temporary testing/demos, there's now a risk of:

  • Forgetting to remove a manual assignment
  • User subscribes to a plan via Pay
  • Manual assignment silently overrides their paid subscription
  • User doesn't get what they paid for

Mitigation ideas (not required for this PR, but worth considering):

  • Add timestamps to assignments (assigned_at, expires_at)
  • Admin UI warning when manual assignment overrides an active subscription
  • Logging/monitoring for this scenario

3. Code Quality Notes

  • The code maintains consistent debug logging throughout
  • Comment at line 13 clearly documents the intent: # 1. Check manual assignment FIRST (admin overrides take precedence)
  • Method names (assign_plan_manually!) clearly indicate manual nature

🔒 Security Considerations

No security issues identified.

The change doesn't introduce:

  • SQL injection risks (uses ActiveRecord safely)
  • Authorization bypasses (manual assignments still require explicit API calls)
  • Data exposure issues

The Assignment model maintains its validations:

  • plan_exists_in_registry ensures only valid plans can be assigned
  • Uniqueness constraint prevents duplicate assignments per plan_owner

🎯 Performance Considerations

Slight performance improvement in scenarios with manual assignments:

  • Before: Check Pay subscription first (may involve external API calls or complex queries), then check manual assignment
  • After: Check manual assignment first (simple DB lookup), which short-circuits before Pay check

For the majority of users (no manual assignment), performance is identical - just an extra simple DB query that returns nil.


✅ Test Coverage

Test coverage appears comprehensive:

  • Unit test: test_manual_assignment_overrides_pay_subscription
  • Integration test: test_manual_plan_assignment_override_workflow
  • Both tests verify the new precedence order with assertions

Suggested additional test (optional):
Consider a test that verifies the scenario where manual assignment is removed and Pay subscription takes over - though this is partially covered in the integration test.


📝 Recommendations

Must fix:

  1. Update README.md:167 to reflect new precedence order

Should consider:
2. Add a note to CHANGELOG/release notes about this behavior change
3. Consider if this warrants a major or minor version bump per semver

Nice to have:
4. Add admin UI warnings when manual assignment overrides active subscription
5. Consider expiration timestamps for manual assignments
6. Add monitoring/logging for manual override scenarios


Verdict

Approve with required documentation fix. The code change is well-implemented and the new behavior makes sense, but the README must be updated to match the implementation.

Great work on the comprehensive test updates and clear PR description!


Review generated by Claude Code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rameerez rameerez merged commit ac2d5ac into main Feb 15, 2026
7 checks passed
@rameerez rameerez deleted the fix/manual-assignment-precedence branch February 15, 2026 19:02
@claude
Copy link
Copy Markdown

claude bot commented Feb 15, 2026

Pull Request Review - PR #20

Summary

This PR fixes an important bug in plan resolution precedence by ensuring manual plan assignments (admin overrides) take precedence over Pay subscription plans. The change is well-motivated and correctly implemented.


✅ Strengths

1. Clear Intent & Good Motivation
The PR addresses a real bug where admin overrides were being ignored for users with active subscriptions, which defeats the purpose of having manual assignment capability. The new resolution order makes logical sense:

  1. Manual assignment (admin override) - FIRST
  2. Pay subscription
  3. Default plan

2. Code Quality

  • Clean refactoring that simply reorders the resolution logic without introducing complexity
  • The code maintains the same structure and readability
  • Good use of debug logging throughout to help troubleshoot plan resolution issues
  • No performance impact - same number of checks, just different order

3. Test Coverage

  • Tests properly updated to reflect the new expected behavior
  • The test name change (test_manual_assignment_overrides_pay_subscription) accurately describes the new behavior
  • Integration test (test_manual_plan_assignment_override_workflow) correctly validates the complete workflow
  • All 481 tests passing confirms no regressions

4. Documentation

  • README updated to reflect the new resolution order in the "Why the models?" section
  • Clear inline comments in the code explaining the precedence

🔍 Code Analysis

lib/pricing_plans/plan_resolver.rb:10-43
The refactoring is straightforward - the two resolution blocks (manual assignment check and Pay subscription check) are simply swapped. This is a low-risk change since:

  • Each block is self-contained with clear early returns
  • No logic within each block was modified
  • The fallback to default plan remains unchanged

Tests (plan_resolver_test.rb:56-67 & complete_workflow_test.rb:225-244)
Both unit and integration tests now verify that:

  • Manual assignment wins over active Pay subscription
  • Removing manual assignment falls back to Pay subscription correctly
  • The test scenarios are realistic and cover the critical path

💡 Observations & Suggestions

1. Potential Edge Case to Consider
When a user has both a manual assignment AND an active subscription, and the manual assignment is removed, they immediately fall back to their subscription plan. This is the correct behavior, but you may want to consider:

  • Should there be an audit log of who assigned/removed manual plans?
  • Should admins get a warning when overriding a paying customer's plan?

This isn't a blocker for this PR, just food for thought for future enhancements.

2. Documentation Enhancement (Optional)
Consider adding a note in the docs about when to use manual assignments vs. relying on Pay subscriptions. Common use cases might include:

  • Testing/QA accounts
  • Employee accounts
  • Gifted/comp'd plans
  • Special partnerships
  • Beta testers

3. Assignment Model Validation
The Assignment model (lib/pricing_plans/models/assignment.rb:12) has a uniqueness constraint on plan_owner_type scoped to plan_owner_id, which correctly ensures only one manual assignment per plan owner. This is good and prevents conflicts.


🔒 Security Considerations

No security concerns identified. The change:

  • Doesn't introduce new attack vectors
  • Doesn't bypass any authorization checks
  • Maintains data integrity through existing model validations
  • The manual assignment feature should already have admin-only access controls at the controller/UI level (outside scope of this gem)

Note: Ensure that in your application code, the ability to create/remove manual assignments via PricingPlans::Assignment.assign_plan_to() is properly protected with admin authorization checks.


⚡ Performance Considerations

No performance impact. The change:

  • Performs the same number of database queries (at most 1 for assignment lookup, then potentially checking Pay)
  • If a manual assignment exists, it's actually more performant now since it short-circuits before checking Pay
  • The check order is now more efficient for organizations using manual assignments

🎯 Overall Assessment

This is a well-executed bug fix. The change is:

  • ✅ Logically sound
  • ✅ Properly tested with both unit and integration tests
  • ✅ Well-documented
  • ✅ Low risk
  • ✅ No breaking changes for existing behavior (unless users were relying on the buggy behavior)

The precedence change makes semantic sense: manual/admin overrides should naturally take precedence over automated subscription-based plans. This gives admins the power to override subscription status for legitimate reasons (comp plans, testing, special cases).


📋 Recommendation

Approve and merge. This PR is ready to go. The implementation is clean, tests are comprehensive, and the change fixes a legitimate bug that improves the gem's functionality.

The only minor suggestions above are optional enhancements for future consideration, not blockers.

Great work! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant