diff --git a/app/controllers/bulk_upload_lettings_logs_controller.rb b/app/controllers/bulk_upload_lettings_logs_controller.rb new file mode 100644 index 000000000..108c25fef --- /dev/null +++ b/app/controllers/bulk_upload_lettings_logs_controller.rb @@ -0,0 +1,50 @@ +class BulkUploadLettingsLogsController < ApplicationController + before_action :authenticate_user! + + def start + if in_crossover_period? + redirect_to bulk_upload_lettings_log_path(id: "year") + else + redirect_to bulk_upload_lettings_log_path(id: "prepare-your-file", form: { year: current_year }) + end + end + + def show + render form.view_path + end + + def update + if form.valid? + redirect_to form.next_path + else + render form.view_path + end + end + +private + + def current_year + FormHandler.instance.forms["current_lettings"].start_date.year + end + + def in_crossover_period? + FormHandler.instance.forms.values.any?(&:in_crossover_period?) + end + + def form + @form ||= case params[:id] + when "year" + Forms::BulkUploadLettings::Year.new(form_params) + when "prepare-your-file" + Forms::BulkUploadLettings::PrepareYourFile.new(form_params) + when "upload-your-file" + Forms::BulkUploadLettings::UploadYourFile.new(form_params) + else + raise "Page not found for path #{params[:id]}" + end + end + + def form_params + params.fetch(:form, {}).permit(:year) + end +end diff --git a/app/controllers/bulk_upload_sales_logs_controller.rb b/app/controllers/bulk_upload_sales_logs_controller.rb new file mode 100644 index 000000000..81d018d4c --- /dev/null +++ b/app/controllers/bulk_upload_sales_logs_controller.rb @@ -0,0 +1,50 @@ +class BulkUploadSalesLogsController < ApplicationController + before_action :authenticate_user! + + def start + if in_crossover_period? + redirect_to bulk_upload_sales_log_path(id: "year") + else + redirect_to bulk_upload_sales_log_path(id: "prepare-your-file", form: { year: current_year }) + end + end + + def show + render form.view_path + end + + def update + if form.valid? + redirect_to form.next_path + else + render form.view_path + end + end + +private + + def current_year + FormHandler.instance.forms["current_sales"].start_date.year + end + + def in_crossover_period? + FormHandler.instance.forms.values.any?(&:in_crossover_period?) + end + + def form + @form ||= case params[:id] + when "year" + Forms::BulkUploadSales::Year.new(form_params) + when "prepare-your-file" + Forms::BulkUploadSales::PrepareYourFile.new(form_params) + when "upload-your-file" + Forms::BulkUploadSales::UploadYourFile.new(form_params) + else + raise "Page not found for path #{params[:id]}" + end + end + + def form_params + params.fetch(:form, {}).permit(:year) + end +end diff --git a/app/helpers/logs_helper.rb b/app/helpers/logs_helper.rb new file mode 100644 index 000000000..6567f0a13 --- /dev/null +++ b/app/helpers/logs_helper.rb @@ -0,0 +1,21 @@ +module LogsHelper + def log_type_for_controller(controller) + case controller.class.to_s + when "LettingsLogsController" + "lettings" + when "SalesLogsController" + "sales" + else + raise "Log type not found for #{controller.class}" + end + end + + def bulk_upload_path_for_controller(controller, id:) + case log_type_for_controller(controller) + when "lettings" + bulk_upload_lettings_log_path(id:) + when "sales" + bulk_upload_sales_log_path(id:) + end + end +end diff --git a/app/helpers/navigation_items_helper.rb b/app/helpers/navigation_items_helper.rb index 07c125a8d..d996894ea 100644 --- a/app/helpers/navigation_items_helper.rb +++ b/app/helpers/navigation_items_helper.rb @@ -65,11 +65,11 @@ module NavigationItemsHelper private def lettings_logs_current?(path) - path == "/lettings-logs" + path.starts_with?("/lettings-logs") end def sales_logs_current?(path) - path == "/sales-logs" + path.starts_with?("/sales-logs") end def users_current?(path) diff --git a/app/helpers/schemes_helper.rb b/app/helpers/schemes_helper.rb index 10a33cab6..3d143f8a2 100644 --- a/app/helpers/schemes_helper.rb +++ b/app/helpers/schemes_helper.rb @@ -1,5 +1,5 @@ module SchemesHelper - def display_scheme_attributes(scheme) + def display_scheme_attributes(scheme, user) base_attributes = [ { name: "Scheme code", value: scheme.id_to_display }, { name: "Name", value: scheme.service_name, edit: true }, @@ -18,11 +18,15 @@ module SchemesHelper ] if FeatureToggle.scheme_toggle_enabled? - base_attributes.append({ name: "Status", value: scheme.status }) + base_attributes.append({ name: "Status", value: status_tag(scheme.status) }) + end + + if user.data_coordinator? + base_attributes.delete_if { |item| item[:name] == "Housing stock owned by" } end if scheme.arrangement_type_same? - base_attributes.delete({ name: "Organisation providing support", value: scheme.managing_organisation&.name }) + base_attributes.delete_if { |item| item[:name] == "Organisation providing support" } end base_attributes end diff --git a/app/models/form.rb b/app/models/form.rb index 22321f431..9d9acf2ea 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -223,4 +223,12 @@ class Form end end end + + def in_crossover_period?(now: Time.zone.now) + ((end_date - 3.months) < now) && (now < end_date) + end + + def inspect + "#<#{self.class} @type=#{type} @name=#{name}>" + end end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index 61b981436..03de5e290 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -49,6 +49,11 @@ class FormHandler today < window_end_date ? today.year - 1 : today.year end + def collection_start_date(date) + window_end_date = Time.zone.local(date.year, 4, 1) + date < window_end_date ? Time.zone.local(date.year - 1, 4, 1) : Time.zone.local(date.year, 4, 1) + end + def current_collection_start_date Time.zone.local(current_collection_start_year, 4, 1) end diff --git a/app/models/forms/bulk_upload_lettings/prepare_your_file.rb b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb new file mode 100644 index 000000000..684ba1437 --- /dev/null +++ b/app/models/forms/bulk_upload_lettings/prepare_your_file.rb @@ -0,0 +1,41 @@ +module Forms + module BulkUploadLettings + class PrepareYourFile + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :year, :integer + + def view_path + "bulk_upload_lettings_logs/forms/prepare_your_file" + end + + def back_path + if in_crossover_period? + Rails.application.routes.url_helpers.bulk_upload_lettings_log_path(id: "year", form: { year: }) + else + Rails.application.routes.url_helpers.lettings_logs_path + end + end + + def next_path + bulk_upload_lettings_log_path(id: "upload-your-file", form: { year: }) + end + + def template_path + "/files/bulk-upload-lettings-template-v1.xlsx" + end + + def year_combo + "#{year}/#{year + 1 - 2000}" + end + + private + + def in_crossover_period? + FormHandler.instance.forms.values.any?(&:in_crossover_period?) + end + end + end +end diff --git a/app/models/forms/bulk_upload_lettings/upload_your_file.rb b/app/models/forms/bulk_upload_lettings/upload_your_file.rb new file mode 100644 index 000000000..1415ffe19 --- /dev/null +++ b/app/models/forms/bulk_upload_lettings/upload_your_file.rb @@ -0,0 +1,19 @@ +module Forms + module BulkUploadLettings + class UploadYourFile + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :year, :integer + + def view_path + "bulk_upload_lettings_logs/forms/upload_your_file" + end + + def back_path + bulk_upload_lettings_log_path(id: "prepare-your-file", form: { year: }) + end + end + end +end diff --git a/app/models/forms/bulk_upload_lettings/year.rb b/app/models/forms/bulk_upload_lettings/year.rb new file mode 100644 index 000000000..9fa17b19e --- /dev/null +++ b/app/models/forms/bulk_upload_lettings/year.rb @@ -0,0 +1,37 @@ +module Forms + module BulkUploadLettings + class Year + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :year, :integer + + validates :year, presence: true + + def view_path + "bulk_upload_lettings_logs/forms/year" + end + + def options + possible_years.map do |year| + OpenStruct.new(id: year, name: "#{year}/#{year + 1}") + end + end + + def back_path + lettings_logs_path + end + + def next_path + bulk_upload_lettings_log_path(id: "prepare-your-file", form: { year: }) + end + + private + + def possible_years + FormHandler.instance.lettings_forms.values.map { |form| form.start_date.year }.sort.reverse + end + end + end +end diff --git a/app/models/forms/bulk_upload_sales/prepare_your_file.rb b/app/models/forms/bulk_upload_sales/prepare_your_file.rb new file mode 100644 index 000000000..da017dbbd --- /dev/null +++ b/app/models/forms/bulk_upload_sales/prepare_your_file.rb @@ -0,0 +1,41 @@ +module Forms + module BulkUploadSales + class PrepareYourFile + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :year, :integer + + def view_path + "bulk_upload_sales_logs/forms/prepare_your_file" + end + + def back_path + if in_crossover_period? + Rails.application.routes.url_helpers.bulk_upload_sales_log_path(id: "year", form: { year: }) + else + Rails.application.routes.url_helpers.sales_logs_path + end + end + + def next_path + bulk_upload_sales_log_path(id: "upload-your-file", form: { year: }) + end + + def template_path + "/files/bulk-upload-sales-template-v1.xlsx" + end + + def year_combo + "#{year}/#{year + 1 - 2000}" + end + + private + + def in_crossover_period? + FormHandler.instance.forms.values.any?(&:in_crossover_period?) + end + end + end +end diff --git a/app/models/forms/bulk_upload_sales/upload_your_file.rb b/app/models/forms/bulk_upload_sales/upload_your_file.rb new file mode 100644 index 000000000..3d421e9f1 --- /dev/null +++ b/app/models/forms/bulk_upload_sales/upload_your_file.rb @@ -0,0 +1,19 @@ +module Forms + module BulkUploadSales + class UploadYourFile + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :year, :integer + + def view_path + "bulk_upload_sales_logs/forms/upload_your_file" + end + + def back_path + bulk_upload_sales_log_path(id: "prepare-your-file", form: { year: }) + end + end + end +end diff --git a/app/models/forms/bulk_upload_sales/year.rb b/app/models/forms/bulk_upload_sales/year.rb new file mode 100644 index 000000000..361061990 --- /dev/null +++ b/app/models/forms/bulk_upload_sales/year.rb @@ -0,0 +1,37 @@ +module Forms + module BulkUploadSales + class Year + include ActiveModel::Model + include ActiveModel::Attributes + include Rails.application.routes.url_helpers + + attribute :year, :integer + + validates :year, presence: true + + def view_path + "bulk_upload_sales_logs/forms/year" + end + + def options + possible_years.map do |year| + OpenStruct.new(id: year, name: "#{year}/#{year + 1}") + end + end + + def back_path + sales_logs_path + end + + def next_path + bulk_upload_sales_log_path(id: "prepare-your-file", form: { year: }) + end + + private + + def possible_years + FormHandler.instance.sales_forms.values.map { |form| form.start_date.year }.sort.reverse + end + end + end +end diff --git a/app/models/location.rb b/app/models/location.rb index 3c9d9e204..10c1e275f 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -375,20 +375,29 @@ class Location < ApplicationRecord end def available_from - startdate || [created_at, FormHandler.instance.current_collection_start_date].min + return startdate if startdate.present? + + FormHandler.instance.collection_start_date(created_at) + end + + def open_deactivation + location_deactivation_periods.deactivations_without_reactivation.first end - def status - open_deactivation = location_deactivation_periods.deactivations_without_reactivation.first - recent_deactivation = location_deactivation_periods.order("created_at").last + def recent_deactivation + location_deactivation_periods.order("created_at").last + end - return :deactivated if open_deactivation&.deactivation_date.present? && Time.zone.now >= open_deactivation.deactivation_date - return :deactivating_soon if open_deactivation&.deactivation_date.present? && Time.zone.now < open_deactivation.deactivation_date - return :reactivating_soon if recent_deactivation&.reactivation_date.present? && Time.zone.now < recent_deactivation.reactivation_date - return :activating_soon if startdate.present? && Time.zone.now < startdate + def status(date = Time.zone.now) + return :incomplete unless confirmed + return :deactivated if open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date + return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date + return :reactivating_soon if recent_deactivation&.reactivation_date.present? && date < recent_deactivation.reactivation_date + return :activating_soon if startdate.present? && date < startdate :active end + alias_method :status_at, :status def active? status == :active diff --git a/app/models/scheme.rb b/app/models/scheme.rb index fce696bb6..bc243d449 100644 --- a/app/models/scheme.rb +++ b/app/models/scheme.rb @@ -210,21 +210,26 @@ class Scheme < ApplicationRecord end def available_from - [created_at, FormHandler.instance.current_collection_start_date].min + FormHandler.instance.collection_start_date(created_at) end - def status - return :incomplete unless confirmed + def open_deactivation + scheme_deactivation_periods.deactivations_without_reactivation.first + end - open_deactivation = scheme_deactivation_periods.deactivations_without_reactivation.first - recent_deactivation = scheme_deactivation_periods.order("created_at").last + def recent_deactivation + scheme_deactivation_periods.order("created_at").last + end - return :deactivated if open_deactivation&.deactivation_date.present? && Time.zone.now >= open_deactivation.deactivation_date - return :deactivating_soon if open_deactivation&.deactivation_date.present? && Time.zone.now < open_deactivation.deactivation_date - return :reactivating_soon if recent_deactivation&.reactivation_date.present? && Time.zone.now < recent_deactivation.reactivation_date + def status(date = Time.zone.now) + return :incomplete unless confirmed + return :deactivated if open_deactivation&.deactivation_date.present? && date >= open_deactivation.deactivation_date + return :deactivating_soon if open_deactivation&.deactivation_date.present? && date < open_deactivation.deactivation_date + return :reactivating_soon if recent_deactivation&.reactivation_date.present? && date < recent_deactivation.reactivation_date :active end + alias_method :status_at, :status def active? status == :active diff --git a/app/models/validations/date_validations.rb b/app/models/validations/date_validations.rb index 0353abd26..ceec8ed9a 100644 --- a/app/models/validations/date_validations.rb +++ b/app/models/validations/date_validations.rb @@ -1,4 +1,6 @@ module Validations::DateValidations + include Validations::SharedValidations + def validate_property_major_repairs(record) date_valid?("mrcdate", record) if record["startdate"].present? && record["mrcdate"].present? && record["startdate"] < record["mrcdate"] @@ -59,6 +61,9 @@ module Validations::DateValidations if record["mrcdate"].present? && record.startdate < record["mrcdate"] record.errors.add :startdate, I18n.t("validations.setup.startdate.after_major_repair_date") end + + location_during_startdate_validation(record, :startdate) + scheme_during_startdate_validation(record, :startdate) end private diff --git a/app/models/validations/setup_validations.rb b/app/models/validations/setup_validations.rb index 44770c947..c1bdc3a71 100644 --- a/app/models/validations/setup_validations.rb +++ b/app/models/validations/setup_validations.rb @@ -1,10 +1,21 @@ module Validations::SetupValidations + include Validations::SharedValidations + def validate_irproduct_other(record) if intermediate_product_rent_type?(record) && record.irproduct_other.blank? record.errors.add :irproduct_other, I18n.t("validations.setup.intermediate_rent_product_name.blank") end end + def validate_location(record) + location_during_startdate_validation(record, :location_id) + end + + def validate_scheme(record) + location_during_startdate_validation(record, :scheme_id) + scheme_during_startdate_validation(record, :scheme_id) + end + private def intermediate_product_rent_type?(record) diff --git a/app/models/validations/shared_validations.rb b/app/models/validations/shared_validations.rb index 9b4f8a3ff..2694fd743 100644 --- a/app/models/validations/shared_validations.rb +++ b/app/models/validations/shared_validations.rb @@ -33,4 +33,39 @@ module Validations::SharedValidations end end end + + def location_during_startdate_validation(record, field) + location_inactive_status = inactive_status(record.startdate, record.location) + + if location_inactive_status.present? + date, scope, deactivation_date = location_inactive_status.values_at(:date, :scope, :deactivation_date) + record.errors.add field, I18n.t("validations.setup.startdate.location.#{scope}", postcode: record.location.postcode, date:, deactivation_date:) + end + end + + def scheme_during_startdate_validation(record, field) + scheme_inactive_status = inactive_status(record.startdate, record.scheme) + if scheme_inactive_status.present? + date, scope, deactivation_date = scheme_inactive_status.values_at(:date, :scope, :deactivation_date) + record.errors.add field, I18n.t("validations.setup.startdate.scheme.#{scope}", name: record.scheme.service_name, date:, deactivation_date:) + end + end + + def inactive_status(date, resource) + return if date.blank? || resource.blank? + + status = resource.status_at(date) + return unless %i[reactivating_soon activating_soon deactivated].include?(status) + + closest_reactivation = resource.recent_deactivation + open_deactivation = resource.open_deactivation + + date = case status + when :reactivating_soon then closest_reactivation.reactivation_date + when :activating_soon then resource&.available_from + when :deactivated then open_deactivation.deactivation_date + end + + { scope: status, date: date&.to_formatted_s(:govuk_date), deactivation_date: closest_reactivation&.deactivation_date&.to_formatted_s(:govuk_date) } + end end diff --git a/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb new file mode 100644 index 000000000..d8cfedd08 --- /dev/null +++ b/app/views/bulk_upload_lettings_logs/forms/prepare_your_file.html.erb @@ -0,0 +1,33 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +
+
+ <%= form_with model: @form, scope: :form, url: bulk_upload_lettings_log_path(id: "prepare-your-file"), method: :patch do |f| %> + <%= f.hidden_field :year %> + + Upload lettings logs in bulk (<%= @form.year_combo %>) +

