diff --git a/app/domain/alumni.rb b/app/domain/alumni.rb new file mode 100644 index 00000000..0f3b93a4 --- /dev/null +++ b/app/domain/alumni.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Copyright (c) 2024, Jungwacht Blauring Schweiz. This file is part of +# hitobito_jubla and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/hitobito/hitobito_jubla. +# +module Alumni + EXCLUDED_ROLE_TYPES = [ + Jubla::Role::External, + Jubla::Role::DispatchAddress, + Jubla::Role::Alumnus + ].freeze + + APPLICABLE_ROLE_TYPES = (::Role.all_types - EXCLUDED_ROLE_TYPES) +end diff --git a/app/jobs/alumni_manager_job.rb b/app/jobs/alumni_manager_job.rb new file mode 100644 index 00000000..3c63948f --- /dev/null +++ b/app/jobs/alumni_manager_job.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Copyright (c) 2024, Jungwacht Blauring Schweiz. This file is part of +# hitobito_jubla and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/hitobito/hitobito_jubla. + +class AlumniManagerJob < RecurringJob + run_every 1.day + + APPLICABLE_ROLE_TYPES = Alumni::APPLICABLE_ROLE_TYPES.map(&:sti_name) + + def perform_internal + destroy_obsolete + create_missing + end + + def create_missing + alumni = Person.joins(:roles).merge(Role.alumnus_members).distinct + inactive = Role.inactive.where(type: APPLICABLE_ROLE_TYPES) + inactive.where.not(person_id: alumni).find_each do |role| + Jubla::Role::AlumnusManager.new(role).create + end + end + + def destroy_obsolete + active = Person.joins(:roles).merge(Role.where(type: APPLICABLE_ROLE_TYPES)).distinct + Role.alumnus_members.where(person_id: active).find_each do |role| + Jubla::Role::AlumnusManager.new(role).destroy + end + end + + def next_run + interval.from_now.midnight + 1.minute + end +end diff --git a/app/models/jubla/role.rb b/app/models/jubla/role.rb index 27abeb0f..3c02d17f 100644 --- a/app/models/jubla/role.rb +++ b/app/models/jubla/role.rb @@ -103,9 +103,7 @@ def alumnus_member? end def alumnus_applicable? - [Jubla::Role::External, - Jubla::Role::DispatchAddress, - Jubla::Role::Alumnus].none? { |r| is_a?(r) } && !group.alumnus? + !group.alumnus? && Alumni::EXCLUDED_ROLE_TYPES.none? { |type| is_a?(type) } end def roles_in_layer diff --git a/lib/hitobito_jubla/wagon.rb b/lib/hitobito_jubla/wagon.rb index 0548b528..6ecd33c9 100644 --- a/lib/hitobito_jubla/wagon.rb +++ b/lib/hitobito_jubla/wagon.rb @@ -75,6 +75,10 @@ class Wagon < Rails::Engine person: [:originating_flock, :originating_state] ] + JobManager.wagon_jobs += [ + AlumniManagerJob + ] + MailingLists::Subscribers.include Jubla::MailingLists::Subscribers Person::Filter::List.include Jubla::Person::Filter::List diff --git a/spec/jobs/alumni_manager_job_spec.rb b/spec/jobs/alumni_manager_job_spec.rb new file mode 100644 index 00000000..1cc87f17 --- /dev/null +++ b/spec/jobs/alumni_manager_job_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# Copyright (c) 2024, Jungwacht Blauring Schweiz. This file is part of +# hitobito_jubla and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/hitobito/hitobito_jubla. + +require "spec_helper" + +describe AlumniManagerJob do + subject(:job) { described_class.new } + let(:person) { role.person } + + let(:role) { roles(:top_leader) } + + it "noops if role is active" do + expect { job.perform }.not_to change { person.roles.count } + end + + it "noops if role expires in the future" do + role.update!(end_on: Time.zone.tomorrow) + expect { job.perform }.not_to change { person.roles.count } + end + + it "creates alumni role in group and layer if role expired" do + role.update!(end_on: Date.yesterday) + expect { job.perform }.to change { person.roles.count }.by(2) + expect(person.roles.map(&:type)).to match_array ["Group::FederalBoard::Alumnus", "Group::FederalAlumnusGroup::Member"] + end + + it "noops if alumni roles have been created" do + role.update_columns(end_on: Date.yesterday) + + Fabricate(Group::FederalAlumnusGroup::Member.sti_name, person: person, group: groups(:ch_ehemalige)) + Fabricate(Group::FederalBoard::Alumnus.sti_name, person: person, group: groups(:federal_board)) + expect { job.perform }.not_to change { person.roles.count } + end + + it "deletes alumni roles if role becomes active" do + role.update_columns(end_on: Date.yesterday) + + Fabricate(Group::FederalAlumnusGroup::Member.sti_name, person: person, group: groups(:ch_ehemalige)) + Fabricate(Group::FederalBoard::Alumnus.sti_name, person: person, group: groups(:federal_board)) + role.update_columns(end_on: nil) + + expect { job.perform }.to change { person.roles.count }.by(-2) + expect(person.roles.map(&:type)).to match_array ["Group::FederalBoard::Member"] + end +end