diff --git a/.github/workflows/production_pipeline.yml b/.github/workflows/production_pipeline.yml index 333e1f87b..23bb69c00 100644 --- a/.github/workflows/production_pipeline.yml +++ b/.github/workflows/production_pipeline.yml @@ -238,6 +238,8 @@ jobs: RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} IMPORT_PAAS_INSTANCE: ${{ secrets.IMPORT_PAAS_INSTANCE }} EXPORT_PAAS_INSTANCE: ${{ secrets.EXPORT_PAAS_INSTANCE }} + S3_CONFIG: ${{ secrets.S3_CONFIG }} + CSV_DOWNLOAD_PAAS_INSTANCE: ${{ secrets.CSV_DOWNLOAD_PAAS_INSTANCE }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} run: | cf api $CF_API_ENDPOINT @@ -248,5 +250,7 @@ jobs: cf set-env $APP_NAME RAILS_MASTER_KEY $RAILS_MASTER_KEY cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE cf set-env $APP_NAME EXPORT_PAAS_INSTANCE $EXPORT_PAAS_INSTANCE + cf set-env $APP_NAME S3_CONFIG $S3_CONFIG + cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE $CSV_DOWNLOAD_PAAS_INSTANCE cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN cf push $APP_NAME --strategy rolling diff --git a/.github/workflows/staging_pipeline.yml b/.github/workflows/staging_pipeline.yml index 4c588e919..d59024cd3 100644 --- a/.github/workflows/staging_pipeline.yml +++ b/.github/workflows/staging_pipeline.yml @@ -214,6 +214,8 @@ jobs: RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} IMPORT_PAAS_INSTANCE: ${{ secrets.IMPORT_PAAS_INSTANCE }} EXPORT_PAAS_INSTANCE: ${{ secrets.EXPORT_PAAS_INSTANCE }} + S3_CONFIG: ${{ secrets.S3_CONFIG }} + CSV_DOWNLOAD_PAAS_INSTANCE: ${{ secrets.CSV_DOWNLOAD_PAAS_INSTANCE }} SENTRY_DSN: ${{ secrets.SENTRY_DSN }} run: | cf api $CF_API_ENDPOINT @@ -226,5 +228,7 @@ jobs: cf set-env $APP_NAME RAILS_MASTER_KEY $RAILS_MASTER_KEY cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE cf set-env $APP_NAME EXPORT_PAAS_INSTANCE $EXPORT_PAAS_INSTANCE + cf set-env $APP_NAME S3_CONFIG $S3_CONFIG + cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE $CSV_DOWNLOAD_PAAS_INSTANCE cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN cf push $APP_NAME --strategy rolling diff --git a/.rubocop.yml b/.rubocop.yml index cf0c1e933..059bc2a84 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -20,3 +20,10 @@ AllCops: Style/Documentation: Enabled: false + +Rails/UnknownEnv: + Environments: + - production + - staging + - development + - test diff --git a/Gemfile b/Gemfile index ad3e1893d..e6dac1995 100644 --- a/Gemfile +++ b/Gemfile @@ -58,6 +58,8 @@ gem "sentry-ruby" gem "possessive" # Strip whitespace from active record attributes gem "auto_strip_attributes" +# Use sidekiq for background processing +gem "sidekiq" group :development, :test do # Check gems for known vulnerabilities diff --git a/Gemfile.lock b/Gemfile.lock index 4f4b352bb..bd7111af6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -381,6 +381,10 @@ GEM sentry-ruby (~> 5.4.2) sentry-ruby (5.4.2) concurrent-ruby (~> 1.0, >= 1.0.2) + sidekiq (6.5.4) + connection_pool (>= 2.2.2) + rack (~> 2.0) + redis (>= 4.5.0) simplecov (0.21.2) docile (~> 1.1) simplecov-html (~> 0.11) @@ -470,6 +474,7 @@ DEPENDENCIES selenium-webdriver sentry-rails sentry-ruby + sidekiq simplecov stimulus-rails timecop (~> 0.9.4) diff --git a/app/components/check_answers_summary_list_card_component.html.erb b/app/components/check_answers_summary_list_card_component.html.erb index 73e9335c8..f299674b2 100644 --- a/app/components/check_answers_summary_list_card_component.html.erb +++ b/app/components/check_answers_summary_list_card_component.html.erb @@ -16,18 +16,18 @@ <% row.key { question.check_answer_label.to_s.presence || question.header.to_s } %> <% row.value do %> <%= get_answer_label(question) %> - <% extra_value = question.get_extra_check_answer_value(lettings_log) %> + <% extra_value = question.get_extra_check_answer_value(log) %> <% if extra_value %> <%= extra_value %> <% end %>
- <% question.get_inferred_answers(lettings_log).each do |inferred_answer| %> + <% question.get_inferred_answers(log).each do |inferred_answer| %> <%= inferred_answer %> <% end %> <% end %> <% row.action( - text: question.action_text(lettings_log), - href: question.action_href(lettings_log, question.page.id), + text: question.action_text(log), + href: question.action_href(log, question.page.id), visually_hidden_text: question.check_answer_label.to_s.downcase, ) %> <% end %> diff --git a/app/components/check_answers_summary_list_card_component.rb b/app/components/check_answers_summary_list_card_component.rb index bc7d8cdb9..de7fe9685 100644 --- a/app/components/check_answers_summary_list_card_component.rb +++ b/app/components/check_answers_summary_list_card_component.rb @@ -1,18 +1,18 @@ class CheckAnswersSummaryListCardComponent < ViewComponent::Base - attr_reader :questions, :lettings_log, :user + attr_reader :questions, :log, :user - def initialize(questions:, lettings_log:, user:) + def initialize(questions:, log:, user:) @questions = questions - @lettings_log = lettings_log + @log = log @user = user super end def applicable_questions - questions.reject { |q| q.hidden_in_check_answers?(lettings_log, user) } + questions.reject { |q| q.hidden_in_check_answers?(log, user) } end def get_answer_label(question) - question.answer_label(lettings_log).presence || "You didn’t answer this question".html_safe + question.answer_label(log).presence || "You didn’t answer this question".html_safe end end diff --git a/app/components/log_summary_component.html.erb b/app/components/log_summary_component.html.erb index 07203b26b..65d2fc045 100644 --- a/app/components/log_summary_component.html.erb +++ b/app/components/log_summary_component.html.erb @@ -3,11 +3,11 @@

- <%= govuk_link_to lettings_log_path(log) do %> + <%= govuk_link_to log.lettings? ? lettings_log_path(log) : sales_log_path(log) do %> Log <%= log.id %> <% end %>

- <% if log.tenancycode? or log.propcode? %> + <% if log.lettings? && (log.tenancycode? or log.propcode?) %>
- <% if log.needstype? or log.startdate? %> + <% if log.is_a?(LettingsLog) && (log.needstype? or log.startdate?) %>

