Browse Source

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
pull/2829/head
Rachael Booth 2 months ago committed by GitHub
parent
commit
0899ed43d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      app/controllers/organisations_controller.rb
  2. 4
      app/controllers/schemes_controller.rb
  3. 8
      app/helpers/filters_helper.rb
  4. 20
      app/helpers/schemes_helper.rb
  5. 8
      app/models/location.rb
  6. 8
      app/models/scheme.rb
  7. 15
      app/policies/location_policy.rb
  8. 15
      app/policies/scheme_policy.rb
  9. 15
      app/views/locations/index.html.erb
  10. 2
      app/views/locations/show.html.erb
  11. 6
      app/views/schemes/details.html.erb
  12. 2
      app/views/schemes/show.html.erb
  13. 5
      spec/models/location_spec.rb
  14. 5
      spec/models/scheme_spec.rb
  15. 86
      spec/requests/schemes_controller_spec.rb

2
app/controllers/organisations_controller.rb

@ -24,7 +24,7 @@ class OrganisationsController < ApplicationController
end end
def schemes 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)) @pagy, @schemes = pagy(filter_manager.filtered_schemes(organisation_schemes, search_term, session_filters))
@searched = search_term.presence @searched = search_term.presence

4
app/controllers/schemes_controller.rb

@ -118,6 +118,10 @@ class SchemesController < ApplicationController
validation_errors scheme_params validation_errors scheme_params
if @scheme.errors.empty? && @scheme.save 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) redirect_to scheme_primary_client_group_path(@scheme)
else else
if @scheme.errors.any? { |error| error.attribute == :owning_organisation } if @scheme.errors.any? { |error| error.attribute == :owning_organisation }

8
app/helpers/filters_helper.rb

@ -192,9 +192,15 @@ module FiltersHelper
end end
def show_scheme_managing_org_filter?(user) def show_scheme_managing_org_filter?(user)
return true if user.support?
org = user.organisation 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 end
def logs_for_both_needstypes_present?(organisation) def logs_for_both_needstypes_present?(organisation)

20
app/helpers/schemes_helper.rb

@ -20,11 +20,14 @@ module SchemesHelper
end end
def owning_organisation_options(current_user) def owning_organisation_options(current_user)
all_orgs = Organisation.visible.map { |org| OpenStruct.new(id: org.id, name: org.name) } if current_user.support?
user_org = [OpenStruct.new(id: current_user.organisation_id, name: current_user.organisation.name)] Organisation.visible.map { |org| OpenStruct.new(id: org.id, name: org.name) }
stock_owners = current_user.organisation.stock_owners.visible.map { |org| OpenStruct.new(id: org.id, name: org.name) } else
merged_organisations = current_user.organisation.absorbed_organisations.visible.merged_during_open_collection_period.map { |org| OpenStruct.new(id: org.id, name: org.name) } user_org = [current_user.organisation]
current_user.support? ? all_orgs : user_org + stock_owners + merged_organisations 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 end
def null_option def null_option
@ -81,7 +84,12 @@ module SchemesHelper
when :deactivating_soon 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." "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 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
end end

8
app/models/location.rb