Prepare your file

+ +

Create your file

+ + +

Check your data

+ + +

Save your file

+ + + <%= f.govuk_submit %> + <% end %> +
+
diff --git a/app/views/bulk_upload_lettings_logs/forms/upload_your_file.html.erb b/app/views/bulk_upload_lettings_logs/forms/upload_your_file.html.erb new file mode 100644 index 000000000..86dde8ae2 --- /dev/null +++ b/app/views/bulk_upload_lettings_logs/forms/upload_your_file.html.erb @@ -0,0 +1,17 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +<%= form_with model: @form, scope: :form, url: bulk_upload_lettings_log_path(id: "upload-your-file"), method: :patch do |f| %> + <%= f.govuk_error_summary %> + +
+ Upload your file goes here +
+ +
+ year selected <%= @form.year %> +
+ + <%= f.govuk_submit %> +<% end %> diff --git a/app/views/bulk_upload_lettings_logs/forms/year.html.erb b/app/views/bulk_upload_lettings_logs/forms/year.html.erb new file mode 100644 index 000000000..8ba1c280f --- /dev/null +++ b/app/views/bulk_upload_lettings_logs/forms/year.html.erb @@ -0,0 +1,16 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +<%= form_with model: @form, scope: :form, url: bulk_upload_lettings_log_path(id: "year"), method: :patch do |f| %> + <%= f.govuk_error_summary %> + + <%= f.govuk_collection_radio_buttons :year, + @form.options, + :id, + :name, + legend: { text: "Which year are you uploading data for?", size: "l" }, + caption: { text: "Upload lettings logs in bulk", size: "l" } %> + + <%= f.govuk_submit %> +<% end %> diff --git a/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb b/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb new file mode 100644 index 000000000..0157b66eb --- /dev/null +++ b/app/views/bulk_upload_sales_logs/forms/prepare_your_file.html.erb @@ -0,0 +1,33 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +
+
+ <%= form_with model: @form, scope: :form, url: bulk_upload_sales_log_path(id: "prepare-your-file"), method: :patch do |f| %> + <%= f.hidden_field :year %> + + Upload sales logs in bulk (<%= @form.year_combo %>) +

