diff --git a/app/controllers/case_logs_controller.rb b/app/controllers/case_logs_controller.rb index 22e6cb599..6a5ea875d 100644 --- a/app/controllers/case_logs_controller.rb +++ b/app/controllers/case_logs_controller.rb @@ -27,11 +27,15 @@ class CaseLogsController < ApplicationController end def create - case_log = CaseLog.create(case_log_params) + case_log = CaseLog.new(case_log_params) + case_log.form.current_user = current_user respond_to do |format| - format.html { redirect_to case_log } + format.html do + case_log.save! + redirect_to case_log + end format.json do - if case_log.persisted? + if case_log.save render json: case_log, status: :created else render json: { errors: case_log.errors.messages }, status: :unprocessable_entity @@ -58,6 +62,7 @@ class CaseLogsController < ApplicationController format.html { edit } format.json do if @case_log + @case_log.form.current_user = current_user render json: @case_log, status: :ok else render_not_found_json("Log", params[:id]) @@ -104,7 +109,7 @@ private end def case_log_params - if current_user + if current_user && !current_user.support? org_params.merge(api_case_log_params) else api_case_log_params diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 5117ca6e2..6f7bc3790 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -25,6 +25,7 @@ class FormController < ApplicationController if @case_log current_url = request.env["PATH_INFO"] subsection = @case_log.form.get_subsection(current_url.split("/")[-2]) + @case_log.form.current_user = current_user render "form/check_answers", locals: { subsection: } else render_not_found @@ -50,6 +51,7 @@ class FormController < ApplicationController end @subsection = @case_log.form.subsection_for_page(page) @page = @case_log.form.get_page(page.id) + @case_log.form.current_user = current_user if @page.routed_to?(@case_log) && @page.subsection.enabled?(@case_log) render "form/page" else diff --git a/app/models/case_log.rb b/app/models/case_log.rb index 00dc9505f..f3976fb06 100644 --- a/app/models/case_log.rb +++ b/app/models/case_log.rb @@ -32,9 +32,9 @@ class CaseLog < ApplicationRecord before_validation :set_derived_fields! before_save :update_status! - belongs_to :owning_organisation, class_name: "Organisation" - belongs_to :managing_organisation, class_name: "Organisation" - belongs_to :created_by, class_name: "User" + belongs_to :owning_organisation, class_name: "Organisation", optional: true + belongs_to :managing_organisation, class_name: "Organisation", optional: true + belongs_to :created_by, class_name: "User", optional: true scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) } scope :filter_by_status, ->(status, _user = nil) { where status: } @@ -474,13 +474,16 @@ private enabled_answer_options = enabled_question_ids.include?(question.id) ? enabled_questions.find { |q| q.id == question.id }.answer_options : {} current_answer_option_valid = enabled_answer_options.present? ? enabled_answer_options.key?(public_send(question.id).to_s) : false if !current_answer_option_valid && respond_to?(question.id.to_s) + Rails.logger.debug("Cleared #{question.id} value") public_send("#{question.id}=", nil) else (question.answer_options.keys - enabled_answer_options.keys).map do |invalid_answer_option| + Rails.logger.debug("Cleared #{invalid_answer_option} value") public_send("#{invalid_answer_option}=", nil) if respond_to?(invalid_answer_option) end end else + Rails.logger.debug("Cleared #{question.id} value") public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id) end end @@ -496,6 +499,7 @@ private condition_key = conditions.first[:key] condition_value = conditions.first[:value] if public_send("#{condition_key}_changed?") && condition_value == public_send(condition_key) && !public_send("#{dependent}_changed?") + Rails.logger.debug("Cleared derived #{dependent} value") self[dependent] = nil end end @@ -585,7 +589,7 @@ private end def get_lettype - return unless renttype.present? && needstype.present? && owning_organisation[:provider_type].present? + return unless renttype.present? && needstype.present? && owning_organisation.present? && owning_organisation[:provider_type].present? case RENT_TYPE_MAPPING_LABELS[renttype] when "Social Rent" diff --git a/app/models/derived_variables/case_log_variables.rb b/app/models/derived_variables/case_log_variables.rb index e63112cb0..16fff3fd0 100644 --- a/app/models/derived_variables/case_log_variables.rb +++ b/app/models/derived_variables/case_log_variables.rb @@ -6,6 +6,8 @@ module DerivedVariables::CaseLogVariables end def set_derived_fields! + # TODO: Remove once we support parent/child relationships + self.managing_organisation_id ||= owning_organisation_id # TODO: Remove once we support supported housing logs self.needstype = 1 unless supported_housing_schemes_enabled? if rsnvac.present? diff --git a/app/models/form.rb b/app/models/form.rb index 0353fb08c..228057dc8 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -2,6 +2,7 @@ class Form attr_reader :form_definition, :sections, :subsections, :pages, :questions, :start_date, :end_date, :type, :name, :setup_definition, :setup_sections, :form_sections + attr_accessor :current_user include Form::Setup @@ -115,7 +116,7 @@ class Form end def invalidated_pages(case_log) - pages.reject { |p| p.routed_to?(case_log) } + pages.select { |p| p.invalidated?(case_log) } end def invalidated_questions(case_log) diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 1ea95ffd2..1b7c76107 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -31,6 +31,10 @@ class Form::Page end end + def invalidated?(case_log) + !routed_to?(case_log) + end + private def conditional_question_ids diff --git a/app/models/form/setup/pages/created_by.rb b/app/models/form/setup/pages/created_by.rb new file mode 100644 index 000000000..e9463ccdb --- /dev/null +++ b/app/models/form/setup/pages/created_by.rb @@ -0,0 +1,24 @@ +class Form::Setup::Pages::CreatedBy < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "created_by" + @header = "" + @description = "" + @questions = questions + @subsection = subsection + end + + def questions + [ + Form::Setup::Questions::CreatedById.new(nil, nil, self), + ] + end + + def routed_to?(_case_log) + !!form.current_user&.support? + end + + def invalidated?(_case_log) + false + end +end diff --git a/app/models/form/setup/pages/organisation.rb b/app/models/form/setup/pages/organisation.rb new file mode 100644 index 000000000..d02cf9b97 --- /dev/null +++ b/app/models/form/setup/pages/organisation.rb @@ -0,0 +1,24 @@ +class Form::Setup::Pages::Organisation < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "organisation" + @header = "" + @description = "" + @questions = questions + @subsection = subsection + end + + def questions + [ + Form::Setup::Questions::OwningOrganisationId.new(nil, nil, self), + ] + end + + def routed_to?(_case_log) + !!form.current_user&.support? + end + + def invalidated?(_case_log) + false + end +end diff --git a/app/models/form/setup/questions/created_by_id.rb b/app/models/form/setup/questions/created_by_id.rb new file mode 100644 index 000000000..af4493542 --- /dev/null +++ b/app/models/form/setup/questions/created_by_id.rb @@ -0,0 +1,39 @@ +class Form::Setup::Questions::CreatedById < ::Form::Question + def initialize(id, hsh, page) + super + @id = "created_by_id" + @check_answer_label = "User" + @header = "Which user are you creating this log for?" + @hint_text = "" + @type = "select" + @page = page + @answer_options = answer_options_values + end + + def answer_options_values + answer_opts = { "" => "Select an option" } + return answer_opts unless ActiveRecord::Base.connected? + + User.select(:id, :name).each_with_object(answer_opts) do |user, hsh| + hsh[user.id] = user.name + hsh + end + end + + def displayed_answer_options(case_log) + return answer_options unless case_log.owning_organisation + + user_ids = case_log.owning_organisation.users.pluck(:id) + [""] + answer_options.select { |k, _v| user_ids.include?(k) } + end + + def label_from_value(value) + return unless value + + answer_options[value] + end + + def hidden_in_check_answers + !form.current_user.support? + end +end diff --git a/app/models/form/setup/questions/owning_organisation_id.rb b/app/models/form/setup/questions/owning_organisation_id.rb new file mode 100644 index 000000000..3ce620681 --- /dev/null +++ b/app/models/form/setup/questions/owning_organisation_id.rb @@ -0,0 +1,39 @@ +class Form::Setup::Questions::OwningOrganisationId < ::Form::Question + def initialize(id, hsh, page) + super + @id = "owning_organisation_id" + @check_answer_label = "Owning organisation" + @header = "Which organisation is the owning organisation for this log?" + @hint_text = "" + @type = "select" + @page = page + @answer_options = answer_options_values + end + + def answer_options_values + answer_opts = { "" => "Select an option" } + return answer_opts unless ActiveRecord::Base.connected? + + Organisation.select(:id, :name).each_with_object(answer_opts) do |organisation, hsh| + hsh[organisation.id] = organisation.name + hsh + end + end + + def displayed_answer_options(case_log) + return answer_options unless case_log.created_by + + ids = ["", case_log.created_by.organisation.id] + answer_options.select { |k, _v| ids.include?(k) } + end + + def label_from_value(value) + return unless value + + answer_options[value] + end + + def hidden_in_check_answers + !form.current_user.support? + end +end diff --git a/app/models/form/setup/subsections/setup.rb b/app/models/form/setup/subsections/setup.rb index a1f60efa4..c99b9f125 100644 --- a/app/models/form/setup/subsections/setup.rb +++ b/app/models/form/setup/subsections/setup.rb @@ -9,6 +9,8 @@ class Form::Subsections::Setup < ::Form::Subsection def pages [ + Form::Setup::Pages::Organisation.new(nil, nil, self), + Form::Setup::Pages::CreatedBy.new(nil, nil, self), Form::Setup::Pages::NeedsType.new(nil, nil, self), Form::Setup::Pages::Renewal.new(nil, nil, self), Form::Setup::Pages::TenancyStartDate.new(nil, nil, self), diff --git a/app/models/validations/financial_validations.rb b/app/models/validations/financial_validations.rb index 5b46e1b4c..bd3d008dd 100644 --- a/app/models/validations/financial_validations.rb +++ b/app/models/validations/financial_validations.rb @@ -141,6 +141,8 @@ private NEEDSTYPE_VALUES = { 2 => :supported_housing, 1 => :general_needs }.freeze def validate_charges(record) + return unless record.owning_organisation + provider_type = record.owning_organisation.provider_type_before_type_cast %i[scharge pscharge supcharg].each do |charge| maximum = CHARGE_MAXIMUMS.dig(charge, PROVIDER_TYPE[provider_type], NEEDSTYPE_VALUES[record.needstype]) diff --git a/app/models/validations/household_validations.rb b/app/models/validations/household_validations.rb index 4584cea52..48b862ab3 100644 --- a/app/models/validations/household_validations.rb +++ b/app/models/validations/household_validations.rb @@ -110,6 +110,8 @@ module Validations::HouseholdValidations end def validate_referral(record) + return unless record.owning_organisation + if record.is_internal_transfer? && record.owning_organisation.provider_type == "PRP" && record.is_prevten_la_general_needs? record.errors.add :prevten, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.prevten.la_general_needs.internal_transfer") record.errors.add :referral, :internal_transfer_fixed_or_lifetime, message: I18n.t("validations.household.referral.la_general_needs.internal_transfer") diff --git a/app/views/case_logs/_log_list.html.erb b/app/views/case_logs/_log_list.html.erb index 997c701f0..c08bfa067 100644 --- a/app/views/case_logs/_log_list.html.erb +++ b/app/views/case_logs/_log_list.html.erb @@ -58,8 +58,8 @@ <%= status_tag(log.status) %> <% end %> <% if current_user.support? %> - <% row.cell(text: log.owning_organisation.name) %> - <% row.cell(text: log.managing_organisation.name) %> + <% row.cell(text: log.owning_organisation&.name) %> + <% row.cell(text: log.managing_organisation&.name) %> <% end %> <% end %> <% end %> diff --git a/config/forms/setup/log_setup.json b/config/forms/setup/log_setup.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/models/form/setup/pages/created_by_spec.rb b/spec/models/form/setup/pages/created_by_spec.rb new file mode 100644 index 000000000..600d34c4f --- /dev/null +++ b/spec/models/form/setup/pages/created_by_spec.rb @@ -0,0 +1,65 @@ +require "rails_helper" + +RSpec.describe Form::Setup::Pages::CreatedBy, type: :model do + subject(:page) { described_class.new(page_id, page_definition, subsection) } + + let(:page_id) { nil } + let(:page_definition) { nil } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + let(:case_log) { instance_double(CaseLog) } + + it "has correct subsection" do + expect(page.subsection).to eq(subsection) + end + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[created_by_id]) + end + + it "has the correct id" do + expect(page.id).to eq("created_by") + end + + it "has the correct header" do + expect(page.header).to eq("") + end + + it "has the correct description" do + expect(page.description).to eq("") + end + + it "has the correct depends_on" do + expect(page.depends_on).to be nil + end + + it "has the correct derived" do + expect(page.derived).to be nil + end + + context "when the current user is a support user" do + let(:support_user) { FactoryBot.build(:user, :support) } + + before do + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(support_user) + end + + it "is shown" do + expect(page.routed_to?(case_log)).to be true + end + end + + context "when the current user is not a support user" do + let(:user) { FactoryBot.build(:user) } + + before do + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(user) + end + + it "is not shown" do + expect(page.routed_to?(case_log)).to be false + end + end +end diff --git a/spec/models/form/setup/pages/organisation_spec.rb b/spec/models/form/setup/pages/organisation_spec.rb new file mode 100644 index 000000000..582525365 --- /dev/null +++ b/spec/models/form/setup/pages/organisation_spec.rb @@ -0,0 +1,65 @@ +require "rails_helper" + +RSpec.describe Form::Setup::Pages::Organisation, type: :model do + subject(:page) { described_class.new(page_id, page_definition, subsection) } + + let(:page_id) { nil } + let(:page_definition) { nil } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + let(:case_log) { instance_double(CaseLog) } + + it "has correct subsection" do + expect(page.subsection).to eq(subsection) + end + + it "has correct questions" do + expect(page.questions.map(&:id)).to eq(%w[owning_organisation_id]) + end + + it "has the correct id" do + expect(page.id).to eq("organisation") + end + + it "has the correct header" do + expect(page.header).to eq("") + end + + it "has the correct description" do + expect(page.description).to eq("") + end + + it "has the correct depends_on" do + expect(page.depends_on).to be nil + end + + it "has the correct derived" do + expect(page.derived).to be nil + end + + context "when the current user is a support user" do + let(:support_user) { FactoryBot.build(:user, :support) } + + before do + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(support_user) + end + + it "is shown" do + expect(page.routed_to?(case_log)).to be true + end + end + + context "when the current user is not a support user" do + let(:user) { FactoryBot.build(:user) } + + before do + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(user) + end + + it "is not shown" do + expect(page.routed_to?(case_log)).to be false + end + end +end diff --git a/spec/models/form/setup/questions/created_by_id_spec.rb b/spec/models/form/setup/questions/created_by_id_spec.rb new file mode 100644 index 000000000..8ec615ebd --- /dev/null +++ b/spec/models/form/setup/questions/created_by_id_spec.rb @@ -0,0 +1,90 @@ +require "rails_helper" + +RSpec.describe Form::Setup::Questions::CreatedById, type: :model do + subject(:question) { described_class.new(question_id, question_definition, page) } + + let(:question_id) { nil } + let(:question_definition) { nil } + let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + let!(:user_1) { FactoryBot.create(:user, name: "first user") } + let!(:user_2) { FactoryBot.create(:user, name: "second user") } + let(:expected_answer_options) do + { + "" => "Select an option", + user_1.id => user_1.name, + user_2.id => user_2.name, + } + end + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("created_by_id") + end + + it "has the correct header" do + expect(question.header).to eq("Which user are you creating this log for?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("User") + end + + it "has the correct type" do + expect(question.type).to eq("select") + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq("") + end + + it "has the correct answer options" do + expect(question.answer_options).to eq(expected_answer_options) + end + + context "when the current user is support" do + let(:support_user) { FactoryBot.build(:user, :support) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(support_user) + end + + it "is shown in check answers" do + expect(question.hidden_in_check_answers).to be false + end + end + + context "when the current user is not support" do + let(:user) { FactoryBot.build(:user) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(user) + end + + it "is not shown in check answers" do + expect(question.hidden_in_check_answers).to be true + end + end + + context "when the owning organisation is already set" do + let(:case_log) { FactoryBot.create(:case_log, owning_organisation: user_2.organisation) } + let(:expected_answer_options) do + { + "" => "Select an option", + user_2.id => user_2.name, + } + end + + it "only displays users that belong to that organisation" do + expect(question.displayed_answer_options(case_log)).to eq(expected_answer_options) + end + end +end diff --git a/spec/models/form/setup/questions/owning_organisation_id_spec.rb b/spec/models/form/setup/questions/owning_organisation_id_spec.rb new file mode 100644 index 000000000..81cf21478 --- /dev/null +++ b/spec/models/form/setup/questions/owning_organisation_id_spec.rb @@ -0,0 +1,91 @@ +require "rails_helper" + +RSpec.describe Form::Setup::Questions::OwningOrganisationId, type: :model do + subject(:question) { described_class.new(question_id, question_definition, page) } + + let(:question_id) { nil } + let(:question_definition) { nil } + let(:page) { instance_double(Form::Page) } + let(:subsection) { instance_double(Form::Subsection) } + let(:form) { instance_double(Form) } + let!(:organisation_1) { FactoryBot.create(:organisation, name: "first test org") } + let!(:organisation_2) { FactoryBot.create(:organisation, name: "second test org") } + let(:expected_answer_options) do + { + "" => "Select an option", + organisation_1.id => organisation_1.name, + organisation_2.id => organisation_2.name, + } + end + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("owning_organisation_id") + end + + it "has the correct header" do + expect(question.header).to eq("Which organisation is the owning organisation for this log?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Owning organisation") + end + + it "has the correct type" do + expect(question.type).to eq("select") + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq("") + end + + it "has the correct answer options" do + expect(question.answer_options).to eq(expected_answer_options) + end + + context "when the current user is support" do + let(:support_user) { FactoryBot.build(:user, :support) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(support_user) + end + + it "is shown in check answers" do + expect(question.hidden_in_check_answers).to be false + end + end + + context "when the current user is not support" do + let(:user) { FactoryBot.build(:user) } + + before do + allow(page).to receive(:subsection).and_return(subsection) + allow(subsection).to receive(:form).and_return(form) + allow(form).to receive(:current_user).and_return(user) + end + + it "is not shown in check answers" do + expect(question.hidden_in_check_answers).to be true + end + end + + context "when the user is already set" do + let(:user) { FactoryBot.create(:user, organisation: organisation_2) } + let(:case_log) { FactoryBot.create(:case_log, created_by: user) } + let(:expected_answer_options) do + { + "" => "Select an option", + organisation_2.id => organisation_2.name, + } + end + + it "only displays users that belong to that organisation" do + expect(question.displayed_answer_options(case_log)).to eq(expected_answer_options) + end + end +end diff --git a/spec/models/form/setup/subsections/setup_spec.rb b/spec/models/form/setup/subsections/setup_spec.rb index 51cf20e19..8da7123a1 100644 --- a/spec/models/form/setup/subsections/setup_spec.rb +++ b/spec/models/form/setup/subsections/setup_spec.rb @@ -12,7 +12,16 @@ RSpec.describe Form::Setup::Subsections::Setup, type: :model do end it "has correct pages" do - expect(setup.pages.map(&:id)).to eq(%w[needs_type renewal tenancy_start_date rent_type tenant_code property_reference]) + expect(setup.pages.map(&:id)).to eq( + %w[organisation + created_by + needs_type + renewal + tenancy_start_date + rent_type + tenant_code + property_reference], + ) end it "has the correct id" do diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index d84d8751d..b8959ca88 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -17,7 +17,7 @@ RSpec.describe FormHandler do form_handler = described_class.instance form = form_handler.get_form(test_form_name) expect(form).to be_a(Form) - expect(form.pages.count).to eq(40) + expect(form.pages.count).to eq(42) end end diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index 287d5d349..bdc3eb226 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -70,6 +70,7 @@ RSpec.describe FormController, type: :request do context "when a user is signed in" do before do + allow(user).to receive(:need_two_factor_authentication?).and_return(false) sign_in user end @@ -141,6 +142,24 @@ RSpec.describe FormController, type: :request do expect(response.body).to match("Review lettings log") end end + + context "when viewing a user dependent page" do + context "when the dependency is met" do + let(:user) { FactoryBot.create(:user, :support) } + + it "routes to the page" do + get "/logs/#{case_log.id}/organisation" + expect(response).to have_http_status(:ok) + end + end + + context "when the dependency is not met" do + it "redirects to the tasklist page" do + get "/logs/#{case_log.id}/organisation" + expect(response).to redirect_to("/logs/#{case_log.id}") + end + end + end end describe "Submit Form" do