@ -54,13 +54,13 @@ class Location < ApplicationRecord
} }
scope :deactivated, lambda { |date = Time.zone.now| scope :deactivated, lambda { |date = Time.zone.now|
deactivated_by_organisation deactivated_by_organisation(date)
.or(deactivated_directly(date)) .or(deactivated_directly(date))
.or(deactivated_by_scheme(date)) .or(deactivated_by_scheme(date))
} }
scope :deactivated_by_organisation, lambda { scope :deactivated_by_organisation, lambda { |date = Time.zone.now|
merge(Organisation.filter_by_inactive) merge(Organisation.filter_by_inactive.or(Organisation.where("merge_date <= ?", date)))
} }
scope :deactivated_by_scheme, lambda { |date = Time.zone.now| scope :deactivated_by_scheme, lambda { |date = Time.zone.now|
@ -206,7 +206,7 @@ class Location < ApplicationRecord
def status_at(date) def status_at(date)
return :deleted if discarded_at.present? return :deleted if discarded_at.present?
return :incomplete unless confirmed 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 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 :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 return :activating_soon if startdate.present? && date < startdate

8
app/models/scheme.rb

@ -57,8 +57,8 @@ class Scheme < ApplicationRecord
.or(deactivated_directly) .or(deactivated_directly)
} }
scope :deactivated_by_organisation, lambda { scope :deactivated_by_organisation, lambda { |date = Time.zone.now|
merge(Organisation.filter_by_inactive) merge(Organisation.filter_by_inactive.or(Organisation.where("merge_date <= ?", date)))
} }
scope :deactivated_directly, lambda { |date = Time.zone.now| scope :deactivated_directly, lambda { |date = Time.zone.now|
@ -96,7 +96,7 @@ class Scheme < ApplicationRecord
scope :active, lambda { |date = Time.zone.now| scope :active, lambda { |date = Time.zone.now|
where.not(id: joins(:scheme_deactivation_periods).reactivating_soon(date).pluck(:id)) where.not(id: joins(:scheme_deactivation_periods).reactivating_soon(date).pluck(:id))
.where.not(id: incomplete.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: joins(:owning_organisation).joins(:scheme_deactivation_periods).deactivated_directly(date).pluck(:id))
.where.not(id: activating_soon(date).pluck(:id)) .where.not(id: activating_soon(date).pluck(:id))
} }
@ -314,7 +314,7 @@ class Scheme < ApplicationRecord
def status_at(date) def status_at(date)
return :deleted if discarded_at.present? return :deleted if discarded_at.present?
return :incomplete unless confirmed && locations.confirmed.any? 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) (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 :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 return :reactivating_soon if last_deactivation_before(date)&.reactivation_date.present? && date < last_deactivation_before(date).reactivation_date

15
app/policies/location_policy.rb

@ -16,14 +16,14 @@ class LocationPolicy
if location == Location if location == Location
user.data_coordinator? user.data_coordinator?
else 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
end end
def update? def update?
return true if user.support? 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
def delete_confirmation? def delete_confirmation?
@ -62,7 +62,7 @@ class LocationPolicy
define_method method_name do define_method method_name do
return true if user.support? 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
end end
@ -73,7 +73,7 @@ class LocationPolicy
define_method method_name do define_method method_name do
return true if user.support? 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
end end
@ -83,8 +83,11 @@ private
location.scheme location.scheme
end end
def scheme_owned_by_user_org_or_stock_owner def scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org
scheme&.owning_organisation == user.organisation || user.organisation.stock_owners.exists?(scheme&.owning_organisation_id) 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 end
def has_any_logs_in_editable_collection_period def has_any_logs_in_editable_collection_period

15
app/policies/scheme_policy.rb

@ -12,7 +12,7 @@ class SchemePolicy
if scheme == Scheme if scheme == Scheme
true true
else else
scheme_owned_by_user_org_or_stock_owner scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org
end end
end end
@ -27,7 +27,7 @@ class SchemePolicy
def update? def update?
return true if user.support? 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
def changes? def changes?
@ -41,7 +41,7 @@ class SchemePolicy
define_method method_name do define_method method_name do
return true if user.support? 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
end end
@ -61,7 +61,7 @@ class SchemePolicy
define_method method_name do define_method method_name do
return true if user.support? 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
end end
@ -78,8 +78,11 @@ class SchemePolicy
private private
def scheme_owned_by_user_org_or_stock_owner def scheme_owned_by_user_org_or_stock_owner_or_recently_absorbed_org
scheme&.owning_organisation == user.organisation || user.organisation.stock_owners.exists?(scheme&.owning_organisation_id) 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 end
def has_any_logs_in_editable_collection_period def has_any_logs_in_editable_collection_period

15
app/views/locations/index.html.erb

@ -56,14 +56,13 @@
<% end %> <% end %>
<% end %> <% end %>
<% if status_hint_message = scheme_status_hint(@scheme) %> <% if LocationPolicy.new(current_user, @scheme.locations.new).create? && [:active, :merged].include?(@scheme.owning_organisation.status) %>
<div class="govuk-hint"> <% if status_hint_message = scheme_status_hint(@scheme) %>
<%= status_hint_message %> <div class="govuk-hint">
</div> <%= status_hint_message %>
<br> </div>
<% end %> <br>
<% end %>
<% if LocationPolicy.new(current_user, @scheme.locations.new).create? %>
<%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post" %> <%= govuk_button_to "Add a location", scheme_locations_path(@scheme), method: "post" %>
<% end %> <% end %>
<br> <br>

2
app/views/locations/show.html.erb

@ -47,7 +47,7 @@
</div> </div>
</div> </div>
<% 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) %> <%= toggle_location_link(@location) %>
<% end %> <% end %>

6
app/views/schemes/details.html.erb

@ -49,11 +49,13 @@
:description, :description,
legend: { text: "Is this scheme registered under the Care Standards Act 2000?", size: "m" } %> 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 %> <%= f.hidden_field :owning_organisation_id, value: current_user.organisation.id %>
<% else %> <% else %>
<%= f.govuk_collection_select :owning_organisation_id, <%= f.govuk_collection_select :owning_organisation_id,
owning_organisation_options(current_user), scheme_owning_organisation_options,
:id, :id,
:name, :name,
label: { text: "Which organisation owns the housing stock for this scheme?", size: "m" }, label: { text: "Which organisation owns the housing stock for this scheme?", size: "m" },

2
app/views/schemes/show.html.erb

@ -52,7 +52,7 @@
</div> </div>
</div> </div>
<% 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) %> <%= toggle_scheme_link(@scheme) %>
<% end %> <% end %>

5
spec/models/location_spec.rb

@ -929,6 +929,11 @@ RSpec.describe Location, type: :model do
expect(location.status).to eq(:deactivated) expect(location.status).to eq(:deactivated)
end 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 it "returns deactivated if deactivation_date is in the past" do
FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.yesterday, location:) FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.yesterday, location:)
location.save! location.save!

5
spec/models/scheme_spec.rb

@ -363,6 +363,11 @@ RSpec.describe Scheme, type: :model do
expect(scheme.status).to eq(:deactivated) expect(scheme.status).to eq(:deactivated)
end 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 it "returns deactivated if deactivation_date is in the past" do
FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.yesterday, scheme:) FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.yesterday, scheme:)
scheme.reload scheme.reload

86
spec/requests/schemes_controller_spec.rb

@ -89,9 +89,47 @@ RSpec.describe SchemesController, type: :request do
end end
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 "when filtering" do
context "with owning organisation filter" 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 it "does not show filter" do
expect(page).not_to have_content("Owned by") expect(page).not_to have_content("Owned by")
end end
@ -700,6 +738,27 @@ RSpec.describe SchemesController, type: :request do
end end
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 context "when the scheme has all details but no confirmed locations" do
it "shows the scheme as incomplete with text to explain" do it "shows the scheme as incomplete with text to explain" do
get scheme_path(specific_scheme) get scheme_path(specific_scheme)
@ -1146,6 +1205,31 @@ RSpec.describe SchemesController, type: :request do
end end
end 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 end
context "when signed in as a support user" do context "when signed in as a support user" do

Loading…
Cancel
Save