Prepare your file

+ +

Create your file

+ + +

Check your data

+ + +

Save your file

+ + + <%= f.govuk_submit %> + <% end %> +
+
diff --git a/app/views/bulk_upload_sales_logs/forms/upload_your_file.html.erb b/app/views/bulk_upload_sales_logs/forms/upload_your_file.html.erb new file mode 100644 index 000000000..a178339e8 --- /dev/null +++ b/app/views/bulk_upload_sales_logs/forms/upload_your_file.html.erb @@ -0,0 +1,17 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +<%= form_with model: @form, scope: :form, url: bulk_upload_sales_log_path(id: "upload-your-file"), method: :patch do |f| %> + <%= f.govuk_error_summary %> + +
+ Upload your file goes here +
+ +
+ year selected <%= @form.year %> +
+ + <%= f.govuk_submit %> +<% end %> diff --git a/app/views/bulk_upload_sales_logs/forms/year.html.erb b/app/views/bulk_upload_sales_logs/forms/year.html.erb new file mode 100644 index 000000000..d8aa09172 --- /dev/null +++ b/app/views/bulk_upload_sales_logs/forms/year.html.erb @@ -0,0 +1,16 @@ +<% content_for :before_content do %> + <%= govuk_back_link href: @form.back_path %> +<% end %> + +<%= form_with model: @form, scope: :form, url: bulk_upload_sales_log_path(id: "year"), method: :patch do |f| %> + <%= f.govuk_error_summary %> + + <%= f.govuk_collection_radio_buttons :year, + @form.options, + :id, + :name, + legend: { text: "Which year are you uploading data for?", size: "l" }, + caption: { text: "Upload sales logs in bulk", size: "l" } %> + + <%= f.govuk_submit %> +<% end %> diff --git a/app/views/locations/index.html.erb b/app/views/locations/index.html.erb index b205b699d..054b223c5 100644 --- a/app/views/locations/index.html.erb +++ b/app/views/locations/index.html.erb @@ -11,57 +11,104 @@ <%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> -<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %> +<% if FeatureToggle.location_toggle_enabled? %> +
+
+<% end %> + <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %> -

Locations

+

Locations