<% if log.needstype? %> <%= log.is_general_needs? ? "General needs" : "Supported housing" %>
diff --git a/app/controllers/bulk_upload_controller.rb b/app/controllers/bulk_upload_controller.rb index ffc22d028..ed2ac2627 100644 --- a/app/controllers/bulk_upload_controller.rb +++ b/app/controllers/bulk_upload_controller.rb @@ -3,7 +3,7 @@ class BulkUploadController < ApplicationController def show @bulk_upload = BulkUpload.new(nil, nil) - render "lettings_logs/bulk_upload" + render "logs/bulk_upload" end def bulk_upload @@ -12,7 +12,7 @@ class BulkUploadController < ApplicationController @bulk_upload = BulkUpload.new(file, content_type) @bulk_upload.process(current_user) if @bulk_upload.errors.present? - render "lettings_logs/bulk_upload", status: :unprocessable_entity + render "logs/bulk_upload", status: :unprocessable_entity else redirect_to(lettings_logs_path) end diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index a7ee469b8..6e71de6c6 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -4,22 +4,22 @@ class FormController < ApplicationController before_action :find_resource_by_named_id, except: %i[submit_form review] def submit_form - if @lettings_log - @page = @lettings_log.form.get_page(params[:lettings_log][:page]) + if @log + @page = @log.form.get_page(params[@log.model_name.param_key][:page]) responses_for_page = responses_for_page(@page) mandatory_questions_with_no_response = mandatory_questions_with_no_response(responses_for_page) - if mandatory_questions_with_no_response.empty? && @lettings_log.update(responses_for_page) + if mandatory_questions_with_no_response.empty? && @log.update(responses_for_page) session[:errors] = session[:fields] = nil redirect_to(successful_redirect_path) else - redirect_path = "lettings_log_#{@page.id}_path" + redirect_path = "#{@log.model_name.param_key}_#{@page.id}_path" mandatory_questions_with_no_response.map do |question| - @lettings_log.errors.add question.id.to_sym, question.unanswered_error_message + @log.errors.add question.id.to_sym, question.unanswered_error_message end - session[:errors] = @lettings_log.errors.to_json - Rails.logger.info "User triggered validation(s) on: #{@lettings_log.errors.map(&:attribute).join(', ')}" - redirect_to(send(redirect_path, @lettings_log)) + session[:errors] = @log.errors.to_json + Rails.logger.info "User triggered validation(s) on: #{@log.errors.map(&:attribute).join(', ')}" + redirect_to(send(redirect_path, @log)) end else render_not_found @@ -27,9 +27,9 @@ class FormController < ApplicationController end def check_answers - if @lettings_log + if @log current_url = request.env["PATH_INFO"] - subsection = @lettings_log.form.get_subsection(current_url.split("/")[-2]) + subsection = @log.form.get_subsection(current_url.split("/")[-2]) render "form/check_answers", locals: { subsection:, current_user: } else render_not_found @@ -37,29 +37,26 @@ class FormController < ApplicationController end def review - if @lettings_log + if @log render "form/review" else render_not_found end end - FormHandler.instance.forms.each do |_key, form| - form.pages.map do |page| - define_method(page.id) do |_errors = {}| - if @lettings_log - restore_error_field_values - @subsection = @lettings_log.form.subsection_for_page(page) - @page = @lettings_log.form.get_page(page.id) - if @page.routed_to?(@lettings_log, current_user) - render "form/page" - else - redirect_to lettings_log_path(@lettings_log) - end - else - render_not_found - end + def show_page + if @log + restore_error_field_values + page_id = request.path.split("/")[-1].underscore + @page = @log.form.get_page(page_id) + @subsection = @log.form.subsection_for_page(@page) + if @page.routed_to?(@log, current_user) + render "form/page" + else + redirect_to lettings_log_path(@log) end + else + render_not_found end end @@ -68,13 +65,13 @@ private def restore_error_field_values if session["errors"] JSON(session["errors"]).each do |field, messages| - messages.each { |message| @lettings_log.errors.add field.to_sym, message } + messages.each { |message| @log.errors.add field.to_sym, message } end end if session["fields"] session["fields"].each do |field, value| - unless @lettings_log.form.get_question(field, @lettings_log)&.type == "date" - @lettings_log[field] = value + if @log.form.get_question(field, @log)&.type != "date" && @log.respond_to?(field) + @log[field] = value end end end @@ -82,11 +79,11 @@ private def responses_for_page(page) page.questions.each_with_object({}) do |question, result| - question_params = params["lettings_log"][question.id] + question_params = params[@log.model_name.param_key][question.id] if question.type == "date" - day = params["lettings_log"]["#{question.id}(3i)"] - month = params["lettings_log"]["#{question.id}(2i)"] - year = params["lettings_log"]["#{question.id}(1i)"] + day = params[@log.model_name.param_key]["#{question.id}(3i)"] + month = params[@log.model_name.param_key]["#{question.id}(2i)"] + year = params[@log.model_name.param_key]["#{question.id}(1i)"] next unless [day, month, year].any?(&:present?) result[question.id] = if Date.valid_date?(year.to_i, month.to_i, day.to_i) && year.to_i.between?(2000, 2200) @@ -109,11 +106,19 @@ private end def find_resource - @lettings_log = current_user.lettings_logs.find_by(id: params[:id]) + @log = if params.key?("sales_log") + current_user.sales_logs.find_by(id: params[:id]) + else + current_user.lettings_logs.find_by(id: params[:id]) + end end def find_resource_by_named_id - @lettings_log = current_user.lettings_logs.find_by(id: params[:lettings_log_id]) + @log = if params[:sales_log_id].present? + current_user.sales_logs.find_by(id: params[:sales_log_id]) + else + current_user.lettings_logs.find_by(id: params[:lettings_log_id]) + end end def is_referrer_check_answers? @@ -123,18 +128,18 @@ private def successful_redirect_path if is_referrer_check_answers? - page_ids = @lettings_log.form.subsection_for_page(@page).pages.map(&:id) + page_ids = @log.form.subsection_for_page(@page).pages.map(&:id) page_index = page_ids.index(@page.id) - next_page = @lettings_log.form.next_page(@page, @lettings_log, current_user) - previous_page = @lettings_log.form.previous_page(page_ids, page_index, @lettings_log, current_user) + next_page = @log.form.next_page(@page, @log, current_user) + previous_page = @log.form.previous_page(page_ids, page_index, @log, current_user) if next_page.to_s.include?("value_check") || next_page == previous_page - return "/logs/#{@lettings_log.id}/#{next_page.dasherize}?referrer=check_answers" + return send("#{@log.class.name.underscore}_#{next_page}_path", @log, { referrer: "check_answers" }) else - return send("lettings_log_#{@lettings_log.form.subsection_for_page(@page).id}_check_answers_path", @lettings_log) + return send("#{@log.model_name.param_key}_#{@log.form.subsection_for_page(@page).id}_check_answers_path", @log) end end - redirect_path = @lettings_log.form.next_page_redirect_path(@page, @lettings_log, current_user) - send(redirect_path, @lettings_log) + redirect_path = @log.form.next_page_redirect_path(@page, @log, current_user) + send(redirect_path, @log) end def mandatory_questions_with_no_response(responses_for_page) @@ -148,12 +153,12 @@ private end def question_is_required?(question) - LettingsLog::OPTIONAL_FIELDS.exclude?(question.id) && required_questions.include?(question.id) + @log.class::OPTIONAL_FIELDS.exclude?(question.id) && required_questions.include?(question.id) end def required_questions @required_questions ||= begin - log = @lettings_log + log = @log log.assign_attributes(responses_for_page(@page)) @page.subsection.applicable_questions(log).select { |q| q.enabled?(log) }.map(&:id) end @@ -162,12 +167,12 @@ private def question_missing_response?(responses_for_page, question) if %w[checkbox validation_override].include?(question.type) answered = question.answer_options.keys.reject { |x| x.match(/divider/) }.map do |option| - session["fields"][option] = @lettings_log[option] = params["lettings_log"][question.id].include?(option) ? 1 : 0 - params["lettings_log"][question.id].exclude?(option) + session["fields"][option] = @log[option] = params[@log.model_name.param_key][question.id].include?(option) ? 1 : 0 + params[@log.model_name.param_key][question.id].exclude?(option) end answered.all? else - session["fields"][question.id] = @lettings_log[question.id] = responses_for_page[question.id] + session["fields"][question.id] = @log[question.id] = responses_for_page[question.id] responses_for_page[question.id].nil? || responses_for_page[question.id].blank? end end diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb index a19fdbe6d..eaec5a604 100644 --- a/app/controllers/lettings_logs_controller.rb +++ b/app/controllers/lettings_logs_controller.rb @@ -1,55 +1,33 @@ -class LettingsLogsController < ApplicationController - include Pagy::Backend - include Modules::LettingsLogsFilter - include Modules::SearchFilter - - skip_before_action :verify_authenticity_token, if: :json_api_request? - before_action :authenticate, if: :json_api_request? - before_action :authenticate_user!, unless: :json_api_request? +class LettingsLogsController < LogsController before_action :find_resource, except: %i[create index edit] + before_action :session_filters, if: :current_user + before_action :set_session_filters, if: :current_user def index - set_session_filters - - all_logs = current_user.lettings_logs - unpaginated_filtered_logs = filtered_lettings_logs(filtered_collection(all_logs, search_term)) - respond_to do |format| format.html do - @pagy, @lettings_logs = pagy(unpaginated_filtered_logs) + all_logs = current_user.lettings_logs + unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters) + + @search_term = search_term + @pagy, @logs = pagy(unpaginated_filtered_logs) @searched = search_term.presence @total_count = all_logs.size - end - - format.csv do - send_data byte_order_mark + unpaginated_filtered_logs.to_csv(current_user), filename: "logs-#{Time.zone.now}.csv" + render "logs/index" end end end def create - lettings_log = LettingsLog.new(lettings_log_params) - respond_to do |format| - format.html do - lettings_log.save! - redirect_to lettings_log_url(lettings_log) - end - format.json do - if lettings_log.save - render json: lettings_log, status: :created - else - render json: { errors: lettings_log.errors.messages }, status: :unprocessable_entity - end - end - end + super { LettingsLog.new(log_params) } end def update - if @lettings_log - if @lettings_log.update(api_lettings_log_params) - render json: @lettings_log, status: :ok + if @log + if @log.update(api_log_params) + render json: @log, status: :ok else - render json: { errors: @lettings_log.errors.messages }, status: :unprocessable_entity + render json: { errors: @log.errors.messages }, status: :unprocessable_entity end else render_not_found_json("Log", params[:id]) @@ -61,8 +39,8 @@ class LettingsLogsController < ApplicationController # We don't have a dedicated non-editable show view format.html { edit } format.json do - if @lettings_log - render json: @lettings_log, status: :ok + if @log + render json: @log, status: :ok else render_not_found_json("Log", params[:id]) end @@ -71,68 +49,51 @@ class LettingsLogsController < ApplicationController end def edit - @lettings_log = current_user.lettings_logs.find_by(id: params[:id]) - if @lettings_log - render :edit, locals: { current_user: } + @log = current_user.lettings_logs.find_by(id: params[:id]) + if @log + render "logs/edit", locals: { current_user: } else render_not_found end end def destroy - if @lettings_log - if @lettings_log.delete + if @log + if @log.delete head :no_content else - render json: { errors: @lettings_log.errors.messages }, status: :unprocessable_entity + render json: { errors: @log.errors.messages }, status: :unprocessable_entity end else render_not_found_json("Log", params[:id]) end end -private - - API_ACTIONS = %w[create show update destroy].freeze + def download_csv + unpaginated_filtered_logs = filtered_logs(current_user.lettings_logs, search_term, @session_filters) - def search_term - params["search"] + render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path } end - def json_api_request? - API_ACTIONS.include?(request["action"]) && request.format.json? + def email_csv + all_orgs = params["organisation_select"] == "all" + EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs) + redirect_to csv_confirmation_lettings_logs_path end - def authenticate - http_basic_authenticate_or_request_with name: ENV["API_USER"], password: ENV["API_KEY"] - end + def csv_confirmation; end - def lettings_log_params - if current_user && !current_user.support? - org_params.merge(api_lettings_log_params) - else - api_lettings_log_params - end - end +private - def org_params - { - "owning_organisation_id" => current_user.organisation.id, - "managing_organisation_id" => current_user.organisation.id, - "created_by_id" => current_user.id, - } + def permitted_log_params + params.require(:lettings_log).permit(LettingsLog.editable_fields) end - def api_lettings_log_params - return {} unless params[:lettings_log] - - permitted = params.require(:lettings_log).permit(LettingsLog.editable_fields) - owning_id = permitted["owning_organisation_id"] - permitted["owning_organisation"] = Organisation.find(owning_id) if owning_id - permitted + def find_resource + @log = LettingsLog.find_by(id: params[:id]) end - def find_resource - @lettings_log = LettingsLog.find_by(id: params[:id]) + def post_create_redirect_url(log) + lettings_log_url(log) end end diff --git a/app/controllers/logs_controller.rb b/app/controllers/logs_controller.rb new file mode 100644 index 000000000..3982d4a54 --- /dev/null +++ b/app/controllers/logs_controller.rb @@ -0,0 +1,73 @@ +class LogsController < ApplicationController + include Pagy::Backend + include Modules::LogsFilter + include Modules::SearchFilter + + skip_before_action :verify_authenticity_token, if: :json_api_request? + before_action :authenticate, if: :json_api_request? + before_action :authenticate_user!, unless: :json_api_request? + +private + + def create + log = yield + raise "Caller must pass a block that implements model creation" if log.blank? + + respond_to do |format| + format.html do + log.save! + redirect_to post_create_redirect_url(log) + end + format.json do + if log.save + render json: log, status: :created + else + render json: { errors: log.errors.messages }, status: :unprocessable_entity + end + end + end + end + + def post_create_redirect_url + raise "implement in sub class" + end + + API_ACTIONS = %w[create show update destroy].freeze + + def json_api_request? + API_ACTIONS.include?(request["action"]) && request.format.json? + end + + def authenticate + http_basic_authenticate_or_request_with name: ENV["API_USER"], password: ENV["API_KEY"] + end + + def log_params + if current_user && !current_user.support? + org_params.merge(api_log_params) + else + api_log_params + end + end + + def api_log_params + return {} unless params[:lettings_log] || params[:sales_log] + + permitted = permitted_log_params + owning_id = permitted["owning_organisation_id"] + permitted["owning_organisation"] = Organisation.find(owning_id) if owning_id + permitted + end + + def org_params + { + "owning_organisation_id" => current_user.organisation.id, + "managing_organisation_id" => current_user.organisation.id, + "created_by_id" => current_user.id, + } + end + + def search_term + params["search"] + end +end diff --git a/app/controllers/modules/lettings_logs_filter.rb b/app/controllers/modules/lettings_logs_filter.rb deleted file mode 100644 index c074e48bb..000000000 --- a/app/controllers/modules/lettings_logs_filter.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Modules::LettingsLogsFilter - def filtered_lettings_logs(logs) - if session[:lettings_logs_filters].present? - filters = JSON.parse(session[:lettings_logs_filters]) - filters.each do |category, values| - next if Array(values).reject(&:empty?).blank? - next if category == "organisation" && params["organisation_select"] == "all" - - logs = logs.public_send("filter_by_#{category}", values, current_user) - end - end - logs = logs.order(created_at: :desc) - current_user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs - end - - def set_session_filters(specific_org: false) - new_filters = session[:lettings_logs_filters].present? ? JSON.parse(session[:lettings_logs_filters]) : {} - current_user.lettings_logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? } - new_filters = new_filters.except("organisation") if params["organisation_select"] == "all" - - session[:lettings_logs_filters] = new_filters.to_json - end -end diff --git a/app/controllers/modules/logs_filter.rb b/app/controllers/modules/logs_filter.rb new file mode 100644 index 000000000..7c60bb027 --- /dev/null +++ b/app/controllers/modules/logs_filter.rb @@ -0,0 +1,21 @@ +module Modules::LogsFilter + def filtered_logs(logs, search_term, filters) + all_orgs = params["organisation_select"] == "all" + FilterService.filter_logs(logs, search_term, filters, all_orgs, current_user) + end + + def load_session_filters(specific_org: false) + current_filters = session[:logs_filters] + new_filters = current_filters.present? ? JSON.parse(current_filters) : {} + current_user.logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? } + params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters + end + + def session_filters(specific_org: false) + @session_filters ||= load_session_filters(specific_org:) + end + + def set_session_filters + session[:logs_filters] = @session_filters.to_json + end +end diff --git a/app/controllers/modules/search_filter.rb b/app/controllers/modules/search_filter.rb index c32298987..82bf0d6c0 100644 --- a/app/controllers/modules/search_filter.rb +++ b/app/controllers/modules/search_filter.rb @@ -1,13 +1,9 @@ module Modules::SearchFilter def filtered_collection(base_collection, search_term = nil) - if search_term.present? - base_collection.search_by(search_term) - else - base_collection - end + FilterService.filter_by_search(base_collection, search_term) end def filtered_users(base_collection, search_term = nil) - filtered_collection(base_collection, search_term).includes(:organisation) + FilterService.filter_by_search(base_collection, search_term).includes(:organisation) end end diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb index 1cf181788..7f2094926 100644 --- a/app/controllers/organisations_controller.rb +++ b/app/controllers/organisations_controller.rb @@ -1,11 +1,13 @@ class OrganisationsController < ApplicationController include Pagy::Backend - include Modules::LettingsLogsFilter + include Modules::LogsFilter include Modules::SearchFilter before_action :authenticate_user! before_action :find_resource, except: %i[index new create] before_action :authenticate_scope!, except: [:index] + before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? } + before_action :set_session_filters, if: -> { current_user.support? } def index redirect_to organisation_path(current_user.organisation) unless current_user.support? @@ -87,27 +89,49 @@ class OrganisationsController < ApplicationController end end - def logs - if current_user.support? - set_session_filters(specific_org: true) + def lettings_logs + organisation_logs = LettingsLog.where(owning_organisation_id: @organisation.id) + unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) - organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id) - unpaginated_filtered_logs = filtered_lettings_logs(filtered_collection(organisation_logs, search_term)) + respond_to do |format| + format.html do + @search_term = search_term + @pagy, @logs = pagy(unpaginated_filtered_logs) + @searched = search_term.presence + @total_count = organisation_logs.size + render "logs", layout: "application" + end + end + end - respond_to do |format| - format.html do - @pagy, @lettings_logs = pagy(unpaginated_filtered_logs) - @searched = search_term.presence - @total_count = organisation_logs.size - render "logs", layout: "application" - end + def download_csv + organisation_logs = LettingsLog.all.where(owning_organisation_id: @organisation.id) + unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) - format.csv do - send_data byte_order_mark + unpaginated_filtered_logs.to_csv, filename: "logs-#{@organisation.name}-#{Time.zone.now}.csv" - end + render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: logs_email_csv_organisation_path } + end + + def email_csv + EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation) + redirect_to logs_csv_confirmation_organisation_path + end + + def sales_logs + organisation_logs = SalesLog.where(owning_organisation_id: @organisation.id) + unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters) + + respond_to do |format| + format.html do + @search_term = search_term + @pagy, @logs = pagy(unpaginated_filtered_logs) + @searched = search_term.presence + @total_count = organisation_logs.size + render "logs", layout: "application" + end + + format.csv do + send_data byte_order_mark + unpaginated_filtered_logs.to_csv, filename: "sales-logs-#{@organisation.name}-#{Time.zone.now}.csv" end - else - redirect_to(lettings_logs_path) end end @@ -122,7 +146,7 @@ private end def authenticate_scope! - if %w[create new].include? action_name + if %w[create new lettings_logs download_csv email_csv].include? action_name head :unauthorized and return unless current_user.support? elsif current_user.organisation != @organisation && !current_user.support? render_not_found diff --git a/app/controllers/sales_logs_controller.rb b/app/controllers/sales_logs_controller.rb new file mode 100644 index 000000000..8a6c9937f --- /dev/null +++ b/app/controllers/sales_logs_controller.rb @@ -0,0 +1,46 @@ +class SalesLogsController < LogsController + before_action :session_filters, if: :current_user + before_action :set_session_filters, if: :current_user + + def create + super { SalesLog.new(log_params) } + end + + def index + respond_to do |format| + format.html do + all_logs = current_user.sales_logs + unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters) + + @search_term = search_term + @pagy, @logs = pagy(unpaginated_filtered_logs) + @searched = search_term.presence + @total_count = all_logs.size + render "logs/index" + end + end + end + + def show + respond_to do |format| + format.html { edit } + end + end + + def edit + @log = current_user.sales_logs.find_by(id: params[:id]) + if @log + render "logs/edit", locals: { current_user: } + else + render_not_found + end + end + + def post_create_redirect_url(log) + sales_log_url(log) + end + + def permitted_log_params + params.require(:sales_log).permit(SalesLog.editable_fields) + end +end diff --git a/app/helpers/check_answers_helper.rb b/app/helpers/check_answers_helper.rb index ebb4bb034..67f582c1c 100644 --- a/app/helpers/check_answers_helper.rb +++ b/app/helpers/check_answers_helper.rb @@ -28,6 +28,10 @@ module CheckAnswersHelper subsection.applicable_questions(lettings_log).map(&:check_answers_card_number).compact.length.positive? end + def next_incomplete_section_path(log, redirect_path) + "#{log.class.name.underscore}_#{redirect_path.underscore.tr('/', '_')}_path" + end + private def answered_questions_count(subsection, lettings_log, current_user) diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb index a8f5582a7..507274be0 100644 --- a/app/helpers/filters_helper.rb +++ b/app/helpers/filters_helper.rb @@ -1,8 +1,8 @@ module FiltersHelper def filter_selected?(filter, value) - return false unless session[:lettings_logs_filters] + return false unless session[:logs_filters] - selected_filters = JSON.parse(session[:lettings_logs_filters]) + selected_filters = JSON.parse(session[:logs_filters]) return true if selected_filters.blank? && filter == "user" && value == :all return true if !selected_filters.key?("organisation") && filter == "organisation_select" && value == :all return true if selected_filters["organisation"].present? && filter == "organisation_select" && value == :specific_org @@ -18,8 +18,8 @@ module FiltersHelper end def selected_option(filter) - return false unless session[:lettings_logs_filters] + return false unless session[:logs_filters] - JSON.parse(session[:lettings_logs_filters])[filter] || "" + JSON.parse(session[:logs_filters])[filter] || "" end end diff --git a/app/helpers/navigation_items_helper.rb b/app/helpers/navigation_items_helper.rb index 244aaf1ab..3ac422c2d 100644 --- a/app/helpers/navigation_items_helper.rb +++ b/app/helpers/navigation_items_helper.rb @@ -6,39 +6,44 @@ module NavigationItemsHelper [ NavigationItem.new("Organisations", organisations_path, organisations_current?(path)), NavigationItem.new("Users", "/users", users_current?(path)), - NavigationItem.new("Logs", lettings_logs_path, logs_current?(path)), + NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)), + FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil, NavigationItem.new("Schemes", "/schemes", supported_housing_schemes_current?(path)), - ] + ].compact elsif current_user.data_coordinator? && current_user.organisation.holds_own_stock? [ - NavigationItem.new("Logs", lettings_logs_path, logs_current?(path)), + NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)), + FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil, NavigationItem.new("Schemes", "/schemes", subnav_supported_housing_schemes_path?(path)), NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)), NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)), - ] + ].compact else [ - NavigationItem.new("Logs", lettings_logs_path, logs_current?(path)), + NavigationItem.new("Lettings logs", lettings_logs_path, lettings_logs_current?(path)), + FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", sales_logs_path, sales_logs_current?(path)) : nil, NavigationItem.new("Users", users_organisation_path(current_user.organisation), subnav_users_path?(path)), NavigationItem.new("About your organisation", "/organisations/#{current_user.organisation.id}", subnav_details_path?(path)), - ] + ].compact end end def secondary_items(path, current_organisation_id) if current_user.organisation.holds_own_stock? [ - NavigationItem.new("Logs", "/organisations/#{current_organisation_id}/logs", subnav_logs_path?(path)), + NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_logs_path?(path)), + FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", sales_logs_current?(path)) : nil, NavigationItem.new("Schemes", "/organisations/#{current_organisation_id}/schemes", subnav_supported_housing_schemes_path?(path)), NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)), NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)), - ] + ].compact else [ - NavigationItem.new("Logs", "/organisations/#{current_organisation_id}/logs", subnav_logs_path?(path)), + NavigationItem.new("Lettings logs", "/organisations/#{current_organisation_id}/lettings-logs", subnav_logs_path?(path)), + FeatureToggle.sales_log_enabled? ? NavigationItem.new("Sales logs", "/organisations/#{current_organisation_id}/sales-logs", sales_logs_current?(path)) : nil, NavigationItem.new("Users", "/organisations/#{current_organisation_id}/users", subnav_users_path?(path)), NavigationItem.new("About this organisation", "/organisations/#{current_organisation_id}", subnav_details_path?(path)), - ] + ].compact end end @@ -51,8 +56,12 @@ module NavigationItemsHelper private - def logs_current?(path) - path == "/logs" + def lettings_logs_current?(path) + path == "/lettings-logs" + end + + def sales_logs_current?(path) + path == "/sales-logs" end def users_current?(path) @@ -76,7 +85,7 @@ private end def subnav_logs_path?(path) - path.include?("/organisations") && path.include?("/logs") + path.include?("/organisations") && path.include?("/lettings-logs") end def subnav_details_path?(path) diff --git a/app/helpers/tasklist_helper.rb b/app/helpers/tasklist_helper.rb index e4308d97e..7ab955a2e 100644 --- a/app/helpers/tasklist_helper.rb +++ b/app/helpers/tasklist_helper.rb @@ -1,36 +1,36 @@ module TasklistHelper include GovukLinkHelper - def get_next_incomplete_section(lettings_log) - lettings_log.form.subsections.find { |subsection| subsection.is_incomplete?(lettings_log) } + def get_next_incomplete_section(log) + log.form.subsections.find { |subsection| subsection.is_incomplete?(log) } end - def get_subsections_count(lettings_log, status = :all) - return lettings_log.form.subsections.count { |subsection| subsection.applicable_questions(lettings_log).count.positive? } if status == :all + def get_subsections_count(log, status = :all) + return log.form.subsections.count { |subsection| subsection.applicable_questions(log).count.positive? } if status == :all - lettings_log.form.subsections.count { |subsection| subsection.status(lettings_log) == status && subsection.applicable_questions(lettings_log).count.positive? } + log.form.subsections.count { |subsection| subsection.status(log) == status && subsection.applicable_questions(log).count.positive? } end - def next_page_or_check_answers(subsection, lettings_log, current_user) - path = if subsection.is_started?(lettings_log) - "lettings_log_#{subsection.id}_check_answers_path" + def next_page_or_check_answers(subsection, log, current_user) + path = if subsection.is_started?(log) + "#{log.class.name.underscore}_#{subsection.id}_check_answers_path" else - "lettings_log_#{next_question_page(subsection, lettings_log, current_user)}_path" + "#{log.class.name.underscore}_#{next_question_page(subsection, log, current_user)}_path" end - send(path, lettings_log) + send(path, log) end - def next_question_page(subsection, lettings_log, current_user) - if subsection.pages.first.routed_to?(lettings_log, current_user) + def next_question_page(subsection, log, current_user) + if subsection.pages.first.routed_to?(log, current_user) subsection.pages.first.id else - lettings_log.form.next_page(subsection.pages.first, lettings_log, current_user) + log.form.next_page(subsection.pages.first, log, current_user) end end - def subsection_link(subsection, lettings_log, current_user) - if subsection.status(lettings_log) != :cannot_start_yet - next_page_path = next_page_or_check_answers(subsection, lettings_log, current_user).to_s + def subsection_link(subsection, log, current_user) + if subsection.status(log) != :cannot_start_yet + next_page_path = next_page_or_check_answers(subsection, log, current_user).to_s govuk_link_to(subsection.label, next_page_path.dasherize, aria: { describedby: subsection.id.dasherize }) else subsection.label diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb new file mode 100644 index 000000000..c7009645f --- /dev/null +++ b/app/jobs/email_csv_job.rb @@ -0,0 +1,21 @@ +class EmailCsvJob < ApplicationJob + queue_as :default + + BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 + + EXPIRATION_TIME = 3.hours.to_i + + def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params + unfiltered_logs = organisation.present? && user.support? ? LettingsLog.where(owning_organisation_id: organisation.id) : user.lettings_logs + filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) + + filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv" + + storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) + storage_service.write_file(filename, BYTE_ORDER_MARK + filtered_logs.to_csv(user)) + + url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) + + CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME) + end +end diff --git a/app/mailers/csv_download_mailer.rb b/app/mailers/csv_download_mailer.rb new file mode 100644 index 000000000..619d5e922 --- /dev/null +++ b/app/mailers/csv_download_mailer.rb @@ -0,0 +1,11 @@ +class CsvDownloadMailer < NotifyMailer + CSV_DOWNLOAD_TEMPLATE_ID = "7890e3b9-8c0d-4d08-bafe-427fd7cd95bf".freeze + + def send_csv_download_mail(user, link, duration) + send_email( + user.email, + CSV_DOWNLOAD_TEMPLATE_ID, + { name: user.name, link:, duration: ActiveSupport::Duration.build(duration).inspect }, + ) + end +end diff --git a/app/mailers/notify_mailer.rb b/app/mailers/notify_mailer.rb new file mode 100644 index 000000000..506df5818 --- /dev/null +++ b/app/mailers/notify_mailer.rb @@ -0,0 +1,37 @@ +class NotifyMailer + require "notifications/client" + + def notify_client + @notify_client ||= ::Notifications::Client.new(ENV["GOVUK_NOTIFY_API_KEY"]) + end + + def send_email(email, template_id, personalisation) + return true if intercept_send?(email) + + notify_client.send_email( + email_address: email, + template_id:, + personalisation:, + ) + end + + def personalisation(record, token, url, username: false) + { + name: record.name || record.email, + email: username || record.email, + organisation: record.respond_to?(:organisation) ? record.organisation.name : "", + link: "#{url}#{token}", + } + end + + def intercept_send?(email) + return false unless email_allowlist + + email_domain = email.split("@").last.downcase + !(Rails.env.production? || Rails.env.test?) && email_allowlist.exclude?(email_domain) + end + + def email_allowlist + Rails.application.credentials[:email_allowlist] + end +end diff --git a/app/models/form.rb b/app/models/form.rb index 6fa92bba0..22321f431 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -3,22 +3,38 @@ class Form :start_date, :end_date, :type, :name, :setup_definition, :setup_sections, :form_sections - include Form::Setup - - def initialize(form_path, name) - raise "No form definition file exists for given year".freeze unless File.exist?(form_path) - - @name = name - @setup_sections = [Form::Setup::Sections::Setup.new(nil, nil, self)] - @form_definition = JSON.parse(File.open(form_path).read) - @form_sections = form_definition["sections"].map { |id, s| Form::Section.new(id, s, self) } - @type = form_definition["form_type"] - @sections = setup_sections + form_sections - @subsections = sections.flat_map(&:subsections) - @pages = subsections.flat_map(&:pages) - @questions = pages.flat_map(&:questions) - @start_date = Time.iso8601(form_definition["start_date"]) - @end_date = Time.iso8601(form_definition["end_date"]) + def initialize(form_path, start_year = "", sections_in_form = [], type = "lettings") + if type == "sales" + @setup_sections = [Form::Sales::Sections::Setup.new(nil, nil, self)] + @form_sections = sections_in_form.map { |sec| sec.new(nil, nil, self) } + @type = "sales" + @sections = setup_sections + form_sections + @subsections = sections.flat_map(&:subsections) + @pages = subsections.flat_map(&:pages) + @questions = pages.flat_map(&:questions) + @start_date = Time.zone.local(start_year, 4, 1) + @end_date = Time.zone.local(start_year + 1, 7, 1) + @form_definition = { + "form_type" => type, + "start_date" => start_date, + "end_date" => end_date, + "sections" => sections, + } + else + raise "No form definition file exists for given year".freeze unless File.exist?(form_path) + + @setup_sections = [Form::Lettings::Sections::Setup.new(nil, nil, self)] + @form_definition = JSON.parse(File.open(form_path).read) + @form_sections = form_definition["sections"].map { |id, s| Form::Section.new(id, s, self) } + @type = form_definition["form_type"] + @sections = setup_sections + form_sections + @subsections = sections.flat_map(&:subsections) + @pages = subsections.flat_map(&:pages) + @questions = pages.flat_map(&:questions) + @start_date = Time.iso8601(form_definition["start_date"]) + @end_date = Time.iso8601(form_definition["end_date"]) + end + @name = "#{start_date.year}_#{end_date.year}_#{type}" end def get_subsection(id) @@ -29,9 +45,9 @@ class Form pages.find { |p| p.id == id.to_s.underscore } end - def get_question(id, lettings_log, current_user = nil) + def get_question(id, log, current_user = nil) all_questions = questions.select { |q| q.id == id.to_s.underscore } - routed_question = all_questions.find { |q| q.page.routed_to?(lettings_log, current_user) } if lettings_log + routed_question = all_questions.find { |q| q.page.routed_to?(log, current_user) } if log routed_question || all_questions[0] end @@ -39,47 +55,51 @@ class Form subsections.find { |s| s.pages.find { |p| p.id == page.id } } end - def next_page(page, lettings_log, current_user) + def next_page(page, log, current_user) page_ids = subsection_for_page(page).pages.map(&:id) page_index = page_ids.index(page.id) - page_id = if page.id.include?("value_check") && lettings_log[page.questions[0].id] == 1 && page.routed_to?(lettings_log, current_user) - previous_page(page_ids, page_index, lettings_log, current_user) + page_id = if page.id.include?("value_check") && log[page.questions[0].id] == 1 && page.routed_to?(log, current_user) + previous_page(page_ids, page_index, log, current_user) else page_ids[page_index + 1] end nxt_page = get_page(page_id) return :check_answers if nxt_page.nil? - return nxt_page.id if nxt_page.routed_to?(lettings_log, current_user) + return nxt_page.id if nxt_page.routed_to?(log, current_user) - next_page(nxt_page, lettings_log, current_user) + next_page(nxt_page, log, current_user) end - def next_page_redirect_path(page, lettings_log, current_user) - nxt_page = next_page(page, lettings_log, current_user) + def next_page_redirect_path(page, log, current_user) + nxt_page = next_page(page, log, current_user) if nxt_page == :check_answers - "lettings_log_#{subsection_for_page(page).id}_check_answers_path" + "#{type}_log_#{subsection_for_page(page).id}_check_answers_path" else - "lettings_log_#{nxt_page}_path" + "#{type}_log_#{nxt_page}_path" end end - def next_incomplete_section_redirect_path(subsection, lettings_log) + def cancel_path(page, log) + "#{log.class.name.underscore}_#{page.subsection.id}_check_answers_path" + end + + def next_incomplete_section_redirect_path(subsection, log) subsection_ids = subsections.map(&:id) - if lettings_log.status == "completed" + if log.status == "completed" return first_question_in_last_subsection(subsection_ids) end - next_subsection = next_subsection(subsection, lettings_log, subsection_ids) + next_subsection = next_subsection(subsection, log, subsection_ids) - case next_subsection.status(lettings_log) + case next_subsection.status(log) when :completed - next_incomplete_section_redirect_path(next_subsection, lettings_log) + next_incomplete_section_redirect_path(next_subsection, log) when :in_progress "#{next_subsection.id}/check_answers".dasherize when :not_started - first_question_in_subsection = next_subsection.pages.find { |page| page.routed_to?(lettings_log, nil) }.id + first_question_in_subsection = next_subsection.pages.find { |page| page.routed_to?(log, nil) }.id first_question_in_subsection.to_s.dasherize else "error" @@ -92,21 +112,21 @@ class Form first_question_in_subsection.to_s.dasherize end - def next_subsection(subsection, lettings_log, subsection_ids) + def next_subsection(subsection, log, subsection_ids) next_subsection_id_index = subsection_ids.index(subsection.id) + 1 next_subsection = get_subsection(subsection_ids[next_subsection_id_index]) - if subsection_ids[subsection_ids.length - 1] == subsection.id && lettings_log.status != "completed" + if subsection_ids[subsection_ids.length - 1] == subsection.id && log.status != "completed" next_subsection = get_subsection(subsection_ids[0]) end next_subsection end - def all_subsections_except_declaration_completed?(lettings_log) + def all_subsections_except_declaration_completed?(log) subsection_ids = subsections.map(&:id) subsection_ids.delete_at(subsection_ids.length - 1) - return true if subsection_ids.all? { |subsection_id| get_subsection(subsection_id).status(lettings_log) == :completed } + return true if subsection_ids.all? { |subsection_id| get_subsection(subsection_id).status(log) == :completed } false end @@ -118,26 +138,50 @@ class Form }.flatten end - def invalidated_pages(lettings_log, current_user = nil) - pages.reject { |p| p.routed_to?(lettings_log, current_user) } + def invalidated_pages(log, current_user = nil) + pages.reject { |p| p.routed_to?(log, current_user) } end - def invalidated_questions(lettings_log) - invalidated_page_questions(lettings_log) + invalidated_conditional_questions(lettings_log) + def invalidated_questions(log) + invalidated_page_questions(log) + invalidated_conditional_questions(log) end - def invalidated_page_questions(lettings_log, current_user = nil) - # we're already treating these fields as a special case and reset their values upon saving a lettings_log + def invalidated_page_questions(log, current_user = nil) + # we're already treating these fields as a special case and reset their values upon saving a log callback_questions = %w[postcode_known la ppcodenk previous_la_known prevloc postcode_full ppostcode_full location_id] - questions.reject { |q| q.page.routed_to?(lettings_log, current_user) || q.derived? || callback_questions.include?(q.id) } || [] + questions.reject { |q| q.page.routed_to?(log, current_user) || q.derived? || callback_questions.include?(q.id) } || [] + end + + def reset_not_routed_questions(log) + enabled_questions = enabled_page_questions(log) + enabled_question_ids = enabled_questions.map(&:id) + + invalidated_page_questions(log).each do |question| + if %w[radio checkbox].include?(question.type) + 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?(log.public_send(question.id).to_s) : false + if !current_answer_option_valid && log.respond_to?(question.id.to_s) + Rails.logger.debug("Cleared #{question.id} value") + log.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") + log.public_send("#{invalid_answer_option}=", nil) if log.respond_to?(invalid_answer_option) + end + end + else + Rails.logger.debug("Cleared #{question.id} value") + log.public_send("#{question.id}=", nil) unless enabled_question_ids.include?(question.id) + end + end end - def enabled_page_questions(lettings_log) - questions - invalidated_page_questions(lettings_log) + def enabled_page_questions(log) + questions - invalidated_page_questions(log) end - def invalidated_conditional_questions(lettings_log) - questions.reject { |q| q.enabled?(lettings_log) } || [] + def invalidated_conditional_questions(log) + questions.reject { |q| q.enabled?(log) } || [] end def readonly_questions @@ -148,18 +192,18 @@ class Form questions.select { |q| q.type == "numeric" } end - def previous_page(page_ids, page_index, lettings_log, current_user) + def previous_page(page_ids, page_index, log, current_user) prev_page = get_page(page_ids[page_index - 1]) - return prev_page.id if prev_page.routed_to?(lettings_log, current_user) + return prev_page.id if prev_page.routed_to?(log, current_user) - previous_page(page_ids, page_index - 1, lettings_log, current_user) + previous_page(page_ids, page_index - 1, log, current_user) end - def send_chain(arr, lettings_log) - Array(arr).inject(lettings_log) { |o, a| o.public_send(*a) } + def send_chain(arr, log) + Array(arr).inject(log) { |o, a| o.public_send(*a) } end - def depends_on_met(depends_on, lettings_log) + def depends_on_met(depends_on, log) return true unless depends_on depends_on.any? do |conditions_set| @@ -169,12 +213,12 @@ class Form if value.is_a?(Hash) && value.key?("operator") operator = value["operator"] operand = value["operand"] - lettings_log[question]&.send(operator, operand) + log[question]&.send(operator, operand) else parts = question.split(".") - lettings_log_value = send_chain(parts, lettings_log) + log_value = send_chain(parts, log) - value.nil? ? lettings_log_value == value : !lettings_log_value.nil? && lettings_log_value == value + value.nil? ? log_value == value : !log_value.nil? && log_value == value end end end diff --git a/app/models/form/setup/pages/created_by.rb b/app/models/form/common/pages/created_by.rb similarity index 59% rename from app/models/form/setup/pages/created_by.rb rename to app/models/form/common/pages/created_by.rb index 8878e9b75..e02f97740 100644 --- a/app/models/form/setup/pages/created_by.rb +++ b/app/models/form/common/pages/created_by.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::CreatedBy < ::Form::Page +class Form::Common::Pages::CreatedBy < ::Form::Page def initialize(id, hsh, subsection) super @id = "created_by" @@ -9,11 +9,11 @@ class Form::Setup::Pages::CreatedBy < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::CreatedById.new(nil, nil, self), + Form::Common::Questions::CreatedById.new(nil, nil, self), ] end - def routed_to?(_lettings_log, current_user) + def routed_to?(_log, current_user) !!current_user&.support? end end diff --git a/app/models/form/setup/pages/organisation.rb b/app/models/form/common/pages/organisation.rb similarity index 57% rename from app/models/form/setup/pages/organisation.rb rename to app/models/form/common/pages/organisation.rb index 2b0c56d12..48b157a56 100644 --- a/app/models/form/setup/pages/organisation.rb +++ b/app/models/form/common/pages/organisation.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::Organisation < ::Form::Page +class Form::Common::Pages::Organisation < ::Form::Page def initialize(id, hsh, subsection) super @id = "organisation" @@ -9,11 +9,11 @@ class Form::Setup::Pages::Organisation < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::OwningOrganisationId.new(nil, nil, self), + Form::Common::Questions::OwningOrganisationId.new(nil, nil, self), ] end - def routed_to?(_lettings_log, current_user) + def routed_to?(_log, current_user) !!current_user&.support? end end diff --git a/app/models/form/setup/questions/created_by_id.rb b/app/models/form/common/questions/created_by_id.rb similarity index 67% rename from app/models/form/setup/questions/created_by_id.rb rename to app/models/form/common/questions/created_by_id.rb index 85567f882..b0558e5e2 100644 --- a/app/models/form/setup/questions/created_by_id.rb +++ b/app/models/form/common/questions/created_by_id.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::CreatedById < ::Form::Question +class Form::Common::Questions::CreatedById < ::Form::Question def initialize(id, hsh, page) super @id = "created_by_id" @@ -19,10 +19,10 @@ class Form::Setup::Questions::CreatedById < ::Form::Question end end - def displayed_answer_options(lettings_log) - return answer_options unless lettings_log.owning_organisation + def displayed_answer_options(log) + return answer_options unless log.owning_organisation - user_ids = lettings_log.owning_organisation.users.pluck(:id) + [""] + user_ids = log.owning_organisation.users.pluck(:id) + [""] answer_options.select { |k, _v| user_ids.include?(k) } end @@ -32,7 +32,7 @@ class Form::Setup::Questions::CreatedById < ::Form::Question answer_options[value] end - def hidden_in_check_answers?(_lettings_log, current_user) + def hidden_in_check_answers?(_log, current_user) !current_user.support? end @@ -42,7 +42,7 @@ class Form::Setup::Questions::CreatedById < ::Form::Question private - def selected_answer_option_is_derived?(_lettings_log) + def selected_answer_option_is_derived?(_log) false end end diff --git a/app/models/form/setup/questions/owning_organisation_id.rb b/app/models/form/common/questions/owning_organisation_id.rb similarity index 76% rename from app/models/form/setup/questions/owning_organisation_id.rb rename to app/models/form/common/questions/owning_organisation_id.rb index a8fd15e50..84eefbf21 100644 --- a/app/models/form/setup/questions/owning_organisation_id.rb +++ b/app/models/form/common/questions/owning_organisation_id.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::OwningOrganisationId < ::Form::Question +class Form::Common::Questions::OwningOrganisationId < ::Form::Question def initialize(id, hsh, page) super @id = "owning_organisation_id" @@ -19,7 +19,7 @@ class Form::Setup::Questions::OwningOrganisationId < ::Form::Question end end - def displayed_answer_options(_lettings_log) + def displayed_answer_options(_log) answer_options end @@ -29,7 +29,7 @@ class Form::Setup::Questions::OwningOrganisationId < ::Form::Question answer_options[value] end - def hidden_in_check_answers?(_lettings_log, current_user) + def hidden_in_check_answers?(_log, current_user) !current_user.support? end @@ -39,7 +39,7 @@ class Form::Setup::Questions::OwningOrganisationId < ::Form::Question private - def selected_answer_option_is_derived?(_lettings_log) + def selected_answer_option_is_derived?(_log) false end end diff --git a/app/models/form/setup/pages/location.rb b/app/models/form/lettings/pages/location.rb similarity index 70% rename from app/models/form/setup/pages/location.rb rename to app/models/form/lettings/pages/location.rb index 7acbc1bbe..982f4c109 100644 --- a/app/models/form/setup/pages/location.rb +++ b/app/models/form/lettings/pages/location.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::Location < ::Form::Page +class Form::Lettings::Pages::Location < ::Form::Page def initialize(_id, hsh, subsection) super("location", hsh, subsection) @header = "" @@ -11,7 +11,7 @@ class Form::Setup::Pages::Location < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::LocationId.new(nil, nil, self), + Form::Lettings::Questions::LocationId.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/needs_type.rb b/app/models/form/lettings/pages/needs_type.rb similarity index 62% rename from app/models/form/setup/pages/needs_type.rb rename to app/models/form/lettings/pages/needs_type.rb index 2625d6cda..0a67d831b 100644 --- a/app/models/form/setup/pages/needs_type.rb +++ b/app/models/form/lettings/pages/needs_type.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::NeedsType < ::Form::Page +class Form::Lettings::Pages::NeedsType < ::Form::Page def initialize(id, hsh, subsection) super @id = "needs_type" @@ -9,7 +9,7 @@ class Form::Setup::Pages::NeedsType < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::NeedsType.new(nil, nil, self), + Form::Lettings::Questions::NeedsType.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/property_reference.rb b/app/models/form/lettings/pages/property_reference.rb similarity index 60% rename from app/models/form/setup/pages/property_reference.rb rename to app/models/form/lettings/pages/property_reference.rb index 14fafade2..150974ee8 100644 --- a/app/models/form/setup/pages/property_reference.rb +++ b/app/models/form/lettings/pages/property_reference.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::PropertyReference < ::Form::Page +class Form::Lettings::Pages::PropertyReference < ::Form::Page def initialize(id, hsh, subsection) super @id = "property_reference" @@ -9,7 +9,7 @@ class Form::Setup::Pages::PropertyReference < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::PropertyReference.new(nil, nil, self), + Form::Lettings::Questions::PropertyReference.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/renewal.rb b/app/models/form/lettings/pages/renewal.rb similarity index 63% rename from app/models/form/setup/pages/renewal.rb rename to app/models/form/lettings/pages/renewal.rb index 8a627d7c9..873ff8822 100644 --- a/app/models/form/setup/pages/renewal.rb +++ b/app/models/form/lettings/pages/renewal.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::Renewal < ::Form::Page +class Form::Lettings::Pages::Renewal < ::Form::Page def initialize(id, hsh, subsection) super @id = "renewal" @@ -9,7 +9,7 @@ class Form::Setup::Pages::Renewal < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::Renewal.new(nil, nil, self), + Form::Lettings::Questions::Renewal.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/rent_type.rb b/app/models/form/lettings/pages/rent_type.rb similarity index 51% rename from app/models/form/setup/pages/rent_type.rb rename to app/models/form/lettings/pages/rent_type.rb index 5e112c3c6..d1ac4de99 100644 --- a/app/models/form/setup/pages/rent_type.rb +++ b/app/models/form/lettings/pages/rent_type.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::RentType < ::Form::Page +class Form::Lettings::Pages::RentType < ::Form::Page def initialize(_id, hsh, subsection) super("rent_type", hsh, subsection) @header = "" @@ -8,8 +8,8 @@ class Form::Setup::Pages::RentType < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::RentType.new(nil, nil, self), - Form::Setup::Questions::IrproductOther.new(nil, nil, self), + Form::Lettings::Questions::RentType.new(nil, nil, self), + Form::Lettings::Questions::IrproductOther.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/scheme.rb b/app/models/form/lettings/pages/scheme.rb similarity index 66% rename from app/models/form/setup/pages/scheme.rb rename to app/models/form/lettings/pages/scheme.rb index e71a8d424..c17eaab04 100644 --- a/app/models/form/setup/pages/scheme.rb +++ b/app/models/form/lettings/pages/scheme.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::Scheme < ::Form::Page +class Form::Lettings::Pages::Scheme < ::Form::Page def initialize(_id, hsh, subsection) super("scheme", hsh, subsection) @header = "" @@ -10,7 +10,7 @@ class Form::Setup::Pages::Scheme < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::SchemeId.new(nil, nil, self), + Form::Lettings::Questions::SchemeId.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/tenancy_start_date.rb b/app/models/form/lettings/pages/tenancy_start_date.rb similarity index 59% rename from app/models/form/setup/pages/tenancy_start_date.rb rename to app/models/form/lettings/pages/tenancy_start_date.rb index 117ef8452..562153dc2 100644 --- a/app/models/form/setup/pages/tenancy_start_date.rb +++ b/app/models/form/lettings/pages/tenancy_start_date.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::TenancyStartDate < ::Form::Page +class Form::Lettings::Pages::TenancyStartDate < ::Form::Page def initialize(id, hsh, subsection) super @id = "tenancy_start_date" @@ -8,7 +8,7 @@ class Form::Setup::Pages::TenancyStartDate < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::TenancyStartDate.new(nil, nil, self), + Form::Lettings::Questions::TenancyStartDate.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/pages/tenant_code.rb b/app/models/form/lettings/pages/tenant_code.rb similarity index 62% rename from app/models/form/setup/pages/tenant_code.rb rename to app/models/form/lettings/pages/tenant_code.rb index f7b8350d1..67771514a 100644 --- a/app/models/form/setup/pages/tenant_code.rb +++ b/app/models/form/lettings/pages/tenant_code.rb @@ -1,4 +1,4 @@ -class Form::Setup::Pages::TenantCode < ::Form::Page +class Form::Lettings::Pages::TenantCode < ::Form::Page def initialize(id, hsh, subsection) super @id = "tenant_code" @@ -9,7 +9,7 @@ class Form::Setup::Pages::TenantCode < ::Form::Page def questions @questions ||= [ - Form::Setup::Questions::TenantCode.new(nil, nil, self), + Form::Lettings::Questions::TenantCode.new(nil, nil, self), ] end end diff --git a/app/models/form/setup/questions/irproduct_other.rb b/app/models/form/lettings/questions/irproduct_other.rb similarity index 74% rename from app/models/form/setup/questions/irproduct_other.rb rename to app/models/form/lettings/questions/irproduct_other.rb index 10c44d1fc..7607d6d9c 100644 --- a/app/models/form/setup/questions/irproduct_other.rb +++ b/app/models/form/lettings/questions/irproduct_other.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::IrproductOther < ::Form::Question +class Form::Lettings::Questions::IrproductOther < ::Form::Question def initialize(id, hsh, page) super @id = "irproduct_other" diff --git a/app/models/form/setup/questions/location_id.rb b/app/models/form/lettings/questions/location_id.rb similarity index 95% rename from app/models/form/setup/questions/location_id.rb rename to app/models/form/lettings/questions/location_id.rb index 58afbe011..cae6463a9 100644 --- a/app/models/form/setup/questions/location_id.rb +++ b/app/models/form/lettings/questions/location_id.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::LocationId < ::Form::Question +class Form::Lettings::Questions::LocationId < ::Form::Question def initialize(_id, hsh, page) super("location_id", hsh, page) @check_answer_label = "Location" diff --git a/app/models/form/setup/questions/needs_type.rb b/app/models/form/lettings/questions/needs_type.rb similarity index 90% rename from app/models/form/setup/questions/needs_type.rb rename to app/models/form/lettings/questions/needs_type.rb index 01c52b8cc..46949a2bf 100644 --- a/app/models/form/setup/questions/needs_type.rb +++ b/app/models/form/lettings/questions/needs_type.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::NeedsType < ::Form::Question +class Form::Lettings::Questions::NeedsType < ::Form::Question def initialize(id, hsh, page) super @id = "needstype" diff --git a/app/models/form/setup/questions/property_reference.rb b/app/models/form/lettings/questions/property_reference.rb similarity index 81% rename from app/models/form/setup/questions/property_reference.rb rename to app/models/form/lettings/questions/property_reference.rb index c52bf1a16..40517b9d3 100644 --- a/app/models/form/setup/questions/property_reference.rb +++ b/app/models/form/lettings/questions/property_reference.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::PropertyReference < ::Form::Question +class Form::Lettings::Questions::PropertyReference < ::Form::Question def initialize(id, hsh, page) super @id = "propcode" diff --git a/app/models/form/setup/questions/renewal.rb b/app/models/form/lettings/questions/renewal.rb similarity index 85% rename from app/models/form/setup/questions/renewal.rb rename to app/models/form/lettings/questions/renewal.rb index 53b68fb68..06b5f0ff4 100644 --- a/app/models/form/setup/questions/renewal.rb +++ b/app/models/form/lettings/questions/renewal.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::Renewal < ::Form::Question +class Form::Lettings::Questions::Renewal < ::Form::Question def initialize(id, hsh, page) super @id = "renewal" diff --git a/app/models/form/setup/questions/rent_type.rb b/app/models/form/lettings/questions/rent_type.rb similarity index 91% rename from app/models/form/setup/questions/rent_type.rb rename to app/models/form/lettings/questions/rent_type.rb index c69b1a100..c7c23c74b 100644 --- a/app/models/form/setup/questions/rent_type.rb +++ b/app/models/form/lettings/questions/rent_type.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::RentType < ::Form::Question +class Form::Lettings::Questions::RentType < ::Form::Question def initialize(id, hsh, page) super @id = "rent_type" diff --git a/app/models/form/setup/questions/scheme_id.rb b/app/models/form/lettings/questions/scheme_id.rb similarity index 96% rename from app/models/form/setup/questions/scheme_id.rb rename to app/models/form/lettings/questions/scheme_id.rb index 9a71e1559..064be8032 100644 --- a/app/models/form/setup/questions/scheme_id.rb +++ b/app/models/form/lettings/questions/scheme_id.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::SchemeId < ::Form::Question +class Form::Lettings::Questions::SchemeId < ::Form::Question def initialize(_id, hsh, page) super("scheme_id", hsh, page) @check_answer_label = "Scheme name" diff --git a/app/models/form/setup/questions/tenancy_start_date.rb b/app/models/form/lettings/questions/tenancy_start_date.rb similarity index 74% rename from app/models/form/setup/questions/tenancy_start_date.rb rename to app/models/form/lettings/questions/tenancy_start_date.rb index 8e05771f7..f7e2622ac 100644 --- a/app/models/form/setup/questions/tenancy_start_date.rb +++ b/app/models/form/lettings/questions/tenancy_start_date.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::TenancyStartDate < ::Form::Question +class Form::Lettings::Questions::TenancyStartDate < ::Form::Question def initialize(id, hsh, page) super @id = "startdate" diff --git a/app/models/form/setup/questions/tenant_code.rb b/app/models/form/lettings/questions/tenant_code.rb similarity index 82% rename from app/models/form/setup/questions/tenant_code.rb rename to app/models/form/lettings/questions/tenant_code.rb index e299a3991..dcbbb72a8 100644 --- a/app/models/form/setup/questions/tenant_code.rb +++ b/app/models/form/lettings/questions/tenant_code.rb @@ -1,4 +1,4 @@ -class Form::Setup::Questions::TenantCode < ::Form::Question +class Form::Lettings::Questions::TenantCode < ::Form::Question def initialize(id, hsh, page) super @id = "tenancycode" diff --git a/app/models/form/setup/sections/setup.rb b/app/models/form/lettings/sections/setup.rb similarity index 51% rename from app/models/form/setup/sections/setup.rb rename to app/models/form/lettings/sections/setup.rb index 9e41eb2bb..e278ad1e3 100644 --- a/app/models/form/setup/sections/setup.rb +++ b/app/models/form/lettings/sections/setup.rb @@ -1,10 +1,10 @@ -class Form::Sections::Setup < ::Form::Section +class Form::Lettings::Sections::Setup < ::Form::Section def initialize(id, hsh, form) super @id = "setup" @label = "Before you start" @description = "" @form = form - @subsections = [Form::Setup::Subsections::Setup.new(nil, nil, self)] + @subsections = [Form::Lettings::Subsections::Setup.new(nil, nil, self)] end end diff --git a/app/models/form/lettings/subsections/setup.rb b/app/models/form/lettings/subsections/setup.rb new file mode 100644 index 000000000..79d346599 --- /dev/null +++ b/app/models/form/lettings/subsections/setup.rb @@ -0,0 +1,37 @@ +class Form::Lettings::Subsections::Setup < ::Form::Subsection + def initialize(id, hsh, section) + super + @id = "setup" + @label = "Set up this lettings log" + @section = section + end + + def pages + @pages ||= [ + Form::Common::Pages::Organisation.new(nil, nil, self), + Form::Common::Pages::CreatedBy.new(nil, nil, self), + Form::Lettings::Pages::NeedsType.new(nil, nil, self), + Form::Lettings::Pages::Scheme.new(nil, nil, self), + Form::Lettings::Pages::Location.new(nil, nil, self), + Form::Lettings::Pages::Renewal.new(nil, nil, self), + Form::Lettings::Pages::TenancyStartDate.new(nil, nil, self), + Form::Lettings::Pages::RentType.new(nil, nil, self), + Form::Lettings::Pages::TenantCode.new(nil, nil, self), + Form::Lettings::Pages::PropertyReference.new(nil, nil, self), + ] + end + + def applicable_questions(lettings_log) + questions.select { |q| support_only_questions.include?(q.id) } + super + end + + def enabled?(_lettings_log) + true + end + +private + + def support_only_questions + %w[owning_organisation_id created_by_id].freeze + end +end diff --git a/app/models/form/page.rb b/app/models/form/page.rb index 031b812a6..3ee1932cc 100644 --- a/app/models/form/page.rb +++ b/app/models/form/page.rb @@ -18,10 +18,10 @@ class Form::Page delegate :form, to: :subsection - def routed_to?(lettings_log, _current_user) + def routed_to?(log, _current_user) return true unless depends_on || subsection.depends_on - subsection.enabled?(lettings_log) && form.depends_on_met(depends_on, lettings_log) + subsection.enabled?(log) && form.depends_on_met(depends_on, log) end def non_conditional_questions diff --git a/app/models/form/question.rb b/app/models/form/question.rb index fc05efe03..4478f63fd 100644 --- a/app/models/form/question.rb +++ b/app/models/form/question.rb @@ -44,31 +44,31 @@ class Form::Question delegate :subsection, to: :page delegate :form, to: :subsection - def answer_label(lettings_log) - return checkbox_answer_label(lettings_log) if type == "checkbox" - return lettings_log[id]&.to_formatted_s(:govuk_date).to_s if type == "date" + def answer_label(log) + return checkbox_answer_label(log) if type == "checkbox" + return log[id]&.to_formatted_s(:govuk_date).to_s if type == "date" - answer = label_from_value(lettings_log[id]) if lettings_log[id].present? - answer_label = [prefix, format_value(answer), suffix_label(lettings_log)].join("") if answer + answer = label_from_value(log[id]) if log[id].present? + answer_label = [prefix, format_value(answer), suffix_label(log)].join("") if answer return answer_label if answer_label - has_inferred_check_answers_value?(lettings_log) ? inferred_check_answers_value["value"] : "" + has_inferred_check_answers_value?(log) ? inferred_check_answers_value["value"] : "" end - def get_inferred_answers(lettings_log) + def get_inferred_answers(log) return [] unless inferred_answers - enabled_inferred_answers(inferred_answers, lettings_log).keys.map do |question_id| - question = form.get_question(question_id, lettings_log) + enabled_inferred_answers(inferred_answers, log).keys.map do |question_id| + question = form.get_question(question_id, log) if question.present? - question.label_from_value(lettings_log[question_id]) + question.label_from_value(log[question_id]) else - Array(question_id.to_s.split(".")).inject(lettings_log) { |log, method| log.present? ? log.public_send(*method) : "" } + Array(question_id.to_s.split(".")).inject(log) { |l, method| l.present? ? l.public_send(*method) : "" } end end end - def get_extra_check_answer_value(_lettings_log) + def get_extra_check_answer_value(_log) nil end @@ -76,59 +76,59 @@ class Form::Question !!readonly end - def enabled?(lettings_log) + def enabled?(log) return true if conditional_on.blank? - conditional_on.all? { |condition| evaluate_condition(condition, lettings_log) } + conditional_on.all? { |condition| evaluate_condition(condition, log) } end - def hidden_in_check_answers?(lettings_log, _current_user = nil) + def hidden_in_check_answers?(log, _current_user = nil) if hidden_in_check_answers.is_a?(Hash) - form.depends_on_met(hidden_in_check_answers["depends_on"], lettings_log) + form.depends_on_met(hidden_in_check_answers["depends_on"], log) else hidden_in_check_answers end end - def displayed_to_user?(lettings_log) - page.routed_to?(lettings_log, nil) && enabled?(lettings_log) + def displayed_to_user?(log) + page.routed_to?(log, nil) && enabled?(log) end def derived? !!derived end - def has_inferred_check_answers_value?(lettings_log) - return true if selected_answer_option_is_derived?(lettings_log) - return inferred_check_answers_value["condition"].values[0] == lettings_log[inferred_check_answers_value["condition"].keys[0]] if inferred_check_answers_value.present? + def has_inferred_check_answers_value?(log) + return true if selected_answer_option_is_derived?(log) + return inferred_check_answers_value["condition"].values[0] == log[inferred_check_answers_value["condition"].keys[0]] if inferred_check_answers_value.present? false end - def displayed_answer_options(lettings_log) + def displayed_answer_options(log) answer_options.select do |_key, val| - !val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], lettings_log) + !val.is_a?(Hash) || !val["depends_on"] || form.depends_on_met(val["depends_on"], log) end end - def action_text(lettings_log) - if has_inferred_check_answers_value?(lettings_log) + def action_text(log) + if has_inferred_check_answers_value?(log) "Change" elsif type == "checkbox" - answer_options.keys.any? { |key| value_is_yes?(lettings_log[key]) } ? "Change" : "Answer" + answer_options.keys.any? { |key| value_is_yes?(log[key]) } ? "Change" : "Answer" else - lettings_log[id].blank? ? "Answer" : "Change" + log[id].blank? ? "Answer" : "Change" end end - def action_href(lettings_log, page_id) - "/logs/#{lettings_log.id}/#{page_id.to_s.dasherize}?referrer=check_answers" + def action_href(log, page_id) + "/#{log.model_name.param_key.dasherize}s/#{log.id}/#{page_id.to_s.dasherize}?referrer=check_answers" end - def completed?(lettings_log) - return answer_options.keys.any? { |key| value_is_yes?(lettings_log[key]) } if type == "checkbox" + def completed?(log) + return answer_options.keys.any? { |key| value_is_yes?(log[key]) } if type == "checkbox" - lettings_log[id].present? || !lettings_log.respond_to?(id.to_sym) || has_inferred_display_value?(lettings_log) + log[id].present? || !log.respond_to?(id.to_sym) || has_inferred_display_value?(log) end def value_from_label(label) @@ -203,7 +203,7 @@ class Form::Question I18n.t("validations.not_answered", question: display_label.downcase) end - def suffix_label(lettings_log) + def suffix_label(log) return "" unless suffix return suffix if suffix.is_a?(String) @@ -213,7 +213,7 @@ class Form::Question condition = s["depends_on"] next unless condition - answer = lettings_log.send(condition.keys.first) + answer = log.send(condition.keys.first) if answer == condition.values.first label = s["label"] end @@ -239,10 +239,10 @@ class Form::Question resource.hint end - def answer_selected?(lettings_log, answer) + def answer_selected?(log, answer) return false unless type == "select" - lettings_log[id].to_s == answer.id.to_s + log[id].to_s == answer.id.to_s end def top_guidance? @@ -255,20 +255,20 @@ class Form::Question private - def selected_answer_option_is_derived?(lettings_log) - selected_option = answer_options&.dig(lettings_log[id].to_s.presence) - selected_option.is_a?(Hash) && selected_option["depends_on"] && form.depends_on_met(selected_option["depends_on"], lettings_log) + def selected_answer_option_is_derived?(log) + selected_option = answer_options&.dig(log[id].to_s.presence) + selected_option.is_a?(Hash) && selected_option["depends_on"] && form.depends_on_met(selected_option["depends_on"], log) end - def has_inferred_display_value?(lettings_log) - inferred_check_answers_value.present? && lettings_log[inferred_check_answers_value["condition"].keys.first] == inferred_check_answers_value["condition"].values.first + def has_inferred_display_value?(log) + inferred_check_answers_value.present? && log[inferred_check_answers_value["condition"].keys.first] == inferred_check_answers_value["condition"].values.first end - def checkbox_answer_label(lettings_log) + def checkbox_answer_label(log) answer = [] - return "Yes" if id == "declaration" && value_is_yes?(lettings_log["declaration"]) + return "Yes" if id == "declaration" && value_is_yes?(log["declaration"]) - answer_options.each { |key, options| value_is_yes?(lettings_log[key]) ? answer << options["value"] : nil } + answer_options.each { |key, options| value_is_yes?(log[key]) ? answer << options["value"] : nil } answer.join(", ") end @@ -282,21 +282,21 @@ private end end - def evaluate_condition(condition, lettings_log) + def evaluate_condition(condition, log) case page.questions.find { |q| q.id == condition[:from] }.type when "numeric" operator = condition[:cond][/[<>=]+/].to_sym operand = condition[:cond][/\d+/].to_i - lettings_log[condition[:from]].present? && lettings_log[condition[:from]].send(operator, operand) + log[condition[:from]].present? && log[condition[:from]].send(operator, operand) when "text", "radio", "select" - lettings_log[condition[:from]].present? && condition[:cond].include?(lettings_log[condition[:from]]) + log[condition[:from]].present? && condition[:cond].include?(log[condition[:from]]) else raise "Not implemented yet" end end - def enabled_inferred_answers(inferred_answers, lettings_log) - inferred_answers.filter { |_key, value| value.all? { |condition_key, condition_value| lettings_log[condition_key] == condition_value } } + def enabled_inferred_answers(inferred_answers, log) + inferred_answers.filter { |_key, value| value.all? { |condition_key, condition_value| log[condition_key] == condition_value } } end RADIO_YES_VALUE = { diff --git a/app/models/form/sales/pages/purchaser_code.rb b/app/models/form/sales/pages/purchaser_code.rb new file mode 100644 index 000000000..79723e0b0 --- /dev/null +++ b/app/models/form/sales/pages/purchaser_code.rb @@ -0,0 +1,15 @@ +class Form::Sales::Pages::PurchaserCode < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "purchaser_code" + @header = "" + @description = "" + @subsection = subsection + end + + def questions + @questions ||= [ + Form::Sales::Questions::PurchaserCode.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/sale_date.rb b/app/models/form/sales/pages/sale_date.rb new file mode 100644 index 000000000..518e9b5ff --- /dev/null +++ b/app/models/form/sales/pages/sale_date.rb @@ -0,0 +1,15 @@ +class Form::Sales::Pages::SaleDate < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "completion_date" + @header = "" + @description = "" + @subsection = subsection + end + + def questions + @questions ||= [ + Form::Sales::Questions::SaleDate.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/shared_ownership_type.rb b/app/models/form/sales/pages/shared_ownership_type.rb new file mode 100644 index 000000000..c90074bae --- /dev/null +++ b/app/models/form/sales/pages/shared_ownership_type.rb @@ -0,0 +1,18 @@ +class Form::Sales::Pages::SharedOwnershipType < ::Form::Page + def initialize(id, hsh, subsection) + super + @id = "shared_ownership_type" + @header = "" + @description = "" + @subsection = subsection + @depends_on = [{ + "ownershipsch" => 1, + }] + end + + def questions + @questions ||= [ + Form::Sales::Questions::SharedOwnershipType.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/questions/purchaser_code.rb b/app/models/form/sales/questions/purchaser_code.rb new file mode 100644 index 000000000..1f9551d05 --- /dev/null +++ b/app/models/form/sales/questions/purchaser_code.rb @@ -0,0 +1,12 @@ +class Form::Sales::Questions::PurchaserCode < ::Form::Question + def initialize(id, hsh, page) + super + @id = "purchid" + @check_answer_label = "Purchaser code" + @header = "What is the purchaser code?" + @hint_text = "This is how you usually refer to the purchaser on your own systems." + @type = "text" + @width = 10 + @page = page + end +end diff --git a/app/models/form/sales/questions/sale_date.rb b/app/models/form/sales/questions/sale_date.rb new file mode 100644 index 000000000..2b0a3d171 --- /dev/null +++ b/app/models/form/sales/questions/sale_date.rb @@ -0,0 +1,10 @@ +class Form::Sales::Questions::SaleDate < ::Form::Question + def initialize(id, hsh, page) + super + @id = "saledate" + @check_answer_label = "Sale completion date" + @header = "What is the sale completion date?" + @type = "date" + @page = page + end +end diff --git a/app/models/form/sales/questions/shared_ownership_type.rb b/app/models/form/sales/questions/shared_ownership_type.rb new file mode 100644 index 000000000..27d6ea074 --- /dev/null +++ b/app/models/form/sales/questions/shared_ownership_type.rb @@ -0,0 +1,22 @@ +class Form::Sales::Questions::SharedOwnershipType < ::Form::Question + def initialize(id, hsh, page) + super + @id = "type" + @check_answer_label = "Type of shared ownership sale" + @header = "What is the type of shared ownership sale?" + @hint_text = "A shared ownership sale is when the purchaser buys up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion" + @type = "radio" + @answer_options = ANSWER_OPTIONS + @page = page + end + + ANSWER_OPTIONS = { + "2" => { "value" => "Shared Ownership" }, + "24" => { "value" => "Old Persons Shared Ownership" }, + "18" => { "value" => "Social HomeBuy (shared ownership purchase)" }, + "16" => { "value" => "Home Ownership for people with Long Term Disabilities (HOLD)" }, + "28" => { "value" => "Rent to Buy - Shared Ownership" }, + "31" => { "value" => "Right to Shared Ownership" }, + "30" => { "value" => "Shared Ownership - 2021 model lease" }, + }.freeze +end diff --git a/app/models/form/sales/sections/setup.rb b/app/models/form/sales/sections/setup.rb new file mode 100644 index 000000000..d90c72036 --- /dev/null +++ b/app/models/form/sales/sections/setup.rb @@ -0,0 +1,10 @@ +class Form::Sales::Sections::Setup < ::Form::Section + def initialize(id, hsh, form) + super + @id = "setup" + @label = "Before you start" + @description = "" + @form = form + @subsections = [Form::Sales::Subsections::Setup.new(nil, nil, self)] || [] + end +end diff --git a/app/models/form/sales/subsections/setup.rb b/app/models/form/sales/subsections/setup.rb new file mode 100644 index 000000000..d72e44aa0 --- /dev/null +++ b/app/models/form/sales/subsections/setup.rb @@ -0,0 +1,18 @@ +class Form::Sales::Subsections::Setup < ::Form::Subsection + def initialize(id, hsh, section) + super + @id = "setup" + @label = "Set up this sales log" + @section = section + end + + def pages + @pages ||= [ + Form::Common::Pages::Organisation.new(nil, nil, self), + Form::Common::Pages::CreatedBy.new(nil, nil, self), + Form::Sales::Pages::SaleDate.new(nil, nil, self), + Form::Sales::Pages::PurchaserCode.new(nil, nil, self), + Form::Sales::Pages::SharedOwnershipType.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/setup/subsections/setup.rb b/app/models/form/setup/subsections/setup.rb deleted file mode 100644 index 7bc4a079d..000000000 --- a/app/models/form/setup/subsections/setup.rb +++ /dev/null @@ -1,37 +0,0 @@ -class Form::Subsections::Setup < ::Form::Subsection - def initialize(id, hsh, section) - super - @id = "setup" - @label = "Set up this lettings log" - @section = section - end - - def pages - @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::Scheme.new(nil, nil, self), - Form::Setup::Pages::Location.new(nil, nil, self), - Form::Setup::Pages::Renewal.new(nil, nil, self), - Form::Setup::Pages::TenancyStartDate.new(nil, nil, self), - Form::Setup::Pages::RentType.new(nil, nil, self), - Form::Setup::Pages::TenantCode.new(nil, nil, self), - Form::Setup::Pages::PropertyReference.new(nil, nil, self), - ] - end - - def applicable_questions(lettings_log) - questions.select { |q| support_only_questions.include?(q.id) } + super - end - - def enabled?(_lettings_log) - true - end - -private - - def support_only_questions - %w[owning_organisation_id created_by_id].freeze - end -end diff --git a/app/models/form/subsection.rb b/app/models/form/subsection.rb index d42f8dadb..d3ab0ddd2 100644 --- a/app/models/form/subsection.rb +++ b/app/models/form/subsection.rb @@ -17,40 +17,40 @@ class Form::Subsection @questions ||= pages.flat_map(&:questions) end - def enabled?(lettings_log) + def enabled?(log) return true unless depends_on depends_on.any? do |conditions_set| conditions_set.all? do |subsection_id, dependent_status| - form.get_subsection(subsection_id).status(lettings_log) == dependent_status.to_sym + form.get_subsection(subsection_id).status(log) == dependent_status.to_sym end end end - def status(lettings_log) - unless enabled?(lettings_log) + def status(log) + unless enabled?(log) return :cannot_start_yet end - qs = applicable_questions(lettings_log) - qs_optional_removed = qs.reject { |q| lettings_log.optional_fields.include?(q.id) } - return :not_started if qs.count.positive? && qs.all? { |question| lettings_log[question.id].blank? || question.read_only? || question.derived? } - return :completed if qs_optional_removed.all? { |question| question.completed?(lettings_log) } + qs = applicable_questions(log) + qs_optional_removed = qs.reject { |q| log.optional_fields.include?(q.id) } + return :not_started if qs.count.positive? && qs.all? { |question| log[question.id].blank? || question.read_only? || question.derived? } + return :completed if qs_optional_removed.all? { |question| question.completed?(log) } :in_progress end - def is_incomplete?(lettings_log) - %i[not_started in_progress].include?(status(lettings_log)) + def is_incomplete?(log) + %i[not_started in_progress].include?(status(log)) end - def is_started?(lettings_log) - %i[in_progress completed].include?(status(lettings_log)) + def is_started?(log) + %i[in_progress completed].include?(status(log)) end - def applicable_questions(lettings_log) + def applicable_questions(log) questions.select do |q| - (q.displayed_to_user?(lettings_log) && !q.derived?) || q.has_inferred_check_answers_value?(lettings_log) + (q.displayed_to_user?(log) && !q.derived?) || q.has_inferred_check_answers_value?(log) end end end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index 13efcb9ca..40d993cd6 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -10,23 +10,52 @@ class FormHandler @forms[form] end - def current_form - forms[forms.keys.max_by(&:to_i)] + def current_lettings_form + forms["current_lettings"] end -private + def current_sales_form + forms["current_sales"] + end - def get_all_forms + def sales_forms + sales_sections = [] # Add section classes here e.g. Form::Sales::Property::Sections::PropertyInformation + current_form = Form.new(nil, current_collection_start_year, sales_sections, "sales") + previous_form = Form.new(nil, current_collection_start_year - 1, sales_sections, "sales") + { "current_sales" => current_form, + "previous_sales" => previous_form } + end + + def lettings_forms forms = {} directories.each do |directory| Dir.glob("#{directory}/*.json").each do |form_path| - form_name = File.basename(form_path, ".json") - forms[form_name] = Form.new(form_path, form_name) + form = Form.new(form_path) + + form_to_set = form_name_from_start_year(form.start_date.year, "lettings") + forms[form_to_set] = form if forms[form_to_set].blank? end end forms end + def current_collection_start_year + today = Time.zone.now + window_end_date = Time.zone.local(today.year, 4, 1) + today < window_end_date ? today.year - 1 : today.year + end + + def form_name_from_start_year(year, type) + form_mappings = { 0 => "current_#{type}", 1 => "previous_#{type}", -1 => "next_#{type}" } + form_mappings[current_collection_start_year - year] + end + +private + + def get_all_forms + lettings_forms.merge(sales_forms) + end + def directories Rails.env.test? ? ["spec/fixtures/forms"] : ["config/forms"] end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 142c48e52..c85f2d0c3 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -14,7 +14,7 @@ class LettingsLogValidator < ActiveModel::Validator end end -class LettingsLog < ApplicationRecord +class LettingsLog < Log include Validations::SoftValidations include DerivedVariables::LettingsLogVariables @@ -29,31 +29,11 @@ class LettingsLog < ApplicationRecord before_validation :reset_location_fields!, unless: :postcode_known? before_validation :reset_previous_location_fields!, unless: :previous_postcode_known? before_validation :set_derived_fields! - before_save :update_status! - 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 belongs_to :scheme, optional: true belongs_to :location, 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: } - scope :filter_by_years, lambda { |years, _user = nil| - first_year = years.shift - query = filter_by_year(first_year) - years.each { |year| query = query.or(filter_by_year(year)) } - query.all - } scope :filter_by_year, ->(year) { where(startdate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) } - - scope :filter_by_user, lambda { |selected_user, user| - if !selected_user.include?("all") && user.present? - where(created_by: user) - end - } - - scope :filter_by_id, ->(id) { where(id:) } scope :filter_by_tenant_code, ->(tenant_code) { where("tenancycode ILIKE ?", "%#{tenant_code}%") } scope :filter_by_propcode, ->(propcode) { where("propcode ILIKE ?", "%#{propcode}%") } scope :filter_by_postcode, ->(postcode_full) { where("REPLACE(postcode_full, ' ', '') ILIKE ?", "%#{postcode_full.delete(' ')}%") } @@ -70,22 +50,12 @@ class LettingsLog < ApplicationRecord OPTIONAL_FIELDS = %w[first_time_property_let_as_social_housing tenancycode propcode].freeze RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze - STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52 }.freeze SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze RETIREMENT_AGES = { "M" => 67, "F" => 60, "X" => 67 }.freeze - enum status: STATUS def form - FormHandler.instance.get_form(form_name) || FormHandler.instance.forms.first.second - end - - def collection_start_year - return @start_year if @start_year - return unless startdate - - window_end_date = Time.zone.local(startdate.year, 4, 1) - @start_year = startdate < window_end_date ? startdate.year - 1 : startdate.year + FormHandler.instance.get_form(form_name) || FormHandler.instance.current_lettings_form end def recalculate_start_year! @@ -96,7 +66,7 @@ class LettingsLog < ApplicationRecord def form_name return unless startdate - "#{collection_start_year}_#{collection_start_year + 1}" + FormHandler.instance.form_name_from_start_year(collection_start_year, "lettings") end def self.editable_fields @@ -527,44 +497,14 @@ class LettingsLog < ApplicationRecord location.type_of_unit_before_type_cast if location end + def lettings? + true + end + private PIO = PostcodeService.new - def update_status! - self.status = if all_fields_completed? && errors.empty? - "completed" - elsif all_fields_nil? - "not_started" - else - "in_progress" - end - end - - def reset_not_routed_questions - enabled_questions = form.enabled_page_questions(self) - enabled_question_ids = enabled_questions.map(&:id) - - form.invalidated_page_questions(self).each do |question| - if %w[radio checkbox].include?(question.type) - 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 - end - def reset_derived_questions dependent_questions = { waityear: [{ key: :renewal, value: 0 }], referral: [{ key: :renewal, value: 0 }], @@ -582,12 +522,6 @@ private end end - def reset_created_by - return unless created_by && owning_organisation - - self.created_by = nil if created_by.organisation != owning_organisation - end - def reset_scheme return unless scheme && owning_organisation @@ -595,11 +529,10 @@ private end def reset_invalidated_dependent_fields! - return unless form + super reset_created_by reset_scheme - reset_not_routed_questions reset_derived_questions end @@ -692,17 +625,6 @@ private end end - def all_fields_completed? - subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq - subsection_statuses == [:completed] - end - - def all_fields_nil? - not_started_statuses = %i[not_started cannot_start_yet] - subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq - subsection_statuses.all? { |status| not_started_statuses.include?(status) } - end - def age_refused? [age1_known, age2_known, age3_known, age4_known, age5_known, age6_known, age7_known, age8_known].any?(1) end diff --git a/app/models/log.rb b/app/models/log.rb new file mode 100644 index 000000000..e24a205fc --- /dev/null +++ b/app/models/log.rb @@ -0,0 +1,73 @@ +class Log < ApplicationRecord + self.abstract_class = true + + 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 + before_save :update_status! + + STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze + enum status: STATUS + + 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: } + scope :filter_by_years, lambda { |years, _user = nil| + first_year = years.shift + query = filter_by_year(first_year) + years.each { |year| query = query.or(filter_by_year(year)) } + query.all + } + scope :filter_by_id, ->(id) { where(id:) } + scope :filter_by_user, lambda { |selected_user, user| + if !selected_user.include?("all") && user.present? + where(created_by: user) + end + } + + def collection_start_year + return @start_year if @start_year + return unless startdate + + window_end_date = Time.zone.local(startdate.year, 4, 1) + @start_year = startdate < window_end_date ? startdate.year - 1 : startdate.year + end + + def lettings? + false + end + +private + + def update_status! + self.status = if all_fields_completed? && errors.empty? + "completed" + elsif all_fields_nil? + "not_started" + else + "in_progress" + end + end + + def all_fields_completed? + subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq + subsection_statuses == [:completed] + end + + def all_fields_nil? + not_started_statuses = %i[not_started cannot_start_yet] + subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq + subsection_statuses.all? { |status| not_started_statuses.include?(status) } + end + + def reset_created_by + return unless created_by && owning_organisation + + self.created_by = nil if created_by.organisation != owning_organisation + end + + def reset_invalidated_dependent_fields! + return unless form + + form.reset_not_routed_questions(self) + end +end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index afaf6de35..9eb039ab1 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -2,6 +2,8 @@ class Organisation < ApplicationRecord has_many :users, dependent: :delete_all has_many :owned_lettings_logs, class_name: "LettingsLog", foreign_key: "owning_organisation_id", dependent: :delete_all has_many :managed_lettings_logs, class_name: "LettingsLog", foreign_key: "managing_organisation_id" + has_many :owned_sales_logs, class_name: "SalesLog", foreign_key: "owning_organisation_id", dependent: :delete_all + has_many :managed_sales_logs, class_name: "SalesLog", foreign_key: "managing_organisation_id" has_many :data_protection_confirmations has_many :organisation_rent_periods has_many :owned_schemes, class_name: "Scheme", foreign_key: "owning_organisation_id", dependent: :delete_all @@ -32,6 +34,10 @@ class Organisation < ApplicationRecord LettingsLog.filter_by_organisation(self) end + def sales_logs + SalesLog.filter_by_organisation(self) + end + def completed_lettings_logs lettings_logs.completed end diff --git a/app/models/rent_period.rb b/app/models/rent_period.rb index 77e154f4c..d48fe20ea 100644 --- a/app/models/rent_period.rb +++ b/app/models/rent_period.rb @@ -1,5 +1,5 @@ class RentPeriod def self.rent_period_mappings - FormHandler.instance.current_form.get_question("period", nil).answer_options + FormHandler.instance.current_lettings_form.get_question("period", nil).answer_options end end diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb new file mode 100644 index 000000000..a2a6dcff3 --- /dev/null +++ b/app/models/sales_log.rb @@ -0,0 +1,45 @@ +class SalesLogValidator < ActiveModel::Validator + def validate(record); end +end + +class SalesLog < Log + self.inheritance_column = :_type_disabled + has_paper_trail + + validates_with SalesLogValidator + + scope :filter_by_year, ->(year) { where(saledate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) } + scope :search_by, ->(param) { filter_by_id(param) } + + OPTIONAL_FIELDS = [].freeze + + def startdate + saledate + end + + def self.editable_fields + attribute_names + end + + def form_name + return unless saledate + + FormHandler.instance.form_name_from_start_year(collection_start_year, "sales") + end + + def form + FormHandler.instance.get_form(form_name) || FormHandler.instance.current_sales_form + end + + def optional_fields + [] + end + + def not_started? + status == "not_started" + end + + def completed? + status == "completed" + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 22cc14b77..56dd2821f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,8 +7,10 @@ class User < ApplicationRecord # Marked as optional because we validate organisation_id below instead so that # the error message is linked to the right field on the form belongs_to :organisation, optional: true - has_many :owned_lettings_logs, through: :organisation, dependent: :delete_all + has_many :owned_lettings_logs, through: :organisation has_many :managed_lettings_logs, through: :organisation + has_many :owned_sales_logs, through: :organisation + has_many :managed_sales_logs, through: :organisation validates :name, presence: true validates :email, presence: true @@ -58,6 +60,14 @@ class User < ApplicationRecord end end + def sales_logs + if support? + SalesLog.all + else + SalesLog.filter_by_organisation(organisation) + end + end + def completed_lettings_logs lettings_logs.completed end @@ -131,7 +141,7 @@ class User < ApplicationRecord ROLES.except(:support) end - def lettings_logs_filters(specific_org: false) + def logs_filters(specific_org: false) if support? && !specific_org %w[status years user organisation] else diff --git a/app/models/validations/date_validations.rb b/app/models/validations/date_validations.rb index 8f8d87220..f5361e5d7 100644 --- a/app/models/validations/date_validations.rb +++ b/app/models/validations/date_validations.rb @@ -60,19 +60,19 @@ module Validations::DateValidations private def first_collection_start_date - @first_collection_start_date ||= FormHandler.instance.forms.map { |form| form.second.start_date }.compact.min + @first_collection_start_date ||= FormHandler.instance.forms.map { |_name, form| form.start_date }.compact.min end def first_collection_end_date - @first_collection_end_date ||= FormHandler.instance.forms.map { |form| form.second.end_date }.compact.min + @first_collection_end_date ||= FormHandler.instance.forms.map { |_name, form| form.end_date }.compact.min end def second_collection_start_date - @second_collection_start_date ||= FormHandler.instance.forms.map { |form| form.second.start_date }.compact.max + @second_collection_start_date ||= FormHandler.instance.forms.map { |_name, form| form.start_date }.compact.max end def second_collection_end_date - @second_collection_end_date ||= FormHandler.instance.forms.map { |form| form.second.end_date }.compact.max + @second_collection_end_date ||= FormHandler.instance.forms.map { |_name, form| form.end_date }.compact.max end def date_valid?(question, record) diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb index 6070e91c2..101cd7e6c 100644 --- a/app/services/csv/lettings_log_csv_service.rb +++ b/app/services/csv/lettings_log_csv_service.rb @@ -51,7 +51,13 @@ module Csv def ordered_form_questions downloaded_form_years = LettingsLog.all.map(&:collection_start_year).uniq.compact - downloaded_form_fields = downloaded_form_years.count == 1 && downloaded_form_years[0].present? ? FormHandler.instance.get_form("#{downloaded_form_years[0]}_#{downloaded_form_years[0] + 1}").questions : FormHandler.instance.forms.first.second.questions + + if downloaded_form_years.count == 1 && downloaded_form_years[0].present? + form_name = FormHandler.instance.form_name_from_start_year(downloaded_form_years[0], "lettings") + downloaded_form_fields = FormHandler.instance.get_form(form_name).questions + else + downloaded_form_fields = FormHandler.instance.current_lettings_form.questions + end move_checkbox_answer_options(downloaded_form_fields) end diff --git a/app/services/filter_service.rb b/app/services/filter_service.rb new file mode 100644 index 000000000..8fe1030de --- /dev/null +++ b/app/services/filter_service.rb @@ -0,0 +1,22 @@ +class FilterService + def self.filter_by_search(base_collection, search_term = nil) + if search_term.present? + base_collection.search_by(search_term) + else + base_collection + end + end + + def self.filter_logs(logs, search_term, filters, all_orgs, user) + logs = filter_by_search(logs, search_term) + + filters.each do |category, values| + next if Array(values).reject(&:empty?).blank? + next if category == "organisation" && all_orgs + + logs = logs.public_send("filter_by_#{category}", values, user) + end + logs = logs.order(created_at: :desc) + user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs + end +end diff --git a/app/services/storage/s3_service.rb b/app/services/storage/s3_service.rb index aee76398d..c972225a1 100644 --- a/app/services/storage/s3_service.rb +++ b/app/services/storage/s3_service.rb @@ -20,6 +20,12 @@ module Storage response.key_count == 1 end + def get_presigned_url(file_name, duration) + Aws::S3::Presigner + .new({ client: @client }) + .presigned_url(:get_object, bucket: @configuration.bucket_name, key: file_name, expires_in: duration) + end + def get_file_io(file_name) @client.get_object(bucket: @configuration.bucket_name, key: file_name) .body diff --git a/app/views/form/_check_answers_summary_list.html.erb b/app/views/form/_check_answers_summary_list.html.erb index 85c29beca..68a9cf00a 100644 --- a/app/views/form/_check_answers_summary_list.html.erb +++ b/app/views/form/_check_answers_summary_list.html.erb @@ -1,21 +1,21 @@ <%= govuk_summary_list do |summary_list| %> - <% total_applicable_questions(subsection, @lettings_log, current_user).each do |question| %> + <% total_applicable_questions(subsection, @log, current_user).each do |question| %> <% summary_list.row do |row| %> <% row.key { question.check_answer_label.to_s.presence || question.header.to_s } %> <% row.value do %> - <%= get_answer_label(question, @lettings_log) %> - <% extra_value = question.get_extra_check_answer_value(@lettings_log) %> + <%= get_answer_label(question, @log) %> + <% extra_value = question.get_extra_check_answer_value(@log) %> <% if extra_value %> <%= extra_value %> <% end %>
- <% question.get_inferred_answers(@lettings_log).each do |inferred_answer| %> + <% question.get_inferred_answers(@log).each do |inferred_answer| %> <%= inferred_answer %> <% end %> <% end %> <% row.action( - text: question.action_text(@lettings_log), - href: question.action_href(@lettings_log, question.page.id), + text: question.action_text(@log), + href: question.action_href(@log, question.page.id), visually_hidden_text: question.check_answer_label.to_s.downcase, ) %> <% end %> diff --git a/app/views/form/_checkbox_question.html.erb b/app/views/form/_checkbox_question.html.erb index 245056c39..c855e2d88 100644 --- a/app/views/form/_checkbox_question.html.erb +++ b/app/views/form/_checkbox_question.html.erb @@ -6,7 +6,7 @@ hint: { text: question.hint_text&.html_safe } do %> <% after_divider = false %> - <% question.displayed_answer_options(@lettings_log).map do |key, options| %> + <% question.displayed_answer_options(@log).map do |key, options| %> <% if key.starts_with?("divider") %> <% after_divider = true %> <%= f.govuk_check_box_divider %> @@ -14,7 +14,7 @@ <%= f.govuk_check_box question.id, key, label: { text: options["value"] }, hint: { text: options["hint"] }, - checked: @lettings_log[key] == 1, + checked: @log[key] == 1, exclusive: after_divider, **stimulus_html_attributes(question) %> <% end %> diff --git a/app/views/form/_numeric_question.html.erb b/app/views/form/_numeric_question.html.erb index fc6aa274a..0aeb63801 100644 --- a/app/views/form/_numeric_question.html.erb +++ b/app/views/form/_numeric_question.html.erb @@ -8,7 +8,7 @@ width: question.width, readonly: question.read_only?, prefix_text: question.prefix.to_s, - suffix_text: question.suffix_label(@lettings_log), + suffix_text: question.suffix_label(@log), **stimulus_html_attributes(question) %> <%= render partial: "form/guidance/#{question.guidance_partial}" if question.bottom_guidance? %> diff --git a/app/views/form/_radio_question.html.erb b/app/views/form/_radio_question.html.erb index f153a838b..ed5e71a0b 100644 --- a/app/views/form/_radio_question.html.erb +++ b/app/views/form/_radio_question.html.erb @@ -5,7 +5,7 @@ legend: legend(question, page_header, conditional), hint: { text: question.hint_text&.html_safe } do %> - <% question.displayed_answer_options(@lettings_log).map do |key, options| %> + <% question.displayed_answer_options(@log).map do |key, options| %> <% if key.starts_with?("divider") %> <%= f.govuk_radio_divider %> <% else %> diff --git a/app/views/form/_select_question.html.erb b/app/views/form/_select_question.html.erb index fbdb20e9a..63f50fb94 100644 --- a/app/views/form/_select_question.html.erb +++ b/app/views/form/_select_question.html.erb @@ -1,7 +1,7 @@ <%= render partial: "form/guidance/#{question.guidance_partial}" if question.top_guidance? %> -<% selected = @lettings_log.public_send(question.id) || "" %> -<% answers = question.displayed_answer_options(@lettings_log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %> +<% selected = @log.public_send(question.id) || "" %> +<% answers = question.displayed_answer_options(@log).map { |key, value| OpenStruct.new(id: key, name: value.respond_to?(:service_name) ? value.service_name : nil, resource: value) } %> <%= f.govuk_select(question.id.to_sym, label: legend(question, page_header, conditional), "data-controller": "accessible-autocomplete", @@ -12,7 +12,7 @@ data-synonyms="<%= question.answer_option_synonyms(answer.resource) %>" data-append="<%= question.answer_option_append(answer.resource) %>" data-hint="<%= question.answer_option_hint(answer.resource) %>" - <%= question.answer_selected?(@lettings_log, answer) ? "selected" : "" %> + <%= question.answer_selected?(@log, answer) ? "selected" : "" %> <%= answer.id == "" ? "disabled" : "" %>><%= answer.name || answer.resource %> <% end %> <% end %> diff --git a/app/views/form/check_answers.html.erb b/app/views/form/check_answers.html.erb index a2bcb09bc..46a945017 100644 --- a/app/views/form/check_answers.html.erb +++ b/app/views/form/check_answers.html.erb @@ -1,9 +1,9 @@ <% content_for :title, "#{subsection.id.humanize} - Check your answers" %> <% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: { "Logs" => "/logs", - "Log #{@lettings_log.id}" => "/logs/#{@lettings_log.id}", + "Log #{@log.id}" => send("#{@log.class.name.underscore}_path", @log), subsection.label => "", -}) %> + }) %>

