From 0899ed43d6e7fff1599e412f9c7183b09c6ae701 Mon Sep 17 00:00:00 2001 From: Rachael Booth Date: Tue, 26 Nov 2024 16:13:12 +0000 Subject: [PATCH] CLDC-3743: Allow coordinators to manage schemes for recently absorbed organisations (#2764) * CLDC-3743: Allow coordinators to manage schemes for recently absorbed organisations * Fix status calculation for locations * Avoid errors with merged stock owners * Adjust when to hide org field in scheme creation * Don't show activation buttons for schemes at merged orgs * Update hint message about created locations, and restrict location creation to relevant org statuses * Remove extra whitespace * Fix references to scheme organisation --- app/controllers/organisations_controller.rb | 2 +- app/controllers/schemes_controller.rb | 4 + app/helpers/filters_helper.rb | 8 +- app/helpers/schemes_helper.rb | 20 +++-- app/models/location.rb | 8 +- app/models/scheme.rb | 8 +- app/policies/location_policy.rb | 15 ++-- app/policies/scheme_policy.rb | 15 ++-- app/views/locations/index.html.erb | 15 ++-- app/views/locations/show.html.erb | 2 +- app/views/schemes/details.html.erb | 6 +- app/views/schemes/show.html.erb | 2 +- spec/models/location_spec.rb | 5 ++ spec/models/scheme_spec.rb | 5 ++ spec/requests/schemes_controller_spec.rb | 86 ++++++++++++++++++++- 15 files changed, 160 insertions(+), 41 deletions(-) diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 61cd43674..8ffe426d7 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -24,7 +24,7 @@ class OrganisationsController < ApplicationController end def schemes - organisation_schemes = Scheme.visible.where(owning_organisation: [@organisation] + @organisation.parent_organisations) + organisation_schemes = Scheme.visible.where(owning_organisation: [@organisation] + @organisation.parent_organisations + @organisation.absorbed_organisations.visible.merged_during_open_collection_period) @pagy, @schemes = pagy(filter_manager.filtered_schemes(organisation_schemes, search_term, session_filters)) @searched = search_term.presence diff --git a/app/controllers/schemes_controller.rb b/app/controllers/schemes_controller.rb index 7f246b1e2..3dc642345 100644 --- a/app/controllers/schemes_controller.rb +++ b/app/controllers/schemes_controller.rb @@ -118,6 +118,10 @@ class SchemesController < ApplicationController validation_errors scheme_params if @scheme.errors.empty? && @scheme.save + if @scheme.owning_organisation.merge_date.present? + deactivation = SchemeDeactivationPeriod.new(scheme: @scheme, deactivation_date: @scheme.owning_organisation.merge_date) + deactivation.save!(validate: false) + end redirect_to scheme_primary_client_group_path(@scheme) else if @scheme.errors.any? { |error| error.attribute == :owning_organisation } diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb index 3a4c337ea..99c69c636 100644 --- a/app/helpers/filters_helper.rb +++ b/app/helpers/filters_helper.rb @@ -192,9 +192,15 @@ module FiltersHelper end def show_scheme_managing_org_filter?(user) + return true if user.support? + org = user.organisation + stock_owners = org.stock_owners.count + recently_absorbed_with_stock = org.absorbed_organisations.visible.merged_during_open_collection_period.where(holds_own_stock: true).count + + relevant_orgs_count = stock_owners + recently_absorbed_with_stock + (org.holds_own_stock? ? 1 : 0) - user.support? || org.stock_owners.count > 1 || (org.holds_own_stock? && org.stock_owners.count.positive?) + relevant_orgs_count > 1 end def logs_for_both_needstypes_present?(organisation) diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb index bcd40b082..12d86aba8 100644 --- a/app/helpers/schemes_helper.rb +++ b/app/helpers/schemes_helper.rb @@ -20,11 +20,14 @@ module SchemesHelper end def owning_organisation_options(current_user) - all_orgs = Organisation.visible.map { |org| OpenStruct.new(id: org.id, name: org.name) } - user_org = [OpenStruct.new(id: current_user.organisation_id, name: current_user.organisation.name)] - stock_owners = current_user.organisation.stock_owners.visible.map { |org| OpenStruct.new(id: org.id, name: org.name) } - merged_organisations = current_user.organisation.absorbed_organisations.visible.merged_during_open_collection_period.map { |org| OpenStruct.new(id: org.id, name: org.name) } - current_user.support? ? all_orgs : user_org + stock_owners + merged_organisations + if current_user.support? + Organisation.visible.map { |org| OpenStruct.new(id: org.id, name: org.name) } + else + user_org = [current_user.organisation] + stock_owners = current_user.organisation.stock_owners.visible.filter { |org| org.status == :active || (org.status == :merged && org.merge_date >= FormHandler.instance.start_date_of_earliest_open_for_editing_collection_period) } + merged_organisations = current_user.organisation.absorbed_organisations.visible.merged_during_open_collection_period + (user_org + stock_owners + merged_organisations).map { |org| OpenStruct.new(id: org.id, name: org.name) } + end end def null_option @@ -81,7 +84,12 @@ module SchemesHelper when :deactivating_soon "This scheme deactivates on #{scheme.last_deactivation_date.to_formatted_s(:govuk_date)}. Any locations you add will be deactivated on the same date. Reactivate the scheme to add locations active after this date." when :deactivated - "This scheme deactivated on #{scheme.last_deactivation_date.to_formatted_s(:govuk_date)}. Any locations you add will be deactivated on the same date. Reactivate the scheme to add locations active after this date." + case scheme.owning_organisation.status + when :active + "This scheme deactivated on #{scheme.last_deactivation_date.to_formatted_s(:govuk_date)}. Any locations you add will be deactivated on the same date. Reactivate the scheme to add locations active after this date." + when :merged + "This scheme has been deactivated due to #{scheme.owning_organisation.name} merging into #{scheme.owning_organisation.absorbing_organisation.name} on #{scheme.owning_organisation.merge_date.to_formatted_s(:govuk_date)}. Any locations you add will be deactivated on the same date. Use the after merge organisation for schemes and locations active after this date." + end end end diff --git a/app/models/location.rb b/app/models/location.rb index 03af24a94..12c6f2fad 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -54,13 +54,13 @@ class Location < ApplicationRecord } scope :deactivated, lambda { |date = Time.zone.now| - deactivated_by_organisation + deactivated_by_organisation(date) .or(deactivated_directly(date)) .or(deactivated_by_scheme(date)) } - scope :deactivated_by_organisation, lambda { - merge(Organisation.filter_by_inactive) + scope :deactivated_by_organisation, lambda { |date = Time.zone.now| + merge(Organisation.filter_by_inactive.or(Organisation.where("merge_date <= ?", date))) } scope :deactivated_by_scheme, lambda { |date = Time.zone.now| @@ -206,7 +206,7 @@ class Location < ApplicationRecord def status_at(date) return :deleted if discarded_at.present? return :incomplete unless confirmed - return :deactivated if scheme.owning_organisation.status_at(date) == :deactivated || + return :deactivated if scheme.owning_organisation.status_at(date) == :deactivated || scheme.owning_organisation.status_at(date) == :merged || open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date || scheme.status_at(date) == :deactivated return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date || scheme.status_at(date) == :deactivating_soon return :activating_soon if startdate.present? && date < startdate diff --git a/app/models/scheme.rb b/app/models/scheme.rb index 2c73acc06..33f236374 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -57,8 +57,8 @@ class Scheme < ApplicationRecord .or(deactivated_directly) } - scope :deactivated_by_organisation, lambda { - merge(Organisation.filter_by_inactive) + scope :deactivated_by_organisation, lambda { |date = Time.zone.now| + merge(Organisation.filter_by_inactive.or(Organisation.where("merge_date <= ?", date))) } scope :deactivated_directly, lambda { |date = Time.zone.now| @@ -96,7 +96,7 @@ class Scheme < ApplicationRecord scope :active, lambda { |date = Time.zone.now| where.not(id: joins(:scheme_deactivation_periods).reactivating_soon(date).pluck(:id)) .where.not(id: incomplete.pluck(:id)) - .where.not(id: joins(:owning_organisation).deactivated_by_organisation.pluck(:id)) + .where.not(id: joins(:owning_organisation).deactivated_by_organisation(date).pluck(:id)) .where.not(id: joins(:owning_organisation).joins(:scheme_deactivation_periods).deactivated_directly(date).pluck(:id)) .where.not(id: activating_soon(date).pluck(:id)) } @@ -314,7 +314,7 @@ class Scheme < ApplicationRecord def status_at(date) return :deleted if discarded_at.present? return :incomplete unless confirmed && locations.confirmed.any? - return :deactivated if owning_organisation.status_at(date) == :deactivated || + return :deactivated if owning_organisation.status_at(date) == :deactivated || owning_organisation.status_at(date) == :merged || (open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date) return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date diff --git a/app/policies/location_policy.rb b/app/policies/location_policy.rb index 436b961c6..3b4a22131 100644 --- a/app/policies/location_policy.rb +++ b/app/policies/location_policy.rb @@ -16,14 +16,14 @@ class LocationPolicy if location == Location user.data_coordinator? else - user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner + user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end end def update? return true if user.support? - user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner + user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end def delete_confirmation? @@ -62,7 +62,7 @@ class LocationPolicy define_method method_name do return true if user.support? - user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner + user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end end @@ -73,7 +73,7 @@ class LocationPolicy define_method method_name do return true if user.support? - scheme_owned_by_user_org_or_stock_owner + scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end end @@ -83,8 +83,11 @@ private location.scheme end - def scheme_owned_by_user_org_or_stock_owner - scheme&.owning_organisation == user.organisation || user.organisation.stock_owners.exists?(scheme&.owning_organisation_id) + def scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org + scheme_owned_by_user_org = scheme&.owning_organisation == user.organisation + scheme_owned_by_stock_owner = user.organisation.stock_owners.exists?(scheme&.owning_organisation_id) + scheme_owned_by_recently_absorbed_org = user.organisation.absorbed_organisations.visible.merged_during_open_collection_period.exists?(scheme&.owning_organisation_id) + scheme_owned_by_user_org || scheme_owned_by_stock_owner || scheme_owned_by_recently_absorbed_org end def has_any_logs_in_editable_collection_period diff --git a/app/policies/scheme_policy.rb b/app/policies/scheme_policy.rb index 6b97a46de..54a2b9e89 100644 --- a/app/policies/scheme_policy.rb +++ b/app/policies/scheme_policy.rb @@ -12,7 +12,7 @@ class SchemePolicy if scheme == Scheme true else - scheme_owned_by_user_org_or_stock_owner + scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end end @@ -27,7 +27,7 @@ class SchemePolicy def update? return true if user.support? - user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner + user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end def changes? @@ -41,7 +41,7 @@ class SchemePolicy define_method method_name do return true if user.support? - scheme_owned_by_user_org_or_stock_owner + scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end end @@ -61,7 +61,7 @@ class SchemePolicy define_method method_name do return true if user.support? - user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner + user.data_coordinator? && scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org end end @@ -78,8 +78,11 @@ class SchemePolicy private - def scheme_owned_by_user_org_or_stock_owner - scheme&.owning_organisation == user.organisation || user.organisation.stock_owners.exists?(scheme&.owning_organisation_id) + def scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org + scheme_owned_by_user_org = scheme&.owning_organisation == user.organisation + scheme_owned_by_stock_owner = user.organisation.stock_owners.exists?(scheme&.owning_organisation_id) + scheme_owned_by_recently_absorbed_org = user.organisation.absorbed_organisations.visible.merged_during_open_collection_period.exists?(scheme&.owning_organisation_id) + scheme_owned_by_user_org || scheme_owned_by_stock_owner || scheme_owned_by_recently_absorbed_org end def has_any_logs_in_editable_collection_period diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb index 64d9bf286..23550f894 100644 --- a/app/views/locations/index.html.erb +++ b/app/views/locations/index.html.erb @@ -56,14 +56,13 @@ <% end %> <% end %> - <% if status_hint_message = scheme_status_hint(@scheme) %> -
- <%= status_hint_message %> -
-
- <% end %> - - <% if LocationPolicy.new(current_user, @scheme.locations.new).create? %> + <% if LocationPolicy.new(current_user, @scheme.locations.new).create? && [:active, :merged].include?(@scheme.owning_organisation.status) %> + <% if status_hint_message = scheme_status_hint(@scheme) %> +
+ <%= status_hint_message %> +
+
+ <% end %> <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post" %> <% end %>
diff --git a/app/views/locations/show.html.erb b/app/views/locations/show.html.erb index 8ac8f6b23..f9ba6496c 100644 --- a/app/views/locations/show.html.erb +++ b/app/views/locations/show.html.erb @@ -47,7 +47,7 @@ -<% if @location.scheme.owning_organisation.active? && LocationPolicy.new(current_user, @location).deactivate? %> +<% if @location.scheme.owning_organisation.status == :active && LocationPolicy.new(current_user, @location).deactivate? %> <%= toggle_location_link(@location) %> <% end %> diff --git a/app/views/schemes/details.html.erb b/app/views/schemes/details.html.erb index cb29a56dc..4b23ab016 100644 --- a/app/views/schemes/details.html.erb +++ b/app/views/schemes/details.html.erb @@ -49,11 +49,13 @@ :description, legend: { text: "Is this scheme registered under the Care Standards Act 2000?", size: "m" } %> - <% if current_user.data_coordinator? && current_user.organisation.stock_owners.count.zero? && !current_user.organisation.has_recent_absorbed_organisations? %> + <% scheme_owning_organisation_options = owning_organisation_options(current_user) %> + + <% if scheme_owning_organisation_options.count == 1 %> <%= f.hidden_field :owning_organisation_id, value: current_user.organisation.id %> <% else %> <%= f.govuk_collection_select :owning_organisation_id, - owning_organisation_options(current_user), + scheme_owning_organisation_options, :id, :name, label: { text: "Which organisation owns the housing stock for this scheme?", size: "m" }, diff --git a/app/views/schemes/show.html.erb b/app/views/schemes/show.html.erb index 6cefa5847..0aa25affc 100644 --- a/app/views/schemes/show.html.erb +++ b/app/views/schemes/show.html.erb @@ -52,7 +52,7 @@ -<% if @scheme.owning_organisation.active? && SchemePolicy.new(current_user, @scheme).deactivate? %> +<% if @scheme.owning_organisation.status == :active && SchemePolicy.new(current_user, @scheme).deactivate? %> <%= toggle_scheme_link(@scheme) %> <% end %> diff --git a/spec/models/location_spec.rb b/spec/models/location_spec.rb index 79265d361..18581fb6e 100644 --- a/spec/models/location_spec.rb +++ b/spec/models/location_spec.rb @@ -929,6 +929,11 @@ RSpec.describe Location, type: :model do expect(location.status).to eq(:deactivated) end + it "returns deactivated if the owning organisation has been merged" do + location.scheme.owning_organisation.merge_date = 2.days.ago + expect(location.status).to eq(:deactivated) + end + it "returns deactivated if deactivation_date is in the past" do FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.yesterday, location:) location.save! diff --git a/spec/models/scheme_spec.rb b/spec/models/scheme_spec.rb index 5ca529d3e..65174388d 100644 --- a/spec/models/scheme_spec.rb +++ b/spec/models/scheme_spec.rb @@ -363,6 +363,11 @@ RSpec.describe Scheme, type: :model do expect(scheme.status).to eq(:deactivated) end + it "returns deactivated if the owning organisation has been merged" do + scheme.owning_organisation.merge_date = 2.days.ago + expect(scheme.status).to eq(:deactivated) + end + it "returns deactivated if deactivation_date is in the past" do FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.yesterday, scheme:) scheme.reload diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb index 19ede5cc4..83ba11fd9 100644 --- a/spec/requests/schemes_controller_spec.rb +++ b/spec/requests/schemes_controller_spec.rb @@ -89,9 +89,47 @@ RSpec.describe SchemesController, type: :request do end end + context "when a recently absorbed organisation has schemes" do + let(:absorbed_org) { create(:organisation) } + let!(:absorbed_org_schemes) { create_list(:scheme, 2, owning_organisation: absorbed_org) } + + before do + absorbed_org.merge_date = 2.days.ago + absorbed_org.absorbing_organisation = user.organisation + absorbed_org.save! + end + + it "shows absorbed organisation schemes" do + get "/schemes" + follow_redirect! + absorbed_org_schemes.each do |scheme| + expect(page).to have_content(scheme.id_to_display) + end + end + end + + context "when a non-recently absorbed organisation has schemes" do + let(:absorbed_org) { create(:organisation) } + let!(:absorbed_org_schemes) { create_list(:scheme, 2, owning_organisation: absorbed_org) } + + before do + absorbed_org.merge_date = 2.years.ago + absorbed_org.absorbing_organisation = user.organisation + absorbed_org.save! + end + + it "shows absorbed organisation schemes" do + get "/schemes" + follow_redirect! + absorbed_org_schemes.each do |scheme| + expect(page).not_to have_content(scheme.id_to_display) + end + end + end + context "when filtering" do context "with owning organisation filter" do - context "when user org does not have owning orgs" do + context "when user org does not have owning orgs or recently absorbed orgs" do it "does not show filter" do expect(page).not_to have_content("Owned by") end @@ -700,6 +738,27 @@ RSpec.describe SchemesController, type: :request do end end + context "when coordinator attempts to see scheme belonging to a recently absorbed organisation" do + let(:absorbed_organisation) { create(:organisation) } + let!(:specific_scheme) { create(:scheme, owning_organisation: absorbed_organisation) } + + before do + absorbed_organisation.merge_date = 2.days.ago + absorbed_organisation.absorbing_organisation = user.organisation + absorbed_organisation.save! + + get "/schemes/#{specific_scheme.id}" + end + + it "shows the scheme" do + expect(page).to have_content(specific_scheme.id_to_display) + end + + it "allows editing" do + expect(page).to have_link("Change") + end + end + context "when the scheme has all details but no confirmed locations" do it "shows the scheme as incomplete with text to explain" do get scheme_path(specific_scheme) @@ -1146,6 +1205,31 @@ RSpec.describe SchemesController, type: :request do end end end + + context "when making a scheme in an organisation recently absorbed by the users organisation" do + let(:absorbed_organisation) { create(:organisation) } + let(:params) do + { scheme: { service_name: " testy ", + sensitive: "1", + scheme_type: "Foyer", + registered_under_care_act: "No", + owning_organisation_id: absorbed_organisation.id, + arrangement_type: "D" } } + end + + before do + absorbed_organisation.merge_date = 2.days.ago + absorbed_organisation.absorbing_organisation = user.organisation + absorbed_organisation.save! + end + + it "creates a new scheme for this organisation and renders correct page" do + expect { post "/schemes", params: }.to change(Scheme, :count).by(1) + follow_redirect! + expect(response).to have_http_status(:ok) + expect(page).to have_content("What client group is this scheme intended for?") + end + end end context "when signed in as a support user" do