-<%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %> + <%= render SearchComponent.new(current_user:, search_label: "Search by location name or postcode", value: @searched) %> -<%= govuk_section_break(visible: true, size: "m") %> + <%= govuk_section_break(visible: true, size: "m") %> +<% if FeatureToggle.location_toggle_enabled? %> +
+
+<% end %> -<%= govuk_table do |table| %> - <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> - <%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %> - <% end %> - <%= table.head do |head| %> - <%= head.row do |row| %> - <% row.cell(header: true, text: "Code", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Postcode", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Units", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Common unit type", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Mobility type", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Local authority", html_attributes: { - scope: "col", - }) %> - <% row.cell(header: true, text: "Available from", html_attributes: { - scope: "col", - }) %> - <% end %> - <% end %> - <% @locations.each do |location| %> - <%= table.body do |body| %> - <%= body.row do |row| %> - <% row.cell(text: location.id) %> - <% row.cell(text: simple_format(location_cell_postcode(location, location.confirmed ? scheme_location_path(@scheme, location) : location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> - <% row.cell(text: location.units) %> - <% row.cell(text: simple_format("#{location.type_of_unit}")) %> - <% row.cell(text: location.mobility_type) %> - <% row.cell(text: location.location_admin_district) %> - <% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %> +<% if FeatureToggle.location_toggle_enabled? %> +
+
+ <%= govuk_table do |table| %> + <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> + <%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %> + <% end %> + <%= table.head do |head| %> + <%= head.row do |row| %> + <% row.cell(header: true, text: "Postcode", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Location code", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Status", html_attributes: { + scope: "col", + }) %> + <% end %> <% end %> + <% @locations.each do |location| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(text: simple_format(location_cell_postcode(location, location.confirmed ? scheme_location_path(@scheme, location) : location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> + <% row.cell(text: location.id) %> + <% row.cell(text: status_tag(location.status)) %> + <% end %> + <% end %> + <% end %> + <% end %> + <%= govuk_button_link_to "Add a location", new_scheme_location_path(@scheme), secondary: true %> +
+
+ +<% else %> + <%= govuk_table do |table| %> + <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> + <%= render(SearchResultCaptionComponent.new(searched: @searched, count: @pagy.count, item_label:, total_count: @total_count, item: "locations", path: request.path)) %> + <% end %> + <%= table.head do |head| %> + <%= head.row do |row| %> + <% row.cell(header: true, text: "Code", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Postcode", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Units", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Common unit type", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Mobility type", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Local authority", html_attributes: { + scope: "col", + }) %> + <% row.cell(header: true, text: "Available from", html_attributes: { + scope: "col", + }) %> + <% end %> + <% end %> + <% @locations.each do |location| %> + <%= table.body do |body| %> + <%= body.row do |row| %> + <% row.cell(text: location.id) %> + <% row.cell(text: simple_format(location_cell_postcode(location, location.confirmed ? scheme_location_path(@scheme, location) : location.postcode.present? ? scheme_location_check_answers_path(@scheme, location, route: "locations") : scheme_location_postcode_path(@scheme, location)), { class: "govuk-!-font-weight-bold" }, wrapper_tag: "div")) %> + <% row.cell(text: location.units) %> + <% row.cell do %> + <%= simple_format(location.type_of_unit) %> + <% end %> + <% row.cell(text: location.mobility_type) %> + <% row.cell(text: location.location_admin_district) %> + <% row.cell(text: location.startdate&.to_formatted_s(:govuk_date)) %> + <% end %> + <% end %> <% end %> <% end %> + <%= govuk_button_link_to "Add a location", new_scheme_location_path(@scheme), secondary: true %> + <% end %> -<%= govuk_button_link_to "Add a location", new_scheme_location_path(@scheme), secondary: true %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "locations" } %> diff --git a/app/views/logs/index.html.erb b/app/views/logs/index.html.erb index 431ca0f45..a340973cf 100644 --- a/app/views/logs/index.html.erb +++ b/app/views/logs/index.html.erb @@ -10,14 +10,18 @@ <% end %>
-
+
<% if current_page?(controller: 'lettings_logs', action: 'index') %> - <%= govuk_button_to "Create a new lettings log", lettings_logs_path %> + <%= govuk_button_to "Create a new lettings log", lettings_logs_path, class: "govuk-!-margin-right-6" %> <% end %> + <% if FeatureToggle.sales_log_enabled? && current_page?(controller: 'sales_logs', action: 'index') %> - <%= govuk_button_to "Create a new sales log", sales_logs_path %> + <%= govuk_button_to "Create a new sales log", sales_logs_path, class: "govuk-!-margin-right-6" %> + <% end %> + + <% if FeatureToggle.bulk_upload_logs? %> + <%= govuk_button_link_to "Upload #{log_type_for_controller(controller)} logs in bulk", bulk_upload_path_for_controller(controller, id: "start"), secondary: true %> <% end %> - <%#= govuk_link_to "Upload logs", bulk_upload_lettings_logs_path %>
<%= render partial: "log_filters" %> diff --git a/app/views/schemes/show.html.erb b/app/views/schemes/show.html.erb index e0b229af4..5d93414d0 100644 --- a/app/views/schemes/show.html.erb +++ b/app/views/schemes/show.html.erb @@ -10,19 +10,27 @@ <%= render partial: "organisations/headings", locals: { main: @scheme.service_name, sub: nil } %> -<%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %> +<% if FeatureToggle.location_toggle_enabled? %> +
+
+<% end %> + <%= render SubNavigationComponent.new(items: scheme_items(request.path, @scheme.id, "Locations")) %> -

Scheme

+

Scheme

-<%= govuk_summary_list do |summary_list| %> - <% display_scheme_attributes(@scheme).each do |attr| %> - <% next if current_user.data_coordinator? && attr[:name] == ("Housing stock owned by") %> - <%= summary_list.row do |row| %> - <% row.key { attr[:name].eql?("Registered under Care Standards Act 2000") ? "Registered under Care Standards Act 2000" : attr[:name].to_s.humanize } %> - <% row.value { attr[:name].eql?("Status") ? status_tag(attr[:value]) : details_html(attr) } %> - <% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %> - <% end %> - <% end %> + <%= govuk_summary_list do |summary_list| %> + <% display_scheme_attributes(@scheme, current_user).each do |attr| %> + <%= summary_list.row do |row| %> + <% row.key { attr[:name] } %> + <% row.value { details_html(attr) } %> + <% row.action(text: "Change", href: scheme_edit_name_path(scheme_id: @scheme.id)) if attr[:edit] %> + <% end %> + <% end %> + <% end %> + +<% if FeatureToggle.location_toggle_enabled? %> +
+
<% end %> <% if FeatureToggle.scheme_toggle_enabled? %> diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json index 2c6d0763f..fe1511153 100644 --- a/config/forms/2022_2023.json +++ b/config/forms/2022_2023.json @@ -5929,7 +5929,7 @@ "description": "", "questions": { "housingneeds_type": { - "header": "What type of access need do they have?", + "header": "What type of access needs do they have?", "hint_text": "", "type": "radio", "check_answer_label": "Disabled access needs", @@ -5947,7 +5947,7 @@ "value": true }, "3": { - "value": "None of the above" + "value": "None of the listed options" } } }, @@ -7470,7 +7470,7 @@ "household_charge": { "check_answer_label": "Does the household pay rent or charges?", "header": "Does the household pay rent or other charges for the accommodation?", - "hint_text": "", + "hint_text": "If rent is charged on the property then answer Yes to this question, even if the tenants do not pay it themselves.", "type": "radio", "answer_options": { "0": { diff --git a/config/initializers/feature_toggle.rb b/config/initializers/feature_toggle.rb index 7cd75ddd3..8fc2cd7d4 100644 --- a/config/initializers/feature_toggle.rb +++ b/config/initializers/feature_toggle.rb @@ -22,4 +22,8 @@ class FeatureToggle def self.managing_for_other_user_enabled? !Rails.env.production? end + + def self.bulk_upload_logs? + !Rails.env.production? + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index ae226ffd7..fbb373a2b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -38,6 +38,18 @@ en: create_password: "Create a password to finish setting up your account" reset_password: "Reset your password" + activemodel: + errors: + models: + forms/bulk_upload_lettings/year: + attributes: + year: + blank: You must select a collection period to upload for + forms/bulk_upload_sales/year: + attributes: + year: + blank: You must select a collection period to upload for + activerecord: errors: models: @@ -120,6 +132,14 @@ en: before_scheme_end_date: "The tenancy start date must be before the end date for this supported housing scheme" after_void_date: "Enter a tenancy start date that is after the void date" after_major_repair_date: "Enter a tenancy start date that is after the major repair date" + location: + deactivated: "The location %{postcode} was deactivated on %{date} and was not available on the day you entered." + reactivating_soon: "The location %{postcode} was deactivated on %{deactivation_date} and is not available on the date you entered. It reactivates on %{date}" + activating_soon: "The location %{postcode} is not available until %{date}. Enter a tenancy start date after %{date}" + scheme: + deactivated: "%{name} was deactivated on %{date} and was not available on the day you entered" + reactivating_soon: "%{name} was deactivated on %{deactivation_date} and is not available on the date you entered. It reactivates on %{date}" + activating_soon: "%{name} is not available until %{date}. Enter a tenancy start date after %{date}" property: mrcdate: diff --git a/config/routes.rb b/config/routes.rb index 6caff0929..baa9de5b5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -125,6 +125,12 @@ Rails.application.routes.draw do get "csv-download", to: "lettings_logs#download_csv" post "email-csv", to: "lettings_logs#email_csv" get "csv-confirmation", to: "lettings_logs#csv_confirmation" + + resources :bulk_upload_lettings_logs, path: "bulk-upload-logs" do + collection do + get :start + end + end end member do @@ -144,6 +150,14 @@ Rails.application.routes.draw do end resources :sales_logs, path: "/sales-logs" do + collection do + resources :bulk_upload_sales_logs, path: "bulk-upload-logs" do + collection do + get :start + end + end + end + FormHandler.instance.sales_forms.each do |_key, form| form.pages.map do |page| get page.id.to_s.dasherize, to: "form#show_page" diff --git a/docs/infrastructure.md b/docs/infrastructure.md index 63bbf2e42..81dc3f165 100644 --- a/docs/infrastructure.md +++ b/docs/infrastructure.md @@ -122,6 +122,57 @@ After a sucessful deployment a comment will be added to the pull request with th Once a pull request has been closed the review app infrastructure will be tore down to save on any costs. Should you wish to re-open a closed pull request the review app will be spun up again. +### How to fix review app deployment failures + +One reason a review app deployment might fail is that it is attempting to run migrations which conflict with data in the database. For example you might have introduced a unique constraint, but the database associated with the review app has duplicate data in it that would violate this constraint, and so the migration cannot be run. There are two main ways to remedy this: + +**Method 1 - Edit database via console** +1. Log in to Cloud Foundry + ```bash + cf login -a api.london.cloud.service.gov.uk -u + ``` + * Your username should be the email address you signed up to GOVUK PaaS with. + * Choose the dev environment whilst logging in. +2. If you were already logged in then Cloud Foundry, then instead just target the dev environment + ```bash + cf target -o dluhc-core -s dev + ``` +3. Find the name of your app + ```bash + cf apps + ``` + * The app name will be in this format: `dluhc-core-review-`. +4. Open a console for your app + ```bash + cf ssh -t -c "/tmp/lifecycle/launcher /home/vcap/app 'rails console' ''" + ``` +5. Edit the database as appropriate, e.g. delete dodgy data and recreate correctly + +**Method 2 - Nuke and restart** + +1. Find the name of your app + ```bash + cf apps + ``` + * The app name will be in this format: `dluhc-core-review-`. +2. Delete the app + ```bash + cf delete + ``` +3. Find the name of the matching Postgres service + ```bash + cf services + ``` + * The service name will be in this format: `dluhc-core-review--postgres`. +4. Delete the service + ```bash + cf delete-service + ``` + * Use `cf services` or `cf service ` to check the operation status. + * There's no need to delete the Redis service. +5. Re-run the whole review app pipeline in GitHub + * If it fails it's likely that the deletion from the previous step hadn't completed yet. So just wait a few minutes and re-run the pipeline again. + ## Setting up Infrastructure for a new environment ### Staging diff --git a/public/files/bulk-upload-lettings-template-v1.xlsx b/public/files/bulk-upload-lettings-template-v1.xlsx new file mode 100644 index 000000000..ca30388ba Binary files /dev/null and b/public/files/bulk-upload-lettings-template-v1.xlsx differ diff --git a/public/files/bulk-upload-sales-template-v1.xlsx b/public/files/bulk-upload-sales-template-v1.xlsx new file mode 100644 index 000000000..73c9f829c Binary files /dev/null and b/public/files/bulk-upload-sales-template-v1.xlsx differ 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 6273da69d..6e63c90cd 100644 --- a/spec/components/check_answers_summary_list_card_component_spec.rb +++ b/spec/components/check_answers_summary_list_card_component_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do context "when given a set of questions" do let(:user) { FactoryBot.build(:user) } - let(:log) { FactoryBot.build(:lettings_log, :completed, age2: 99) } + let(:log) { FactoryBot.build(:lettings_log, :completed, age2: 99, startdate: Time.zone.local(2021, 5, 1)) } let(:subsection_id) { "household_characteristics" } let(:subsection) { log.form.get_subsection(subsection_id) } let(:questions) { subsection.applicable_questions(log) } diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index d337bc592..217e8a605 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -60,7 +60,7 @@ FactoryBot.define do illness { 1 } preg_occ { 2 } startertenancy { 1 } - tenancylength { 5 } + tenancylength { nil } tenancy { 1 } ppostcode_full { Faker::Address.postcode } rsnvac { 6 } @@ -134,7 +134,7 @@ FactoryBot.define do property_relet { 0 } mrcdate { Time.zone.local(2020, 5, 5, 10, 36, 49) } incref { 0 } - startdate { Time.utc(2022, 2, 2, 10, 36, 49) } + startdate { Time.zone.today } armedforces { 1 } builtype { 1 } unitletas { 2 } diff --git a/spec/factories/location.rb b/spec/factories/location.rb index 3359f64cd..870140cd5 100644 --- a/spec/factories/location.rb +++ b/spec/factories/location.rb @@ -7,7 +7,7 @@ FactoryBot.define do mobility_type { %w[A M N W X].sample } location_code { "E09000033" } location_admin_district { "Westminster" } - startdate { Faker::Date.between(from: 6.months.ago, to: Time.zone.today) } + startdate { nil } confirmed { true } scheme trait :export do diff --git a/spec/factories/scheme.rb b/spec/factories/scheme.rb index 4a85f2036..031d9b8c1 100644 --- a/spec/factories/scheme.rb +++ b/spec/factories/scheme.rb @@ -12,7 +12,7 @@ FactoryBot.define do owning_organisation { FactoryBot.create(:organisation) } managing_organisation { FactoryBot.create(:organisation) } confirmed { true } - created_at { Time.zone.now } + created_at { Time.zone.local(2021, 4, 1) } trait :export do sensitive { 1 } registered_under_care_act { 1 } diff --git a/spec/features/bulk_upload_lettings_logs_spec.rb b/spec/features/bulk_upload_lettings_logs_spec.rb new file mode 100644 index 000000000..e9a05b07d --- /dev/null +++ b/spec/features/bulk_upload_lettings_logs_spec.rb @@ -0,0 +1,51 @@ +require "rails_helper" + +RSpec.describe "Bulk upload lettings log" do + let(:user) { create(:user) } + + before do + sign_in user + end + + context "when during crossover period" do + it "shows journey with year option" do + Timecop.freeze(2023, 6, 1) do + visit("/lettings-logs") + expect(page).to have_link("Upload lettings logs in bulk") + click_link("Upload lettings logs in bulk") + + expect(page).to have_content("Which year") + click_button("Continue") + + expect(page).to have_content("You must select a collection period to upload for") + choose("2022/2023") + click_button("Continue") + + click_link("Back") + + expect(page.find_field("form-year-2022-field")).to be_checked + click_button("Continue") + + expect(page).to have_content("Upload lettings logs in bulk (2022/23)") + click_button("Continue") + + expect(page).to have_content("Upload your file") + end + end + end + + context "when not it crossover period" do + it "shows journey with year option" do + Timecop.freeze(2023, 10, 1) do + visit("/lettings-logs") + expect(page).to have_link("Upload lettings logs in bulk") + click_link("Upload lettings logs in bulk") + + expect(page).to have_content("Upload lettings logs in bulk (2022/23)") + click_button("Continue") + + expect(page).to have_content("Upload your file") + end + end + end +end diff --git a/spec/features/bulk_upload_sales_logs_spec.rb b/spec/features/bulk_upload_sales_logs_spec.rb new file mode 100644 index 000000000..67187ff78 --- /dev/null +++ b/spec/features/bulk_upload_sales_logs_spec.rb @@ -0,0 +1,51 @@ +require "rails_helper" + +RSpec.describe "Bulk upload sales log" do + let(:user) { create(:user) } + + before do + sign_in user + end + + context "when during crossover period" do + it "shows journey with year option" do + Timecop.freeze(2023, 6, 1) do + visit("/sales-logs") + expect(page).to have_link("Upload sales logs in bulk") + click_link("Upload sales logs in bulk") + + expect(page).to have_content("Which year") + click_button("Continue") + + expect(page).to have_content("You must select a collection period to upload for") + choose("2022/2023") + click_button("Continue") + + click_link("Back") + + expect(page.find_field("form-year-2022-field")).to be_checked + click_button("Continue") + + expect(page).to have_content("Upload sales logs in bulk (2022/23)") + click_button("Continue") + + expect(page).to have_content("Upload your file") + end + end + end + + context "when not it crossover period" do + it "shows journey with year option" do + Timecop.freeze(2023, 10, 1) do + visit("/sales-logs") + expect(page).to have_link("Upload sales logs in bulk") + click_link("Upload sales logs in bulk") + + expect(page).to have_content("Upload sales logs in bulk (2022/23)") + click_button("Continue") + + expect(page).to have_content("Upload your file") + end + end + end +end diff --git a/spec/features/form/check_answers_page_spec.rb b/spec/features/form/check_answers_page_spec.rb index 03d237dd2..5f4665725 100644 --- a/spec/features/form/check_answers_page_spec.rb +++ b/spec/features/form/check_answers_page_spec.rb @@ -7,7 +7,7 @@ RSpec.describe "Form Check Answers Page" do let(:subsection) { "household-characteristics" } let(:conditional_subsection) { "conditional-question" } let(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let(:location) { FactoryBot.create(:location, scheme:, mobility_type: "N") } + let(:location) { FactoryBot.create(:location, scheme:, mobility_type: "N", startdate: Time.zone.local(2021, 4, 1)) } let(:lettings_log) do FactoryBot.create( @@ -36,6 +36,7 @@ RSpec.describe "Form Check Answers Page" do :completed, owning_organisation: user.organisation, managing_organisation: user.organisation, + startdate: Time.zone.local(2021, 5, 1), ) end let(:id) { lettings_log.id } diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb index 54d4f1a48..0790096e2 100644 --- a/spec/features/form/validations_spec.rb +++ b/spec/features/form/validations_spec.rb @@ -28,6 +28,7 @@ RSpec.describe "validations" do managing_organisation: user.organisation, status: 1, declaration: nil, + startdate: Time.zone.local(2021, 5, 1), ) end let(:id) { lettings_log.id } diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb index c830d6a0d..0f9659c82 100644 --- a/spec/features/schemes_spec.rb +++ b/spec/features/schemes_spec.rb @@ -195,11 +195,27 @@ RSpec.describe "Schemes scheme Features" do expect(page).to have_link("Locations") end - context "when I click locations link" do + context "when I click locations link and the new locations layout feature toggle is enabled" do before do click_link("Locations") end + it "shows details of those locations" do + locations.each do |location| + expect(page).to have_content(location.id) + expect(page).to have_content(location.postcode) + expect(page).to have_content(location.name) + expect(page).to have_content("Active") + end + end + end + + context "when I click locations link and the new locations layout feature toggle is disabled" do + before do + allow(FeatureToggle).to receive(:location_toggle_enabled?).and_return(false) + click_link("Locations") + end + it "shows details of those locations" do locations.each do |location| expect(page).to have_content(location.id) diff --git a/spec/fixtures/files/lettings_logs_download.csv b/spec/fixtures/files/lettings_logs_download.csv index f16b5f8ad..18c679926 100644 --- a/spec/fixtures/files/lettings_logs_download.csv +++ b/spec/fixtures/files/lettings_logs_download.csv @@ -1,2 +1,2 @@ id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,illness_type_0,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_managing_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,,9,1,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2022-06-05 01:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,,9,1,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/fixtures/files/lettings_logs_download_non_support.csv b/spec/fixtures/files/lettings_logs_download_non_support.csv index ae0beeee0..0687c536e 100644 --- a/spec/fixtures/files/lettings_logs_download_non_support.csv +++ b/spec/fixtures/files/lettings_logs_download_non_support.csv @@ -1,2 +1,2 @@ id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,renewal,startdate,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,relat2,age2,sex2,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,prevloc_label,illness_type_1,illness_type_2,la_label,postcode_full,wchair,preg_occ,cbl,earnings,incfreq,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,unitletas,builtype,voiddate,lettype,nocharge,household_charge,referral,tshortfall,chcharge,ppcodenk,ethnic_group,has_benefits,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,illness_type_0,sheltered,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_managing_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,2 October 2021,,,,,,,,,,,,,,,,,,,,,Westminster,SE1 1TE,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,0,,,,,,,0,0,,,,,,,,,,,,,,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2022-06-05 01:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} +{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,2 October 2021,,,,,,,,,,,,,,,,,,,,,Westminster,SE1 1TE,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,0,,,,,,,0,0,,,,,,,,,,,,,,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/helpers/locations_helper_spec.rb b/spec/helpers/locations_helper_spec.rb index ebd8f1435..164fe79ae 100644 --- a/spec/helpers/locations_helper_spec.rb +++ b/spec/helpers/locations_helper_spec.rb @@ -154,11 +154,11 @@ RSpec.describe LocationsHelper do context "when viewing availability" do context "with no deactivations" do - it "displays created_at as availability date if startdate is not present" do + it "displays previous collection start date as availability date if created_at is earlier than collection start date" do location.update!(startdate: nil) availability_attribute = display_location_attributes(location).find { |x| x[:name] == "Availability" }[:value] - expect(availability_attribute).to eq("Active from #{location.created_at.to_formatted_s(:govuk_date)}") + expect(availability_attribute).to eq("Active from 1 April 2021") end it "displays current collection start date as availability date if created_at is later than collection start date" do diff --git a/spec/helpers/schemes_helper_spec.rb b/spec/helpers/schemes_helper_spec.rb index 96f472457..af9f133ed 100644 --- a/spec/helpers/schemes_helper_spec.rb +++ b/spec/helpers/schemes_helper_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" RSpec.describe SchemesHelper do describe "Active periods" do - let(:scheme) { FactoryBot.create(:scheme) } + let(:scheme) { FactoryBot.create(:scheme, created_at: Time.zone.today) } before do Timecop.freeze(2022, 10, 10) @@ -87,40 +87,97 @@ RSpec.describe SchemesHelper do end end + include TagHelper describe "display_scheme_attributes" do - let!(:scheme) { FactoryBot.create(:scheme, created_at: Time.zone.local(2022, 4, 1)) } + let(:owning_organisation) { FactoryBot.create(:organisation, name: "Acme LTD Owning") } + let(:managing_organisation) { FactoryBot.create(:organisation, name: "Acme LTD Managing") } + let!(:scheme) do + FactoryBot.create(:scheme, + service_name: "Test service_name", + sensitive: 0, + scheme_type: 7, + registered_under_care_act: 3, + owning_organisation:, + managing_organisation:, + arrangement_type: "V", + primary_client_group: "S", + has_other_client_group: 1, + secondary_client_group: "I", + support_type: 4, + intended_stay: "P", + created_at: Time.zone.local(2022, 4, 1)) + end + let!(:scheme_where_managing_organisation_is_owning_organisation) { FactoryBot.create(:scheme, arrangement_type: "D") } + let(:support_user) { FactoryBot.create(:user, :support) } + let(:coordinator_user) { FactoryBot.create(:user, :data_coordinator) } + + it "returns correct display attributes for a support user" do + attributes = [ + { name: "Scheme code", value: "S#{scheme.id}" }, + { name: "Name", value: "Test service_name", edit: true }, + { name: "Confidential information", value: "No", edit: true }, + { name: "Type of scheme", value: "Housing for older people" }, + { name: "Registered under Care Standards Act 2000", value: "Yes – registered care home providing personal care" }, + { name: "Housing stock owned by", value: "Acme LTD Owning", edit: true }, + { name: "Support services provided by", value: "A registered charity or voluntary organisation" }, + { name: "Organisation providing support", value: "Acme LTD Managing" }, + { name: "Primary client group", value: "Rough sleepers" }, + { name: "Has another client group", value: "Yes" }, + { name: "Secondary client group", value: "Refugees (permanent)" }, + { name: "Level of support given", value: "High level" }, + { name: "Intended length of stay", value: "Permanent" }, + { name: "Availability", value: "Active from 1 April 2022" }, + { name: "Status", value: status_tag(:active) }, + ] + expect(display_scheme_attributes(scheme, support_user)).to eq(attributes) + end - it "returns correct display attributes" do + it "returns correct display attributes for a coordinator user" do attributes = [ - { name: "Scheme code", value: scheme.id_to_display }, - { name: "Name", value: scheme.service_name, edit: true }, - { name: "Confidential information", value: scheme.sensitive, edit: true }, - { name: "Type of scheme", value: scheme.scheme_type }, - { name: "Registered under Care Standards Act 2000", value: scheme.registered_under_care_act }, - { name: "Housing stock owned by", value: scheme.owning_organisation.name, edit: true }, - { name: "Support services provided by", value: scheme.arrangement_type }, - { name: "Primary client group", value: scheme.primary_client_group }, - { name: "Has another client group", value: scheme.has_other_client_group }, - { name: "Secondary client group", value: scheme.secondary_client_group }, - { name: "Level of support given", value: scheme.support_type }, - { name: "Intended length of stay", value: scheme.intended_stay }, + { name: "Scheme code", value: "S#{scheme.id}" }, + { name: "Name", value: "Test service_name", edit: true }, + { name: "Confidential information", value: "No", edit: true }, + { name: "Type of scheme", value: "Housing for older people" }, + { name: "Registered under Care Standards Act 2000", value: "Yes – registered care home providing personal care" }, + { name: "Support services provided by", value: "A registered charity or voluntary organisation" }, + { name: "Organisation providing support", value: "Acme LTD Managing" }, + { name: "Primary client group", value: "Rough sleepers" }, + { name: "Has another client group", value: "Yes" }, + { name: "Secondary client group", value: "Refugees (permanent)" }, + { name: "Level of support given", value: "High level" }, + { name: "Intended length of stay", value: "Permanent" }, { name: "Availability", value: "Active from 1 April 2022" }, - { name: "Status", value: :active }, + { name: "Status", value: status_tag(:active) }, ] - expect(display_scheme_attributes(scheme)).to eq(attributes) + expect(display_scheme_attributes(scheme, coordinator_user)).to eq(attributes) + end + + context "when the scheme toggle is disabled" do + it "doesn't show the scheme status" do + allow(FeatureToggle).to receive(:scheme_toggle_enabled?).and_return(false) + attributes = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Status" } + expect(attributes).to be_nil + end + end + + context "when the managing organisation is the owning organisation" do + it "doesn't show the organisation providing support" do + attributes = display_scheme_attributes(scheme_where_managing_organisation_is_owning_organisation, support_user).find { |x| x[:name] == "Organisation providing support" } + expect(attributes).to be_nil + end end context "when viewing availability" do context "with no deactivations" do it "displays created_at as availability date" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from #{scheme.created_at.to_formatted_s(:govuk_date)}") end it "displays current collection start date as availability date if created_at is later than collection start date" do scheme.update!(created_at: Time.zone.local(2022, 4, 16)) - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022") end @@ -135,7 +192,7 @@ RSpec.describe SchemesHelper do end it "displays the timeline of availability" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022 to 9 August 2022\nDeactivated on 10 August 2022\nActive from 1 September 2022 to 14 September 2022\nDeactivated on 15 September 2022\nActive from 28 September 2022") end @@ -149,7 +206,7 @@ RSpec.describe SchemesHelper do end it "displays the timeline of availability" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022 to 9 August 2022\nDeactivated on 10 August 2022\nActive from 1 September 2022 to 14 September 2022\nDeactivated on 15 September 2022") end @@ -165,7 +222,7 @@ RSpec.describe SchemesHelper do end it "displays the timeline of availability" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022 to 14 June 2022\nDeactivated on 15 June 2022\nActive from 18 June 2022 to 23 September 2022\nDeactivated on 24 September 2022\nActive from 28 September 2022") end @@ -179,7 +236,7 @@ RSpec.describe SchemesHelper do end it "displays the timeline of availability" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022 to 14 June 2022\nDeactivated on 15 June 2022\nActive from 28 September 2022") end @@ -196,7 +253,7 @@ RSpec.describe SchemesHelper do end it "displays the timeline of availability" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022 to 14 June 2022\nDeactivated on 15 June 2022\nActive from 28 September 2022 to 23 October 2022\nDeactivated on 24 October 2022\nActive from 28 October 2022") end @@ -211,7 +268,7 @@ RSpec.describe SchemesHelper do end it "displays the timeline of availability" do - availability_attribute = display_scheme_attributes(scheme).find { |x| x[:name] == "Availability" }[:value] + availability_attribute = display_scheme_attributes(scheme, support_user).find { |x| x[:name] == "Availability" }[:value] expect(availability_attribute).to eq("Active from 1 April 2022 to 9 October 2022\nDeactivated on 10 October 2022\nActive from 11 December 2022") end diff --git a/spec/helpers/tag_helper_spec.rb b/spec/helpers/tag_helper_spec.rb index 3f32b8502..3373737de 100644 --- a/spec/helpers/tag_helper_spec.rb +++ b/spec/helpers/tag_helper_spec.rb @@ -11,6 +11,15 @@ RSpec.describe TagHelper do it "returns tag with correct status text and colour and custom class" do expect(status_tag("not_started", "app-tag--small")).to eq("Not started") + expect(status_tag("cannot_start_yet", "app-tag--small")).to eq("Cannot start yet") + expect(status_tag("in_progress", "app-tag--small")).to eq("In progress") + expect(status_tag("completed", "app-tag--small")).to eq("Completed") + expect(status_tag("active", "app-tag--small")).to eq("Active") + expect(status_tag("incomplete", "app-tag--small")).to eq("Incomplete") + expect(status_tag("deactivating_soon", "app-tag--small")).to eq("Deactivating soon") + expect(status_tag("activating_soon", "app-tag--small")).to eq("Activating soon") + expect(status_tag("reactivating_soon", "app-tag--small")).to eq("Reactivating soon") + expect(status_tag("deactivated", "app-tag--small")).to eq("Deactivated") end end end diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb index aa27c421b..f4939b855 100644 --- a/spec/jobs/email_csv_job_spec.rb +++ b/spec/jobs/email_csv_job_spec.rb @@ -33,7 +33,8 @@ describe EmailCsvJob do :completed, owning_organisation: organisation, managing_organisation: organisation, - created_by: user) + created_by: user, + startdate: Time.zone.local(2021, 5, 1)) allow(Storage::S3Service).to receive(:new).and_return(storage_service) allow(storage_service).to receive(:write_file) diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb index a062a4aa5..f8bcddcca 100644 --- a/spec/models/form_spec.rb +++ b/spec/models/form_spec.rb @@ -235,4 +235,38 @@ RSpec.describe Form, type: :model do expect(form.sections[1].class).to eq(Form::Sales::Sections::PropertyInformation) end end + + describe "#in_crossover_period?" do + context "when now not specified" do + context "when after end period" do + subject(:form) { described_class.new(nil, 2022, [], "sales") } + + it "returns false" do + Timecop.freeze(2023, 8, 1) do + expect(form).not_to be_in_crossover_period + end + end + end + + context "when during crossover" do + subject(:form) { described_class.new(nil, 2022, [], "sales") } + + it "returns true" do + Timecop.freeze(2023, 6, 1) do + expect(form).to be_in_crossover_period + end + end + end + + context "when before crossover" do + subject(:form) { described_class.new(nil, 2022, [], "sales") } + + it "returns false" do + Timecop.freeze(2023, 1, 1) do + expect(form).not_to be_in_crossover_period + end + end + end + end + end end diff --git a/spec/models/forms/bulk_upload_lettings/year_spec.rb b/spec/models/forms/bulk_upload_lettings/year_spec.rb new file mode 100644 index 000000000..0b0babb30 --- /dev/null +++ b/spec/models/forms/bulk_upload_lettings/year_spec.rb @@ -0,0 +1,12 @@ +require "rails_helper" + +RSpec.describe Forms::BulkUploadLettings::Year do + subject(:form) { described_class.new } + + describe "#options" do + it "returns correct years" do + expect(form.options.map(&:id)).to eql([2022, 2021]) + expect(form.options.map(&:name)).to eql(%w[2022/2023 2021/2022]) + end + end +end diff --git a/spec/models/forms/bulk_upload_sales/year_spec.rb b/spec/models/forms/bulk_upload_sales/year_spec.rb new file mode 100644 index 000000000..2276b1e4d --- /dev/null +++ b/spec/models/forms/bulk_upload_sales/year_spec.rb @@ -0,0 +1,12 @@ +require "rails_helper" + +RSpec.describe Forms::BulkUploadSales::Year do + subject(:form) { described_class.new } + + describe "#options" do + it "returns correct years" do + expect(form.options.map(&:id)).to eql([2022, 2021]) + expect(form.options.map(&:name)).to eql(%w[2022/2023 2021/2022]) + end + end +end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index 45a24b964..15936b767 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -1672,7 +1672,7 @@ RSpec.describe LettingsLog do let(:scheme) { FactoryBot.create(:scheme) } let!(:location) { FactoryBot.create(:location, scheme:) } - before { lettings_log.update!(scheme:) } + before { lettings_log.update!(startdate: Time.zone.local(2022, 4, 2), scheme:) } it "derives the scheme location" do record_from_db = ActiveRecord::Base.connection.execute("select location_id from lettings_logs where id=#{lettings_log.id}").to_a[0] @@ -2375,7 +2375,7 @@ RSpec.describe LettingsLog do describe "csv download" do let(:scheme) { FactoryBot.create(:scheme) } - let(:location) { FactoryBot.create(:location, :export, scheme:, type_of_unit: 6, postcode: "SE11TE") } + let(:location) { FactoryBot.create(:location, :export, scheme:, type_of_unit: 6, postcode: "SE11TE", startdate: Time.zone.local(2021, 10, 1)) } let(:user) { FactoryBot.create(:user, organisation: location.scheme.owning_organisation) } let(:expected_content) { csv_export_file.read } diff --git a/spec/models/location_spec.rb b/spec/models/location_spec.rb index 472ed2b0c..8c3b055bf 100644 --- a/spec/models/location_spec.rb +++ b/spec/models/location_spec.rb @@ -34,12 +34,18 @@ RSpec.describe Location, type: :model do location.valid?(:postcode) expect(location.errors.count).to eq(1) end + + it "does add an error when the postcode is missing" do + location.postcode = nil + expect { location.save! } + .to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Postcode #{I18n.t('validations.postcode')}") + end end describe "#units" do let(:location) { FactoryBot.build(:location) } - it "does add an error when units is nil" do + it "does add an error when the number of units is invalid" do location.units = nil location.valid?(:units) expect(location.errors.count).to eq(1) @@ -49,13 +55,23 @@ RSpec.describe Location, type: :model do describe "#type_of_unit" do let(:location) { FactoryBot.build(:location) } - it "does add an error when the type_of_unit is nil" do + it "does add an error when the type of unit is invalid" do location.type_of_unit = nil location.valid?(:type_of_unit) expect(location.errors.count).to eq(1) end end + describe "#mobility_type" do + let(:location) { FactoryBot.build(:location) } + + it "does add an error when the mobility type is invalid" do + location.mobility_type = nil + expect { location.save! } + .to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Mobility type #{I18n.t('activerecord.errors.models.location.attributes.mobility_type.blank')}") + end + end + describe "paper trail" do let(:location) { FactoryBot.create(:location) } let!(:name) { location.name } @@ -123,6 +139,13 @@ RSpec.describe Location, type: :model do Timecop.unfreeze end + context "when location is not confirmed" do + it "returns incomplete " do + location.confirmed = false + expect(location.status).to eq(:incomplete) + end + end + context "when there have not been any previous deactivations" do it "returns active if the location has no deactivation records" do expect(location.status).to eq(:active) @@ -208,4 +231,48 @@ RSpec.describe Location, type: :model do end end end + + describe "available_from" do + context "when there is a startdate" do + let(:location) { FactoryBot.build(:location, startdate: Time.zone.local(2022, 4, 6)) } + + it "returns the startdate" do + expect(location.available_from).to eq(Time.zone.local(2022, 4, 6)) + end + end + + context "when there is no start date" do + context "and the location was created at the start of the 2022/23 collection window" do + let(:location) { FactoryBot.build(:location, created_at: Time.zone.local(2022, 4, 6), startdate: nil) } + + it "returns the beginning of 22/23 collection window" do + expect(location.available_from).to eq(Time.zone.local(2022, 4, 1)) + end + end + + context "and the location was created at the end of the 2022/23 collection window" do + let(:location) { FactoryBot.build(:location, created_at: Time.zone.local(2023, 2, 6), startdate: nil) } + + it "returns the beginning of 22/23 collection window" do + expect(location.available_from).to eq(Time.zone.local(2022, 4, 1)) + end + end + + context "and the location was created at the start of the 2021/22 collection window" do + let(:location) { FactoryBot.build(:location, created_at: Time.zone.local(2021, 4, 6), startdate: nil) } + + it "returns the beginning of 21/22 collection window" do + expect(location.available_from).to eq(Time.zone.local(2021, 4, 1)) + end + end + + context "and the location was created at the end of the 2021/22 collection window" do + let(:location) { FactoryBot.build(:location, created_at: Time.zone.local(2022, 2, 6), startdate: nil) } + + it "returns the beginning of 21/22 collection window" do + expect(location.available_from).to eq(Time.zone.local(2021, 4, 1)) + end + end + end + end end diff --git a/spec/models/scheme_spec.rb b/spec/models/scheme_spec.rb index 43a4112d4..d6bacb11a 100644 --- a/spec/models/scheme_spec.rb +++ b/spec/models/scheme_spec.rb @@ -190,4 +190,38 @@ RSpec.describe Scheme, type: :model do expect(all_schemes[2].status).to eq(:incomplete) end end + + describe "available_from" do + context "when the scheme was created at the start of the 2022/23 collection window" do + let(:scheme) { FactoryBot.build(:scheme, created_at: Time.zone.local(2022, 4, 6)) } + + it "returns the beginning of 22/23 collection window" do + expect(scheme.available_from).to eq(Time.zone.local(2022, 4, 1)) + end + end + + context "when the scheme was created at the end of the 2022/23 collection window" do + let(:scheme) { FactoryBot.build(:scheme, created_at: Time.zone.local(2023, 2, 6)) } + + it "returns the beginning of 22/23 collection window" do + expect(scheme.available_from).to eq(Time.zone.local(2022, 4, 1)) + end + end + + context "when the scheme was created at the start of the 2021/22 collection window" do + let(:scheme) { FactoryBot.build(:scheme, created_at: Time.zone.local(2021, 4, 6)) } + + it "returns the beginning of 21/22 collection window" do + expect(scheme.available_from).to eq(Time.zone.local(2021, 4, 1)) + end + end + + context "when the scheme was created at the end of the 2021/22 collection window" do + let(:scheme) { FactoryBot.build(:scheme, created_at: Time.zone.local(2022, 2, 6)) } + + it "returns the beginning of 21/22 collection window" do + expect(scheme.available_from).to eq(Time.zone.local(2021, 4, 1)) + end + end + end end diff --git a/spec/models/validations/date_validations_spec.rb b/spec/models/validations/date_validations_spec.rb index 0b20ab07a..87351b9dd 100644 --- a/spec/models/validations/date_validations_spec.rb +++ b/spec/models/validations/date_validations_spec.rb @@ -83,6 +83,153 @@ RSpec.describe Validations::DateValidations do date_validator.validate_startdate(record) expect(record.errors["startdate"]).to be_empty end + + context "with a deactivated location" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]) + .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 6, 1) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]).to be_empty + end + end + + context "with a location that is reactivating soon" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022", deactivation_date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 9, 1) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]).to be_empty + end + end + + context "with a location that has many reactivations soon" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:) + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 2), reactivation_date: Time.zone.local(2022, 8, 3), location:) + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 1), reactivation_date: Time.zone.local(2022, 9, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 September 2022", deactivation_date: "1 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 10, 1) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]).to be_empty + end + end + + context "with a location with no deactivation periods" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: Time.zone.local(2022, 9, 15)) } + + it "produces no error" do + record.startdate = Time.zone.local(2022, 10, 15) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]).to be_empty + end + + it "produces an error when the date is before available_from date" do + record.startdate = Time.zone.local(2022, 8, 15) + record.location = location + date_validator.validate_startdate(record) + expect(record.errors["startdate"]) + .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) + end + end + + context "with a scheme that is reactivating soon" do + let(:scheme) { create(:scheme) } + + before do + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), scheme:) + scheme.reload + end + + it "produces error when tenancy start date is during deactivated scheme period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.scheme = scheme + date_validator.validate_startdate(record) + expect(record.errors["startdate"]) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 August 2022", deactivation_date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active scheme period" do + record.startdate = Time.zone.local(2022, 9, 1) + record.scheme = scheme + date_validator.validate_startdate(record) + expect(record.errors["startdate"]).to be_empty + end + end + + context "with a scheme that has many reactivations soon" do + let(:scheme) { create(:scheme) } + + before do + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), scheme:) + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 2), reactivation_date: Time.zone.local(2022, 8, 3), scheme:) + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 1), reactivation_date: Time.zone.local(2022, 9, 4), scheme:) + scheme.reload + end + + it "produces error when tenancy start date is during deactivated scheme period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.scheme = scheme + date_validator.validate_startdate(record) + expect(record.errors["startdate"]) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 September 2022", deactivation_date: "1 June 2022")) + end + + it "produces no error when tenancy start date is during an active scheme period" do + record.startdate = Time.zone.local(2022, 10, 1) + record.scheme = scheme + date_validator.validate_startdate(record) + expect(record.errors["startdate"]).to be_empty + end + end end describe "major repairs date" do diff --git a/spec/models/validations/setup_validations_spec.rb b/spec/models/validations/setup_validations_spec.rb index 378aef904..fa95757c4 100644 --- a/spec/models/validations/setup_validations_spec.rb +++ b/spec/models/validations/setup_validations_spec.rb @@ -30,4 +30,198 @@ RSpec.describe Validations::SetupValidations do expect(record.errors["irproduct_other"]).to be_empty end end + + describe "#validate_scheme" do + context "with a deactivated location" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]) + .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 6, 1) + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]).to be_empty + end + end + + context "with a location that is reactivating soon" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022", deactivation_date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 9, 1) + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]).to be_empty + end + end + + context "with a location with no deactivation periods" do + let(:scheme) { create(:scheme, created_at: Time.zone.local(2022, 10, 3)) } + let(:location) { create(:location, scheme:, startdate: Time.zone.local(2022, 9, 15)) } + + it "produces no error" do + record.startdate = Time.zone.local(2022, 10, 15) + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]).to be_empty + end + + it "produces an error when the date is before available_from date" do + record.startdate = Time.zone.local(2022, 8, 15) + record.location = location + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]) + .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) + end + end + + context "with a scheme that is reactivating soon" do + let(:scheme) { create(:scheme, created_at: Time.zone.local(2022, 4, 1)) } + + before do + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), scheme:) + scheme.reload + end + + it "produces error when tenancy start date is during deactivated scheme period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.scheme = scheme + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 August 2022", deactivation_date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active scheme period" do + record.startdate = Time.zone.local(2022, 9, 1) + record.scheme = scheme + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]).to be_empty + end + end + + context "with a scheme that has many reactivations soon" do + let(:scheme) { create(:scheme, created_at: Time.zone.local(2022, 4, 1)) } + + before do + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), scheme:) + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 2), reactivation_date: Time.zone.local(2022, 8, 3), scheme:) + create(:scheme_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 1), reactivation_date: Time.zone.local(2022, 9, 4), scheme:) + scheme.reload + end + + it "produces error when tenancy start date is during deactivated scheme period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.scheme = scheme + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]) + .to include(match I18n.t("validations.setup.startdate.scheme.reactivating_soon", name: scheme.service_name, date: "4 September 2022", deactivation_date: "1 June 2022")) + end + + it "produces no error when tenancy start date is during an active scheme period" do + record.startdate = Time.zone.local(2022, 10, 1) + record.scheme = scheme + setup_validator.validate_scheme(record) + expect(record.errors["scheme_id"]).to be_empty + end + end + end + + describe "#validate_location" do + context "with a deactivated location" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["location_id"]) + .to include(match I18n.t("validations.setup.startdate.location.deactivated", postcode: location.postcode, date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 6, 1) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["location_id"]).to be_empty + end + end + + context "with a location that is reactivating soon" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: nil) } + + before do + create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4), reactivation_date: Time.zone.local(2022, 8, 4), location:) + location.reload + end + + it "produces error when tenancy start date is during deactivated location period" do + record.startdate = Time.zone.local(2022, 7, 5) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["location_id"]) + .to include(match I18n.t("validations.setup.startdate.location.reactivating_soon", postcode: location.postcode, date: "4 August 2022", deactivation_date: "4 June 2022")) + end + + it "produces no error when tenancy start date is during an active location period" do + record.startdate = Time.zone.local(2022, 9, 1) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["location_id"]).to be_empty + end + end + + context "with a location with no deactivation periods" do + let(:scheme) { create(:scheme) } + let(:location) { create(:location, scheme:, startdate: Time.zone.local(2022, 9, 15)) } + + it "produces no error" do + record.startdate = Time.zone.local(2022, 10, 15) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["location_id"]).to be_empty + end + + it "produces an error when the date is before available_from date" do + record.startdate = Time.zone.local(2022, 8, 15) + record.location = location + setup_validator.validate_location(record) + expect(record.errors["location_id"]) + .to include(match I18n.t("validations.setup.startdate.location.activating_soon", postcode: location.postcode, date: "15 September 2022")) + end + end + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index cdeb71092..e25631df7 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -82,6 +82,7 @@ RSpec.configure do |config| config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :view config.include Devise::Test::IntegrationHelpers, type: :request + config.include Devise::Test::IntegrationHelpers, type: :feature config.include ViewComponent::TestHelpers, type: :component config.include Capybara::RSpecMatchers, type: :component config.include ActiveJob::TestHelper diff --git a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb new file mode 100644 index 000000000..3b59d449d --- /dev/null +++ b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb @@ -0,0 +1,34 @@ +require "rails_helper" + +RSpec.describe BulkUploadLettingsLogsController, type: :request do + let(:user) { FactoryBot.create(:user) } + let(:organisation) { user.organisation } + + before do + sign_in user + end + + describe "GET /lettings-logs/bulk-upload-logs/start" do + context "when not in crossover period" do + let(:expected_year) { FormHandler.instance.forms["current_lettings"].start_date.year } + + it "redirects to /prepare-your-file" do + Timecop.freeze(2022, 1, 1) do + get "/lettings-logs/bulk-upload-logs/start", params: {} + + expect(response).to redirect_to("/lettings-logs/bulk-upload-logs/prepare-your-file?form%5Byear%5D=#{expected_year}") + end + end + end + + context "when in crossover period" do + it "redirects to /year" do + Timecop.freeze(2023, 6, 1) do + get "/lettings-logs/bulk-upload-logs/start", params: {} + + expect(response).to redirect_to("/lettings-logs/bulk-upload-logs/year") + end + end + end + end +end diff --git a/spec/requests/bulk_upload_sales_logs_controller_spec.rb b/spec/requests/bulk_upload_sales_logs_controller_spec.rb new file mode 100644 index 000000000..348179dd2 --- /dev/null +++ b/spec/requests/bulk_upload_sales_logs_controller_spec.rb @@ -0,0 +1,34 @@ +require "rails_helper" + +RSpec.describe BulkUploadSalesLogsController, type: :request do + let(:user) { FactoryBot.create(:user) } + let(:organisation) { user.organisation } + + before do + sign_in user + end + + describe "GET /sales-logs/bulk-upload-logs/start" do + context "when not in crossover period" do + let(:expected_year) { FormHandler.instance.forms["current_sales"].start_date.year } + + it "redirects to /prepare-your-file" do + Timecop.freeze(2022, 1, 1) do + get "/sales-logs/bulk-upload-logs/start", params: {} + + expect(response).to redirect_to("/sales-logs/bulk-upload-logs/prepare-your-file?form%5Byear%5D=#{expected_year}") + end + end + end + + context "when in crossover period" do + it "redirects to /year" do + Timecop.freeze(2023, 6, 1) do + get "/sales-logs/bulk-upload-logs/start", params: {} + + expect(response).to redirect_to("/sales-logs/bulk-upload-logs/year") + end + end + end + end +end diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb index 1bb73f399..a7f374f27 100644 --- a/spec/requests/form_controller_spec.rb +++ b/spec/requests/form_controller_spec.rb @@ -28,6 +28,7 @@ RSpec.describe FormController, type: :request do :completed, owning_organisation: organisation, managing_organisation: organisation, + startdate: Time.zone.local(2021, 5, 1), ) end let(:headers) { { "Accept" => "text/html" } } diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb index 5a721aa01..682c2776b 100644 --- a/spec/requests/locations_controller_spec.rb +++ b/spec/requests/locations_controller_spec.rb @@ -125,7 +125,7 @@ RSpec.describe LocationsController, type: :request do context "when signed in as a data coordinator user" do let(:user) { FactoryBot.create(:user, :data_coordinator) } let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } - let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) } + let!(:locations) { FactoryBot.create_list(:location, 3, scheme:, startdate: Time.zone.local(2022, 4, 1)) } before do sign_in user @@ -136,7 +136,7 @@ RSpec.describe LocationsController, type: :request do let!(:another_scheme) { FactoryBot.create(:scheme) } before do - FactoryBot.create(:location, scheme:) + FactoryBot.create(:location, scheme:, startdate: Time.zone.local(2022, 4, 1)) end it "returns 404 not found" do @@ -145,7 +145,18 @@ RSpec.describe LocationsController, type: :request do end end - it "shows scheme" do + it "shows locations with correct data wben the new locations layout feature toggle is enabled" do + locations.each do |location| + expect(page).to have_content(location.id) + expect(page).to have_content(location.postcode) + expect(page).to have_content(location.name) + expect(page).to have_content(location.status) + end + end + + it "shows locations with correct data wben the new locations layout feature toggle is disabled" do + allow(FeatureToggle).to receive(:location_toggle_enabled?).and_return(false) + get "/schemes/#{scheme.id}/locations" locations.each do |location| expect(page).to have_content(location.id) expect(page).to have_content(location.postcode) @@ -242,7 +253,7 @@ RSpec.describe LocationsController, type: :request do context "when signed in as a support user" do let(:user) { FactoryBot.create(:user, :support) } let!(:scheme) { FactoryBot.create(:scheme) } - let!(:locations) { FactoryBot.create_list(:location, 3, scheme:) } + let!(:locations) { FactoryBot.create_list(:location, 3, scheme:, startdate: Time.zone.local(2022, 4, 1)) } before do allow(user).to receive(:need_two_factor_authentication?).and_return(false) @@ -250,11 +261,24 @@ RSpec.describe LocationsController, type: :request do get "/schemes/#{scheme.id}/locations" end - it "shows scheme" do + it "shows locations with correct data wben the new locations layout feature toggle is enabled" do + locations.each do |location| + expect(page).to have_content(location.id) + expect(page).to have_content(location.postcode) + expect(page).to have_content(location.name) + expect(page).to have_content(location.status) + end + end + + it "shows locations with correct data wben the new locations layout feature toggle is disabled" do + allow(FeatureToggle).to receive(:location_toggle_enabled?).and_return(false) + get "/schemes/#{scheme.id}/locations" locations.each do |location| expect(page).to have_content(location.id) expect(page).to have_content(location.postcode) expect(page).to have_content(location.type_of_unit) + expect(page).to have_content(location.mobility_type) + expect(page).to have_content(location.location_admin_district) expect(page).to have_content(location.startdate&.to_formatted_s(:govuk_date)) end end diff --git a/spec/requests/schemes_controller_spec.rb b/spec/requests/schemes_controller_spec.rb index 0b5881828..c605d584f 100644 --- a/spec/requests/schemes_controller_spec.rb +++ b/spec/requests/schemes_controller_spec.rb @@ -1767,7 +1767,7 @@ RSpec.describe SchemesController, type: :request do context "when signed in as a data coordinator" do let(:user) { FactoryBot.create(:user, :data_coordinator) } - let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation) } + let!(:scheme) { FactoryBot.create(:scheme, owning_organisation: user.organisation, created_at: Time.zone.today) } let!(:location) { FactoryBot.create(:location, scheme:) } let(:deactivation_date) { Time.utc(2022, 10, 10) } let!(:lettings_log) { FactoryBot.create(:lettings_log, :sh, location:, scheme:, startdate:, owning_organisation: user.organisation) } diff --git a/spec/services/exports/lettings_log_export_service_spec.rb b/spec/services/exports/lettings_log_export_service_spec.rb index 044fe6c93..71ad30400 100644 --- a/spec/services/exports/lettings_log_export_service_spec.rb +++ b/spec/services/exports/lettings_log_export_service_spec.rb @@ -59,7 +59,7 @@ RSpec.describe Exports::LettingsLogExportService do end context "and one lettings log is available for export" do - let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737") } + let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.utc(2022, 2, 2, 10, 36, 49), tenancylength: 5) } it "generates a ZIP export file with the expected filename" do expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) @@ -237,7 +237,7 @@ RSpec.describe Exports::LettingsLogExportService do let(:csv_export_file) { File.open("spec/fixtures/exports/general_needs_log.csv", "r:UTF-8") } let(:expected_csv_filename) { "export_2022_05_01.csv" } - let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737") } + let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, propcode: "123", ppostcode_full: "SE2 6RT", postcode_full: "NW1 5TY", tenancycode: "BZ737", startdate: Time.utc(2022, 2, 2, 10, 36, 49), tenancylength: 5) } it "generates an CSV export file with the expected content" do expected_content = replace_entity_ids(lettings_log, csv_export_file.read) @@ -254,9 +254,9 @@ RSpec.describe Exports::LettingsLogExportService do let(:organisation) { FactoryBot.create(:organisation, provider_type: "LA") } let(:user) { FactoryBot.create(:user, organisation:) } let(:scheme) { FactoryBot.create(:scheme, :export, owning_organisation: organisation) } - let(:location) { FactoryBot.create(:location, :export, scheme:) } + let(:location) { FactoryBot.create(:location, :export, scheme:, startdate: Time.zone.local(2021, 4, 1)) } - let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, :export, :sh, scheme:, location:, created_by: user, owning_organisation: organisation) } + let(:lettings_log) { FactoryBot.create(:lettings_log, :completed, :export, :sh, scheme:, location:, created_by: user, owning_organisation: organisation, startdate: Time.utc(2022, 2, 2, 10, 36, 49)) } it "generates an XML export file with the expected content" do expected_content = replace_entity_ids(lettings_log, export_file.read) diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb index 453ee7e30..acb9d9dd4 100644 --- a/spec/services/imports/lettings_logs_import_service_spec.rb +++ b/spec/services/imports/lettings_logs_import_service_spec.rb @@ -30,9 +30,9 @@ RSpec.describe Imports::LettingsLogsImportService do FactoryBot.create(:user, old_user_id: "e29c492473446dca4d50224f2bb7cf965a261d6f", organisation:) # Location setup - FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme1.id, mobility_type: "W") - FactoryBot.create(:location, scheme_id: scheme1.id) - FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme2.id, mobility_type: "W") + FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme1.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) + FactoryBot.create(:location, scheme_id: scheme1.id, startdate: Time.zone.local(2021, 4, 1)) + FactoryBot.create(:location, old_visible_id: "10", postcode: "LS166FT", scheme_id: scheme2.id, mobility_type: "W", startdate: Time.zone.local(2021, 4, 1)) # Stub the form handler to use the real form allow(FormHandler.instance).to receive(:get_form).with("previous_lettings").and_return(real_2021_2022_form)