@@ -12,27 +12,27 @@ Check your answers - <% if subsection.id == "setup" && subsection.status(@lettings_log) == :completed %> + <% if subsection.id == "setup" && subsection.status(@log) == :completed %> <%= govuk_inset_text(text: "Changing these answers might remove answers you’ve already given in other sections.") %> <% end %> - <%= display_answered_questions_summary(subsection, @lettings_log, current_user) %> + <%= display_answered_questions_summary(subsection, @log, current_user) %> - <% if any_questions_have_summary_card_number?(subsection, @lettings_log) %> - <% subsection.applicable_questions(@lettings_log).group_by(&:check_answers_card_number).values.each do |question_group| %> - <%= render CheckAnswersSummaryListCardComponent.new(questions: question_group, lettings_log: @lettings_log, user: current_user) %> + <% if any_questions_have_summary_card_number?(subsection, @log) %> + <% subsection.applicable_questions(@log).group_by(&:check_answers_card_number).values.each do |question_group| %> + <%= render CheckAnswersSummaryListCardComponent.new(questions: question_group, log: @log, user: current_user) %> <% end %> <% else %> <%= render partial: "form/check_answers_summary_list", locals: { subsection:, - lettings_log: @lettings_log, + lettings_log: @log, } %> <% end %> - <%= form_with model: @lettings_log, method: "get" do |f| %> + <%= form_with model: @log, method: "get" do |f| %> <%= f.govuk_submit "Save and return to log" do %> - <% next_incomplete_section_redirect_path = @lettings_log.form.next_incomplete_section_redirect_path(subsection, @lettings_log) %> - <% if @lettings_log.status == "in_progress" && next_incomplete_section_redirect_path != "error" %> - <%= govuk_button_link_to "Save and go to next incomplete section", "/logs/#{@lettings_log.id}/#{next_incomplete_section_redirect_path}", secondary: true %> + <% next_incomplete_section_redirect_path = @log.form.next_incomplete_section_redirect_path(subsection, @log) %> + <% if @log.status == "in_progress" && next_incomplete_section_redirect_path != "error" %> + <%= govuk_button_link_to "Save and go to next incomplete section", send(next_incomplete_section_path(@log, next_incomplete_section_redirect_path), @log), secondary: true %> <% end %> <% end %> <% end %> diff --git a/app/views/form/page.html.erb b/app/views/form/page.html.erb index 0fe72f647..8d90f12b9 100644 --- a/app/views/form/page.html.erb +++ b/app/views/form/page.html.erb @@ -5,10 +5,10 @@ <% end %>
-<%= form_with model: @lettings_log, url: form_lettings_log_path(@lettings_log), method: "post", local: true do |f| %> +<%= form_with model: @log, url: form_lettings_log_path(@log), method: "post", local: true do |f| %>
"> - <% remove_other_page_errors(@lettings_log, @page) %> + <% remove_other_page_errors(@log, @page) %> <%= f.govuk_error_summary %> <% if @page.header.present? %> @@ -30,9 +30,9 @@ <%= govuk_section_break(visible: true, size: "m") %> <% end %> <% if question.type == "interruption_screen" %> - <%= render partial: "form/#{question.type}_question", locals: { question:, caption_text: @subsection.label, page_header: @page.header, lettings_log: @lettings_log, title_text: @page.title_text, informative_text: @page.informative_text, form: @form, f:, conditional: false } %> + <%= render partial: "form/#{question.type}_question", locals: { question:, caption_text: @subsection.label, page_header: @page.header, lettings_log: @log, title_text: @page.title_text, informative_text: @page.informative_text, form: @form, f:, conditional: false } %> <% else %> - <%= render partial: "form/#{question.type}_question", locals: { question:, caption_text: @subsection.label, page_header: @page.header, lettings_log: @lettings_log, f:, conditional: false } %> + <%= render partial: "form/#{question.type}_question", locals: { question:, caption_text: @subsection.label, page_header: @page.header, lettings_log: @log, f:, conditional: false } %> <% end %>
<% end %> @@ -42,10 +42,10 @@
<% if !@page.id.include?("value_check") && if request.query_parameters["referrer"] != "check_answers" %> <%= f.govuk_submit "Save and continue" %> - <%= govuk_link_to "Skip for now", send(@lettings_log.form.next_page_redirect_path(@page, @lettings_log, current_user), @lettings_log) %> + <%= govuk_link_to "Skip for now", send(@log.form.next_page_redirect_path(@page, @log, current_user), @log) %> <% else %> <%= f.govuk_submit "Save changes" %> - <%= govuk_link_to "Cancel", "/logs/#{@lettings_log.id}/setup/check-answers" %> + <%= govuk_link_to "Cancel", send(@log.form.cancel_path(@page, @log), @log) %> <% end %> <% end %>
diff --git a/app/views/form/review.html.erb b/app/views/form/review.html.erb index 55a693913..59e0bde40 100644 --- a/app/views/form/review.html.erb +++ b/app/views/form/review.html.erb @@ -1,7 +1,7 @@ <% content_for :title, "Review lettings log" %> <% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: { "Logs" => "/logs", - "Log #{@lettings_log.id}" => "/logs/#{@lettings_log.id}", + "Log #{@log.id}" => "/logs/#{@log.id}", "Review lettings log" => "", }) %> @@ -11,9 +11,9 @@ <%= content_for(:title) %>

