diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index ea0b154e5..d4e4f34fc 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -91,6 +91,7 @@ class OrganisationsController < ApplicationController selected_rent_periods = rent_period_params[:rent_periods].compact_blank @organisation = Organisation.new(org_params) if @organisation.save + @organisation.update!(group: assign_group_number(@organisation.id, org_params[:group_member_id])) if org_params[:group_member] OrganisationRentPeriod.transaction do selected_rent_periods.each { |period| OrganisationRentPeriod.create!(organisation: @organisation, rent_period: period) } end @@ -142,6 +143,9 @@ class OrganisationsController < ApplicationController end flash[:notice] = I18n.t("organisation.reactivated", organisation: @organisation.name) else + if org_params[:group_member] && org_params[:group_member_id] + @organisation.group = assign_group_number(@organisation.id, org_params[:group_member_id]) + end flash[:notice] = I18n.t("organisation.updated") end if rent_period_params[:rent_periods].present? @@ -341,7 +345,7 @@ private end def org_params - params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no, :active) + params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no, :active, :profit_status, :group_member, :group_member_id) end def rent_period_params @@ -385,4 +389,19 @@ private end end end + + def assign_group_number(current_org_id, selected_org_id) + selected_org = Organisation.find_by(id: selected_org_id) + if selected_org&.group.present? + selected_org.group + else + next_group_number = next_available_group_number + selected_org.update!(group_member: true, group_member_id: current_org_id, group: next_group_number) if selected_org + next_group_number + end + end + + def next_available_group_number + Organisation.maximum(:group).to_i + 1 + end end diff --git a/app/frontend/controllers/index.js b/app/frontend/controllers/index.js index fa7944045..b8f91a7ce 100644 --- a/app/frontend/controllers/index.js +++ b/app/frontend/controllers/index.js @@ -21,6 +21,8 @@ import TabsController from './tabs_controller.js' import AddressSearchController from './address_search_controller.js' +import OrganisationsController from './organisations_controller.js' + application.register('accessible-autocomplete', AccessibleAutocompleteController) application.register('conditional-filter', ConditionalFilterController) application.register('conditional-question', ConditionalQuestionController) @@ -30,3 +32,4 @@ application.register('filter-layout', FilterLayoutController) application.register('search', SearchController) application.register('tabs', TabsController) application.register('address-search', AddressSearchController) +application.register('organisations', OrganisationsController) diff --git a/app/frontend/controllers/organisations_controller.js b/app/frontend/controllers/organisations_controller.js new file mode 100644 index 000000000..060265f27 --- /dev/null +++ b/app/frontend/controllers/organisations_controller.js @@ -0,0 +1,27 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + updateProfitStatusOptions (event) { + const profitStatusSelect = document.getElementById('organisation-profit-status-field') + if (!profitStatusSelect) return + + const localAuthorityOption = profitStatusSelect.querySelector('option[value="local_authority"]') + const nonProfitOption = profitStatusSelect.querySelector('option[value="non_profit"]') + const profitOption = profitStatusSelect.querySelector('option[value="profit"]') + const providerType = event.target.value + + profitStatusSelect.disabled = false + localAuthorityOption.hidden = false + nonProfitOption.hidden = false + profitOption.hidden = false + + if (providerType === 'LA') { + profitStatusSelect.value = 'local_authority' + nonProfitOption.hidden = true + profitOption.hidden = true + } else if (providerType === 'PRP') { + profitStatusSelect.value = '' + localAuthorityOption.hidden = true + } + } +} diff --git a/app/helpers/organisations_helper.rb b/app/helpers/organisations_helper.rb index 1467d7c2e..8706b1748 100644 --- a/app/helpers/organisations_helper.rb +++ b/app/helpers/organisations_helper.rb @@ -9,18 +9,27 @@ module OrganisationsHelper end end - def display_organisation_attributes(organisation) - [ + def display_organisation_attributes(user, organisation) + attributes = [ { name: "Organisation ID", value: "ORG#{organisation.id}", editable: false }, { name: "Address", value: organisation.address_string, editable: true }, { name: "Telephone number", value: organisation.phone, editable: true }, { name: "Registration number", value: organisation.housing_registration_no || "", editable: false }, { name: "Type of provider", value: organisation.display_provider_type, editable: false }, { name: "Owns housing stock", value: organisation.holds_own_stock ? "Yes" : "No", editable: false }, - { name: "Rent periods", value: organisation.rent_period_labels, editable: true, format: :bullet }, - { name: "Data Sharing Agreement" }, - { name: "Status", value: status_tag(organisation.status) + delete_organisation_text(organisation), editable: false }, + { name: "Part of group", value: organisation.group_member ? "Yes" : "No", editable: user.support? }, ] + + if organisation.group_member + attributes << { name: "Group number", value: "GROUP#{organisation.group}", editable: user.support? } + end + + attributes << { name: "For profit", value: organisation.display_profit_status, editable: user.support? } + attributes << { name: "Rent periods", value: organisation.rent_period_labels, editable: true, format: :bullet } + attributes << { name: "Data Sharing Agreement" } + attributes << { name: "Status", value: status_tag(organisation.status) + delete_organisation_text(organisation), editable: false } + + attributes end def organisation_name_row(user:, organisation:, summary_list:) @@ -64,7 +73,40 @@ module OrganisationsHelper def organisation_details_link_message(attribute) text = lowercase_first_letter(attribute[:name]) return "Add #{text}" if attribute[:name] == "Rent periods" + return "Select profit status" if attribute[:name] == "For profit" "Enter #{text}" end + + def group_organisation_options + null_option = [OpenStruct.new(id: "", name: "Select an option", group: nil)] + organisations = Organisation.visible.map { |org| OpenStruct.new(id: org.id, name: group_organisation_options_name(org)) } + null_option + organisations + end + + def group_organisation_options_name(org) + "#{org.name}#{group_organisation_options_group_text(org)}" + end + + def group_organisation_options_group_text(org) + return "" unless org.oldest_group_member + + " (GROUP#{org.oldest_group_member&.group})" + end + + def profit_status_options(provider_type = nil) + null_option = [OpenStruct.new(id: "", name: "Select an option")] + profit_statuses = Organisation::PROFIT_STATUS.map do |key, _value| + OpenStruct.new(id: key, name: Organisation::DISPLAY_PROFIT_STATUS[key]) + end + + case provider_type + when "LA" + profit_statuses.select! { |option| option.id == :local_authority } + when "PRP" + profit_statuses.reject! { |option| option.id == :local_authority } + end + + null_option + profit_statuses + end end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index 5fbce5716..0febfa727 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -54,13 +54,27 @@ class Organisation < ApplicationRecord PRP: 2, }.freeze + PROFIT_STATUS = { + non_profit: 1, + profit: 2, + local_authority: 3, + }.freeze + enum :provider_type, PROVIDER_TYPE + enum :profit_status, PROFIT_STATUS + + attribute :group_member, :boolean + attr_accessor :skip_group_member_validation + + before_save :clear_group_member_fields_if_not_group_member alias_method :la?, :LA? validates :name, presence: { message: I18n.t("validations.organisation.name_missing") } validates :name, uniqueness: { case_sensitive: false, message: I18n.t("validations.organisation.name_not_unique") } validates :provider_type, presence: { message: I18n.t("validations.organisation.provider_type_missing") } + validates :group_member_id, presence: { message: I18n.t("validations.organisation.group_missing") }, if: -> { group_member? && !skip_group_member_validation } + validate :validate_profit_status def self.find_by_id_on_multiple_fields(id) return if id.nil? @@ -142,6 +156,12 @@ class Organisation < ApplicationRecord DISPLAY_PROVIDER_TYPE[provider_type.to_sym] end + DISPLAY_PROFIT_STATUS = { "non_profit": "Non-profit", "profit": "Profit", "local_authority": "Local authority" }.freeze + + def display_profit_status + DISPLAY_PROFIT_STATUS.fetch(profit_status&.to_sym, "") + end + def has_managing_agents? managing_agents.count.positive? end @@ -232,4 +252,31 @@ class Organisation < ApplicationRecord def has_visible_schemes? owned_schemes.visible.count.positive? end + + def oldest_group_member + return nil if group.blank? + + Organisation.visible.where(group:).order(:created_at).first + end + +private + + def validate_profit_status + return if profit_status.nil? + + if provider_type == "LA" && profit_status != "local_authority" + errors.add(:profit_status, I18n.t("validations.organisation.profit_status.must_be_LA")) + end + + if provider_type == "PRP" && profit_status == "local_authority" + errors.add(:profit_status, I18n.t("validations.organisation.profit_status.must_not_be_LA")) + end + end + + def clear_group_member_fields_if_not_group_member + unless group_member + self.group_member_id = nil + self.group = nil + end + end end diff --git a/app/services/exports/organisation_export_service.rb b/app/services/exports/organisation_export_service.rb index 7bceca840..0e8967bbc 100644 --- a/app/services/exports/organisation_export_service.rb +++ b/app/services/exports/organisation_export_service.rb @@ -74,8 +74,7 @@ module Exports attribute_hash["provider_type"] = organisation.provider_type_before_type_cast attribute_hash["merge_date"] = organisation.merge_date&.iso8601 attribute_hash["available_from"] = organisation.available_from&.iso8601 - attribute_hash["profit_status"] = nil # will need update when we add the field to the org - attribute_hash["group"] = nil # will need update when we add the field to the org + attribute_hash["profit_status"] = organisation.profit_status_before_type_cast attribute_hash["status"] = organisation.status attribute_hash["active"] = attribute_hash["status"] == :active diff --git a/app/views/organisations/edit.html.erb b/app/views/organisations/edit.html.erb index cf9033180..94dd974da 100644 --- a/app/views/organisations/edit.html.erb +++ b/app/views/organisations/edit.html.erb @@ -29,6 +29,33 @@ autocomplete: "tel", width: 20 %> + <% if current_user.support? %> + <%= f.govuk_radio_buttons_fieldset :group_member, + legend: { text: "Is this organisation part of a housing provider group structure?", size: "m" } do %> + <%= f.govuk_radio_button :group_member, true, + label: { text: "Yes" }, + "data-controller": "conditional-question", + "data-action": "click->conditional-question#displayConditional", + "data-info": { conditional_questions: { group: [true] }, type: "organisation" }.to_json do %> + <%= f.govuk_collection_select :group_member_id, + group_organisation_options, + :id, + :name, + label: { text: "Search for an organisation that is part of the same group as this organisation", size: "m" }, + options: { disabled: [""], selected: @organisation.oldest_group_member&.id || "" }, + "data-controller": %w[accessible-autocomplete conditional-filter] %> + <% end %> + <%= f.govuk_radio_button :group_member, false, label: { text: "No" } %> + <% end %> + + <%= f.govuk_collection_select :profit_status, + profit_status_options(@organisation.provider_type), + :id, + :name, + label: { text: "Is the organisation for profit?", size: "m" }, + options: { disabled: [""], selected: @organisation.profit_status || "" } %> + <% end %> + <%= f.govuk_check_boxes_fieldset :rent_periods, legend: { text: "What are the rent periods for the organisation?" }, hint: { text: "It is not possible to deselect rent periods that are used in logs" } do %> diff --git a/app/views/organisations/new.html.erb b/app/views/organisations/new.html.erb index 20d4d2cc6..9752155ac 100644 --- a/app/views/organisations/new.html.erb +++ b/app/views/organisations/new.html.erb @@ -48,6 +48,8 @@ :id, :name, label: { text: "Organisation type", size: "m" }, + "data-controller": "organisations", + "data-action": "change->organisations#updateProfitStatusOptions", options: { disabled: [""], selected: @organisation.provider_type || "" } %> <%= f.govuk_collection_radio_buttons :holds_own_stock, @@ -56,6 +58,31 @@ :name, legend: { text: "Does the organisation hold its own stock?", size: "m" } %> + <%= f.govuk_radio_buttons_fieldset :group_member, + legend: { text: "Is this organisation part of a housing provider group structure?", size: "m" } do %> + <%= f.govuk_radio_button :group_member, true, + label: { text: "Yes" }, + "data-controller": "conditional-question", + "data-action": "click->conditional-question#displayConditional", + "data-info": { conditional_questions: { group: [true] }, type: "organisation" }.to_json do %> + <%= f.govuk_collection_select :group_member_id, + group_organisation_options, + :id, + :name, + label: { text: "Search for an organisation that is part of the same group as this organisation", size: "m" }, + options: { disabled: [""], selected: @organisation.oldest_group_member&.id || "" }, + "data-controller": %w[accessible-autocomplete conditional-filter] %> + <% end %> + <%= f.govuk_radio_button :group_member, false, label: { text: "No" } %> + <% end %> + + <%= f.govuk_collection_select :profit_status, + profit_status_options, + :id, + :name, + label: { text: "Is the organisation for profit?", size: "m" }, + options: { disabled: [""], selected: @organisation.profit_status || "" } %> + <%= f.govuk_check_boxes_fieldset :rent_periods, legend: { text: "What are the rent periods for the organisation?" } do %> <% @rent_periods.map do |key, period| %> diff --git a/app/views/organisations/show.html.erb b/app/views/organisations/show.html.erb index 42c2482f7..e275769ee 100644 --- a/app/views/organisations/show.html.erb +++ b/app/views/organisations/show.html.erb @@ -15,7 +15,7 @@