-
Notifications
You must be signed in to change notification settings - Fork 24
WAIT: Derive organization status from affiliations; gate active on start date #1823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
maebeale
wants to merge
3
commits into
main
Choose a base branch
from
maebeale/org-status-from-affiliations
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,14 @@ class Affiliation < ApplicationRecord | |
| .where("affiliations.end_date IS NULL OR affiliations.end_date >= ?", Date.current) | ||
| } | ||
|
|
||
| scope :active, -> { active_or_pending } | ||
| # Currently active: active-or-pending AND its start_date has arrived (or is | ||
| # unset). A future-dated affiliation (e.g. a Facilitator dated to an upcoming | ||
| # training's month) is therefore "pending" — counted by active_or_pending but | ||
| # not yet active. | ||
| scope :active, -> { | ||
| active_or_pending | ||
| .where("affiliations.start_date IS NULL OR affiliations.start_date <= ?", Date.current) | ||
| } | ||
|
|
||
| # Only the exact, case-sensitive title "Facilitator" counts — variants like | ||
| # "Lead Facilitator" or "facilitator" are deliberately excluded. BINARY forces | ||
|
|
@@ -31,9 +38,9 @@ class Affiliation < ApplicationRecord | |
|
|
||
| before_validation :skip_if_duplicate | ||
| before_save :set_inactive_from_dates | ||
| after_save :deactivate_organization_if_no_active_people | ||
| after_save :sync_organization_status_from_affiliations | ||
| after_save :sync_organization_affiliation_dates | ||
| after_destroy :deactivate_organization_if_no_active_people | ||
| after_destroy :sync_organization_status_from_affiliations | ||
| after_destroy :sync_organization_affiliation_dates | ||
|
|
||
| # Methods | ||
|
|
@@ -45,13 +52,20 @@ def facilitator? | |
| title.to_s.strip == "Facilitator" | ||
| end | ||
|
|
||
| # Current: not flagged inactive and not past its end date. Mirrors the `active` | ||
| # scope so already-loaded affiliations can be filtered in Ruby without another | ||
| # query (e.g. on list pages that preload affiliations). | ||
| def active? | ||
| # Active-or-pending: not flagged inactive and not past its end date. A future | ||
| # start_date still counts (the affiliation is pending, not ended). Mirrors the | ||
| # `active_or_pending` scope for in-memory filtering of preloaded affiliations. | ||
| def active_or_pending? | ||
| !inactive? && (end_date.nil? || end_date >= Date.current) | ||
| end | ||
|
|
||
| # Currently active: active-or-pending AND its start_date has arrived. Mirrors | ||
| # the `active` scope so already-loaded affiliations can be filtered in Ruby | ||
| # without another query (e.g. on list pages that preload affiliations). | ||
| def active? | ||
| active_or_pending? && (start_date.nil? || start_date <= Date.current) | ||
| end | ||
|
|
||
| def name | ||
| "#{person.name}" if person | ||
| end | ||
|
|
@@ -93,7 +107,7 @@ def sync_organization_affiliation_dates | |
| affiliations = org.affiliations.where.not(id: destroyed_by_association ? id : nil) | ||
|
|
||
| earliest_start = affiliations.minimum(:start_date) | ||
| has_active = affiliations.active.exists? | ||
| has_active = affiliations.active_or_pending.exists? | ||
|
|
||
| updates = {} | ||
| updates[:start_date] = earliest_start if org.start_date != earliest_start | ||
|
|
@@ -107,22 +121,31 @@ def sync_organization_affiliation_dates | |
| org.update_columns(updates) if updates.any? | ||
| end | ||
|
|
||
| def deactivate_organization_if_no_active_people | ||
| return if organization.affiliations.active.exists? | ||
| # Keep the organization's Active/Inactive status in sync with whether it has any | ||
| # active-or-pending affiliations (an incoming facilitator counts). Symmetric: | ||
| # activates an Inactive org that gains people and deactivates one that loses | ||
| # them. Only ever toggles between Active and Inactive — manual states (Pending, | ||
| # Reinstate, Suspended, Unknown) are left untouched so they stick. | ||
| def sync_organization_status_from_affiliations | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 From Claude: Now symmetric (was deactivate-only). Guarded to toggle only Active↔Inactive — a manually set Pending/Reinstate/Suspended/Unknown is left alone, so admins keep control of those. Uses |
||
| has_people = organization.affiliations.active_or_pending.exists? | ||
| current_name = organization.organization_status&.name | ||
| target_name = has_people ? "Active" : "Inactive" | ||
|
|
||
| return if current_name == target_name | ||
| return unless [ "Active", "Inactive", nil ].include?(current_name) | ||
|
|
||
| inactive_status = OrganizationStatus.find_by(name: "Inactive") | ||
| return unless inactive_status | ||
| return if organization.organization_status_id == inactive_status.id | ||
| target_status = OrganizationStatus.find_by(name: target_name) | ||
| return unless target_status | ||
|
|
||
| organization.update_column(:organization_status_id, inactive_status.id) | ||
| organization.update_column(:organization_status_id, target_status.id) | ||
|
|
||
| Ahoy::Tracker.new(user: Current.user).track( | ||
| "autochange.organization", | ||
| resource_type: "Organization", | ||
| resource_id: organization.id, | ||
| resource_title: organization.name, | ||
| change: "status_set_to_inactive", | ||
| reason: "no_active_affiliations" | ||
| change: has_people ? "status_set_to_active" : "status_set_to_inactive", | ||
| reason: has_people ? "active_affiliation_present" : "no_active_affiliations" | ||
| ) | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 From Claude: The crux:
activenow excludes future-dated affiliations (start_date not yet arrived). Sites that should still count an incoming facilitator were moved toactive_or_pending; only present-tense checks (person summary, searchability, "active facilitator") keep this stricter scope.