- You can review and make changes to this log until 2nd June <%= @lettings_log.collection_start_year.present? ? @lettings_log.collection_start_year + 1 : "" %>. + You can review and make changes to this log until 2nd June <%= @log.collection_start_year.present? ? @log.collection_start_year + 1 : "" %>.

- <% @lettings_log.form.sections.map do |section| %> + <% @log.form.sections.map do |section| %>

<%= section.label %>

<% section.subsections.map do |subsection| %>
@@ -23,7 +23,7 @@
<%= render partial: "form/check_answers_summary_list", locals: { subsection:, - lettings_log: @lettings_log, + lettings_log: @log, } %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 5dc643e4e..f6111fce5 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,7 +1,7 @@ - <%= browser_title(yield(:title), @pagy, @admin_user, @user, @organisation, @lettings_log, @resource) %> + <%= browser_title(yield(:title), @pagy, @admin_user, @user, @organisation, @log, @resource) %> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= tag.meta name: "viewport", content: "width=device-width, initial-scale=1" %> @@ -34,7 +34,7 @@ <% end %> - <% if Rails.env.production? %> + <% if Rails.env.production? && ENV["APP_HOST"].present? %> <% end %> diff --git a/app/views/lettings_logs/_log_filters.erb b/app/views/logs/_log_filters.erb similarity index 95% rename from app/views/lettings_logs/_log_filters.erb rename to app/views/logs/_log_filters.erb index b0f3e07be..9defa0c84 100644 --- a/app/views/lettings_logs/_log_filters.erb +++ b/app/views/logs/_log_filters.erb @@ -10,7 +10,7 @@ <%= render partial: "filters/checkbox_filter", locals: { f: f, options: years, label: "Collection year", category: "years" } %> <%= render partial: "filters/checkbox_filter", locals: { f: f, options: status_filters, label: "Status", category: "status" } %> <%= render partial: "filters/radio_filter", locals: { f: f, options: all_or_yours, label: "Logs", category: "user", } %> - <% if @current_user.support? && request.path == "/logs" %> + <% if @current_user.support? && request.path == "/lettings-logs" %> <%= render partial: "filters/radio_filter", locals: { f: f, options: { diff --git a/app/views/lettings_logs/_log_list.html.erb b/app/views/logs/_log_list.html.erb similarity index 67% rename from app/views/lettings_logs/_log_list.html.erb rename to app/views/logs/_log_list.html.erb index 93d365c40..702cd7d1f 100644 --- a/app/views/lettings_logs/_log_list.html.erb +++ b/app/views/logs/_log_list.html.erb @@ -1,8 +1,7 @@

<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> - <%= govuk_link_to "Download (CSV)", "#{request.path}.csv", type: "text/csv" %> + <%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv" %>

- -<% lettings_logs.map do |log| %> +<% logs.map do |log| %> <%= render(LogSummaryComponent.new(current_user:, log:)) %> <% end %> diff --git a/app/views/lettings_logs/_tasklist.html.erb b/app/views/logs/_tasklist.html.erb similarity index 68% rename from app/views/lettings_logs/_tasklist.html.erb rename to app/views/logs/_tasklist.html.erb index 03e712889..6c82eb377 100644 --- a/app/views/lettings_logs/_tasklist.html.erb +++ b/app/views/logs/_tasklist.html.erb @@ -1,5 +1,5 @@
    - <% @lettings_log.form.sections.map do |section| %> + <% @log.form.sections.map do |section| %>
  1. <%= section.label %> @@ -9,11 +9,11 @@ <% end %>
      <% section.subsections.map do |subsection| %> - <% if subsection.applicable_questions(@lettings_log).count > 0 || !subsection.enabled?(@lettings_log) %> - <% subsection_status = subsection.status(@lettings_log) %> + <% if subsection.applicable_questions(@log).count > 0 || !subsection.enabled?(@log) %> + <% subsection_status = subsection.status(@log) %>
    • - <%= subsection_link(subsection, @lettings_log, current_user) %> + <%= subsection_link(subsection, @log, current_user) %> <%= status_tag(subsection_status, "app-task-list__tag") %>
    • diff --git a/app/views/lettings_logs/bulk_upload.html.erb b/app/views/logs/bulk_upload.html.erb similarity index 100% rename from app/views/lettings_logs/bulk_upload.html.erb rename to app/views/logs/bulk_upload.html.erb diff --git a/app/views/logs/csv_confirmation.html.erb b/app/views/logs/csv_confirmation.html.erb new file mode 100644 index 000000000..90f90813c --- /dev/null +++ b/app/views/logs/csv_confirmation.html.erb @@ -0,0 +1,15 @@ +<% content_for :title, "We’re sending you an email" %> +
      +
      + <%= govuk_panel(title_text: "We’re sending you an email") %> + +

      It should arrive in a few minutes, but it could take longer.

      + +

      What happens next

      +

      Open your email inbox and click the link to download your CSV file.

      + +

      + <%= govuk_link_to "Return to logs", lettings_logs_path %> +

      +
      +
      diff --git a/app/views/logs/download_csv.html.erb b/app/views/logs/download_csv.html.erb new file mode 100644 index 000000000..4e0da2704 --- /dev/null +++ b/app/views/logs/download_csv.html.erb @@ -0,0 +1,16 @@ +<% content_for :title, "Download CSV" %> + +<% content_for :before_content do %> + <%= govuk_back_link(href: :back) %> +<% end %> + +
      +
      +

      Download CSV

      + +

      We'll send a secure download link to your email address <%= @current_user.email %>.

      +

      You've selected <%= count %> logs.

      + + <%= govuk_button_to "Send email", post_path, method: :post, params: { search: search_term } %> +
      +
      diff --git a/app/views/lettings_logs/edit.html.erb b/app/views/logs/edit.html.erb similarity index 63% rename from app/views/lettings_logs/edit.html.erb rename to app/views/logs/edit.html.erb index acfbba053..52943e539 100644 --- a/app/views/lettings_logs/edit.html.erb +++ b/app/views/logs/edit.html.erb @@ -1,6 +1,6 @@ -<% content_for :title, "Log #{@lettings_log.id}" %> +<% content_for :title, "Log #{@log.id}" %> <% content_for :breadcrumbs, govuk_breadcrumbs(breadcrumbs: { - "Logs" => "/logs", + "Logs" => @log.lettings? ? lettings_logs_path : sales_logs_path, content_for(:title) => "", }) %> @@ -10,10 +10,10 @@ <%= content_for(:title) %>

    - <% if @lettings_log.status == "in_progress" %> -

    <%= get_subsections_count(@lettings_log, :completed) %> of <%= get_subsections_count(@lettings_log, :all) %> sections completed.

    + <% if @log.status == "in_progress" %> +

    <%= get_subsections_count(@log, :completed) %> of <%= get_subsections_count(@log, :all) %> sections completed.

    - <% next_incomplete_section = get_next_incomplete_section(@lettings_log) %> + <% next_incomplete_section = get_next_incomplete_section(@log) %>

    <% if next_incomplete_section.present? %> @@ -22,14 +22,14 @@ <% end %>

    - <% elsif @lettings_log.status == "not_started" %> + <% elsif @log.status == "not_started" %>

    This log has not been started.

    - <% elsif @lettings_log.status == "completed" %> + <% elsif @log.status == "completed" %>

    - <%= status_tag(@lettings_log.status) %> + <%= status_tag(@log.status) %>

    - You can <%= govuk_link_to "review and make changes to this log", "/logs/#{@lettings_log.id}/review" %> until 2nd June <%= @lettings_log.collection_start_year.present? ? @lettings_log.collection_start_year + 1 : "" %>. + You can <%= govuk_link_to "review and make changes to this log", "/lettings-logs/#{@log.id}/review" %> until 2nd June <%= @log.collection_start_year.present? ? @log.collection_start_year + 1 : "" %>.

    <% end %> <%= render "tasklist" %> diff --git a/app/views/lettings_logs/index.html.erb b/app/views/logs/index.html.erb similarity index 61% rename from app/views/lettings_logs/index.html.erb rename to app/views/logs/index.html.erb index 9378aee09..7c75230d4 100644 --- a/app/views/lettings_logs/index.html.erb +++ b/app/views/logs/index.html.erb @@ -7,7 +7,12 @@
    - <%= govuk_button_to "Create a new lettings log", lettings_logs_path %> + <% if current_page?(:controller => 'lettings_logs', :action => 'index') %> + <%= govuk_button_to "Create a new lettings log", lettings_logs_path %> + <% end %> + <% if FeatureToggle.sales_log_enabled? && current_page?(:controller => 'sales_logs', :action => 'index') %> + <%= govuk_button_to "Create a new sales log", sales_logs_path %> + <% end %> <%#= govuk_link_to "Upload logs", bulk_upload_lettings_logs_path %>
    @@ -15,7 +20,7 @@
    <%= render SearchComponent.new(current_user:, search_label: "Search by log ID, tenant code, property reference or postcode", value: @searched) %> <%= govuk_section_break(visible: true, size: "m") %> - <%= render partial: "log_list", locals: { lettings_logs: @lettings_logs, title: "Logs", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %> + <%= render partial: "log_list", locals: { logs: @logs, title: "Logs", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, csv_download_url: csv_download_lettings_logs_path(search: @search_term) } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
    diff --git a/app/views/organisations/_organisation_list.html.erb b/app/views/organisations/_organisation_list.html.erb index 68b8f3233..389dff6b7 100644 --- a/app/views/organisations/_organisation_list.html.erb +++ b/app/views/organisations/_organisation_list.html.erb @@ -22,7 +22,7 @@ <% row.cell(header: true, html_attributes: { scope: "row", }) do %> - <%= govuk_link_to(organisation.name, "organisations/#{organisation.id}/logs") %> + <%= govuk_link_to(organisation.name, "organisations/#{organisation.id}/lettings-logs") %> <% end %> <% row.cell(text: organisation.housing_registration_no) %> <% row.cell(text: organisation.display_provider_type) %> diff --git a/app/views/organisations/logs.html.erb b/app/views/organisations/logs.html.erb index 916ac65ae..3b03e55e4 100644 --- a/app/views/organisations/logs.html.erb +++ b/app/views/organisations/logs.html.erb @@ -17,11 +17,11 @@ <%= govuk_button_to "Create a new lettings log for this organisation", lettings_logs_path(lettings_log: { owning_organisation_id: @organisation.id }, method: :post) %>
- <%= render partial: "lettings_logs/log_filters" %> + <%= render partial: "logs/log_filters" %>
<%= render SearchComponent.new(current_user:, search_label: "Search by log ID, tenant code, property reference or postcode", value: @searched) %> <%= govuk_section_break(visible: true, size: "m") %> - <%= render partial: "lettings_logs/log_list", locals: { lettings_logs: @lettings_logs, title: "Logs", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %> + <%= render partial: "logs/log_list", locals: { logs: @logs, title: "Logs", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, csv_download_url: logs_csv_download_organisation_path(@organisation, search: @search_term) } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "logs" } %>
diff --git a/config/application.rb b/config/application.rb index 7bdbfb93e..153782f54 100644 --- a/config/application.rb +++ b/config/application.rb @@ -33,5 +33,7 @@ module DataCollector # config.eager_load_paths << Rails.root.join("extras") config.exceptions_app = routes + + config.active_job.queue_adapter = :sidekiq end end diff --git a/config/initializers/feature_toggle.rb b/config/initializers/feature_toggle.rb index 50501fb65..0e01531ca 100644 --- a/config/initializers/feature_toggle.rb +++ b/config/initializers/feature_toggle.rb @@ -2,4 +2,10 @@ class FeatureToggle def self.startdate_two_week_validation_enabled? true end + + def self.sales_log_enabled? + return true unless Rails.env.production? + + false + end end diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 3bfcff86b..bc484e77a 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -5,7 +5,7 @@ if Rails.env.development? || Rails.env.test? Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new Rack::Attack.enabled = false else - redis_url = Configuration::PaasConfigurationService.new.redis_uris[:"dluhc-core-#{Rails.env}-redis-rate-limit"] + redis_url = Configuration::PaasConfigurationService.new.redis_uris[:"dluhc-core-#{Rails.env}-redis"] Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(url: redis_url) end diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb new file mode 100644 index 000000000..4db5237e4 --- /dev/null +++ b/config/initializers/sidekiq.rb @@ -0,0 +1,11 @@ +if Rails.env.staging? || Rails.env.production? + redis_url = Configuration::PaasConfigurationService.new.redis_uris[:"dluhc-core-#{Rails.env}-redis"] + + Sidekiq.configure_server do |config| + config.redis = { url: redis_url } + end + + Sidekiq.configure_client do |config| + config.redis = { url: redis_url } + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index c6403408c..388dff133 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -136,8 +136,8 @@ en: first_let_not_social: "Enter a reason for vacancy that is not 'first let' if unit has been previously let as social housing" first_let_social: "Reason for vacancy must be first let if unit has been previously let as social housing" previous_let_social: "Property cannot have a previous let type if being let as social housing for the first time" - non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as you already told us this accommodation is not temporary" - referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as you already told us a different source of referral for this letting" + non_temp_accommodation: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as this accommodation is not temporary" + referral_invalid: "Answer cannot be re-let to tenant who occupied the same property as temporary accommodation as a different source of referral for this letting" unittype_gn: one_bedroom_bedsit: "A bedsit can only have one bedroom" one_seven_bedroom_shared: "A shared house must have 1 to 7 bedrooms" @@ -207,7 +207,7 @@ en: not_provided: "Enter how much rent and other charges the household pays %{period}" household: reasonpref: - not_homeless: "Answer cannot be ‘homeless or about to lose their home’ as you already told us the tenant was not homeless immediately prior to this letting" + not_homeless: "Answer cannot be ‘homeless or about to lose their home’ as the tenant was not homeless immediately prior to this letting" reasonable_preference_reason: reason_required: "Enter a reason if you've answered 'yes' to reasonable preference" reason_not_required: "Do not enter a reason if you've answered 'no' to reasonable preference" @@ -245,31 +245,31 @@ en: housingneeds_a: one_or_two_choices: "You can only select one option or ‘other disabled access needs’ plus ‘wheelchair-accessible housing’, ‘wheelchair access to essential rooms’ or ‘level access housing’" prevten: - non_temp_accommodation: "Answer cannot be non-temporary accommodation as you already told us this is a re-let to a tenant who occupied the same property as temporary accommodation" + non_temp_accommodation: "Answer cannot be non-temporary accommodation as this is a re-let to a tenant who occupied the same property as temporary accommodation" over_20_foster_care: "Answer cannot be a children’s home or foster care as the lead tenant is 20 or older" male_refuge: "Answer cannot be a refuge as the lead tenant identifies as male" - internal_transfer: "Answer cannot be %{prevten} as you already told us this tenancy is an internal transfer" + internal_transfer: "Answer cannot be %{prevten} as this tenancy is an internal transfer" la_general_needs: - internal_transfer: "Answer cannot be a fixed-term or lifetime local authority general needs tenancy as you already told us it’s an internal transfer and a private registered provider is on the tenancy agreement" + internal_transfer: "Answer cannot be a fixed-term or lifetime local authority general needs tenancy as it’s an internal transfer and a private registered provider is on the tenancy agreement" referral: - secure_tenancy: "Answer must be internal transfer as you already told us this is a secure tenancy" - rsnvac_non_temp: "Answer cannot be this source of referral as you already told us this is a re-let to tenant who occupied the same property as temporary accommodation" - cannot_be_secure_tenancy: "Answer cannot be secure tenancy as you already told us this is not an internal transfer" - assessed_homeless: "Answer cannot be internal transfer as you already told us the tenant was assessed as homeless" - other_homeless: "Answer cannot be internal transfer as you already told us the tenant was considered homeless by their landlord" - prevten_invalid: "Answer cannot be internal transfer as you already told us the household situation immediately before this letting was %{prevten}" - reason_permanently_decanted: "Answer must be internal transfer as you already told us the tenant was permanently decanted from another property owned by this landlord" + secure_tenancy: "Answer must be internal transfer as this is a secure tenancy" + rsnvac_non_temp: "Answer cannot be this source of referral as this is a re-let to tenant who occupied the same property as temporary accommodation" + cannot_be_secure_tenancy: "Answer cannot be secure tenancy as this is not an internal transfer" + assessed_homeless: "Answer cannot be internal transfer as the tenant was assessed as homeless" + other_homeless: "Answer cannot be internal transfer as the tenant was considered homeless by their landlord" + prevten_invalid: "Answer cannot be internal transfer as the household situation immediately before this letting was %{prevten}" + reason_permanently_decanted: "Answer must be internal transfer as the tenant was permanently decanted from another property owned by this landlord" la_general_needs: - internal_transfer: "Answer cannot be internal transfer as you already told us it’s the same landlord on the tenancy agreement and the household had either a fixed-term or lifetime local authority general needs tenancy immediately before this letting" + internal_transfer: "Answer cannot be internal transfer as it’s the same landlord on the tenancy agreement and the household had either a fixed-term or lifetime local authority general needs tenancy immediately before this letting" prp: - local_housing_referral: "Answer cannot be ‘nominated by a local housing authority’ as you already told us a local authority is on the tenancy agreement" + local_housing_referral: "Answer cannot be ‘nominated by a local housing authority’ as a local authority is on the tenancy agreement" homeless: assessed: - internal_transfer: "Answer cannot be 'assessed as homeless' as you already told us this tenancy is an internal transfer" + internal_transfer: "Answer cannot be 'assessed as homeless' as this tenancy is an internal transfer" other: - internal_transfer: "Answer cannot be 'other homelessness' as you already told us this tenancy was an internal transfer" + internal_transfer: "Answer cannot be 'other homelessness' as this tenancy was an internal transfer" reasonpref: - not_homeless: "Answer cannot be ‘no’ as you already told us the tenant was homeless or about to lose their home" + not_homeless: "Answer cannot be ‘no’ as the tenant was homeless or about to lose their home" previous_la_known: "Enter name of local authority" gender: retired_male: "Answer cannot be ‘male’ as tenant is under 65 and retired" @@ -285,8 +285,8 @@ en: fixed_term_not_required: "You must only answer the length of the tenancy if it's fixed-term" shorthold: "Enter a tenancy length between 2 and 99 years for a Fixed Term – Assured Shorthold Tenancy (AST)" secure: "Enter a tenancy length between 2 and 99 years (or don't specify the length) for a Secure (including flexible) tenancy" - internal_transfer: "Answer must be secure tenancy as you already told us this tenancy is an internal transfer" - cannot_be_internal_transfer: "Answer cannot be internal transfer as you already told us this is not a secure tenancy" + internal_transfer: "Answer must be secure tenancy as this tenancy is an internal transfer" + cannot_be_internal_transfer: "Answer cannot be internal transfer as this is not a secure tenancy" not_joint: "This cannot be a joint tenancy as you've told us there's only one person in the household" joint_more_than_one_member: "There must be more than one person in the household as you've told us this is a joint tenancy" diff --git a/config/routes.rb b/config/routes.rb index 5a2fda475..0c4409713 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ Rails.application.routes.draw do root to: "start#index" + get "/logs", to: redirect("/lettings-logs") get "/accessibility-statement", to: "content#accessibility_statement" get "/privacy-notice", to: "content#privacy_notice" get "/data-sharing-agreement", to: "content#data_sharing_agreement" @@ -68,15 +69,22 @@ Rails.application.routes.draw do get "details", to: "organisations#details" get "users", to: "organisations#users" get "users/invite", to: "users/account#new" - get "logs", to: "organisations#logs" + get "lettings-logs", to: "organisations#lettings_logs" + get "sales-logs", to: "organisations#sales_logs" + get "logs/csv-download", to: "organisations#download_csv" + post "logs/email-csv", to: "organisations#email_csv" + get "logs/csv-confirmation", to: "lettings_logs#csv_confirmation" get "schemes", to: "organisations#schemes" end end - resources :lettings_logs, path: "/logs" do + resources :lettings_logs, path: "/lettings-logs" do collection do post "bulk-upload", to: "bulk_upload#bulk_upload" get "bulk-upload", to: "bulk_upload#show" + get "csv-download", to: "lettings_logs#download_csv" + post "email-csv", to: "lettings_logs#email_csv" + get "csv-confirmation", to: "lettings_logs#csv_confirmation" end member do @@ -84,9 +92,21 @@ Rails.application.routes.draw do get "review", to: "form#review" end - FormHandler.instance.forms.each do |_key, form| + FormHandler.instance.lettings_forms.each do |_key, form| form.pages.map do |page| - get page.id.to_s.dasherize, to: "form##{page.id}" + get page.id.to_s.dasherize, to: "form#show_page" + end + + form.subsections.map do |subsection| + get "#{subsection.id.to_s.dasherize}/check-answers", to: "form#check_answers" + end + end + end + + resources :sales_logs, path: "/sales-logs" do + FormHandler.instance.sales_forms.each do |_key, form| + form.pages.map do |page| + get page.id.to_s.dasherize, to: "form#show_page" end form.subsections.map do |subsection| diff --git a/db/migrate/20220826093411_add_sales_log.rb b/db/migrate/20220826093411_add_sales_log.rb new file mode 100644 index 000000000..8b14d7f5b --- /dev/null +++ b/db/migrate/20220826093411_add_sales_log.rb @@ -0,0 +1,12 @@ +class AddSalesLog < ActiveRecord::Migration[7.0] + def change + create_table :sales_logs do |t| + t.integer :status, default: 0 + t.datetime :saledate + t.timestamps + t.references :owning_organisation, class_name: "Organisation", foreign_key: { to_table: :organisations, on_delete: :cascade } + t.references :managing_organisation, class_name: "Organisation" + t.references :created_by, class_name: "User" + end + end +end diff --git a/db/migrate/20220915093559_add_purchid.rb b/db/migrate/20220915093559_add_purchid.rb new file mode 100644 index 000000000..665373b9e --- /dev/null +++ b/db/migrate/20220915093559_add_purchid.rb @@ -0,0 +1,5 @@ +class AddPurchid < ActiveRecord::Migration[7.0] + def change + add_column :sales_logs, :purchid, :string + end +end diff --git a/db/migrate/20220916110129_add_shared_ownership_type.rb b/db/migrate/20220916110129_add_shared_ownership_type.rb new file mode 100644 index 000000000..24c930991 --- /dev/null +++ b/db/migrate/20220916110129_add_shared_ownership_type.rb @@ -0,0 +1,5 @@ +class AddSharedOwnershipType < ActiveRecord::Migration[7.0] + def change + add_column :sales_logs, :type, :integer + end +end diff --git a/db/migrate/20220916125704_add_ownership_scheme_to_sales_log.rb b/db/migrate/20220916125704_add_ownership_scheme_to_sales_log.rb new file mode 100644 index 000000000..bb34ec864 --- /dev/null +++ b/db/migrate/20220916125704_add_ownership_scheme_to_sales_log.rb @@ -0,0 +1,5 @@ +class AddOwnershipSchemeToSalesLog < ActiveRecord::Migration[7.0] + def change + add_column :sales_logs, :ownershipsch, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 23db463fc..6548f2c86 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_09_02_082245) do +ActiveRecord::Schema[7.0].define(version: 2022_09_16_125704) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -313,6 +313,22 @@ ActiveRecord::Schema[7.0].define(version: 2022_09_02_082245) do t.index ["old_visible_id"], name: "index_organisations_on_old_visible_id", unique: true end + create_table "sales_logs", force: :cascade do |t| + t.integer "status", default: 0 + t.datetime "saledate" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.bigint "owning_organisation_id" + t.bigint "managing_organisation_id" + t.bigint "created_by_id" + t.string "purchid" + t.integer "type" + t.integer "ownershipsch" + t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" + t.index ["managing_organisation_id"], name: "index_sales_logs_on_managing_organisation_id" + t.index ["owning_organisation_id"], name: "index_sales_logs_on_owning_organisation_id" + end + create_table "schemes", force: :cascade do |t| t.string "service_name" t.bigint "owning_organisation_id", null: false @@ -395,6 +411,7 @@ ActiveRecord::Schema[7.0].define(version: 2022_09_02_082245) do add_foreign_key "lettings_logs", "organisations", column: "owning_organisation_id", on_delete: :cascade add_foreign_key "lettings_logs", "schemes" add_foreign_key "locations", "schemes" + add_foreign_key "sales_logs", "organisations", column: "owning_organisation_id", on_delete: :cascade add_foreign_key "schemes", "organisations", column: "managing_organisation_id" add_foreign_key "schemes", "organisations", column: "owning_organisation_id", on_delete: :cascade add_foreign_key "users", "organisations", on_delete: :cascade diff --git a/docs/infrastructure.md b/docs/infrastructure.md index 5246c701c..ca1324231 100644 --- a/docs/infrastructure.md +++ b/docs/infrastructure.md @@ -124,10 +124,12 @@ When a pull request is opened to `main` only the Test stage runs. cf target -o dluhc-core -s staging ``` -3. Create required Postgres and S3 bucket backing services (this will take ~15 mins to finish creating): +3. Create required Postgres, Redis and S3 bucket backing services (this will take ~15 mins to finish creating): ```bash cf create-service postgres tiny-unencrypted-13 dluhc-core-staging-postgres + cf create-service redis micro-6.x dluhc-core-staging-redis + cf create-service aws-s3-bucket default dluhc-core-staging-csv-bucket cf create-service aws-s3-bucket default dluhc-core-staging-import-bucket cf create-service aws-s3-bucket default dluhc-core-staging-export-bucket ``` @@ -141,6 +143,8 @@ When a pull request is opened to `main` only the Test stage runs. 5. Bind S3 services to app: ```bash + cf bind-service dluhc-core-staging dluhc-core-staging-csv-bucket + cf bind-service dluhc-core-staging dluhc-core-staging-redis cf bind-service dluhc-core-staging dluhc-core-staging-import-bucket -c '{"permissions": "read-only"}' cf bind-service dluhc-core-staging dluhc-core-staging-export-bucket -c '{"permissions": "read-write"}' ``` @@ -148,6 +152,7 @@ When a pull request is opened to `main` only the Test stage runs. 6. Create a service keys for accessing the S3 bucket from outside Gov PaaS: ```bash + cf create-service-key dluhc-core-staging-csv-bucket csv-bucket -c '{"allow_external_access": true}' cf create-service-key dluhc-core-staging-import-bucket data-import -c '{"allow_external_access": true}' cf create-service-key dluhc-core-staging-export-bucket data-export -c '{"allow_external_access": true, "permissions": "read-only"}' ``` @@ -166,10 +171,12 @@ When a pull request is opened to `main` only the Test stage runs. cf target -o dluhc-core -s production ``` -3. Create required Postgres and S3 bucket backing services (this will take ~15 mins to finish creating): +3. Create required Postgres, Redis and S3 bucket backing services (this will take ~15 mins to finish creating): ```bash cf create-service postgres small-ha-13 dluhc-core-production-postgres + cf create-service redis micro-ha-6.x dluhc-core-production-redis + cf create-service aws-s3-bucket default dluhc-core-production-csv-bucket cf create-service aws-s3-bucket default dluhc-core-production-import-bucket cf create-service aws-s3-bucket default dluhc-core-production-export-bucket ``` @@ -183,6 +190,8 @@ When a pull request is opened to `main` only the Test stage runs. 5. Bind S3 services to app: ```bash + cf bind-service dluhc-core-production dluhc-core-production-csv-bucket + cf bind-service dluhc-core-production dluhc-core-production-redis cf bind-service dluhc-core-production dluhc-core-production-import-bucket -c '{"permissions": "read-only"}' cf bind-service dluhc-core-production dluhc-core-production-export-bucket -c '{"permissions": "read-write"}' ``` @@ -190,6 +199,7 @@ When a pull request is opened to `main` only the Test stage runs. 6. Create a service keys for accessing the S3 bucket from outside Gov PaaS: ```bash + cf create-service-key dluhc-core-production-csv-bucket dluhc-core-production-csv-bucket-service-key -c '{"allow_external_access": true}' cf create-service-key dluhc-core-production-import-bucket data-import -c '{"allow_external_access": true}' cf create-service-key dluhc-core-production-export-bucket data-export -c '{"allow_external_access": true, "permissions": "read-only"}' ``` diff --git a/lib/tasks/correct_referral_value.rake b/lib/tasks/correct_referral_value.rake new file mode 100644 index 000000000..7bd9711db --- /dev/null +++ b/lib/tasks/correct_referral_value.rake @@ -0,0 +1,4 @@ +desc "Alter referral values for lettings logs in the database to 1 if they are 0" +task correct_referral_value: :environment do + LettingsLog.where(referral: 0).update_all(referral: 1) +end diff --git a/manifest.yml b/manifest.yml index be4095eff..6832f279a 100644 --- a/manifest.yml +++ b/manifest.yml @@ -7,6 +7,10 @@ defaults: &defaults command: bundle exec rake cf:on_first_instance db:migrate && bin/rails server instances: 2 memory: 1G + - type: worker + command: bundle exec sidekiq + health-check-type: process + instances: 2 health-check-type: http health-check-http-endpoint: /health @@ -17,7 +21,7 @@ applications: RAILS_ENV: staging services: - dluhc-core-staging-postgres - - dluhc-core-staging-redis-rate-limit + - dluhc-core-staging-redis - name: dluhc-core-production <<: *defaults @@ -31,4 +35,4 @@ applications: host: submit-social-housing-lettings-sales-data services: - dluhc-core-production-postgres - - dluhc-core-production-redis-rate-limit + - dluhc-core-production-redis diff --git a/spec/components/check_answers_summary_list_card_component_spec.rb b/spec/components/check_answers_summary_list_card_component_spec.rb index 3c16a6254..6273da69d 100644 --- a/spec/components/check_answers_summary_list_card_component_spec.rb +++ b/spec/components/check_answers_summary_list_card_component_spec.rb @@ -3,23 +3,23 @@ require "rails_helper" RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do context "when given a set of questions" do let(:user) { FactoryBot.build(:user) } - let(:lettings_log) { FactoryBot.build(:lettings_log, :completed, age2: 99) } + let(:log) { FactoryBot.build(:lettings_log, :completed, age2: 99) } let(:subsection_id) { "household_characteristics" } - let(:subsection) { lettings_log.form.get_subsection(subsection_id) } - let(:questions) { subsection.applicable_questions(lettings_log) } + let(:subsection) { log.form.get_subsection(subsection_id) } + let(:questions) { subsection.applicable_questions(log) } it "renders a summary list card for the answers to those questions" do - result = render_inline(described_class.new(questions:, lettings_log:, user:)) - expect(result).to have_content(questions.first.answer_label(lettings_log)) + result = render_inline(described_class.new(questions:, log:, user:)) + expect(result).to have_content(questions.first.answer_label(log)) end it "applicable questions doesn't return questions that are hidden in check answers" do - summary_list = described_class.new(questions:, lettings_log:, user:) + summary_list = described_class.new(questions:, log:, user:) expect(summary_list.applicable_questions.map(&:id).include?("retirement_value_check")).to eq(false) end it "has the correct answer label for a question" do - summary_list = described_class.new(questions:, lettings_log:, user:) + summary_list = described_class.new(questions:, log:, user:) sex1_question = questions[2] expect(summary_list.get_answer_label(sex1_question)).to eq("Female") end diff --git a/spec/factories/sales_log.rb b/spec/factories/sales_log.rb new file mode 100644 index 000000000..7efdd4039 --- /dev/null +++ b/spec/factories/sales_log.rb @@ -0,0 +1,13 @@ +FactoryBot.define do + factory :sales_log do + created_by { FactoryBot.create(:user) } + owning_organisation { created_by.organisation } + managing_organisation { created_by.organisation } + created_at { Time.utc(2022, 2, 8, 16, 52, 15) } + updated_at { Time.utc(2022, 2, 8, 16, 52, 15) } + trait :completed do + purchid { "PC123" } + saledate { Time.utc(2022, 2, 2, 10, 36, 49) } + end + end +end diff --git a/spec/features/form/accessible_autocomplete_spec.rb b/spec/features/form/accessible_autocomplete_spec.rb index 8e69b4bd1..52317107a 100644 --- a/spec/features/form/accessible_autocomplete_spec.rb +++ b/spec/features/form/accessible_autocomplete_spec.rb @@ -24,7 +24,7 @@ RSpec.describe "Accessible Automcomplete" do context "when using accessible autocomplete" do before do - visit("/logs/#{lettings_log.id}/accessible-select") + visit("/lettings-logs/#{lettings_log.id}/accessible-select") end it "allows type ahead filtering", js: true do @@ -66,7 +66,7 @@ RSpec.describe "Accessible Automcomplete" do FactoryBot.create(:location, scheme:, postcode: "W6 0ST") FactoryBot.create(:location, scheme:, postcode: "SE6 1LB") lettings_log.update!(needstype: 2) - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") end it "can match on synonyms", js: true do @@ -88,7 +88,7 @@ RSpec.describe "Accessible Automcomplete" do it "has the correct option selected if one has been saved" do lettings_log.update!(postcode_known: 0, previous_la_known: 1, prevloc: "E07000178") - visit("/logs/#{lettings_log.id}/accessible-select") + visit("/lettings-logs/#{lettings_log.id}/accessible-select") expect(page).to have_select("lettings-log-prevloc-field", selected: %w[Oxford]) end end diff --git a/spec/features/form/check_answers_page_spec.rb b/spec/features/form/check_answers_page_spec.rb index cff1b8d30..c5080b4bc 100644 --- a/spec/features/form/check_answers_page_spec.rb +++ b/spec/features/form/check_answers_page_spec.rb @@ -39,26 +39,28 @@ RSpec.describe "Form Check Answers Page" do ) end let(:id) { lettings_log.id } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } before do sign_in user + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) end context "when the user needs to check their answers for a subsection" do let(:last_question_for_subsection) { "propcode" } it "can be visited by URL" do - visit("/logs/#{id}/#{subsection}/check-answers") + visit("/lettings-logs/#{id}/#{subsection}/check-answers") expect(page).to have_content("#{subsection.tr('-', ' ').humanize} Check your answers") end it "redirects to the check answers page when answering the last question and clicking save and continue" do fill_in_number_question(id, "propcode", 0, last_question_for_subsection) - expect(page).to have_current_path("/logs/#{id}/#{subsection}/check-answers") + expect(page).to have_current_path("/lettings-logs/#{id}/#{subsection}/check-answers") end it "has question headings based on the subsection" do - visit("/logs/#{id}/#{subsection}/check-answers") + visit("/lettings-logs/#{id}/#{subsection}/check-answers") question_labels = ["Tenant code", "Lead tenant’s age", "Lead tenant’s gender identity", "Number of Household Members"] question_labels.each do |label| expect(page).to have_content(label) @@ -69,7 +71,7 @@ RSpec.describe "Form Check Answers Page" do fill_in_number_question(empty_lettings_log.id, "age1", 28, "person-1-age") choose("lettings-log-sex1-x-field") click_button("Save and continue") - visit("/logs/#{empty_lettings_log.id}/#{subsection}/check-answers") + visit("/lettings-logs/#{empty_lettings_log.id}/#{subsection}/check-answers") expect(page).to have_content("28") expect(page).to have_content("Non-binary") end @@ -77,35 +79,35 @@ RSpec.describe "Form Check Answers Page" do # Regex explanation: match the string "Answer" but not if it's follow by "the missing questions" # This way only the links in the table will get picked up it "has an answer link for questions missing an answer" do - visit("/logs/#{empty_lettings_log.id}/#{subsection}/check-answers?referrer=check_answers") + visit("/lettings-logs/#{empty_lettings_log.id}/#{subsection}/check-answers?referrer=check_answers") assert_selector "a", text: /Answer (?!the missing questions)/, count: 5 assert_selector "a", text: "Change", count: 0 - expect(page).to have_link("Answer", href: "/logs/#{empty_lettings_log.id}/person-1-age?referrer=check_answers") + expect(page).to have_link("Answer", href: "/lettings-logs/#{empty_lettings_log.id}/person-1-age?referrer=check_answers") end it "has a change link for answered questions" do fill_in_number_question(empty_lettings_log.id, "age1", 28, "person-1-age") - visit("/logs/#{empty_lettings_log.id}/#{subsection}/check-answers") + visit("/lettings-logs/#{empty_lettings_log.id}/#{subsection}/check-answers") assert_selector "a", text: /Answer (?!the missing questions)/, count: 4 assert_selector "a", text: "Change", count: 1 - expect(page).to have_link("Change", href: "/logs/#{empty_lettings_log.id}/person-1-age?referrer=check_answers") + expect(page).to have_link("Change", href: "/lettings-logs/#{empty_lettings_log.id}/person-1-age?referrer=check_answers") end it "updates the change/answer link when answers get updated" do - visit("/logs/#{empty_lettings_log.id}/household-needs/check-answers") + visit("/lettings-logs/#{empty_lettings_log.id}/household-needs/check-answers") assert_selector "a", text: /Answer (?!the missing questions)/, count: 3 assert_selector "a", text: "Change", count: 1 - visit("/logs/#{empty_lettings_log.id}/accessibility-requirements") + visit("/lettings-logs/#{empty_lettings_log.id}/accessibility-requirements") check("lettings-log-accessibility-requirements-housingneeds-c-field") click_button("Save and continue") - visit("/logs/#{empty_lettings_log.id}/household-needs/check-answers") + visit("/lettings-logs/#{empty_lettings_log.id}/household-needs/check-answers") assert_selector "a", text: /Answer (?!the missing questions)/, count: 2 assert_selector "a", text: "Change", count: 2 - expect(page).to have_link("Change", href: "/logs/#{empty_lettings_log.id}/accessibility-requirements?referrer=check_answers") + expect(page).to have_link("Change", href: "/lettings-logs/#{empty_lettings_log.id}/accessibility-requirements?referrer=check_answers") end it "does not display conditional questions that were not visited" do - visit("/logs/#{id}/#{conditional_subsection}/check-answers") + visit("/lettings-logs/#{id}/#{conditional_subsection}/check-answers") question_labels = ["Has the condition been met?"] question_labels.each do |label| expect(page).to have_content(label) @@ -118,10 +120,10 @@ RSpec.describe "Form Check Answers Page" do end it "displays conditional question that were visited" do - visit("/logs/#{id}/conditional-question") + visit("/lettings-logs/#{id}/conditional-question") choose("lettings-log-preg-occ-2-field", allow_label_click: true) click_button("Save and continue") - visit("/logs/#{id}/#{conditional_subsection}/check-answers") + visit("/lettings-logs/#{id}/#{conditional_subsection}/check-answers") question_labels = ["Has the condition been met?", "Has the condition not been met?"] question_labels.each do |label| expect(page).to have_content(label) @@ -134,13 +136,13 @@ RSpec.describe "Form Check Answers Page" do end it "does not group questions into summary cards if the questions in the subsection don't have a check_answers_card_number attribute" do - visit("/logs/#{completed_lettings_log.id}/household-needs/check-answers") + visit("/lettings-logs/#{completed_lettings_log.id}/household-needs/check-answers") assert_selector ".x-govuk-summary-card__title", count: 0 end context "when the user is checking their answers for the household characteristics subsection" do it "they see a seperate summary card for each member of the household" do - visit("/logs/#{completed_lettings_log.id}/#{subsection}/check-answers") + visit("/lettings-logs/#{completed_lettings_log.id}/#{subsection}/check-answers") assert_selector ".x-govuk-summary-card__title", text: "Lead tenant", count: 1 assert_selector ".x-govuk-summary-card__title", text: "Person 2", count: 1 end @@ -153,14 +155,14 @@ RSpec.describe "Form Check Answers Page" do it "displays inferred postcode with the location id" do lettings_log.update!(location:) - visit("/logs/#{id}/setup/check-answers") + visit("/lettings-logs/#{id}/setup/check-answers") expect(page).to have_content("Location") expect(page).to have_content(location.name) end it "displays inferred postcode with the location_admin_district" do lettings_log.update!(location:) - visit("/logs/#{id}/setup/check-answers") + visit("/lettings-logs/#{id}/setup/check-answers") expect(page).to have_content("Location") expect(page).to have_content(location.location_admin_district) end @@ -168,15 +170,15 @@ RSpec.describe "Form Check Answers Page" do context "when the user changes their answer from check answer page" do it "routes back to check answers" do - visit("/logs/#{empty_lettings_log.id}/accessibility-requirements") + visit("/lettings-logs/#{empty_lettings_log.id}/accessibility-requirements") check("lettings-log-accessibility-requirements-housingneeds-c-field") click_button("Save and continue") - visit("/logs/#{empty_lettings_log.id}/household-needs/check-answers") + visit("/lettings-logs/#{empty_lettings_log.id}/household-needs/check-answers") first("a", text: /Change/).click uncheck("lettings-log-accessibility-requirements-housingneeds-c-field") check("lettings-log-accessibility-requirements-housingneeds-b-field") click_button("Save changes") - expect(page).to have_current_path("/logs/#{empty_lettings_log.id}/household-needs/check-answers") + expect(page).to have_current_path("/lettings-logs/#{empty_lettings_log.id}/household-needs/check-answers") end end @@ -247,27 +249,27 @@ RSpec.describe "Form Check Answers Page" do end it "they can click a button to move onto the first page of the next (not started) incomplete section" do - visit("/logs/#{section_completed_lettings_log.id}/household-characteristics/check-answers") + visit("/lettings-logs/#{section_completed_lettings_log.id}/household-characteristics/check-answers") click_link("Save and go to next incomplete section") - expect(page).to have_current_path("/logs/#{section_completed_lettings_log.id}/armed-forces") + expect(page).to have_current_path("/lettings-logs/#{section_completed_lettings_log.id}/armed-forces") end it "they can click a button to move onto the check answers page of the next (in progress) incomplete section" do - visit("/logs/#{next_section_in_progress_lettings_log.id}/household-characteristics/check-answers") + visit("/lettings-logs/#{next_section_in_progress_lettings_log.id}/household-characteristics/check-answers") click_link("Save and go to next incomplete section") - expect(page).to have_current_path("/logs/#{next_section_in_progress_lettings_log.id}/household-needs/check-answers") + expect(page).to have_current_path("/lettings-logs/#{next_section_in_progress_lettings_log.id}/household-needs/check-answers") end it "they can click a button to skip sections until the next incomplete section" do - visit("/logs/#{skip_section_lettings_log.id}/household-characteristics/check-answers") + visit("/lettings-logs/#{skip_section_lettings_log.id}/household-characteristics/check-answers") click_link("Save and go to next incomplete section") - expect(page).to have_current_path("/logs/#{skip_section_lettings_log.id}/property-information/check-answers") + expect(page).to have_current_path("/lettings-logs/#{skip_section_lettings_log.id}/property-information/check-answers") end it "they can click a button to cycle around to the next incomplete section" do - visit("/logs/#{cycle_sections_lettings_log.id}/declaration/check-answers") + visit("/lettings-logs/#{cycle_sections_lettings_log.id}/declaration/check-answers") click_link("Save and go to next incomplete section") - expect(page).to have_current_path("/logs/#{cycle_sections_lettings_log.id}/tenant-code-test") + expect(page).to have_current_path("/lettings-logs/#{cycle_sections_lettings_log.id}/tenant-code-test") end end end diff --git a/spec/features/form/checkboxes_spec.rb b/spec/features/form/checkboxes_spec.rb index e9386e458..f7a3df152 100644 --- a/spec/features/form/checkboxes_spec.rb +++ b/spec/features/form/checkboxes_spec.rb @@ -22,14 +22,14 @@ RSpec.describe "Checkboxes" do context "when exclusive checkbox is selected", js: true do it "deselects all other checkboxes" do - visit("/logs/#{id}/accessibility-requirements") + visit("/lettings-logs/#{id}/accessibility-requirements") page.check("lettings-log-accessibility-requirements-housingneeds-a-field", allow_label_click: true) click_button("Save and continue") lettings_log.reload expect(lettings_log["housingneeds_a"]).to eq(1) - visit("/logs/#{id}/accessibility-requirements") + visit("/lettings-logs/#{id}/accessibility-requirements") page.check("lettings-log-accessibility-requirements-housingneeds-h-field", allow_label_click: true) click_button("Save and continue") @@ -46,7 +46,7 @@ RSpec.describe "Checkboxes" do end it "shows an error summary" do - visit("/logs/#{id}/condition-effects") + visit("/lettings-logs/#{id}/condition-effects") page.check("lettings-log-condition-effects-illness-type-1-field") page.check("lettings-log-condition-effects-illness-type-2-field") click_button("Save and continue") @@ -54,7 +54,7 @@ RSpec.describe "Checkboxes" do end it "persists the original selections" do - visit("/logs/#{id}/condition-effects") + visit("/lettings-logs/#{id}/condition-effects") page.check("lettings-log-condition-effects-illness-type-1-field") page.check("lettings-log-condition-effects-illness-type-2-field") click_button("Save and continue") diff --git a/spec/features/form/conditional_questions_spec.rb b/spec/features/form/conditional_questions_spec.rb index 0643bf12c..4f3f44b5a 100644 --- a/spec/features/form/conditional_questions_spec.rb +++ b/spec/features/form/conditional_questions_spec.rb @@ -13,19 +13,21 @@ RSpec.describe "Form Conditional Questions" do ) end let(:id) { lettings_log.id } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } before do sign_in user + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) end context "with a page where some questions are only conditionally shown, depending on how you answer the first question" do it "initially hides conditional questions" do - visit("/logs/#{id}/armed-forces") + visit("/lettings-logs/#{id}/armed-forces") expect(page).not_to have_selector("#armed_forces_injured_div") end it "shows conditional questions if the required answer is selected and hides it again when a different answer option is selected", js: true do - visit("/logs/#{id}/armed-forces") + visit("/lettings-logs/#{id}/armed-forces") # Something about our styling makes the selenium webdriver think the actual radio buttons are not visible so we allow label click here choose("lettings-log-armedforces-1-field", allow_label_click: true) fill_in("lettings-log-leftreg-field", with: "text") @@ -39,7 +41,7 @@ RSpec.describe "Form Conditional Questions" do context "when a conditional question has a saved answer", js: true do it "is displayed correctly" do lettings_log.update!(postcode_known: 1, postcode_full: "NW1 6RT") - visit("/logs/#{id}/property-postcode") + visit("/lettings-logs/#{id}/property-postcode") expect(page).to have_field("lettings-log-postcode-full-field", with: "NW1 6RT") end end diff --git a/spec/features/form/form_navigation_spec.rb b/spec/features/form/form_navigation_spec.rb index abc4e4fdf..ffa9bdd88 100644 --- a/spec/features/form/form_navigation_spec.rb +++ b/spec/features/form/form_navigation_spec.rb @@ -32,14 +32,16 @@ RSpec.describe "Form Navigation" do hhmemb: { type: "numeric", answer: 1, path: "household-number-of-members" }, } end + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } before do sign_in user + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) end describe "Create a new lettings log" do it "redirects to the task list for the new log" do - visit("/logs") + visit("/lettings-logs") click_button("Create a new lettings log") id = LettingsLog.order(created_at: :desc).first.id expect(page).to have_content("Log #{id}") @@ -48,64 +50,64 @@ RSpec.describe "Form Navigation" do describe "Viewing a log" do it "questions can be accessed by url" do - visit("/logs/#{id}/person-1-age") + visit("/lettings-logs/#{id}/person-1-age") expect(page).to have_field("lettings-log-age1-field") end it "a question page leads to the next question defined in the form definition" do pages = question_answers.map { |_key, val| val[:path] } pages[0..-2].each_with_index do |val, index| - visit("/logs/#{id}/#{val}") + visit("/lettings-logs/#{id}/#{val}") click_link("Skip for now") - expect(page).to have_current_path("/logs/#{id}/#{pages[index + 1]}") + expect(page).to have_current_path("/lettings-logs/#{id}/#{pages[index + 1]}") end end it "a question page has a Skip for now link that lets you move on to the next question without inputting anything" do - visit("logs/#{empty_lettings_log.id}/tenant-code-test") + visit("/lettings-logs/#{empty_lettings_log.id}/tenant-code-test") click_link(text: "Skip for now") - expect(page).to have_current_path("/logs/#{empty_lettings_log.id}/person-1-age") + expect(page).to have_current_path("/lettings-logs/#{empty_lettings_log.id}/person-1-age") end it "routes to check answers when skipping on the last page in the form" do - visit("logs/#{empty_lettings_log.id}/propcode") + visit("/lettings-logs/#{empty_lettings_log.id}/propcode") click_link(text: "Skip for now") - expect(page).to have_current_path("/logs/#{empty_lettings_log.id}/household-characteristics/check-answers") + expect(page).to have_current_path("/lettings-logs/#{empty_lettings_log.id}/household-characteristics/check-answers") end describe "Back link directs correctly", js: true do it "go back to tasklist page from tenant code" do - visit("/logs/#{id}") - visit("/logs/#{id}/tenant-code-test") + visit("/lettings-logs/#{id}") + visit("/lettings-logs/#{id}/tenant-code-test") click_link(text: "Back") expect(page).to have_content("Log #{id}") end it "go back to tenant code page from tenant age page", js: true do - visit("/logs/#{id}/tenant-code-test") + visit("/lettings-logs/#{id}/tenant-code-test") click_button("Save and continue") - visit("/logs/#{id}/person-1-age") + visit("/lettings-logs/#{id}/person-1-age") click_link(text: "Back") expect(page).to have_field("lettings-log-tenancycode-field") end it "doesn't get stuck in infinite loops", js: true do - visit("/logs") - visit("/logs/#{id}/net-income") + visit("/lettings-logs") + visit("/lettings-logs/#{id}/net-income") fill_in("lettings-log-earnings-field", with: 740) choose("lettings-log-incfreq-1-field", allow_label_click: true) click_button("Save and continue") click_link(text: "Back") click_link(text: "Back") - expect(page).to have_current_path("/logs") + expect(page).to have_current_path("/lettings-logs") end context "when changing an answer from the check answers page", js: true do it "the back button routes correctly" do - visit("/logs/#{id}/household-characteristics/check-answers") + visit("/lettings-logs/#{id}/household-characteristics/check-answers") first("a", text: /Answer/).click click_link("Back") - expect(page).to have_current_path("/logs/#{id}/household-characteristics/check-answers") + expect(page).to have_current_path("/lettings-logs/#{id}/household-characteristics/check-answers") end end end @@ -113,16 +115,16 @@ RSpec.describe "Form Navigation" do describe "Editing a log" do it "a question page has a link allowing you to cancel your input and return to the check answers page" do - visit("logs/#{id}/tenant-code-test?referrer=check_answers") + visit("lettings-logs/#{id}/tenant-code-test?referrer=check_answers") click_link(text: "Cancel") - expect(page).to have_current_path("/logs/#{id}/setup/check-answers") + expect(page).to have_current_path("/lettings-logs/#{id}/household-characteristics/check-answers") end context "when clicking save and continue on a mandatory question with no input" do let(:id) { empty_lettings_log.id } it "shows a validation error on radio questions" do - visit("/logs/#{id}/renewal") + visit("/lettings-logs/#{id}/renewal") click_button("Save and continue") expect(page).to have_selector("#error-summary-title") expect(page).to have_selector("#lettings-log-renewal-error") @@ -130,7 +132,7 @@ RSpec.describe "Form Navigation" do end it "shows a validation error on date questions" do - visit("/logs/#{id}/tenancy-start-date") + visit("/lettings-logs/#{id}/tenancy-start-date") click_button("Save and continue") expect(page).to have_selector("#error-summary-title") expect(page).to have_selector("#lettings-log-startdate-error") @@ -140,7 +142,7 @@ RSpec.describe "Form Navigation" do context "when the page has a main and conditional question" do context "when the conditional question is required but not answered" do it "shows a validation error for the conditional question" do - visit("/logs/#{id}/armed-forces") + visit("/lettings-logs/#{id}/armed-forces") choose("lettings-log-armedforces-1-field", allow_label_click: true) click_button("Save and continue") expect(page).to have_selector("#error-summary-title") @@ -155,11 +157,11 @@ RSpec.describe "Form Navigation" do let(:id) { empty_lettings_log.id } it "does not show a validation error" do - visit("/logs/#{id}/tenant-code") + visit("/lettings-logs/#{id}/tenant-code") click_button("Save and continue") expect(page).not_to have_selector("#error-summary-title") expect(page).not_to have_title("Error") - expect(page).to have_current_path("/logs/#{id}/property-reference") + expect(page).to have_current_path("/lettings-logs/#{id}/property-reference") end end end diff --git a/spec/features/form/helpers.rb b/spec/features/form/helpers.rb index 44d8c62ad..572a14d61 100644 --- a/spec/features/form/helpers.rb +++ b/spec/features/form/helpers.rb @@ -1,12 +1,12 @@ module Helpers def fill_in_number_question(lettings_log_id, question, value, path) - visit("/logs/#{lettings_log_id}/#{path}") + visit("/lettings-logs/#{lettings_log_id}/#{path}") fill_in("lettings-log-#{question.to_s.dasherize}-field", with: value) click_button("Save and continue") end def answer_all_questions_in_income_subsection(lettings_log) - visit("/logs/#{lettings_log.id}/net-income") + visit("/lettings-logs/#{lettings_log.id}/net-income") fill_in("lettings-log-earnings-field", with: 18_000) choose("lettings-log-incfreq-2-field") click_button("Save and continue") @@ -17,7 +17,7 @@ module Helpers end def sign_in(user) - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") diff --git a/spec/features/form/page_routing_spec.rb b/spec/features/form/page_routing_spec.rb index a568e3316..27f4feaf5 100644 --- a/spec/features/form/page_routing_spec.rb +++ b/spec/features/form/page_routing_spec.rb @@ -21,77 +21,77 @@ RSpec.describe "Form Page Routing" do end it "can route the user to a different page based on their answer on the current page", js: true do - visit("/logs/#{id}/conditional-question") + visit("/lettings-logs/#{id}/conditional-question") # using a question name that is already in the db to avoid # having to add a new column to the db for this test choose("lettings-log-preg-occ-1-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/conditional-question-yes-page") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-yes-page") click_link(text: "Back") - expect(page).to have_current_path("/logs/#{id}/conditional-question") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question") choose("lettings-log-preg-occ-2-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/conditional-question-no-page") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") end it "can route based on multiple conditions", js: true do - visit("/logs/#{id}/person-1-gender") + visit("/lettings-logs/#{id}/person-1-gender") choose("lettings-log-sex1-f-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/person-1-working-situation") - visit("/logs/#{id}/conditional-question") + expect(page).to have_current_path("/lettings-logs/#{id}/person-1-working-situation") + visit("/lettings-logs/#{id}/conditional-question") choose("lettings-log-preg-occ-2-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/conditional-question-no-page") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question-no-page") choose("lettings-log-cbl-0-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/conditional-question/check-answers") + expect(page).to have_current_path("/lettings-logs/#{id}/conditional-question/check-answers") end context "when the answers are inferred", js: true do it "shows question if the answer could not be inferred" do - visit("/logs/#{id}/property-postcode") + visit("/lettings-logs/#{id}/property-postcode") fill_in("lettings-log-postcode-full-field", with: "PO5 3TE") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/do-you-know-the-local-authority") + expect(page).to have_current_path("/lettings-logs/#{id}/do-you-know-the-local-authority") end it "shows question if the answer could not be inferred from an empty input" do - visit("/logs/#{id}/property-postcode") + visit("/lettings-logs/#{id}/property-postcode") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/do-you-know-the-local-authority") + expect(page).to have_current_path("/lettings-logs/#{id}/do-you-know-the-local-authority") end it "does not show question if the answer could be inferred" do stub_request(:get, /api.postcodes.io/) .to_return(status: 200, body: "{\"status\":200,\"result\":{\"admin_district\":\"Manchester\", \"codes\":{\"admin_district\": \"E08000003\"}}}", headers: {}) - visit("/logs/#{id}/property-postcode") + visit("/lettings-logs/#{id}/property-postcode") fill_in("lettings-log-postcode-full-field", with: "P0 5ST") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-wheelchair-accessible") + expect(page).to have_current_path("/lettings-logs/#{id}/property-wheelchair-accessible") end end context "when answer is invalid" do it "shows error with invalid value in the field" do - visit("/logs/#{id}/property-postcode") + visit("/lettings-logs/#{id}/property-postcode") fill_in("lettings-log-postcode-full-field", with: "fake_postcode") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-postcode") + expect(page).to have_current_path("/lettings-logs/#{id}/property-postcode") expect(find("#lettings-log-postcode-full-field-error").value).to eq("fake_postcode") end it "does not reset the displayed date" do lettings_log.update!(startdate: "2021/10/13") - visit("/logs/#{id}/tenancy-start-date") + visit("/lettings-logs/#{id}/tenancy-start-date") fill_in("lettings_log[startdate(1i)]", with: "202") fill_in("lettings_log[startdate(2i)]", with: "32") fill_in("lettings_log[startdate(3i)]", with: "0") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/tenancy-start-date") + expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") expect(find_field("lettings_log[startdate(3i)]").value).to eq("13") expect(find_field("lettings_log[startdate(2i)]").value).to eq("10") expect(find_field("lettings_log[startdate(1i)]").value).to eq("2021") @@ -99,13 +99,13 @@ RSpec.describe "Form Page Routing" do it "does not reset the displayed date if it's empty" do lettings_log.update!(startdate: nil) - visit("/logs/#{id}/tenancy-start-date") + visit("/lettings-logs/#{id}/tenancy-start-date") fill_in("lettings_log[startdate(1i)]", with: "202") fill_in("lettings_log[startdate(2i)]", with: "32") fill_in("lettings_log[startdate(3i)]", with: "0") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/tenancy-start-date") + expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date") expect(find_field("lettings_log[startdate(3i)]").value).to eq(nil) expect(find_field("lettings_log[startdate(2i)]").value).to eq(nil) expect(find_field("lettings_log[startdate(1i)]").value).to eq(nil) @@ -129,13 +129,13 @@ RSpec.describe "Form Page Routing" do before do FactoryBot.create(:location, scheme:, startdate: Time.zone.today + 20.days) - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") select(scheme.service_name, from: "lettings_log[scheme_id]") click_button("Save and continue") end it "does not route to the scheme location question" do - expect(page).to have_current_path("/logs/#{lettings_log.id}/renewal") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/renewal") end it "infers the scheme location" do diff --git a/spec/features/form/progressive_total_field_spec.rb b/spec/features/form/progressive_total_field_spec.rb index 5be6ef1c2..5080de08c 100644 --- a/spec/features/form/progressive_total_field_spec.rb +++ b/spec/features/form/progressive_total_field_spec.rb @@ -18,12 +18,12 @@ RSpec.describe "Accessible Automcomplete" do end it "does not show when js is not enabled" do - visit("/logs/#{lettings_log.id}/rent") + visit("/lettings-logs/#{lettings_log.id}/rent") expect(page).to have_selector("#tcharge_div", visible: :all) end it "does show when js is enabled and calculates the total", js: true do - visit("/logs/#{lettings_log.id}/rent") + visit("/lettings-logs/#{lettings_log.id}/rent") expect(page).to have_selector("#tcharge_div") fill_in("lettings-log-brent-field", with: 5) expect(find("#lettings-log-tcharge-field").value).to eq("5.00") @@ -32,7 +32,7 @@ RSpec.describe "Accessible Automcomplete" do end it "total displays despite error message", js: true do - visit("/logs/#{lettings_log.id}/rent") + visit("/lettings-logs/#{lettings_log.id}/rent") choose("lettings-log-period-1-field", allow_label_click: true) fill_in("lettings-log-brent-field", with: 500) fill_in("lettings-log-scharge-field", with: 50) diff --git a/spec/features/form/saving_data_spec.rb b/spec/features/form/saving_data_spec.rb index ca08ee1fd..8312cfad9 100644 --- a/spec/features/form/saving_data_spec.rb +++ b/spec/features/form/saving_data_spec.rb @@ -39,7 +39,7 @@ RSpec.describe "Form Saving Data" do answer = hsh[:answer].respond_to?(:keys) ? hsh[:answer].keys.first : hsh[:answer] path = hsh[:path] original_value = lettings_log.send(question) - visit("/logs/#{id}/#{path.to_s.dasherize}") + visit("/lettings-logs/#{id}/#{path.to_s.dasherize}") case type when "text" fill_in("lettings-log-#{question.to_s.dasherize}-field", with: answer) @@ -55,7 +55,7 @@ RSpec.describe "Form Saving Data" do end it "updates total value of the rent", js: true do - visit("/logs/#{id}/rent") + visit("/lettings-logs/#{id}/rent") fill_in("lettings-log-brent-field", with: 3.02) expect(page.find("#lettings-log-tcharge-field")).to have_content("3.02") @@ -71,17 +71,17 @@ RSpec.describe "Form Saving Data" do end it "displays number answers in inputs if they are already saved" do - visit("/logs/#{id}/property-postcode") + visit("/lettings-logs/#{id}/property-postcode") expect(page).to have_field("lettings-log-postcode-full-field", with: lettings_log.postcode_full) end it "displays text answers in inputs if they are already saved" do - visit("/logs/#{id}/person-1-age") + visit("/lettings-logs/#{id}/person-1-age") expect(page).to have_field("lettings-log-age1-field", with: "17") end it "displays checkbox answers in inputs if they are already saved" do - visit("/logs/#{lettings_log_with_checkbox_questions_answered.id.to_s.dasherize}/accessibility-requirements") + visit("/lettings-logs/#{lettings_log_with_checkbox_questions_answered.id.to_s.dasherize}/accessibility-requirements") expect(page).to have_checked_field( "lettings-log-accessibility-requirements-housingneeds-a-field", visible: :all, diff --git a/spec/features/form/tasklist_page_spec.rb b/spec/features/form/tasklist_page_spec.rb index a82276c60..3f7ba6f1e 100644 --- a/spec/features/form/tasklist_page_spec.rb +++ b/spec/features/form/tasklist_page_spec.rb @@ -48,25 +48,25 @@ RSpec.describe "Task List" do end it "shows if the section has not been started" do - visit("/logs/#{empty_lettings_log.id}") + visit("/lettings-logs/#{empty_lettings_log.id}") expect(page).to have_content("This log has not been started.") end it "shows number of completed sections if one section is completed" do - visit("/logs/#{setup_completed_log.id}") + visit("/lettings-logs/#{setup_completed_log.id}") expect(page).to have_content("1 of 8 sections completed.") end it "show skip link for next incomplete section" do answer_all_questions_in_income_subsection(setup_completed_log) - visit("/logs/#{setup_completed_log.id}") + visit("/lettings-logs/#{setup_completed_log.id}") expect(page).to have_link("Skip to next incomplete section", href: /#household-characteristics/) end it "has a review section which has a button that allows the data inputter to review the lettings log" do - visit("/logs/#{completed_lettings_log.id}") + visit("/lettings-logs/#{completed_lettings_log.id}") expect(page).to have_content("review and make changes to this log") click_link(text: "review and make changes to this log") - expect(page).to have_current_path("/logs/#{completed_lettings_log.id}/review") + expect(page).to have_current_path("/lettings-logs/#{completed_lettings_log.id}/review") end end diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb index d944f5161..8defb15f6 100644 --- a/spec/features/form/validations_spec.rb +++ b/spec/features/form/validations_spec.rb @@ -2,11 +2,7 @@ require "rails_helper" require_relative "helpers" RSpec.describe "validations" do - before do - sign_in user - end - - include Helpers + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } let(:user) { FactoryBot.create(:user) } let(:lettings_log) do FactoryBot.create( @@ -35,10 +31,17 @@ RSpec.describe "validations" do end let(:id) { lettings_log.id } + before do + sign_in user + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end + + include Helpers + describe "Question validation" do context "when the tenant age is invalid" do it "shows validation for under 0" do - visit("/logs/#{id}/person-1-age") + visit("/lettings-logs/#{id}/person-1-age") fill_in_number_question(empty_lettings_log.id, "age1", -5, "person-1-age") expect(page).to have_selector("#error-summary-title") expect(page).to have_selector("#lettings-log-age1-error") @@ -47,7 +50,7 @@ RSpec.describe "validations" do end it "shows validation for over 120" do - visit("/logs/#{id}/person-1-age") + visit("/lettings-logs/#{id}/person-1-age") fill_in_number_question(empty_lettings_log.id, "age1", 121, "person-1-age") expect(page).to have_selector("#error-summary-title") expect(page).to have_selector("#lettings-log-age1-error") @@ -59,7 +62,7 @@ RSpec.describe "validations" do describe "date validation", js: true do def fill_in_date(lettings_log_id, question, day, month, year, path) - visit("/logs/#{lettings_log_id}/#{path}") + visit("/lettings-logs/#{lettings_log_id}/#{path}") fill_in("lettings_log[#{question}(1i)]", with: year) fill_in("lettings_log[#{question}(2i)]", with: month) fill_in("lettings_log[#{question}(3i)]", with: day) @@ -68,45 +71,45 @@ RSpec.describe "validations" do it "does not allow out of range dates to be submitted" do fill_in_date(id, "mrcdate", 3100, 12, 2000, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") fill_in_date(id, "mrcdate", 12, 1, 20_000, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") fill_in_date(id, "mrcdate", 13, 100, 2020, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") fill_in_date(id, "mrcdate", 21, 11, 2020, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/local-authority/check-answers") + expect(page).to have_current_path("/lettings-logs/#{id}/local-authority/check-answers") end it "does not allow non numeric inputs to be submitted" do fill_in_date(id, "mrcdate", "abc", "de", "ff", "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") end it "does not allow partial inputs to be submitted" do fill_in_date(id, "mrcdate", 21, 12, nil, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") fill_in_date(id, "mrcdate", 12, nil, 2000, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") fill_in_date(id, "mrcdate", nil, 10, 2020, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/property-major-repairs") + expect(page).to have_current_path("/lettings-logs/#{id}/property-major-repairs") end it "allows valid inputs to be submitted" do fill_in_date(id, "mrcdate", 21, 11, 2020, "property-major-repairs") click_button("Save and continue") - expect(page).to have_current_path("/logs/#{id}/local-authority/check-answers") + expect(page).to have_current_path("/lettings-logs/#{id}/local-authority/check-answers") end end @@ -125,27 +128,27 @@ RSpec.describe "validations" do let(:income_under_soft_limit) { 700 } it "prompts the user to confirm the value is correct with an interruption screen" do - visit("/logs/#{lettings_log.id}/net-income") + visit("/lettings-logs/#{lettings_log.id}/net-income") fill_in("lettings-log-earnings-field", with: income_over_soft_limit) choose("lettings-log-incfreq-1-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{lettings_log.id}/net-income-value-check") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check") expect(page).to have_content("Net income is outside the expected range based on the lead tenant’s working situation") expect(page).to have_content("You told us the lead tenant’s working situation is: full-time – 30 hours or more") expect(page).to have_content("The household income you have entered is £750.00 every week") choose("lettings-log-net-income-value-check-0-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{lettings_log.id}/net-income-uc-proportion") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-uc-proportion") end it "returns the user to the previous question if they do not confirm the value as correct on the interruption screen" do - visit("/logs/#{lettings_log.id}/net-income") + visit("/lettings-logs/#{lettings_log.id}/net-income") fill_in("lettings-log-earnings-field", with: income_over_soft_limit) choose("lettings-log-incfreq-1-field", allow_label_click: true) click_button("Save and continue") choose("lettings-log-net-income-value-check-1-field", allow_label_click: true) click_button("Save and continue") - expect(page).to have_current_path("/logs/#{lettings_log.id}/net-income") + expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income") end end end @@ -153,7 +156,7 @@ RSpec.describe "validations" do describe "Submission validation" do context "when tenant has not seen the privacy notice" do it "shows a warning" do - visit("/logs/#{completed_without_declaration.id}/declaration") + visit("/lettings-logs/#{completed_without_declaration.id}/declaration") click_button("Save and continue") expect(page).to have_content("You must show the DLUHC privacy notice to the tenant") end diff --git a/spec/features/log_spec.rb b/spec/features/lettings_log_spec.rb similarity index 87% rename from spec/features/log_spec.rb rename to spec/features/lettings_log_spec.rb index cef344145..1bd72b5cf 100644 --- a/spec/features/log_spec.rb +++ b/spec/features/lettings_log_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe "Log Features" do +RSpec.describe "Lettings Log Features" do context "when searching for specific logs" do context "when I am signed in and there are logs in the database" do let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) } @@ -9,7 +9,7 @@ RSpec.describe "Log Features" do let!(:another_organisation_log) { FactoryBot.create(:lettings_log) } before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") @@ -77,9 +77,9 @@ RSpec.describe "Log Features" do click_button("Submit") end - context "when completing the setup log section" do + context "when completing the setup lettings log section" do it "includes the organisation and created by questions" do - visit("/logs") + visit("/lettings-logs") click_button("Create a new lettings log") click_link("Set up this lettings log") select(support_user.organisation.name, from: "lettings-log-owning-organisation-id-field") @@ -87,7 +87,7 @@ RSpec.describe "Log Features" do select(support_user.name, from: "lettings-log-created-by-id-field") click_button("Save and continue") log_id = page.current_path.scan(/\d/).join - visit("logs/#{log_id}/setup/check-answers") + visit("lettings-logs/#{log_id}/setup/check-answers") expect(page).to have_content("Owning organisation #{support_user.organisation.name}") expect(page).to have_content("User #{support_user.name}") expect(page).to have_content("You have answered 2 of 8 questions") @@ -112,16 +112,28 @@ RSpec.describe "Log Features" do context "when completing the setup log section" do it "does not include the organisation and created by questions" do - visit("/logs") + visit("/lettings-logs") click_button("Create a new lettings log") click_link("Set up this lettings log") log_id = page.current_path.scan(/\d/).join - expect(page).to have_current_path("/logs/#{log_id}/needs-type") - visit("logs/#{log_id}/setup/check-answers") + expect(page).to have_current_path("/lettings-logs/#{log_id}/needs-type") + visit("lettings-logs/#{log_id}/setup/check-answers") expect(page).not_to have_content("Owning organisation #{support_user.organisation.name}") expect(page).not_to have_content("User #{support_user.name}") expect(page).to have_content("You have answered 0 of 6 questions") end end + + context "when returning to the list of logs via breadcrumbs link" do + before do + visit("/lettings-logs") + click_button("Create a new lettings log") + click_link("Logs") + end + + it "navigates you to the lettings logs page" do + expect(page).to have_current_path("/lettings-logs") + end + end end end diff --git a/spec/features/organisation_spec.rb b/spec/features/organisation_spec.rb index e14618109..a0558d167 100644 --- a/spec/features/organisation_spec.rb +++ b/spec/features/organisation_spec.rb @@ -27,7 +27,7 @@ RSpec.describe "User Features" do context "when viewing organisation page" do it "defaults to organisation details" do - visit("/logs") + visit("/lettings-logs") click_link("About your organisation") expect(page).to have_content(user.organisation.name) end @@ -46,7 +46,7 @@ RSpec.describe "User Features" do end it "does not show schemes in the navigation bar" do - visit("/logs") + visit("/lettings-logs") expect(page).not_to have_link("Schemes", href: "/schemes") end end @@ -57,7 +57,7 @@ RSpec.describe "User Features" do end it "shows schemes in the navigation bar" do - visit("/logs") + visit("/lettings-logs") expect(page).to have_link("Schemes", href: "/schemes") end end @@ -109,10 +109,10 @@ RSpec.describe "User Features" do context "when viewing organisation page" do it "can see the details tab and users tab" do - visit("/logs") + visit("/lettings-logs") click_link("About your organisation") expect(page).to have_current_path("/organisations/#{org_id}/details") - expect(page).to have_link("Logs") + expect(page).to have_link("Lettings logs") expect(page).to have_link("Users") expect(page).to have_link("About your organisation") end @@ -141,7 +141,7 @@ RSpec.describe "User Features" do let(:number_of_lettings_logs) { LettingsLog.count } before do - visit("/organisations/#{org_id}/logs") + visit("/organisations/#{org_id}/lettings-logs") end it "shows a create button for that organisation" do @@ -201,12 +201,12 @@ RSpec.describe "User Features" do it "can filter lettings logs" do expect(page).to have_content("#{number_of_lettings_logs} total logs") organisation.lettings_logs.map(&:id).each do |lettings_log_id| - expect(page).to have_link lettings_log_id.to_s, href: "/logs/#{lettings_log_id}" + expect(page).to have_link lettings_log_id.to_s, href: "/lettings-logs/#{lettings_log_id}" end check("years-2021-field") click_button("Apply filters") - expect(page).to have_current_path("/organisations/#{org_id}/logs?years[]=&years[]=2021&status[]=&user=all") - expect(page).not_to have_link first_log.id.to_s, href: "/logs/#{first_log.id}" + expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2021&status[]=&user=all") + expect(page).not_to have_link first_log.id.to_s, href: "/lettings-logs/#{first_log.id}" end end @@ -237,7 +237,7 @@ RSpec.describe "User Features" do it "shows submenu for selected organisation" do expect(page).to have_css('[aria-current="page"]', text: "Users") expect(page).to have_current_path("/organisations/#{org_id}/users") - expect(page).to have_link("Logs") + expect(page).to have_link("Lettings logs") expect(page).to have_link("About this organisation") end @@ -252,36 +252,36 @@ RSpec.describe "User Features" do end end end + end + end + + describe "delete cascade" do + context "when the organisation is deleted" do + let!(:organisation) { FactoryBot.create(:organisation) } + let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) } + let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let!(:log_to_delete) { FactoryBot.create(:lettings_log, owning_organisation: user.organisation) } + + context "when organisation is deleted" do + it "child relationships ie logs, schemes and users are deleted too - application" do + organisation.destroy! + expect { organisation.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { LettingsLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) + end - describe "delete cascade" do context "when the organisation is deleted" do let!(:organisation) { FactoryBot.create(:organisation) } let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) } let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let!(:log_to_delete) { FactoryBot.create(:lettings_log, owning_organisation: user.organisation) } - - context "when organisation is deleted" do - it "child relationships ie logs, schemes and users are deleted too - application" do - organisation.destroy! - expect { organisation.reload }.to raise_error(ActiveRecord::RecordNotFound) - expect { LettingsLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) - expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) - expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) - end - - context "when the organisation is deleted" do - let!(:organisation) { FactoryBot.create(:organisation) } - let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) } - let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let!(:log_to_delete) { FactoryBot.create(:lettings_log, :in_progress, needstype: 1, owning_organisation: user.organisation) } - - it "child relationships ie logs, schemes and users are deleted too - database" do - ActiveRecord::Base.connection.exec_query("DELETE FROM organisations WHERE id = #{organisation.id};") - expect { LettingsLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) - expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) - expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) - end - end + let!(:log_to_delete) { FactoryBot.create(:lettings_log, :in_progress, needstype: 1, owning_organisation: user.organisation) } + + it "child relationships ie logs, schemes and users are deleted too - database" do + ActiveRecord::Base.connection.exec_query("DELETE FROM organisations WHERE id = #{organisation.id};") + expect { LettingsLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) end end end diff --git a/spec/features/sales_log_spec.rb b/spec/features/sales_log_spec.rb new file mode 100644 index 000000000..270d9bf37 --- /dev/null +++ b/spec/features/sales_log_spec.rb @@ -0,0 +1,68 @@ +require "rails_helper" + +RSpec.describe "Sales Log Features" do + context "when searching for specific sales logs" do + context "when I am signed in and there are sales logs in the database" do + let(:user) { FactoryBot.create(:user, last_sign_in_at: Time.zone.now) } + let!(:log_to_search) { FactoryBot.create(:sales_log, owning_organisation: user.organisation) } + let!(:same_organisation_log) { FactoryBot.create(:sales_log, owning_organisation: user.organisation) } + let!(:another_organisation_log) { FactoryBot.create(:sales_log) } + + before do + visit("/sales-logs") + fill_in("user[email]", with: user.email) + fill_in("user[password]", with: user.password) + click_button("Sign in") + end + + it "displays the logs belonging to the same organisation" do + expect(page).to have_link(log_to_search.id.to_s) + expect(page).to have_link(same_organisation_log.id.to_s) + expect(page).not_to have_link(another_organisation_log.id.to_s) + end + + context "when returning to the list of logs via breadcrumbs link" do + before do + visit("/sales-logs") + click_button("Create a new sales log") + click_link("Logs") + end + + it "navigates you to the lettings logs page" do + expect(page).to have_current_path("/sales-logs") + end + end + + context "when completing the setup sales log section" do + it "includes the purchaser code and sale completion date questions" do + visit("/sales-logs") + click_button("Create a new sales log") + click_link("Set up this sales log") + fill_in("sales_log[saledate(1i)]", with: "2022") + fill_in("sales_log[saledate(2i)]", with: "08") + fill_in("sales_log[saledate(3i)]", with: "10") + click_button("Save and continue") + fill_in("sales_log[purchid]", with: "PC123") + click_button("Save and continue") + log_id = page.current_path.scan(/\d/).join + visit("sales-logs/#{log_id}/setup/check-answers") + expect(page).to have_content("Sale completion date") + expect(page).to have_content("2022") + expect(page).to have_content("Purchaser code") + expect(page).to have_content("PC123") + end + end + + context "when the sales log feature flag is toggled" do + before do + allow(Rails.env).to receive(:production?).and_return(true) + end + + it "hides the create sales log button in production" do + visit("/sales-logs") + expect(page).not_to have_content("Create a new sales log") + end + end + end + end +end diff --git a/spec/features/schemes_helpers.rb b/spec/features/schemes_helpers.rb index 63d0d847b..0592a8ef2 100644 --- a/spec/features/schemes_helpers.rb +++ b/spec/features/schemes_helpers.rb @@ -1,12 +1,12 @@ module SchemesHelpers def fill_in_number_question(lettings_log_id, question, value, path) - visit("/logs/#{lettings_log_id}/#{path}") + visit("/lettings-logs/#{lettings_log_id}/#{path}") fill_in("lettings-log-#{question.to_s.dasherize}-field", with: value) click_button("Save and continue") end def answer_all_questions_in_income_subsection(lettings_log) - visit("/logs/#{lettings_log.id}/net-income") + visit("/lettings-logs/#{lettings_log.id}/net-income") fill_in("lettings-log-earnings-field", with: 18_000) choose("lettings-log-incfreq-2-field") click_button("Save and continue") @@ -17,7 +17,7 @@ module SchemesHelpers end def sign_in(user) - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index f189b2497..35a380fb5 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "Schemes scheme Features" do let!(:scheme_to_search) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") @@ -82,7 +82,7 @@ RSpec.describe "Schemes scheme Features" do allow(Devise).to receive(:friendly_token).and_return(confirmation_token) allow(notify_client).to receive(:send_email).and_return(true) allow(SecureRandom).to receive(:random_number).and_return(otp) - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") @@ -882,7 +882,7 @@ RSpec.describe "Schemes scheme Features" do let!(:schemes) { FactoryBot.create_list(:scheme, 5, owning_organisation_id: user.organisation_id) } before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") @@ -939,7 +939,7 @@ RSpec.describe "Schemes scheme Features" do FactoryBot.create(:location, scheme: schemes[1], startdate: nil) FactoryBot.create(:location, scheme: schemes[1], startdate: nil) FactoryBot.create(:location, scheme: schemes[1], startdate: Time.utc(2023, 6, 3)) - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") @@ -950,31 +950,31 @@ RSpec.describe "Schemes scheme Features" do end it "does not display the schemes without a location" do - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") expect(find("#lettings-log-scheme-id-field").all("option").count).to eq(4) end it "does not display the schemes with a location with a startdate in the future" do location.update!(startdate: Time.utc(2022, 7, 4)) - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") expect(find("#lettings-log-scheme-id-field").all("option").count).to eq(3) end it "does display the schemes with a location with a startdate in the past" do location.update!(startdate: Time.utc(2022, 5, 2)) - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") expect(find("#lettings-log-scheme-id-field").all("option").count).to eq(4) end it "does display the schemes with a location with a startdate being today" do location.update!(startdate: Time.utc(2022, 6, 3)) - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") expect(find("#lettings-log-scheme-id-field").all("option").count).to eq(4) end it "does display the schemes that are not completed" do schemes[2].update!(confirmed: false) - visit("/logs/#{lettings_log.id}/scheme") + visit("/lettings-logs/#{lettings_log.id}/scheme") expect(find("#lettings-log-scheme-id-field").all("option").count).to eq(3) end end diff --git a/spec/features/start_page_spec.rb b/spec/features/start_page_spec.rb index 625c4130b..569ea4cfa 100644 --- a/spec/features/start_page_spec.rb +++ b/spec/features/start_page_spec.rb @@ -12,7 +12,7 @@ RSpec.describe "Start Page Features" do it "takes you to logs" do visit("/") - expect(page).to have_current_path("/logs") + expect(page).to have_current_path("/lettings-logs") end end @@ -24,7 +24,7 @@ RSpec.describe "Start Page Features" do fill_in("user[email]", with: user.email) fill_in("user[password]", with: user.password) click_button("Sign in") - expect(page).to have_current_path("/logs") + expect(page).to have_current_path("/lettings-logs") end end end diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb index 89447da65..0c7f6c2c6 100644 --- a/spec/features/user_spec.rb +++ b/spec/features/user_spec.rb @@ -16,26 +16,26 @@ RSpec.describe "User Features" do context "when the user navigates to lettings logs" do it "is required to log in" do - visit("/logs") + visit("/lettings-logs") expect(page).to have_current_path("/account/sign-in") expect(page).to have_content("Sign in to your account to submit CORE data") end it "does not see the default devise error message" do - visit("/logs") + visit("/lettings-logs") expect(page).to have_no_content("You need to sign in or sign up before continuing.") end it "is redirected to lettings logs after signing in" do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") - expect(page).to have_current_path("/logs") + expect(page).to have_current_path("/lettings-logs") end it "can log out again", js: true do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -45,7 +45,7 @@ RSpec.describe "User Features" do end it "can log out again with js disabled" do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -57,7 +57,7 @@ RSpec.describe "User Features" do context "when the user has forgotten their password" do it "is redirected to the reset password page when they click the reset password link" do - visit("/logs") + visit("/lettings-logs") click_link("reset your password") expect(page).to have_current_path("/account/password/new") end @@ -121,7 +121,7 @@ RSpec.describe "User Features" do context "when the user is not logged in" do it "'Your account' link does not display" do - visit("/logs") + visit("/lettings-logs") expect(page).to have_no_link("Your account") end @@ -132,7 +132,7 @@ RSpec.describe "User Features" do fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") - expect(page).to have_current_path("/logs") + expect(page).to have_current_path("/lettings-logs") end it "tries to access account page, redirects to log in page" do @@ -143,7 +143,7 @@ RSpec.describe "User Features" do context "when the user is trying to log in with incorrect credentials" do it "shows a gov uk error summary and no flash message" do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "nonsense") click_button("Sign in") @@ -153,7 +153,7 @@ RSpec.describe "User Features" do end it "show specific field error messages if a field was omitted" do - visit("/logs") + visit("/lettings-logs") click_button("Sign in") expect(page).to have_selector("#error-summary-title") expect(page).to have_selector("#user-email-field-error") @@ -162,7 +162,7 @@ RSpec.describe "User Features" do end it "show specific field error messages if an invalid email address is entered" do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: "thisisn'tanemail") click_button("Sign in") expect(page).to have_selector("#error-summary-title") @@ -178,7 +178,7 @@ RSpec.describe "User Features" do end it "shows a gov uk error summary and no flash message" do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -191,7 +191,7 @@ RSpec.describe "User Features" do context "when signed in as a data provider" do context "when viewing your account" do before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -216,7 +216,7 @@ RSpec.describe "User Features" do context "when viewing users" do before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -232,7 +232,7 @@ RSpec.describe "User Features" do context "when viewing your organisation details" do before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -248,14 +248,14 @@ RSpec.describe "User Features" do context "when viewing your account" do before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") end it "shows 'Your account' link in navigation if logged in and redirect to correct page" do - visit("/logs") + visit("/lettings-logs") expect(page).to have_link("Your account") click_link("Your account") expect(page).to have_current_path("/account") @@ -295,7 +295,7 @@ RSpec.describe "User Features" do context "when adding a new user" do before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -343,7 +343,7 @@ RSpec.describe "User Features" do let!(:other_user) { FactoryBot.create(:user, name: "Other name", is_dpo: false, organisation: user.organisation) } before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -392,7 +392,7 @@ RSpec.describe "User Features" do let!(:other_user) { FactoryBot.create(:user, name: "Other name", organisation: user.organisation) } before do - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -429,7 +429,7 @@ RSpec.describe "User Features" do before do other_user.update!(confirmation_token: "abc") - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -465,7 +465,7 @@ RSpec.describe "User Features" do allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer) allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client) allow(notify_client).to receive(:send_email).and_return(true) - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: support_user.email) fill_in("user[password]", with: "pAssword1") end @@ -512,7 +512,7 @@ RSpec.describe "User Features" do click_button("Sign in") fill_in("code", with: otp) click_button("Submit") - expect(page).to have_content("Logs") + expect(page).to have_content("Lettings logs") end context "but it is more than 15 minutes old" do @@ -568,7 +568,7 @@ RSpec.describe "User Features" do fill_in("code", with: otp) click_button("Submit") click_link("Sign out") - visit("/logs") + visit("/lettings-logs") fill_in("user[email]", with: support_user.email) fill_in("user[password]", with: "pAssword1") click_button("Sign in") @@ -638,15 +638,15 @@ RSpec.describe "User Features" do end it "clears the previously selected organisation value" do - visit("/logs") + visit("/lettings-logs") choose("organisation-select-specific-org-field", allow_label_click: true) expect(page).to have_field("organisation-field", with: "") find("#organisation-field").click.native.send_keys("F", "i", "l", "t", :down, :enter) click_button("Apply filters") - expect(page).to have_current_path("/logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&user=all&organisation_select=specific_org&organisation=#{organisation.id}") + expect(page).to have_current_path("/lettings-logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&user=all&organisation_select=specific_org&organisation=#{organisation.id}") choose("organisation-select-all-field", allow_label_click: true) click_button("Apply filters") - expect(page).to have_current_path("/logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&user=all&organisation_select=all") + expect(page).to have_current_path("/lettings-logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&user=all&organisation_select=all") end end end diff --git a/spec/helpers/conditional_questions_helper_spec.rb b/spec/helpers/conditional_questions_helper_spec.rb index 6a83b1d0a..c1b8f3a7f 100644 --- a/spec/helpers/conditional_questions_helper_spec.rb +++ b/spec/helpers/conditional_questions_helper_spec.rb @@ -3,6 +3,11 @@ require "rails_helper" RSpec.describe ConditionalQuestionsHelper do let(:lettings_log) { FactoryBot.build(:lettings_log) } let(:page) { lettings_log.form.get_page("armed_forces") } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end describe "conditional questions for page" do let(:conditional_pages) { %w[leftreg] } diff --git a/spec/helpers/filters_helper_spec.rb b/spec/helpers/filters_helper_spec.rb index 02ad348fb..b19f044be 100644 --- a/spec/helpers/filters_helper_spec.rb +++ b/spec/helpers/filters_helper_spec.rb @@ -11,7 +11,7 @@ RSpec.describe FiltersHelper do context "when the filter is the user filter but session filters is empty" do before do - session[:lettings_logs_filters] = {}.to_json + session[:logs_filters] = {}.to_json end context "when looking at the all value" do @@ -24,7 +24,7 @@ RSpec.describe FiltersHelper do context "when one filter is selected" do before do - session[:lettings_logs_filters] = { "status": "in_progress" }.to_json + session[:logs_filters] = { "status": "in_progress" }.to_json end it "returns false for non selected filters" do @@ -38,7 +38,7 @@ RSpec.describe FiltersHelper do context "when support user is using the organisation filter" do before do - session[:lettings_logs_filters] = { "organisation": "1" }.to_json + session[:logs_filters] = { "organisation": "1" }.to_json end it "returns true for the parent organisation_select filter" do @@ -49,7 +49,7 @@ RSpec.describe FiltersHelper do context "when support user has not set the organisation_select filter" do before do - session[:lettings_logs_filters] = {}.to_json + session[:logs_filters] = {}.to_json end it "defaults to all organisations" do @@ -60,7 +60,7 @@ RSpec.describe FiltersHelper do context "when the specific organisation filter is not set" do before do - session[:lettings_logs_filters] = { "status" => [""], "years" => [""], "user" => "all" }.to_json + session[:logs_filters] = { "status" => [""], "years" => [""], "user" => "all" }.to_json end it "marks the all options as checked" do @@ -72,7 +72,7 @@ RSpec.describe FiltersHelper do describe "#selected_option" do before do - session[:lettings_logs_filters] = {}.to_json + session[:logs_filters] = {}.to_json end context "when nothing has been selected" do diff --git a/spec/helpers/navigation_items_helper_spec.rb b/spec/helpers/navigation_items_helper_spec.rb index 60d0f3d0e..fc656d024 100644 --- a/spec/helpers/navigation_items_helper_spec.rb +++ b/spec/helpers/navigation_items_helper_spec.rb @@ -7,324 +7,708 @@ RSpec.describe NavigationItemsHelper do let(:organisation_path) { "/organisations/#{current_user.organisation.id}" } describe "#primary items" do - context "when the user is a data coordinator" do - context "when the user is on the logs page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", users_path, false), - NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), - ] - end - - it "returns navigation items with the users item set as current" do - expect(primary_items("/logs", current_user)).to eq(expected_navigation_items) - end + context "when the sales log feature flag is enabled" do + before do + allow(Rails.env).to receive(:production?).and_return(true) end - context "when the user is on the users page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", users_path, true), - NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), - ] - end + context "when the user is a data coordinator" do + context "when the user is on the logs page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end - it "returns navigation items with the users item set as current" do - expect(primary_items(users_path, current_user)).to eq(expected_navigation_items) + it "returns navigation items with the users item set as current" do + expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on their organisation details page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", users_path, false), - NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true), - ] - end + context "when the user is on the users page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, true), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end - it "returns navigation items with the users item set as current" do - expect(primary_items("#{organisation_path}/details", current_user)).to eq(expected_navigation_items) + it "returns navigation items with the users item set as current" do + expect(primary_items(users_path, current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on the account page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), - NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), - ] + context "when the user is on their organisation details page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("#{organisation_path}/details", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/account", current_user)).to eq(expected_navigation_items) + context "when the user is on the account page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/account", current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on the individual user's page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), - NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), - ] + context "when the user is on the individual user's page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/users/1", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/users/1", current_user)).to eq(expected_navigation_items) + context "when the user is on the individual scheme's page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with Schemes item set as current" do + expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) + end end end - context "when the user is on the individual scheme's page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), - NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), - ] + context "when the user is a support user" do + let(:current_user) { FactoryBot.create(:user, :support) } + + context "when the user is on the logs page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with Schemes item set as current" do - expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) + context "when the user is on the users page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", true), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/users", current_user)).to eq(expected_navigation_items) + end end - end - end - context "when the user is a support user" do - let(:current_user) { FactoryBot.create(:user, :support) } - - context "when the user is on the logs page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", true), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - ] + context "when the user is on the account page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/account", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/logs", current_user)).to eq(expected_navigation_items) + context "when the user is on the Schemes page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/schemes", current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on the users page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", true), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - ] + context "when the user is on the individual user's page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", true), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/users/1", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/users", current_user)).to eq(expected_navigation_items) + context "when the user is on the individual scheme's page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), + ] + end + + let(:expected_scheme_items) do + [ + NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", true), + NavigationItemsHelper::NavigationItem.new("Locations", "/schemes/1/locations", false), + ] + end + + it "returns navigation items with Schemes item set as current" do + expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) + expect(scheme_items("/schemes/1", 1, "Locations")).to eq(expected_scheme_items) + end end - end - context "when the user is on the account page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - ] + context "when the user is on the scheme locations page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), + ] + end + + let(:expected_scheme_items) do + [ + NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", false), + NavigationItemsHelper::NavigationItem.new("Locations", "/schemes/1/locations", true), + ] + end + + it "returns navigation items with Schemes item set as current" do + expect(primary_items("/schemes/1/locations", current_user)).to eq(expected_navigation_items) + expect(scheme_items("/schemes/1/locations", 1, "Locations")).to eq(expected_scheme_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/account", current_user)).to eq(expected_navigation_items) + context "when the user is on the specific organisation's page" do + context "when the user is on organisation logs page" do + let(:required_sub_path) { "lettings-logs" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", true), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + ] + end + + it "returns navigation items with the logs item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end + + context "when the user is on organisation users page" do + let(:required_sub_path) { "users" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + ] + end + + it "returns navigation items with the logs item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end + + context "when the user is on organisation schemes page" do + let(:required_sub_path) { "schemes" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", true), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + ] + end + + it "returns navigation items with the schemes item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end + + context "when the user is on organisation details page" do + let(:required_sub_path) { "details" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true), + ] + end + + it "returns navigation items with the logs item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end end end + end + + context "when the sales log feature flag is disabled" do + context "when the user is a data coordinator" do + context "when the user is on the lettings logs page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end - context "when the user is on the Schemes page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), - ] + it "returns navigation items with the users item set as current" do + expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/schemes", current_user)).to eq(expected_navigation_items) + context "when the user is on the sales logs page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", true), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/sales-logs", current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on the individual user's page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", true), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), - ] + context "when the user is on the users page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, true), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items(users_path, current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with the users item set as current" do - expect(primary_items("/users/1", current_user)).to eq(expected_navigation_items) + context "when the user is on their organisation details page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", users_path, false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, true), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("#{organisation_path}/details", current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on the individual scheme's page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), - ] + context "when the user is on the account page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/account", current_user)).to eq(expected_navigation_items) + end end - let(:expected_scheme_items) do - [ - NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", true), - NavigationItemsHelper::NavigationItem.new("Locations", "/schemes/1/locations", false), - ] + context "when the user is on the individual user's page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/users/1", current_user)).to eq(expected_navigation_items) + end end - it "returns navigation items with Schemes item set as current" do - expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) - expect(scheme_items("/schemes/1", 1, "Locations")).to eq(expected_scheme_items) + context "when the user is on the individual scheme's page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About your organisation", organisation_path, false), + ] + end + + it "returns navigation items with Schemes item set as current" do + expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) + end end end - context "when the user is on the scheme locations page" do - let(:expected_navigation_items) do - [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), - NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), - ] - end + context "when the user is a support user" do + let(:current_user) { FactoryBot.create(:user, :support) } - let(:expected_scheme_items) do - [ - NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", false), - NavigationItemsHelper::NavigationItem.new("Locations", "/schemes/1/locations", true), - ] - end + context "when the user is on the lettings logs page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", true), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end - it "returns navigation items with Schemes item set as current" do - expect(primary_items("/schemes/1/locations", current_user)).to eq(expected_navigation_items) - expect(scheme_items("/schemes/1/locations", 1, "Locations")).to eq(expected_scheme_items) + it "returns navigation items with the users item set as current" do + expect(primary_items("/lettings-logs", current_user)).to eq(expected_navigation_items) + end end - end - context "when the user is on the specific organisation's page" do - context "when the user is on organisation logs page" do - let(:required_sub_path) { "logs" } + context "when the user is on the sales logs page" do let(:expected_navigation_items) do [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", true), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), ] end - let(:expected_secondary_navigation_items) do + it "returns navigation items with the users item set as current" do + expect(primary_items("/sales-logs", current_user)).to eq(expected_navigation_items) + end + end + + context "when the user is on the users page" do + let(:expected_navigation_items) do [ - NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", true), - NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), - NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", true), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), ] end - it "returns navigation items with the logs item set as current" do - expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) - expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + it "returns navigation items with the users item set as current" do + expect(primary_items("/users", current_user)).to eq(expected_navigation_items) end end - context "when the user is on organisation users page" do - let(:required_sub_path) { "users" } + context "when the user is on the account page" do let(:expected_navigation_items) do [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), ] end - let(:expected_secondary_navigation_items) do + it "returns navigation items with the users item set as current" do + expect(primary_items("/account", current_user)).to eq(expected_navigation_items) + end + end + + context "when the user is on the Schemes page" do + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), + ] + end + + it "returns navigation items with the users item set as current" do + expect(primary_items("/schemes", current_user)).to eq(expected_navigation_items) + end + end + + context "when the user is on the individual user's page" do + let(:expected_navigation_items) do [ - NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), - NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), + NavigationItemsHelper::NavigationItem.new("Users", "/users", true), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), ] end - it "returns navigation items with the logs item set as current" do - expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) - expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + it "returns navigation items with the users item set as current" do + expect(primary_items("/users/1", current_user)).to eq(expected_navigation_items) end end - context "when the user is on organisation schemes page" do - let(:required_sub_path) { "schemes" } + context "when the user is on the individual scheme's page" do let(:expected_navigation_items) do [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), ] end - let(:expected_secondary_navigation_items) do + let(:expected_scheme_items) do [ - NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", true), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), - NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", true), + NavigationItemsHelper::NavigationItem.new("Locations", "/schemes/1/locations", false), ] end - it "returns navigation items with the schemes item set as current" do - expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) - expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + it "returns navigation items with Schemes item set as current" do + expect(primary_items("/schemes/1", current_user)).to eq(expected_navigation_items) + expect(scheme_items("/schemes/1", 1, "Locations")).to eq(expected_scheme_items) end end - context "when the user is on organisation details page" do - let(:required_sub_path) { "details" } + context "when the user is on the scheme locations page" do let(:expected_navigation_items) do [ - NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", false), NavigationItemsHelper::NavigationItem.new("Users", "/users", false), - NavigationItemsHelper::NavigationItem.new("Logs", "/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", true), ] end - let(:expected_secondary_navigation_items) do + let(:expected_scheme_items) do [ - NavigationItemsHelper::NavigationItem.new("Logs", "/organisations/#{current_user.organisation.id}/logs", false), - NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), - NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), - NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true), + NavigationItemsHelper::NavigationItem.new("Scheme", "/schemes/1", false), + NavigationItemsHelper::NavigationItem.new("Locations", "/schemes/1/locations", true), ] end - it "returns navigation items with the logs item set as current" do - expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) - expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + it "returns navigation items with Schemes item set as current" do + expect(primary_items("/schemes/1/locations", current_user)).to eq(expected_navigation_items) + expect(scheme_items("/schemes/1/locations", 1, "Locations")).to eq(expected_scheme_items) + end + end + + context "when the user is on the specific organisation's page" do + context "when the user is on organisation logs page" do + let(:required_sub_path) { "lettings-logs" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", true), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/organisations/#{current_user.organisation.id}/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + ] + end + + it "returns navigation items with the logs item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end + + context "when the user is on organisation users page" do + let(:required_sub_path) { "users" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/organisations/#{current_user.organisation.id}/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", true), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + ] + end + + it "returns navigation items with the logs item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end + + context "when the user is on organisation schemes page" do + let(:required_sub_path) { "schemes" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/organisations/#{current_user.organisation.id}/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", true), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", false), + ] + end + + it "returns navigation items with the schemes item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end + end + + context "when the user is on organisation details page" do + let(:required_sub_path) { "details" } + let(:expected_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Organisations", "/organisations", true), + NavigationItemsHelper::NavigationItem.new("Users", "/users", false), + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/schemes", false), + ] + end + + let(:expected_secondary_navigation_items) do + [ + NavigationItemsHelper::NavigationItem.new("Lettings logs", "/organisations/#{current_user.organisation.id}/lettings-logs", false), + NavigationItemsHelper::NavigationItem.new("Sales logs", "/organisations/#{current_user.organisation.id}/sales-logs", false), + NavigationItemsHelper::NavigationItem.new("Schemes", "/organisations/#{current_user.organisation.id}/schemes", false), + NavigationItemsHelper::NavigationItem.new("Users", "/organisations/#{current_user.organisation.id}/users", false), + NavigationItemsHelper::NavigationItem.new("About this organisation", "/organisations/#{current_user.organisation.id}", true), + ] + end + + it "returns navigation items with the logs item set as current" do + expect(primary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user)).to eq(expected_navigation_items) + expect(secondary_items("/organisations/#{current_user.organisation.id}/#{required_sub_path}", current_user.organisation.id)).to eq(expected_secondary_navigation_items) + end end end end diff --git a/spec/helpers/question_attribute_helper_spec.rb b/spec/helpers/question_attribute_helper_spec.rb index 393db953e..96f2f7ef6 100644 --- a/spec/helpers/question_attribute_helper_spec.rb +++ b/spec/helpers/question_attribute_helper_spec.rb @@ -4,6 +4,11 @@ RSpec.describe QuestionAttributeHelper do let(:lettings_log) { FactoryBot.build(:lettings_log) } let(:form) { lettings_log.form } let(:questions) { form.get_page("rent").questions } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end describe "html attributes" do it "returns empty hash if fields-to-add or result-field are empty " do diff --git a/spec/helpers/tasklist_helper_spec.rb b/spec/helpers/tasklist_helper_spec.rb index 9e770e1ff..ff4902a55 100644 --- a/spec/helpers/tasklist_helper_spec.rb +++ b/spec/helpers/tasklist_helper_spec.rb @@ -3,6 +3,11 @@ require "rails_helper" RSpec.describe TasklistHelper do let(:empty_lettings_log) { FactoryBot.create(:lettings_log) } let(:lettings_log) { FactoryBot.create(:lettings_log, :in_progress, needstype: 1) } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end describe "get next incomplete section" do it "returns the first subsection name if it is not completed" do diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb new file mode 100644 index 000000000..1e3860af9 --- /dev/null +++ b/spec/jobs/email_csv_job_spec.rb @@ -0,0 +1,164 @@ +require "rails_helper" + +describe EmailCsvJob do + include Helpers + + test_url = :test_url + + let(:job) { described_class.new } + let(:user) { FactoryBot.create(:user) } + let(:organisation) { user.organisation } + let(:other_organisation) { FactoryBot.create(:organisation) } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end + + context "when a log exists" do + let!(:lettings_log) do + FactoryBot.create( + :lettings_log, + owning_organisation: organisation, + managing_organisation: organisation, + ecstat1: 1, + ) + end + + let(:storage_service) { instance_double(Storage::S3Service) } + let(:mailer) { instance_double(CsvDownloadMailer) } + + before do + FactoryBot.create(:lettings_log, + :completed, + owning_organisation: organisation, + managing_organisation: organisation, + created_by: user) + + allow(Storage::S3Service).to receive(:new).and_return(storage_service) + allow(storage_service).to receive(:write_file) + allow(storage_service).to receive(:get_presigned_url).and_return(test_url) + + allow(CsvDownloadMailer).to receive(:new).and_return(mailer) + allow(mailer).to receive(:send_csv_download_mail) + end + + it "uses an appropriate filename in S3" do + expect(storage_service).to receive(:write_file).with(/logs-.*\.csv/, anything) + job.perform(user) + end + + it "includes the organisation name in the filename when one is provided" do + expect(storage_service).to receive(:write_file).with(/logs-#{organisation.name}-.*\.csv/, anything) + job.perform(user, nil, {}, nil, organisation) + end + + it "sends an E-mail with the presigned URL and duration" do + expect(mailer).to receive(:send_csv_download_mail).with(user, test_url, instance_of(Integer)) + job.perform(user) + end + + context "when writing to S3" do + before do + FactoryBot.create_list(:lettings_log, 4, owning_organisation: other_organisation) + end + + def expect_csv + expect(storage_service).to receive(:write_file) do |_filename, data| + # Ignore byte order marker + csv = CSV.parse(data[1..]) + yield(csv) + end + end + + it "writes CSV data with headers" do + expect_csv do |csv| + expect(csv.first.first).to eq("id") + expect(csv.second.first).to eq(lettings_log.id.to_s) + end + + job.perform(user) + end + + context "when there is no organisation provided" do + it "only writes logs from the user's organisation" do + expect_csv do |csv| + # Headings + 2 rows + expect(csv.count).to eq(3) + end + + job.perform(user) + end + end + + context "when the user is support and an organisation is provided" do + let(:user) { FactoryBot.create(:user, :support) } + + it "only writes logs from that organisation" do + expect_csv do |csv| + # other organisation => Headings + 4 rows + expect(csv.count).to eq(5) + end + + job.perform(user, nil, {}, nil, other_organisation) + end + end + + it "writes answer labels rather than values" do + expect_csv do |csv| + expect(csv.second[15]).to eq("Full-time – 30 hours or more") + end + + job.perform(user) + end + + it "writes filtered logs" do + expect_csv do |csv| + expect(csv.count).to eq(2) + end + + job.perform(user, nil, { status: "completed" }) + end + + it "writes searched logs" do + expect_csv do |csv| + expect(csv.count).to eq(LettingsLog.search_by(lettings_log.id.to_s).count + 1) + end + + job.perform(user, lettings_log.id.to_s) + end + + context "when both filter and search applied" do + let(:postcode) { "XX1 1TG" } + + before do + FactoryBot.create(:lettings_log, :in_progress, postcode_full: postcode, owning_organisation: organisation, created_by: user) + FactoryBot.create(:lettings_log, :completed, postcode_full: postcode, owning_organisation: organisation, created_by: user) + end + + it "downloads logs matching both csv and filter logs" do + expect_csv do |csv| + expect(csv.count).to eq(2) + end + + job.perform(user, postcode, { status: "completed" }) + end + end + + context "when there are more than 20 logs" do + before do + FactoryBot.create_list(:lettings_log, 26, owning_organisation: organisation) + end + + it "does not paginate, it downloads all the user's logs" do + expect_csv do |csv| + # Heading + 2 + 26 + expect(csv.count).to eq(29) + end + + job.perform(user) + end + end + end + end +end diff --git a/spec/mailers/csv_download_mailer_spec.rb b/spec/mailers/csv_download_mailer_spec.rb new file mode 100644 index 000000000..6c145c508 --- /dev/null +++ b/spec/mailers/csv_download_mailer_spec.rb @@ -0,0 +1,30 @@ +require "rails_helper" + +RSpec.describe CsvDownloadMailer do + describe "#send_csv_download_mail" do + let(:notify_client) { instance_double(Notifications::Client) } + let(:user) { FactoryBot.create(:user, email: "user@example.com") } + + before do + allow(Notifications::Client).to receive(:new).and_return(notify_client) + allow(notify_client).to receive(:send_email).and_return(true) + end + + it "sends a CSV download E-mail via notify" do + link = :link + duration = 20.minutes.to_i + + expect(notify_client).to receive(:send_email).with( + email_address: user.email, + template_id: described_class::CSV_DOWNLOAD_TEMPLATE_ID, + personalisation: { + name: user.name, + link:, + duration: "20 minutes", + }, + ) + + described_class.new.send_csv_download_mail(user, link, duration) + end + end +end diff --git a/spec/models/form/setup/pages/created_by_spec.rb b/spec/models/form/common/pages/created_by_spec.rb similarity index 95% rename from spec/models/form/setup/pages/created_by_spec.rb rename to spec/models/form/common/pages/created_by_spec.rb index 7ae803d0d..1f11a5889 100644 --- a/spec/models/form/setup/pages/created_by_spec.rb +++ b/spec/models/form/common/pages/created_by_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::CreatedBy, type: :model do +RSpec.describe Form::Common::Pages::CreatedBy, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/organisation_spec.rb b/spec/models/form/common/pages/organisation_spec.rb similarity index 95% rename from spec/models/form/setup/pages/organisation_spec.rb rename to spec/models/form/common/pages/organisation_spec.rb index 50d8b90fd..ae7a1027c 100644 --- a/spec/models/form/setup/pages/organisation_spec.rb +++ b/spec/models/form/common/pages/organisation_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::Organisation, type: :model do +RSpec.describe Form::Common::Pages::Organisation, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/questions/created_by_id_spec.rb b/spec/models/form/common/questions/created_by_id_spec.rb similarity index 97% rename from spec/models/form/setup/questions/created_by_id_spec.rb rename to spec/models/form/common/questions/created_by_id_spec.rb index 95203a555..b00e21f90 100644 --- a/spec/models/form/setup/questions/created_by_id_spec.rb +++ b/spec/models/form/common/questions/created_by_id_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::CreatedById, type: :model do +RSpec.describe Form::Common::Questions::CreatedById, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/owning_organisation_id_spec.rb b/spec/models/form/common/questions/owning_organisation_id_spec.rb similarity index 97% rename from spec/models/form/setup/questions/owning_organisation_id_spec.rb rename to spec/models/form/common/questions/owning_organisation_id_spec.rb index 8c74a169d..9ee32e342 100644 --- a/spec/models/form/setup/questions/owning_organisation_id_spec.rb +++ b/spec/models/form/common/questions/owning_organisation_id_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::OwningOrganisationId, type: :model do +RSpec.describe Form::Common::Questions::OwningOrganisationId, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/pages/location_spec.rb b/spec/models/form/lettings/pages/location_spec.rb similarity index 92% rename from spec/models/form/setup/pages/location_spec.rb rename to spec/models/form/lettings/pages/location_spec.rb index 10db3d331..bdf08c244 100644 --- a/spec/models/form/setup/pages/location_spec.rb +++ b/spec/models/form/lettings/pages/location_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::Location, type: :model do +RSpec.describe Form::Lettings::Pages::Location, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/needs_type_spec.rb b/spec/models/form/lettings/pages/needs_type_spec.rb similarity index 90% rename from spec/models/form/setup/pages/needs_type_spec.rb rename to spec/models/form/lettings/pages/needs_type_spec.rb index 772c2a11d..37978fdf8 100644 --- a/spec/models/form/setup/pages/needs_type_spec.rb +++ b/spec/models/form/lettings/pages/needs_type_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::NeedsType, type: :model do +RSpec.describe Form::Lettings::Pages::NeedsType, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/property_reference_spec.rb b/spec/models/form/lettings/pages/property_reference_spec.rb similarity index 90% rename from spec/models/form/setup/pages/property_reference_spec.rb rename to spec/models/form/lettings/pages/property_reference_spec.rb index 555ee97f2..cb2f2c76a 100644 --- a/spec/models/form/setup/pages/property_reference_spec.rb +++ b/spec/models/form/lettings/pages/property_reference_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::PropertyReference, type: :model do +RSpec.describe Form::Lettings::Pages::PropertyReference, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/renewal_spec.rb b/spec/models/form/lettings/pages/renewal_spec.rb similarity index 91% rename from spec/models/form/setup/pages/renewal_spec.rb rename to spec/models/form/lettings/pages/renewal_spec.rb index f37cb3ef1..0fceb1a99 100644 --- a/spec/models/form/setup/pages/renewal_spec.rb +++ b/spec/models/form/lettings/pages/renewal_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::Renewal, type: :model do +RSpec.describe Form::Lettings::Pages::Renewal, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/rent_type_spec.rb b/spec/models/form/lettings/pages/rent_type_spec.rb similarity index 91% rename from spec/models/form/setup/pages/rent_type_spec.rb rename to spec/models/form/lettings/pages/rent_type_spec.rb index d558ea75c..6541d542d 100644 --- a/spec/models/form/setup/pages/rent_type_spec.rb +++ b/spec/models/form/lettings/pages/rent_type_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::RentType, type: :model do +RSpec.describe Form::Lettings::Pages::RentType, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/scheme_spec.rb b/spec/models/form/lettings/pages/scheme_spec.rb similarity index 91% rename from spec/models/form/setup/pages/scheme_spec.rb rename to spec/models/form/lettings/pages/scheme_spec.rb index 7c3566009..d369caf72 100644 --- a/spec/models/form/setup/pages/scheme_spec.rb +++ b/spec/models/form/lettings/pages/scheme_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::Scheme, type: :model do +RSpec.describe Form::Lettings::Pages::Scheme, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/tenancy_start_date_spec.rb b/spec/models/form/lettings/pages/tenancy_start_date_spec.rb similarity index 91% rename from spec/models/form/setup/pages/tenancy_start_date_spec.rb rename to spec/models/form/lettings/pages/tenancy_start_date_spec.rb index 546038287..99597e428 100644 --- a/spec/models/form/setup/pages/tenancy_start_date_spec.rb +++ b/spec/models/form/lettings/pages/tenancy_start_date_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::TenancyStartDate, type: :model do +RSpec.describe Form::Lettings::Pages::TenancyStartDate, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/pages/tenant_code_spec.rb b/spec/models/form/lettings/pages/tenant_code_spec.rb similarity index 91% rename from spec/models/form/setup/pages/tenant_code_spec.rb rename to spec/models/form/lettings/pages/tenant_code_spec.rb index dea2eb73d..e91303bc3 100644 --- a/spec/models/form/setup/pages/tenant_code_spec.rb +++ b/spec/models/form/lettings/pages/tenant_code_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Pages::TenantCode, type: :model do +RSpec.describe Form::Lettings::Pages::TenantCode, type: :model do subject(:page) { described_class.new(page_id, page_definition, subsection) } let(:page_id) { nil } diff --git a/spec/models/form/setup/questions/irproduct_other_spec.rb b/spec/models/form/lettings/questions/irproduct_other_spec.rb similarity index 91% rename from spec/models/form/setup/questions/irproduct_other_spec.rb rename to spec/models/form/lettings/questions/irproduct_other_spec.rb index 1d3d9ef3d..45182104a 100644 --- a/spec/models/form/setup/questions/irproduct_other_spec.rb +++ b/spec/models/form/lettings/questions/irproduct_other_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::IrproductOther, type: :model do +RSpec.describe Form::Lettings::Questions::IrproductOther, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/location_id_spec.rb b/spec/models/form/lettings/questions/location_id_spec.rb similarity index 97% rename from spec/models/form/setup/questions/location_id_spec.rb rename to spec/models/form/lettings/questions/location_id_spec.rb index 90822b41c..ce723ce38 100644 --- a/spec/models/form/setup/questions/location_id_spec.rb +++ b/spec/models/form/lettings/questions/location_id_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::LocationId, type: :model do +RSpec.describe Form::Lettings::Questions::LocationId, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/needs_type_spec.rb b/spec/models/form/lettings/questions/needs_type_spec.rb similarity index 93% rename from spec/models/form/setup/questions/needs_type_spec.rb rename to spec/models/form/lettings/questions/needs_type_spec.rb index c10fcb3b1..343a10ffd 100644 --- a/spec/models/form/setup/questions/needs_type_spec.rb +++ b/spec/models/form/lettings/questions/needs_type_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::NeedsType, type: :model do +RSpec.describe Form::Lettings::Questions::NeedsType, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/property_reference_spec.rb b/spec/models/form/lettings/questions/property_reference_spec.rb similarity index 93% rename from spec/models/form/setup/questions/property_reference_spec.rb rename to spec/models/form/lettings/questions/property_reference_spec.rb index 6e9f8b087..82f60c421 100644 --- a/spec/models/form/setup/questions/property_reference_spec.rb +++ b/spec/models/form/lettings/questions/property_reference_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::PropertyReference, type: :model do +RSpec.describe Form::Lettings::Questions::PropertyReference, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/renewal_spec.rb b/spec/models/form/lettings/questions/renewal_spec.rb similarity index 93% rename from spec/models/form/setup/questions/renewal_spec.rb rename to spec/models/form/lettings/questions/renewal_spec.rb index 5ecb5daef..961d65153 100644 --- a/spec/models/form/setup/questions/renewal_spec.rb +++ b/spec/models/form/lettings/questions/renewal_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::Renewal, type: :model do +RSpec.describe Form::Lettings::Questions::Renewal, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/rent_type_spec.rb b/spec/models/form/lettings/questions/rent_type_spec.rb similarity index 95% rename from spec/models/form/setup/questions/rent_type_spec.rb rename to spec/models/form/lettings/questions/rent_type_spec.rb index e52a89627..45906ec81 100644 --- a/spec/models/form/setup/questions/rent_type_spec.rb +++ b/spec/models/form/lettings/questions/rent_type_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::RentType, type: :model do +RSpec.describe Form::Lettings::Questions::RentType, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/scheme_id_spec.rb b/spec/models/form/lettings/questions/scheme_id_spec.rb similarity index 98% rename from spec/models/form/setup/questions/scheme_id_spec.rb rename to spec/models/form/lettings/questions/scheme_id_spec.rb index 29cfa9d0c..280bca5c8 100644 --- a/spec/models/form/setup/questions/scheme_id_spec.rb +++ b/spec/models/form/lettings/questions/scheme_id_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::SchemeId, type: :model do +RSpec.describe Form::Lettings::Questions::SchemeId, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/tenancy_start_date_spec.rb b/spec/models/form/lettings/questions/tenancy_start_date_spec.rb similarity index 90% rename from spec/models/form/setup/questions/tenancy_start_date_spec.rb rename to spec/models/form/lettings/questions/tenancy_start_date_spec.rb index 8fee79422..56031ffd9 100644 --- a/spec/models/form/setup/questions/tenancy_start_date_spec.rb +++ b/spec/models/form/lettings/questions/tenancy_start_date_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::TenancyStartDate, type: :model do +RSpec.describe Form::Lettings::Questions::TenancyStartDate, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/setup/questions/tenant_code_spec.rb b/spec/models/form/lettings/questions/tenant_code_spec.rb similarity index 93% rename from spec/models/form/setup/questions/tenant_code_spec.rb rename to spec/models/form/lettings/questions/tenant_code_spec.rb index 7a38f230f..a60383119 100644 --- a/spec/models/form/setup/questions/tenant_code_spec.rb +++ b/spec/models/form/lettings/questions/tenant_code_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Questions::TenantCode, type: :model do +RSpec.describe Form::Lettings::Questions::TenantCode, type: :model do subject(:question) { described_class.new(question_id, question_definition, page) } let(:question_id) { nil } diff --git a/spec/models/form/lettings/sections/setup_spec.rb b/spec/models/form/lettings/sections/setup_spec.rb new file mode 100644 index 000000000..6db166bd6 --- /dev/null +++ b/spec/models/form/lettings/sections/setup_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +RSpec.describe Form::Lettings::Sections::Setup, type: :model do + subject(:setup) { described_class.new(section_id, section_definition, form) } + + let(:section_id) { nil } + let(:section_definition) { nil } + let(:form) { instance_double(Form) } + + it "has correct form" do + expect(setup.form).to eq(form) + end + + it "has correct subsections" do + expect(setup.subsections.map(&:id)).to eq(%w[setup]) + end + + it "has the correct id" do + expect(setup.id).to eq("setup") + end + + it "has the correct label" do + expect(setup.label).to eq("Before you start") + end + + it "has the correct description" do + expect(setup.description).to eq("") + end +end diff --git a/spec/models/form/setup/subsections/setup_spec.rb b/spec/models/form/lettings/subsections/setup_spec.rb similarity index 84% rename from spec/models/form/setup/subsections/setup_spec.rb rename to spec/models/form/lettings/subsections/setup_spec.rb index 08744eadc..3e2fba32f 100644 --- a/spec/models/form/setup/subsections/setup_spec.rb +++ b/spec/models/form/lettings/subsections/setup_spec.rb @@ -1,11 +1,11 @@ require "rails_helper" -RSpec.describe Form::Setup::Subsections::Setup, type: :model do +RSpec.describe Form::Lettings::Subsections::Setup, type: :model do subject(:setup) { described_class.new(subsection_id, subsection_definition, section) } let(:subsection_id) { nil } let(:subsection_definition) { nil } - let(:section) { instance_double(Form::Setup::Sections::Setup) } + let(:section) { instance_double(Form::Lettings::Sections::Setup) } it "has correct section" do expect(setup.section).to eq(section) diff --git a/spec/models/form/page_spec.rb b/spec/models/form/page_spec.rb index e4a780beb..bb448aca6 100644 --- a/spec/models/form/page_spec.rb +++ b/spec/models/form/page_spec.rb @@ -14,6 +14,11 @@ RSpec.describe Form::Page, type: :model do let(:subsection) { Form::Subsection.new(subsection_id, subsection_definition, section) } let(:page_id) { "net_income" } let(:page_definition) { subsection_definition["pages"][page_id] } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end it "has an id" do expect(page.id).to eq(page_id) diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb index d1e2702fd..da59ad354 100644 --- a/spec/models/form/question_spec.rb +++ b/spec/models/form/question_spec.rb @@ -16,6 +16,11 @@ RSpec.describe Form::Question, type: :model do let(:page) { Form::Page.new(page_id, page_definition, subsection) } let(:question_id) { "earnings" } let(:question_definition) { page_definition["questions"][question_id] } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end it "has an id" do expect(question.id).to eq(question_id) @@ -233,7 +238,7 @@ RSpec.describe Form::Question, type: :model do it "has an update answer link href helper" do lettings_log.id = 1 - expect(question.action_href(lettings_log, page.id)).to eq("/logs/1/net-income?referrer=check_answers") + expect(question.action_href(lettings_log, page.id)).to eq("/lettings-logs/1/net-income?referrer=check_answers") end context "when the question has an inferred answer" do diff --git a/spec/models/form/sales/pages/purchaser_code_spec.rb b/spec/models/form/sales/pages/purchaser_code_spec.rb new file mode 100644 index 000000000..05ee6becc --- /dev/null +++ b/spec/models/form/sales/pages/purchaser_code_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::PurchaserCode, 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) } + + 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[purchid]) + end + + it "has the correct id" do + expect(page.id).to eq("purchaser_code") + 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 +end diff --git a/spec/models/form/sales/pages/sale_date_spec.rb b/spec/models/form/sales/pages/sale_date_spec.rb new file mode 100644 index 000000000..7f78182a8 --- /dev/null +++ b/spec/models/form/sales/pages/sale_date_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::SaleDate, 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) } + + 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[saledate]) + end + + it "has the correct id" do + expect(page.id).to eq("completion_date") + 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 +end diff --git a/spec/models/form/sales/pages/shared_ownership_type_spec.rb b/spec/models/form/sales/pages/shared_ownership_type_spec.rb new file mode 100644 index 000000000..7a7f71f8e --- /dev/null +++ b/spec/models/form/sales/pages/shared_ownership_type_spec.rb @@ -0,0 +1,35 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Pages::SharedOwnershipType, 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) } + + 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[type]) + end + + it "has the correct id" do + expect(page.id).to eq("shared_ownership_type") + 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 correct depends_on" do + expect(page.depends_on).to eq([{ + "ownershipsch" => 1, + }]) + end +end diff --git a/spec/models/form/sales/questions/purchaser_code_spec.rb b/spec/models/form/sales/questions/purchaser_code_spec.rb new file mode 100644 index 000000000..8f75d676b --- /dev/null +++ b/spec/models/form/sales/questions/purchaser_code_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Questions::PurchaserCode, 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) } + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("purchid") + end + + it "has the correct header" do + expect(question.header).to eq("What is the purchaser code?") + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq("This is how you usually refer to the purchaser on your own systems.") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Purchaser code") + end + + it "has the correct type" do + expect(question.type).to eq("text") + end + + it "has the correct width" do + expect(question.width).to eq(10) + end + + it "is not marked as derived" do + expect(question.derived?).to be false + end +end diff --git a/spec/models/form/sales/questions/sale_date_spec.rb b/spec/models/form/sales/questions/sale_date_spec.rb new file mode 100644 index 000000000..0f368abbb --- /dev/null +++ b/spec/models/form/sales/questions/sale_date_spec.rb @@ -0,0 +1,33 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Questions::SaleDate, 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) } + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("saledate") + end + + it "has the correct header" do + expect(question.header).to eq("What is the sale completion date?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Sale completion date") + end + + it "has the correct type" do + expect(question.type).to eq("date") + end + + it "is not marked as derived" do + expect(question.derived?).to be false + end +end diff --git a/spec/models/form/sales/questions/shared_ownership_type_spec.rb b/spec/models/form/sales/questions/shared_ownership_type_spec.rb new file mode 100644 index 000000000..c164c5794 --- /dev/null +++ b/spec/models/form/sales/questions/shared_ownership_type_spec.rb @@ -0,0 +1,49 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Questions::SharedOwnershipType, 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) } + + it "has correct page" do + expect(question.page).to eq(page) + end + + it "has the correct id" do + expect(question.id).to eq("type") + end + + it "has the correct header" do + expect(question.header).to eq("What is the type of shared ownership sale?") + end + + it "has the correct check_answer_label" do + expect(question.check_answer_label).to eq("Type of shared ownership sale") + end + + it "has the correct type" do + expect(question.type).to eq("radio") + end + + it "is not marked as derived" do + expect(question.derived?).to be false + end + + it "has the correct hint_text" do + expect(question.hint_text).to eq("A shared ownership sale is when the purchaser buys up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion") + end + + it "has the correct answer_options" do + expect(question.answer_options).to eq({ + "2" => { "value" => "Shared Ownership" }, + "24" => { "value" => "Old Persons Shared Ownership" }, + "18" => { "value" => "Social HomeBuy (shared ownership purchase)" }, + "16" => { "value" => "Home Ownership for people with Long Term Disabilities (HOLD)" }, + "28" => { "value" => "Rent to Buy - Shared Ownership" }, + "31" => { "value" => "Right to Shared Ownership" }, + "30" => { "value" => "Shared Ownership - 2021 model lease" }, + }) + end +end diff --git a/spec/models/form/setup/sections/setup_spec.rb b/spec/models/form/sales/sections/setup_spec.rb similarity index 91% rename from spec/models/form/setup/sections/setup_spec.rb rename to spec/models/form/sales/sections/setup_spec.rb index 39843e1ec..f3dde50be 100644 --- a/spec/models/form/setup/sections/setup_spec.rb +++ b/spec/models/form/sales/sections/setup_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -RSpec.describe Form::Setup::Sections::Setup, type: :model do +RSpec.describe Form::Sales::Sections::Setup, type: :model do subject(:setup) { described_class.new(section_id, section_definition, form) } let(:section_id) { nil } diff --git a/spec/models/form/sales/subsections/setup_spec.rb b/spec/models/form/sales/subsections/setup_spec.rb new file mode 100644 index 000000000..80b27e261 --- /dev/null +++ b/spec/models/form/sales/subsections/setup_spec.rb @@ -0,0 +1,27 @@ +require "rails_helper" + +RSpec.describe Form::Sales::Subsections::Setup, type: :model do + subject(:setup) { described_class.new(subsection_id, subsection_definition, section) } + + let(:subsection_id) { nil } + let(:subsection_definition) { nil } + let(:section) { instance_double(Form::Sales::Sections::Setup) } + + it "has correct section" do + expect(setup.section).to eq(section) + end + + it "has correct pages" do + expect(setup.pages.map(&:id)).to eq( + %w[organisation created_by completion_date purchaser_code shared_ownership_type], + ) + end + + it "has the correct id" do + expect(setup.id).to eq("setup") + end + + it "has the correct label" do + expect(setup.label).to eq("Set up this sales log") + end +end diff --git a/spec/models/form/section_spec.rb b/spec/models/form/section_spec.rb index 52f6effc8..2585b67a8 100644 --- a/spec/models/form/section_spec.rb +++ b/spec/models/form/section_spec.rb @@ -7,6 +7,11 @@ RSpec.describe Form::Section, type: :model do let(:form) { lettings_log.form } let(:section_id) { "household" } let(:section_definition) { form.form_definition["sections"][section_id] } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end it "has an id" do expect(section.id).to eq(section_id) diff --git a/spec/models/form/subsection_spec.rb b/spec/models/form/subsection_spec.rb index a119a212e..46880a33a 100644 --- a/spec/models/form/subsection_spec.rb +++ b/spec/models/form/subsection_spec.rb @@ -11,9 +11,11 @@ RSpec.describe Form::Subsection, type: :model do let(:section) { Form::Section.new(section_id, section_definition, form) } let(:subsection_id) { "household_characteristics" } let(:subsection_definition) { section_definition["subsections"][subsection_id] } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } before do RequestHelper.stub_http_requests + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) end it "has an id" do diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index 94a3af2fe..499101f2b 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -1,38 +1,173 @@ require "rails_helper" RSpec.describe FormHandler do - let(:test_form_name) { "2021_2022" } + let(:form_handler) { described_class.instance } + + before do + Timecop.freeze(Time.utc(2022, 9, 20)) + Singleton.__init__(described_class) + end + + after do + Timecop.unfreeze + Singleton.__init__(described_class) + end + + context "when accessing a form in a different year" do + before do + Timecop.freeze(Time.utc(2021, 8, 3)) + Singleton.__init__(described_class) + end + + after do + Timecop.unfreeze + Singleton.__init__(described_class) # reload FormHandler Instance to update form definitions between runs + end + + it "is able to load a current lettings form" do + form = form_handler.get_form("current_lettings") + expect(form).to be_a(Form) + expect(form.pages.count).to eq(45) + end + + it "is able to load a next lettings form" do + form = form_handler.get_form("next_lettings") + expect(form).to be_a(Form) + expect(form.pages.count).to eq(12) + end + end describe "Get all forms" do it "is able to load all the forms" do - form_handler = described_class.instance all_forms = form_handler.forms expect(all_forms.count).to be >= 1 - expect(all_forms[test_form_name]).to be_a(Form) + expect(all_forms["current_sales"]).to be_a(Form) end end describe "Get specific form" do - it "is able to load a specific form" do - form_handler = described_class.instance - form = form_handler.get_form(test_form_name) + it "is able to load a current lettings form" do + form = form_handler.get_form("current_lettings") + expect(form).to be_a(Form) + expect(form.pages.count).to eq(12) + end + + it "is able to load a previous lettings form" do + form = form_handler.get_form("previous_lettings") expect(form).to be_a(Form) expect(form.pages.count).to eq(45) end + + it "is able to load a current sales form" do + form = form_handler.get_form("current_sales") + expect(form).to be_a(Form) + expect(form.pages.count).to eq(5) + expect(form.name).to eq("2022_2023_sales") + end + + it "is able to load a previous sales form" do + form = form_handler.get_form("previous_sales") + expect(form).to be_a(Form) + expect(form.pages.count).to eq(5) + expect(form.name).to eq("2021_2022_sales") + end end describe "Current form" do it "returns the latest form by date" do - form_handler = described_class.instance - form = form_handler.current_form + form = form_handler.current_lettings_form expect(form).to be_a(Form) expect(form.start_date.year).to eq(2022) end end + describe "Current collection start year" do + context "when the date is after 1st of April" do + before do + Timecop.freeze(Time.utc(2022, 8, 3)) + end + + after do + Timecop.unfreeze + end + + it "returns the same year as the the current start year" do + expect(form_handler.current_collection_start_year).to eq(2022) + end + + it "returns the correct current lettings form name" do + expect(form_handler.form_name_from_start_year(2022, "lettings")).to eq("current_lettings") + end + + it "returns the correct previous lettings form name" do + expect(form_handler.form_name_from_start_year(2021, "lettings")).to eq("previous_lettings") + end + + it "returns the correct next lettings form name" do + expect(form_handler.form_name_from_start_year(2023, "lettings")).to eq("next_lettings") + end + + it "returns the correct current sales form name" do + expect(form_handler.form_name_from_start_year(2022, "sales")).to eq("current_sales") + end + + it "returns the correct previous sales form name" do + expect(form_handler.form_name_from_start_year(2021, "sales")).to eq("previous_sales") + end + + it "returns the correct next sales form name" do + expect(form_handler.form_name_from_start_year(2023, "sales")).to eq("next_sales") + end + end + + context "with the date before 1st of April" do + before do + Timecop.freeze(Time.utc(2022, 2, 3)) + end + + after do + Timecop.unfreeze + end + + it "returns the previous year as the current start year" do + expect(form_handler.current_collection_start_year).to eq(2021) + end + + it "returns the correct current lettings form name" do + expect(form_handler.form_name_from_start_year(2021, "lettings")).to eq("current_lettings") + end + + it "returns the correct previous lettings form name" do + expect(form_handler.form_name_from_start_year(2020, "lettings")).to eq("previous_lettings") + end + + it "returns the correct next lettings form name" do + expect(form_handler.form_name_from_start_year(2022, "lettings")).to eq("next_lettings") + end + + it "returns the correct current sales form name" do + expect(form_handler.form_name_from_start_year(2021, "sales")).to eq("current_sales") + end + + it "returns the correct previous sales form name" do + expect(form_handler.form_name_from_start_year(2020, "sales")).to eq("previous_sales") + end + + it "returns the correct next sales form name" do + expect(form_handler.form_name_from_start_year(2022, "sales")).to eq("next_sales") + end + end + end + it "loads the form once at boot time" do form_handler = described_class.instance - expect(Form).not_to receive(:new).with(:any, test_form_name) - expect(form_handler.get_form(test_form_name)).to be_a(Form) + expect(Form).not_to receive(:new).with(:any, "current_sales") + expect(form_handler.get_form("current_sales")).to be_a(Form) + end + + it "correctly sets form type and start year" do + form = form_handler.forms["current_lettings"] + expect(form.type).to eq("lettings") + expect(form.start_date.year).to eq(2022) end end diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index 7f1d4cb81..38caa5ec6 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -205,4 +205,25 @@ RSpec.describe Form, type: :model do end end end + + describe "when creating a sales log" do + it "creates a valid sales form" do + sections = [] + form = described_class.new(nil, 2022, sections, "sales") + expect(form.type).to eq("sales") + expect(form.name).to eq("2022_2023_sales") + expect(form.setup_sections.count).to eq(1) + expect(form.setup_sections[0].class).to eq(Form::Sales::Sections::Setup) + expect(form.sections.count).to eq(1) + expect(form.sections[0].class).to eq(Form::Sales::Sections::Setup) + expect(form.subsections.count).to eq(1) + expect(form.subsections.first.id).to eq("setup") + expect(form.pages.count).to eq(5) + expect(form.pages.first.id).to eq("organisation") + expect(form.questions.count).to eq(5) + expect(form.questions.first.id).to eq("owning_organisation_id") + expect(form.start_date).to eq(Time.zone.parse("2022-04-01")) + expect(form.end_date).to eq(Time.zone.parse("2023-07-01")) + end + end end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 772255b19..2dbf53595 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -4,6 +4,21 @@ RSpec.describe LettingsLog do let(:owning_organisation) { FactoryBot.create(:organisation) } let(:different_managing_organisation) { FactoryBot.create(:organisation) } let(:created_by_user) { FactoryBot.create(:user) } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end + + it "inherits from log" do + expect(described_class).to be < Log + expect(described_class).to be < ApplicationRecord + end + + it "is a lettings log" do + lettings_log = FactoryBot.build(:lettings_log, created_by: created_by_user) + expect(lettings_log).to be_lettings + end describe "#form" do let(:lettings_log) { FactoryBot.build(:lettings_log, created_by: created_by_user) } @@ -13,9 +28,9 @@ RSpec.describe LettingsLog do it "has returns the correct form based on the start date" do expect(lettings_log.form_name).to be_nil expect(lettings_log.form).to be_a(Form) - expect(lettings_log_2.form_name).to eq("2021_2022") + expect(lettings_log_2.form_name).to eq("previous_lettings") expect(lettings_log_2.form).to be_a(Form) - expect(lettings_log_year_2.form_name).to eq("2023_2024") + expect(lettings_log_year_2.form_name).to eq("next_lettings") expect(lettings_log_year_2.form).to be_a(Form) end @@ -1637,7 +1652,7 @@ RSpec.describe LettingsLog do end context "when a lettings log is a supported housing log" do - let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json", "2021_2022") } + let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } before do lettings_log.needstype = 2 diff --git a/spec/models/log_spec.rb b/spec/models/log_spec.rb new file mode 100644 index 000000000..0cba24086 --- /dev/null +++ b/spec/models/log_spec.rb @@ -0,0 +1,8 @@ +require "rails_helper" + +RSpec.describe Log, type: :model do + it "has two child log classes" do + expect(SalesLog).to be < described_class + expect(LettingsLog).to be < described_class + end +end diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb index 9340e1b3a..7bc1d7cc1 100644 --- a/spec/models/organisation_spec.rb +++ b/spec/models/organisation_spec.rb @@ -131,6 +131,35 @@ RSpec.describe Organisation, type: :model do end end + describe "delete cascade" do + context "when the organisation is deleted" do + let!(:organisation) { FactoryBot.create(:organisation) } + let!(:user) { FactoryBot.create(:user, :support, last_sign_in_at: Time.zone.now, organisation:) } + let!(:scheme_to_delete) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let!(:log_to_delete) { FactoryBot.create(:lettings_log, owning_organisation: user.organisation) } + let!(:sales_log_to_delete) { FactoryBot.create(:sales_log, owning_organisation: user.organisation, managing_organisation: user.organisation) } + + context "when organisation is deleted" do + it "child relationships ie logs, schemes and users are deleted too - application" do + organisation.destroy! + expect { organisation.reload }.to raise_error(ActiveRecord::RecordNotFound) + expect { LettingsLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { SalesLog.find(sales_log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "child relationships ie logs, schemes and users are deleted too - database" do + ActiveRecord::Base.connection.exec_query("DELETE FROM organisations WHERE id = #{organisation.id};") + expect { LettingsLog.find(log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { Scheme.find(scheme_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) + expect { SalesLog.find(sales_log_to_delete.id) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end + describe "scopes" do before do FactoryBot.create(:organisation, name: "Joe Bloggs") diff --git a/spec/models/rent_period_spec.rb b/spec/models/rent_period_spec.rb index 5749d7660..266c839ff 100644 --- a/spec/models/rent_period_spec.rb +++ b/spec/models/rent_period_spec.rb @@ -2,10 +2,10 @@ require "rails_helper" RSpec.describe RentPeriod, type: :model do describe "rent period mapping" do - let(:form) { Form.new("spec/fixtures/forms/2021_2022.json", "2021_2022") } + let(:form) { Form.new("spec/fixtures/forms/2021_2022.json") } before do - allow(FormHandler.instance).to receive(:current_form).and_return(form) + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(form) end it "maps rent period id to display names" do diff --git a/spec/models/sales_log_spec.rb b/spec/models/sales_log_spec.rb new file mode 100644 index 000000000..7ab423266 --- /dev/null +++ b/spec/models/sales_log_spec.rb @@ -0,0 +1,90 @@ +require "rails_helper" + +RSpec.describe SalesLog, type: :model do + let(:owning_organisation) { FactoryBot.create(:organisation) } + let(:created_by_user) { FactoryBot.create(:user) } + + it "inherits from log" do + expect(described_class).to be < Log + expect(described_class).to be < ApplicationRecord + end + + it "is a sales log" do + sales_log = FactoryBot.build(:sales_log, created_by: created_by_user) + expect(sales_log.lettings?).to be false + end + + describe "#new" do + context "when creating a record" do + let(:sales_log) do + described_class.create + end + + it "attaches the correct custom validator" do + expect(sales_log._validators.values.flatten.map(&:class)) + .to include(SalesLogValidator) + end + end + end + + describe "#form" do + let(:sales_log) { FactoryBot.build(:sales_log, created_by: created_by_user) } + let(:sales_log_2) { FactoryBot.build(:sales_log, saledate: Time.zone.local(2022, 5, 1), created_by: created_by_user) } + + it "has returns the correct form based on the start date" do + expect(sales_log.form_name).to be_nil + expect(sales_log.form).to be_a(Form) + expect(sales_log_2.form_name).to eq("current_sales") + expect(sales_log_2.form).to be_a(Form) + end + end + + describe "status" do + let!(:empty_sales_log) { FactoryBot.create(:sales_log) } + # let!(:in_progress_sales_log) { FactoryBot.create(:sales_log, :in_progress) } + let!(:completed_sales_log) { FactoryBot.create(:sales_log, :completed) } + + it "is set to not started for an empty sales log" do + expect(empty_sales_log.not_started?).to be(true) + # expect(empty_sales_log.in_progress?).to be(false) + expect(empty_sales_log.completed?).to be(false) + end + + # it "is set to in progress for a started sales log" do + # expect(in_progress_sales_log.in_progress?).to be(true) + # expect(in_progress_sales_log.not_started?).to be(false) + # expect(in_progress_sales_log.completed?).to be(false) + # end + + it "is set to completed for a completed sales log" do + # expect(completed_sales_log.in_progress?).to be(false) + expect(completed_sales_log.not_started?).to be(false) + expect(completed_sales_log.completed?).to be(true) + end + end + + context "when filtering by organisation" do + let(:organisation_1) { FactoryBot.create(:organisation) } + let(:organisation_2) { FactoryBot.create(:organisation) } + let(:organisation_3) { FactoryBot.create(:organisation) } + + before do + FactoryBot.create(:sales_log, :in_progress, owning_organisation: organisation_1, managing_organisation: organisation_1) + FactoryBot.create(:sales_log, :completed, owning_organisation: organisation_1, managing_organisation: organisation_2) + FactoryBot.create(:sales_log, :completed, owning_organisation: organisation_2, managing_organisation: organisation_1) + FactoryBot.create(:sales_log, :completed, owning_organisation: organisation_2, managing_organisation: organisation_2) + end + + it "filters by given organisation id" do + expect(described_class.filter_by_organisation([organisation_1.id]).count).to eq(3) + expect(described_class.filter_by_organisation([organisation_1.id, organisation_2.id]).count).to eq(4) + expect(described_class.filter_by_organisation([organisation_3.id]).count).to eq(0) + end + + it "filters by given organisation" do + expect(described_class.filter_by_organisation([organisation_1]).count).to eq(3) + expect(described_class.filter_by_organisation([organisation_1, organisation_2]).count).to eq(4) + expect(described_class.filter_by_organisation([organisation_3]).count).to eq(0) + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3fa6354a8..ed77046fd 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -115,7 +115,7 @@ RSpec.describe User, type: :model do end it "can filter lettings logs by user, year and status" do - expect(user.lettings_logs_filters).to eq(%w[status years user]) + expect(user.logs_filters).to eq(%w[status years user]) end end @@ -140,7 +140,7 @@ RSpec.describe User, type: :model do end it "can filter lettings logs by user, year, status and organisation" do - expect(user.lettings_logs_filters).to eq(%w[status years user organisation]) + expect(user.logs_filters).to eq(%w[status years user organisation]) end end @@ -269,4 +269,38 @@ RSpec.describe User, type: :model do end end end + + describe "delete" do + let(:user) { FactoryBot.create(:user) } + + before do + FactoryBot.create( + :lettings_log, + :completed, + owning_organisation: user.organisation, + managing_organisation: user.organisation, + created_by: user, + ) + + FactoryBot.create( + :sales_log, + owning_organisation: user.organisation, + created_by: user, + ) + end + + context "when the user is deleted" do + it "owned lettings logs are not deleted as a result" do + expect { user.destroy! } + .to change(described_class, :count).from(1).to(0) + .and change(LettingsLog, :count).by(0) + end + + it "owned sales logs are not deleted as a result" do + expect { user.destroy! } + .to change(described_class, :count).from(1).to(0) + .and change(SalesLog, :count).by(0) + end + end + end end diff --git a/spec/models/validations/financial_validations_spec.rb b/spec/models/validations/financial_validations_spec.rb index 3a97afe94..895840cdb 100644 --- a/spec/models/validations/financial_validations_spec.rb +++ b/spec/models/validations/financial_validations_spec.rb @@ -5,6 +5,11 @@ RSpec.describe Validations::FinancialValidations do let(:validator_class) { Class.new { include Validations::FinancialValidations } } let(:record) { FactoryBot.create(:lettings_log) } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end describe "earnings and income frequency" do it "when earnings are provided it validates that income frequency must be provided" do diff --git a/spec/models/validations/household_validations_spec.rb b/spec/models/validations/household_validations_spec.rb index c5e523c2b..7b467d384 100644 --- a/spec/models/validations/household_validations_spec.rb +++ b/spec/models/validations/household_validations_spec.rb @@ -5,6 +5,11 @@ RSpec.describe Validations::HouseholdValidations do let(:validator_class) { Class.new { include Validations::HouseholdValidations } } let(:record) { FactoryBot.create(:lettings_log) } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end describe "reasonable preference validations" do context "when reasonable preference is homeless" do diff --git a/spec/models/validations/shared_validations_spec.rb b/spec/models/validations/shared_validations_spec.rb index 0a6ab16c6..2be3f52f3 100644 --- a/spec/models/validations/shared_validations_spec.rb +++ b/spec/models/validations/shared_validations_spec.rb @@ -5,8 +5,13 @@ RSpec.describe Validations::SharedValidations do let(:validator_class) { Class.new { include Validations::SharedValidations } } let(:record) { FactoryBot.create(:lettings_log) } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } describe "numeric min max validations" do + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end + context "when validating age" do it "validates that person 1's age is a number" do record.age1 = "random" diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 78867bad9..cdeb71092 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -84,4 +84,5 @@ RSpec.configure do |config| config.include Devise::Test::IntegrationHelpers, type: :request config.include ViewComponent::TestHelpers, type: :component config.include Capybara::RSpecMatchers, type: :component + config.include ActiveJob::TestHelper end diff --git a/spec/requests/auth/passwords_controller_spec.rb b/spec/requests/auth/passwords_controller_spec.rb index 888bb8c70..830a06713 100644 --- a/spec/requests/auth/passwords_controller_spec.rb +++ b/spec/requests/auth/passwords_controller_spec.rb @@ -133,7 +133,7 @@ RSpec.describe Auth::PasswordsController, type: :request do it "sends you to the 2FA page and does not allow bypassing 2FA code" do put "/account/password", headers: headers, params: params expect(response).to redirect_to("/account/two-factor-authentication") - get "/logs", headers: headers + get "/lettings-logs", headers: headers expect(response).to redirect_to("/account/two-factor-authentication") end diff --git a/spec/requests/bulk_upload_controller_spec.rb b/spec/requests/bulk_upload_controller_spec.rb index 9869079af..bc75dbe84 100644 --- a/spec/requests/bulk_upload_controller_spec.rb +++ b/spec/requests/bulk_upload_controller_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe BulkUploadController, type: :request do - let(:url) { "/logs/bulk-upload" } + let(:url) { "/lettings-logs/bulk-upload" } let(:user) { FactoryBot.create(:user) } let(:organisation) { user.organisation } let(:valid_file) { fixture_file_upload("2021_22_lettings_bulk_upload.xlsx", "application/vnd.ms-excel") } diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index 4d3a6dcc5..f879985b8 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -5,13 +5,6 @@ RSpec.describe FormController, type: :request do let(:user) { FactoryBot.create(:user) } let(:organisation) { user.organisation } let(:other_organisation) { FactoryBot.create(:organisation) } - let!(:lettings_log) do - FactoryBot.create( - :lettings_log, - owning_organisation: organisation, - managing_organisation: organisation, - ) - end let!(:unauthorized_lettings_log) do FactoryBot.create( :lettings_log, @@ -38,29 +31,50 @@ RSpec.describe FormController, type: :request do ) end let(:headers) { { "Accept" => "text/html" } } + let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + + before do + allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + end context "when a user is not signed in" do + let!(:lettings_log) do + FactoryBot.create( + :lettings_log, + owning_organisation: organisation, + managing_organisation: organisation, + ) + end + describe "GET" do it "does not let you get lettings logs pages you don't have access to" do - get "/logs/#{lettings_log.id}/person-1-age", headers: headers, params: {} + get "/lettings-logs/#{lettings_log.id}/person-1-age", headers: headers, params: {} expect(response).to redirect_to("/account/sign-in") end it "does not let you get lettings log check answer pages you don't have access to" do - get "/logs/#{lettings_log.id}/household-characteristics/check-answers", headers: headers, params: {} + get "/lettings-logs/#{lettings_log.id}/household-characteristics/check-answers", headers: headers, params: {} expect(response).to redirect_to("/account/sign-in") end end describe "POST" do it "does not let you post form answers to lettings logs you don't have access to" do - post "/logs/#{lettings_log.id}/form", params: {} + post "/lettings-logs/#{lettings_log.id}/form", params: {} expect(response).to redirect_to("/account/sign-in") end end end context "when a user is signed in" do + let!(:lettings_log) do + FactoryBot.create( + :lettings_log, + owning_organisation: organisation, + managing_organisation: organisation, + ) + end + before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) sign_in user @@ -73,23 +87,23 @@ RSpec.describe FormController, type: :request do let(:lettings_log_year_2) { FactoryBot.create(:lettings_log, :about_completed, startdate: Time.zone.local(2022, 5, 1), owning_organisation: organisation, created_by: user) } it "displays the correct question details for each lettings log based on form year" do - get "/logs/#{lettings_log_year_1.id}/tenant-code-test", headers: headers, params: {} + get "/lettings-logs/#{lettings_log_year_1.id}/tenant-code-test", headers: headers, params: {} expect(response.body).to include("What is the tenant code?") - get "/logs/#{lettings_log_year_2.id}/tenant-code-test", headers: headers, params: {} + get "/lettings-logs/#{lettings_log_year_2.id}/tenant-code-test", headers: headers, params: {} expect(response.body).to match("Different question header text for this year - 2023") end end context "when lettings logs are not owned or managed by your organisation" do it "does not show form pages for lettings logs you don't have access to" do - get "/logs/#{unauthorized_lettings_log.id}/person-1-age", headers: headers, params: {} + get "/lettings-logs/#{unauthorized_lettings_log.id}/person-1-age", headers: headers, params: {} expect(response).to have_http_status(:not_found) end end context "with a form page that has custom guidance" do it "displays the correct partial" do - get "/logs/#{lettings_log.id}/net-income", headers: headers, params: {} + get "/lettings-logs/#{lettings_log.id}/net-income", headers: headers, params: {} expect(response.body).to match("What counts as income?") end end @@ -115,7 +129,7 @@ RSpec.describe FormController, type: :request do end it "returns an unfiltered list of schemes" do - get "/logs/#{lettings_log.id}/scheme", headers: headers, params: {} + get "/lettings-logs/#{lettings_log.id}/scheme", headers: headers, params: {} expect(response.body.scan("