diff --git a/Gemfile b/Gemfile
index 599c5d369..3c8d77954 100644
--- a/Gemfile
+++ b/Gemfile
@@ -29,6 +29,8 @@ gem "discard"
gem "activeadmin"
# Admin charts
gem "chartkick"
+# Spreadsheet parsing
+gem "roo"
# Json Schema
gem "json-schema"
gem "uk_postcode"
diff --git a/Gemfile.lock b/Gemfile.lock
index d2a3b4a15..04db4e6f3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -294,6 +294,9 @@ GEM
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
+ roo (2.8.3)
+ nokogiri (~> 1)
+ rubyzip (>= 1.3.0, < 3.0.0)
rubocop (1.21.0)
parallel (~> 1.10)
parser (>= 3.0.0.0)
@@ -409,6 +412,7 @@ DEPENDENCIES
puma (~> 5.0)
rack-mini-profiler (~> 2.0)
rails (~> 6.1.4)
+ roo
rspec-core!
rspec-expectations!
rspec-mocks!
diff --git a/app/constants/db_enums.rb b/app/constants/db_enums.rb
index 23e4b4bd4..d28393105 100644
--- a/app/constants/db_enums.rb
+++ b/app/constants/db_enums.rb
@@ -166,11 +166,10 @@ module DbEnums
def self.tenancy
{
- "Fixed term – Secure" => 1,
- "Fixed term – Assured Shorthold Tenancy (AST)" => 4,
- "Lifetime – Secure" => 100,
- "Lifetime – Assured" => 2,
- "License agreement" => 5,
+ "Secure (including flexible)" => 1,
+ "Assured" => 2,
+ "Assured Shorthold" => 4,
+ "Licence agreement (almshouses only)" => 5,
"Other" => 3,
}
end
diff --git a/app/controllers/bulk_upload_controller.rb b/app/controllers/bulk_upload_controller.rb
new file mode 100644
index 000000000..ba552cb49
--- /dev/null
+++ b/app/controllers/bulk_upload_controller.rb
@@ -0,0 +1,22 @@
+class BulkUploadController < ApplicationController
+ def show
+ @bulk_upload = BulkUpload.new(nil, nil)
+ render "case_logs/bulk_upload"
+ end
+
+ def bulk_upload
+ file = upload_params.tempfile
+ content_type = upload_params.content_type
+ @bulk_upload = BulkUpload.new(file, content_type)
+ @bulk_upload.process
+ if @bulk_upload.errors.present?
+ render "case_logs/bulk_upload", status: :unprocessable_entity
+ else
+ redirect_to(case_logs_path)
+ end
+ end
+
+ def upload_params
+ params.require("bulk_upload")["case_log_bulk_upload"]
+ end
+end
diff --git a/app/helpers/check_answers_helper.rb b/app/helpers/check_answers_helper.rb
index 6282f9d06..ba6373b72 100644
--- a/app/helpers/check_answers_helper.rb
+++ b/app/helpers/check_answers_helper.rb
@@ -16,7 +16,7 @@ module CheckAnswersHelper
while page_name.to_s != "check_answers" && subsection_keys.include?(page_name)
questions = form.questions_for_page(page_name)
- applicable_questions = filter_conditional_questions(questions, case_log)
+ applicable_questions = form.filter_conditional_questions(questions, case_log)
total_questions = total_questions.merge(applicable_questions)
page_name = get_next_page_name(form, page_name, case_log)
@@ -25,19 +25,6 @@ module CheckAnswersHelper
total_questions
end
- def filter_conditional_questions(questions, case_log)
- applicable_questions = questions
-
- questions.each do |k, question|
- question.fetch("conditional_for", []).each do |conditional_question_key, condition|
- if condition_not_met(case_log, k, question, condition)
- applicable_questions = applicable_questions.reject { |z| z == conditional_question_key }
- end
- end
- end
- applicable_questions
- end
-
def get_next_page_name(form, page_name, case_log)
page = form.all_pages[page_name]
if page.key?("conditional_route_to")
@@ -50,23 +37,6 @@ module CheckAnswersHelper
form.next_page(page_name)
end
- def condition_not_met(case_log, question_key, question, condition)
- case question["type"]
- when "numeric"
- operator = condition[/[<>=]+/].to_sym
- operand = condition[/\d+/].to_i
- case_log[question_key].blank? || !case_log[question_key].send(operator, operand)
- when "text"
- case_log[question_key].blank? || !condition.include?(case_log[question_key])
- when "radio"
- case_log[question_key].blank? || !condition.include?(case_log[question_key])
- when "select"
- case_log[question_key].blank? || !condition.include?(case_log[question_key])
- else
- raise "Not implemented yet"
- end
- end
-
def create_update_answer_link(case_log_answer, case_log_id, page)
link_name = case_log_answer.blank? ? "Answer" : "Change"
link_to(link_name, "/case_logs/#{case_log_id}/#{page}", class: "govuk-link").html_safe
diff --git a/app/helpers/tasklist_helper.rb b/app/helpers/tasklist_helper.rb
index 4ca9f182d..1d3f376c5 100644
--- a/app/helpers/tasklist_helper.rb
+++ b/app/helpers/tasklist_helper.rb
@@ -13,31 +13,32 @@ module TasklistHelper
in_progress: "govuk-tag--blue",
}.freeze
- def get_subsection_status(subsection_name, case_log, questions)
+ def get_subsection_status(subsection_name, case_log, form, questions)
+ applicable_questions = form.filter_conditional_questions(questions, case_log).keys
if subsection_name == "declaration"
return case_log.completed? ? :not_started : :cannot_start_yet
end
- return :not_started if questions.all? { |question| case_log[question].blank? }
- return :completed if questions.all? { |question| case_log[question].present? }
+ return :not_started if applicable_questions.all? { |question| case_log[question].blank? }
+ return :completed if applicable_questions.all? { |question| case_log[question].present? }
:in_progress
end
def get_next_incomplete_section(form, case_log)
subsections = form.all_subsections.keys
- subsections.find { |subsection| is_incomplete?(subsection, case_log, form.questions_for_subsection(subsection).keys) }
+ subsections.find { |subsection| is_incomplete?(subsection, case_log, form, form.questions_for_subsection(subsection)) }
end
def get_subsections_count(form, case_log, status = :all)
subsections = form.all_subsections.keys
return subsections.count if status == :all
- subsections.count { |subsection| get_subsection_status(subsection, case_log, form.questions_for_subsection(subsection).keys) == status }
+ subsections.count { |subsection| get_subsection_status(subsection, case_log, form, form.questions_for_subsection(subsection)) == status }
end
def get_first_page_or_check_answers(subsection, case_log, form, questions)
- path = if is_started?(subsection, case_log, questions)
+ path = if is_started?(subsection, case_log, form, questions)
"case_log_#{subsection}_check_answers_path"
else
"case_log_#{form.first_page_for_subsection(subsection)}_path"
@@ -47,13 +48,13 @@ module TasklistHelper
private
- def is_incomplete?(subsection, case_log, questions)
- status = get_subsection_status(subsection, case_log, questions)
+ def is_incomplete?(subsection, case_log, form, questions)
+ status = get_subsection_status(subsection, case_log, form, questions)
%i[not_started in_progress].include?(status)
end
- def is_started?(subsection, case_log, questions)
- status = get_subsection_status(subsection, case_log, questions)
+ def is_started?(subsection, case_log, form, questions)
+ status = get_subsection_status(subsection, case_log, form, questions)
%i[in_progress completed].include?(status)
end
end
diff --git a/app/models/bulk_upload.rb b/app/models/bulk_upload.rb
new file mode 100644
index 000000000..f546ea56d
--- /dev/null
+++ b/app/models/bulk_upload.rb
@@ -0,0 +1,199 @@
+class BulkUpload
+ include ActiveModel::Model
+ include ActiveModel::Validations
+ include ActiveModel::Conversion
+
+ SPREADSHEET_CONTENT_TYPES = %w[
+ application/vnd.ms-excel
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+ ].freeze
+
+ FIRST_DATA_ROW = 7
+
+ def initialize(file, content_type)
+ @file = file
+ @content_type = content_type
+ end
+
+ def process
+ return unless valid_content_type?
+
+ xlsx = Roo::Spreadsheet.open(@file, extension: :xlsx)
+ sheet = xlsx.sheet(0)
+ last_row = sheet.last_row
+ if last_row < FIRST_DATA_ROW
+ errors.add(:case_log_bulk_upload, "No data found")
+ else
+ data_range = FIRST_DATA_ROW..last_row
+ data_range.map do |row_num|
+ case_log = CaseLog.create
+ map_row(sheet.row(row_num)).each do |attr_key, attr_val|
+ begin
+ case_log.update_attribute(attr_key, attr_val)
+ rescue ArgumentError
+ end
+ end
+ end
+ end
+ end
+
+ def valid_content_type?
+ if SPREADSHEET_CONTENT_TYPES.include?(@content_type)
+ true
+ else
+ errors.add(:case_log_bulk_upload, "Invalid file type")
+ false
+ end
+ end
+
+ def map_row(row)
+ {
+ lettype: row[1],
+ landlord: row[2],
+ # reg_num_la_core_code: row[3],
+ # managementgroup: row[4],
+ # schemecode: row[5],
+ # firstletting: row[6],
+ tenant_code: row[7],
+ startertenancy: row[8],
+ tenancy: row[9],
+ tenancyother: row[10],
+ # tenancyduration: row[11],
+ other_hhmemb: other_hhmemb(row),
+ hhmemb: other_hhmemb(row) + 1,
+ age1: row[12],
+ age2: row[13],
+ age3: row[14],
+ age4: row[15],
+ age5: row[16],
+ age6: row[17],
+ age7: row[18],
+ age8: row[19],
+ sex1: row[20],
+ sex2: row[21],
+ sex3: row[22],
+ sex4: row[23],
+ sex5: row[24],
+ sex6: row[25],
+ sex7: row[26],
+ sex8: row[27],
+ relat2: row[28],
+ relat3: row[29],
+ relat4: row[30],
+ relat5: row[31],
+ relat6: row[32],
+ relat7: row[33],
+ relat8: row[34],
+ ecstat1: row[35],
+ ecstat2: row[36],
+ ecstat3: row[37],
+ ecstat4: row[38],
+ ecstat5: row[39],
+ ecstat6: row[40],
+ ecstat7: row[41],
+ ecstat8: row[42],
+ ethnic: row[43],
+ national: row[44],
+ armed_forces: row[45],
+ reservist: row[46],
+ preg_occ: row[47],
+ hb: row[48],
+ benefits: row[49],
+ net_income_known: row[50].present? ? 1 : nil,
+ earnings: row[50],
+ # increfused: row[51],
+ reason: row[52],
+ other_reason_for_leaving_last_settled_home: row[53],
+ underoccupation_benefitcap: row[54],
+ housingneeds_a: row[55],
+ housingneeds_b: row[56],
+ housingneeds_c: row[57],
+ housingneeds_f: row[58],
+ housingneeds_g: row[59],
+ housingneeds_h: row[60],
+ prevten: row[61],
+ prevloc: row[62],
+ # ppostc1: row[63],
+ # ppostc2: row[64],
+ # prevpco_unknown: row[65],
+ layear: row[66],
+ lawaitlist: row[67],
+ homeless: row[68],
+ reasonpref: row[69],
+ rp_homeless: row[70],
+ rp_insan_unsat: row[71],
+ rp_medwel: row[72],
+ rp_hardship: row[73],
+ rp_dontknow: row[74],
+ cbl: row[75],
+ chr: row[76],
+ cap: row[77],
+ # referral_source: row[78],
+ period: row[79],
+ brent: row[80],
+ scharge: row[81],
+ pscharge: row[82],
+ supcharg: row[83],
+ tcharge: row[84],
+ # tcharge_care_homes: row[85],
+ # no_rent_or_charge: row[86],
+ hbrentshortfall: row[87],
+ tshortfall: row[88],
+ property_void_date: row[89].to_s + row[90].to_s + row[91].to_s,
+ # property_void_date_day: row[89],
+ # property_void_date_month: row[90],
+ # property_void_date_year: row[91],
+ majorrepairs: row[92].present? ? "1" : nil,
+ mrcdate: row[92].to_s + row[93].to_s + row[94].to_s,
+ mrcday: row[92],
+ mrcmonth: row[93],
+ mrcyear: row[94],
+ # supported_scheme: row[95],
+ startdate: row[96].to_s + row[97].to_s + row[98].to_s,
+ # startdate_day: row[96],
+ # startdate_month: row[97],
+ # startdate_year: row[98],
+ offered: row[99],
+ # property_reference: row[100],
+ beds: row[101],
+ unittype_gn: row[102],
+ property_building_type: row[103],
+ wchair: row[104],
+ property_relet: row[105],
+ rsnvac: row[106],
+ la: row[107],
+ # postcode: row[108],
+ # postcod2: row[109],
+ # row[110] removed
+ property_owner_organisation: row[111],
+ # username: row[112],
+ property_manager_organisation: row[113],
+ leftreg: row[114],
+ # uprn: row[115],
+ incfreq: row[116],
+ # sheltered_accom: row[117],
+ illness: row[118],
+ illness_type_1: row[119],
+ illness_type_2: row[120],
+ illness_type_3: row[121],
+ illness_type_4: row[122],
+ illness_type_8: row[123],
+ illness_type_5: row[124],
+ illness_type_6: row[125],
+ illness_type_7: row[126],
+ illness_type_9: row[127],
+ illness_type_10: row[128],
+ # london_affordable: row[129],
+ rent_type: row[130],
+ intermediate_rent_product_name: row[131],
+ # data_protection: row[132],
+ sale_or_letting: "letting",
+ gdpr_acceptance: 1,
+ gdpr_declined: 0,
+ }
+ end
+
+ def other_hhmemb(row)
+ [13, 14, 15, 16, 17, 18, 19].count { |idx| row[idx].present? }
+ end
+end
diff --git a/app/models/case_log.rb b/app/models/case_log.rb
index 5553b5a78..5b5dc6f40 100644
--- a/app/models/case_log.rb
+++ b/app/models/case_log.rb
@@ -39,7 +39,6 @@ class CaseLog < ApplicationRecord
include SoftValidations
include DbEnums
default_scope -> { kept }
- scope :not_completed, -> { where.not(status: "completed") }
validates_with CaseLogValidator
before_save :update_status!
@@ -129,6 +128,8 @@ class CaseLog < ApplicationRecord
end
def weekly_net_income
+ return unless earnings && incfreq
+
case incfreq
when "Weekly"
earnings
@@ -230,7 +231,7 @@ private
dynamically_not_required << "incfreq"
end
- if tenancy == "Fixed term – Secure"
+ if tenancy == "Secure (including flexible)"
dynamically_not_required << "tenancylength"
end
diff --git a/app/models/form.rb b/app/models/form.rb
index 3eeac8987..a2479e20b 100644
--- a/app/models/form.rb
+++ b/app/models/form.rb
@@ -97,6 +97,36 @@ class Form
}.reduce(:merge)
end
+ def filter_conditional_questions(questions, case_log)
+ applicable_questions = questions
+
+ questions.each do |k, question|
+ question.fetch("conditional_for", []).each do |conditional_question_key, condition|
+ if condition_not_met(case_log, k, question, condition)
+ applicable_questions = applicable_questions.reject { |z| z == conditional_question_key }
+ end
+ end
+ end
+ applicable_questions
+ end
+
+ def condition_not_met(case_log, question_key, question, condition)
+ case question["type"]
+ when "numeric"
+ operator = condition[/[<>=]+/].to_sym
+ operand = condition[/\d+/].to_i
+ case_log[question_key].blank? || !case_log[question_key].send(operator, operand)
+ when "text"
+ case_log[question_key].blank? || !condition.include?(case_log[question_key])
+ when "radio"
+ case_log[question_key].blank? || !condition.include?(case_log[question_key])
+ when "select"
+ case_log[question_key].blank? || !condition.include?(case_log[question_key])
+ else
+ raise "Not implemented yet"
+ end
+ end
+
def get_answer_label(case_log, question_title)
question = all_questions[question_title]
if question["type"] == "checkbox"
diff --git a/app/validations/tenancy_validations.rb b/app/validations/tenancy_validations.rb
index 32ff8e2b0..fc275f91e 100644
--- a/app/validations/tenancy_validations.rb
+++ b/app/validations/tenancy_validations.rb
@@ -4,12 +4,12 @@ module TenancyValidations
def validate_fixed_term_tenancy(record)
is_present = record.tenancylength.present?
is_in_range = record.tenancylength.to_i.between?(2, 99)
- is_secure = record.tenancy == "Fixed term – Secure"
- is_ast = record.tenancy == "Fixed term – Assured Shorthold Tenancy (AST)"
+ is_secure = record.tenancy == "Secure (including flexible)"
+ is_ast = record.tenancy == "Assured Shorthold"
conditions = [
{ condition: !(is_secure || is_ast) && is_present, error: "You must only answer the fixed term tenancy length question if the tenancy type is fixed term" },
{ condition: is_ast && !is_in_range, error: "Fixed term – Assured Shorthold Tenancy (AST) should be between 2 and 99 years" },
- { condition: is_secure && (!is_in_range && is_present), error: "Fixed term – Secure should be between 2 and 99 years or not specified" },
+ { condition: is_secure && (!is_in_range && is_present), error: "Secure (including flexible) should be between 2 and 99 years or not specified" },
]
conditions.each { |condition| condition[:condition] ? (record.errors.add :tenancylength, condition[:error]) : nil }
diff --git a/app/views/case_logs/_tasklist.html.erb b/app/views/case_logs/_tasklist.html.erb
index fdf4c5aa9..1e75f207b 100644
--- a/app/views/case_logs/_tasklist.html.erb
+++ b/app/views/case_logs/_tasklist.html.erb
@@ -9,10 +9,10 @@
<% section_value["subsections"].map do |subsection_key, subsection_value| %>
- >
- <% questions_for_subsection = @form.questions_for_subsection(subsection_key).keys %>
+ <% questions_for_subsection = @form.questions_for_subsection(subsection_key) %>
<% next_page_path = get_first_page_or_check_answers(subsection_key, @case_log, @form, questions_for_subsection) %>
<%= link_to subsection_value["label"], next_page_path, class: "task-name govuk-link" %>
- <% subsection_status = get_subsection_status(subsection_key, @case_log, questions_for_subsection) %>
+ <% subsection_status = get_subsection_status(subsection_key, @case_log, @form, questions_for_subsection) %>
<%= TasklistHelper::STATUSES[subsection_status] %>
diff --git a/app/views/case_logs/bulk_upload.html.erb b/app/views/case_logs/bulk_upload.html.erb
new file mode 100644
index 000000000..02cb109df
--- /dev/null
+++ b/app/views/case_logs/bulk_upload.html.erb
@@ -0,0 +1,10 @@
+
+ <%= form_for @bulk_upload, url: bulk_upload_case_logs_path, method: "post", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
+ <%= f.govuk_error_summary %>
+ <%= f.govuk_file_field :case_log_bulk_upload,
+ label: { text: "Bulk Upload", size: "l" },
+ hint: { text: "Upload a spreadsheet using the template" }
+ %>
+ <%= f.govuk_submit "Upload" %>
+ <% end %>
+
diff --git a/app/views/form/page.html.erb b/app/views/form/page.html.erb
index d47e89e7b..238f11439 100644
--- a/app/views/form/page.html.erb
+++ b/app/views/form/page.html.erb
@@ -10,7 +10,7 @@
<%= page_info["header"] %>
<% end %>
- <%= form_with model: @case_log, method: "submit_form", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
+ <%= form_with model: @case_log, url: form_case_log_path(@case_log), method: "post", builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f| %>
<%= f.govuk_error_summary %>
<% page_info["questions"].map do |question_key, question| %>
<%= display_question_key_div(page_info, question_key) %> >
diff --git a/config/forms/2021_2022.json b/config/forms/2021_2022.json
index 0f59cad7f..62dd50fc3 100644
--- a/config/forms/2021_2022.json
+++ b/config/forms/2021_2022.json
@@ -80,9 +80,8 @@
}
},
"conditional_route_to": {
- "tenant_same_property_renewal": { "sale_or_letting": "Letting" }
- },
- "default_next_page" : "check_answers"
+ "sale_completion_date": { "sale_or_letting": "Sale" }
+ }
},
"tenant_same_property_renewal": {
"header": "About this log",
@@ -100,7 +99,7 @@
}
}
},
- "tenancy_start_date": {
+ "startdate": {
"header": "About this log",
"description": "",
"questions": {
@@ -112,8 +111,8 @@
}
}
},
- "rent_type": {
- "header": "About this log",
+ "about_this_letting": {
+ "header": "Tell us about this letting",
"description": "",
"questions": {
"rent_type": {
@@ -150,12 +149,25 @@
}
}
},
+ "tenant_code": {
+ "header": "",
+ "description": "",
+ "questions": {
+ "tenant_code": {
+ "check_answer_label": "Tenant code",
+ "header": "What is the tenant code?",
+ "hint_text": "",
+ "type": "text"
+ }
+ },
+ "default_next_page": "check_answers"
+ },
"sale_completion_date": {
- "header": "About this log",
+ "header": "Sale Completion Date",
"description": "",
"questions": {
"sale_completion_date": {
- "check_answer_label": "What is the sale completion date?",
+ "check_answer_label": "Sale completion date",
"header": "What is the sale completion date?",
"hint_text": "For example, 27 3 2007",
"type": "date"
@@ -172,7 +184,8 @@
"hint_text": "",
"type": "text"
}
- }
+ },
+ "default_next_page": "check_answers"
}
}
}
@@ -1068,12 +1081,11 @@
"hint_text": "",
"type": "radio",
"answer_options": {
- "0": "Fixed term – Secure",
- "1": "Fixed term – Assured Shorthold Tenancy (AST)",
- "2": "Lifetime – Secure",
- "3": "Lifetime – Assured",
- "4": "License agreement",
- "5": "Other"
+ "0": "Secure (including flexible)",
+ "1": "Assured",
+ "2": "Assured Shorthold",
+ "3": "Licence agreement (almshouses only)",
+ "4": "Other"
},
"conditional_for": {
"other_tenancy_type": ["Other"]
diff --git a/config/routes.rb b/config/routes.rb
index 3bbc0e161..79bbece4e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -4,15 +4,24 @@ Rails.application.routes.draw do
root to: "test#index"
get "about", to: "about#index"
- post "/case_logs/:id", to: "case_logs#submit_form"
-
form_handler = FormHandler.instance
form = form_handler.get_form("2021_2022")
+
resources :case_logs do
+ collection do
+ post "/bulk_upload", to: "bulk_upload#bulk_upload"
+ get "/bulk_upload", to: "bulk_upload#show"
+ end
+
+ member do
+ post "/form", to: "case_logs#submit_form"
+ end
+
form.all_pages.keys.map do |page|
get page.to_s, to: "case_logs##{page}"
- get "#{page}/soft_validations", to: "soft_validations#show"
+ get "#{page}/soft_validations", to: "soft_validations#show" if form.soft_validations_for_page(page)
end
+
form.all_subsections.keys.map do |subsection|
get "#{subsection}/check_answers", to: "case_logs#check_answers"
end
diff --git a/db/migrate/20211116102527_change_datetime.rb b/db/migrate/20211116102527_change_datetime.rb
new file mode 100644
index 000000000..f3b78282b
--- /dev/null
+++ b/db/migrate/20211116102527_change_datetime.rb
@@ -0,0 +1,10 @@
+class ChangeDatetime < ActiveRecord::Migration[6.1]
+ def change
+ change_table :case_logs, bulk: true do |t|
+ t.remove :sale_completion_date
+ t.column :sale_completion_date, :datetime
+ t.remove :startdate
+ t.column :startdate, :datetime
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1ef990508..fa2839c20 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_11_12_105348) do
+ActiveRecord::Schema.define(version: 2021_11_16_102527) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -66,7 +66,6 @@ ActiveRecord::Schema.define(version: 2021_11_12_105348) do
t.string "accessibility_requirements"
t.string "condition_effects"
t.string "tenancy_code"
- t.string "startdate"
t.integer "startertenancy"
t.integer "tenancylength"
t.integer "tenancy"
@@ -131,7 +130,6 @@ ActiveRecord::Schema.define(version: 2021_11_12_105348) do
t.string "rent_type"
t.string "intermediate_rent_product_name"
t.string "needs_type"
- t.string "sale_completion_date"
t.string "purchaser_code"
t.integer "override_net_income_validation"
t.string "net_income_known"
@@ -154,6 +152,8 @@ ActiveRecord::Schema.define(version: 2021_11_12_105348) do
t.integer "mrcyear"
t.integer "other_hhmemb"
t.integer "incref"
+ t.datetime "sale_completion_date"
+ t.datetime "startdate"
t.index ["discarded_at"], name: "index_case_logs_on_discarded_at"
end
diff --git a/docs/api/DLUHC-CORE-Data.v1.json b/docs/api/DLUHC-CORE-Data.v1.json
index 17560d14c..5723c9311 100644
--- a/docs/api/DLUHC-CORE-Data.v1.json
+++ b/docs/api/DLUHC-CORE-Data.v1.json
@@ -306,7 +306,7 @@
"startdate": "12/03/2019",
"startertenancy": "No",
"tenancylength": "No",
- "tenancy": "Fixed term – Secure",
+ "tenancy": "Secure (including flexible)",
"lettype": "Affordable Rent - General Needs",
"landlord": "This landlord",
"la": "Barnet",
diff --git a/lib/tasks/form_definition.rake b/lib/tasks/form_definition.rake
index 8155f76e1..9e357f931 100644
--- a/lib/tasks/form_definition.rake
+++ b/lib/tasks/form_definition.rake
@@ -32,12 +32,12 @@ namespace :form_definition do
schema = JSON.parse(file.read)
meta_schema = JSON::Validator.validator_for_name("draft4").metaschema
- puts path
+ puts path unless Rails.env.test?
if JSON::Validator.validate(meta_schema, schema)
- puts "Schema Definition is Valid"
+ puts "Schema Definition is Valid" unless Rails.env.test?
else
- puts "Schema Definition in #{path} is not valid against draft4 json schema."
+ puts "Schema Definition in #{path} is not valid against draft4 json schema." unless Rails.env.test?
next
end
@@ -45,8 +45,8 @@ namespace :form_definition do
file = File.open(path)
form_definition = JSON.parse(file.read)
- puts path
- puts JSON::Validator.fully_validate(schema, form_definition, strict: true)
+ puts path unless Rails.env.test?
+ puts JSON::Validator.fully_validate(schema, form_definition, strict: true) unless Rails.env.test?
begin
JSON::Validator.validate!(schema, form_definition)
diff --git a/spec/factories/case_log.rb b/spec/factories/case_log.rb
index b46606279..b1c856c16 100644
--- a/spec/factories/case_log.rb
+++ b/spec/factories/case_log.rb
@@ -19,6 +19,15 @@ FactoryBot.define do
earnings { 750 }
incfreq { "Weekly" }
end
+ trait :conditional_section_complete do
+ tenant_code { "TH356" }
+ age1 { 34 }
+ sex1 { "M" }
+ ethnic { 2 }
+ national { 4 }
+ ecstat1 { 2 }
+ other_hhmemb { 0 }
+ end
created_at { Time.zone.now }
updated_at { Time.zone.now }
end
diff --git a/spec/fixtures/complete_case_log.json b/spec/fixtures/complete_case_log.json
index 4656a04f8..bb16bc42e 100644
--- a/spec/fixtures/complete_case_log.json
+++ b/spec/fixtures/complete_case_log.json
@@ -52,7 +52,7 @@
"startdate": "12/03/2019",
"startertenancy": "No",
"tenancylength": "5",
- "tenancy": "Fixed term – Secure",
+ "tenancy": "Secure (including flexible)",
"lettype": "Affordable Rent - General Needs",
"landlord": "This landlord",
"la": "Barnet",
@@ -129,10 +129,9 @@
"rent_type": "",
"intermediate_rent_product_name": "",
"needs_type": "",
- "sale_completion_date": "",
+ "sale_completion_date": "01/01/2020",
"purchaser_code": "",
"propcode": "123",
- "majorrepairs": "Yes",
"postcode": "a1",
"postcod2": "w3",
"ppostc1": "w3",
diff --git a/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx b/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx
new file mode 100644
index 000000000..c8e86cdbe
Binary files /dev/null and b/spec/fixtures/files/2021_22_lettings_bulk_upload.xlsx differ
diff --git a/spec/fixtures/files/2021_22_lettings_bulk_upload_empty.xlsx b/spec/fixtures/files/2021_22_lettings_bulk_upload_empty.xlsx
new file mode 100644
index 000000000..f1b7de0bb
Binary files /dev/null and b/spec/fixtures/files/2021_22_lettings_bulk_upload_empty.xlsx differ
diff --git a/spec/fixtures/files/random.txt b/spec/fixtures/files/random.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/spec/helpers/tasklist_helper_spec.rb b/spec/helpers/tasklist_helper_spec.rb
index e900363f2..2acc82518 100644
--- a/spec/helpers/tasklist_helper_spec.rb
+++ b/spec/helpers/tasklist_helper_spec.rb
@@ -6,26 +6,27 @@ RSpec.describe TasklistHelper do
let(:completed_case_log) { FactoryBot.build(:case_log, :completed) }
form_handler = FormHandler.instance
let(:form) { form_handler.get_form("test_form") }
+ let(:household_characteristics_questions) { form.questions_for_subsection("household_characteristics") }
describe "get subsection status" do
let(:section) { "income_and_benefits" }
- let(:income_and_benefits_questions) { form.questions_for_subsection("income_and_benefits").keys }
- let(:declaration_questions) { form.questions_for_subsection("declaration").keys }
- let(:local_authority_questions) { form.questions_for_subsection("local_authority").keys }
+ let(:income_and_benefits_questions) { form.questions_for_subsection("income_and_benefits") }
+ let(:declaration_questions) { form.questions_for_subsection("declaration") }
+ let(:local_authority_questions) { form.questions_for_subsection("local_authority") }
it "returns not started if none of the questions in the subsection are answered" do
- status = get_subsection_status("income_and_benefits", case_log, income_and_benefits_questions)
+ status = get_subsection_status("income_and_benefits", case_log, form, income_and_benefits_questions)
expect(status).to eq(:not_started)
end
it "returns cannot start yet if the subsection is declaration" do
- status = get_subsection_status("declaration", case_log, declaration_questions)
+ status = get_subsection_status("declaration", case_log, form, declaration_questions)
expect(status).to eq(:cannot_start_yet)
end
it "returns in progress if some of the questions have been answered" do
case_log["previous_postcode"] = "P0 5TT"
- status = get_subsection_status("local_authority", case_log, local_authority_questions)
+ status = get_subsection_status("local_authority", case_log, form, local_authority_questions)
expect(status).to eq(:in_progress)
end
@@ -35,14 +36,22 @@ RSpec.describe TasklistHelper do
case_log["benefits"] = "All"
case_log["hb"] = "Do not know"
- status = get_subsection_status("income_and_benefits", case_log, income_and_benefits_questions)
+ status = get_subsection_status("income_and_benefits", case_log, form, income_and_benefits_questions)
expect(status).to eq(:completed)
end
it "returns not started if the subsection is declaration and all the questions are completed" do
- status = get_subsection_status("declaration", completed_case_log, declaration_questions)
+ status = get_subsection_status("declaration", completed_case_log, form, declaration_questions)
expect(status).to eq(:not_started)
end
+
+ let(:conditional_section_complete_case_log) { FactoryBot.build(:case_log, :conditional_section_complete) }
+ it "sets the correct status for sections with conditional questions" do
+ status = get_subsection_status(
+ "household_characteristics", conditional_section_complete_case_log, form, household_characteristics_questions
+ )
+ expect(status).to eq(:completed)
+ end
end
describe "get next incomplete section" do
@@ -79,8 +88,6 @@ RSpec.describe TasklistHelper do
end
describe "get_first_page_or_check_answers" do
- let(:household_characteristics_questions) { form.questions_for_subsection("household_characteristics").keys }
-
it "returns the check answers page path if the section has been started already" do
expect(get_first_page_or_check_answers("household_characteristics", case_log, form, household_characteristics_questions)).to match(/check_answers/)
end
diff --git a/spec/lib/tasks/form_definition_validator_spec.rb b/spec/lib/tasks/form_definition_validator_spec.rb
index c61b39c3e..e08b69f58 100644
--- a/spec/lib/tasks/form_definition_validator_spec.rb
+++ b/spec/lib/tasks/form_definition_validator_spec.rb
@@ -27,7 +27,7 @@ describe "rake form_definition:validate", type: :task do
end
it "runs the validate task for the given form definition" do
- expect(JSON::Validator).to receive(:validate!).at_least(2).times
+ expect(JSON::Validator).to receive(:validate!).at_least(1).time
task.invoke("config/forms/2021_2022.json")
end
end
diff --git a/spec/models/case_log_spec.rb b/spec/models/case_log_spec.rb
index e663937db..fad6ca4b9 100644
--- a/spec/models/case_log_spec.rb
+++ b/spec/models/case_log_spec.rb
@@ -182,39 +182,39 @@ RSpec.describe Form, type: :model do
it "Must be completed and between 2 and 99 if type of tenancy is Assured shorthold" do
expect {
- CaseLog.create!(tenancy: "Fixed term – Assured Shorthold Tenancy (AST)",
+ CaseLog.create!(tenancy: "Assured Shorthold",
tenancylength: 1)
}.to raise_error(ActiveRecord::RecordInvalid)
expect {
- CaseLog.create!(tenancy: "Fixed term – Assured Shorthold Tenancy (AST)",
+ CaseLog.create!(tenancy: "Assured Shorthold",
tenancylength: nil)
}.to raise_error(ActiveRecord::RecordInvalid)
expect {
- CaseLog.create!(tenancy: "Fixed term – Assured Shorthold Tenancy (AST)",
+ CaseLog.create!(tenancy: "Assured Shorthold",
tenancylength: 2)
}.not_to raise_error
end
it "Must be empty or between 2 and 99 if type of tenancy is Secure" do
expect {
- CaseLog.create!(tenancy: "Fixed term – Secure",
+ CaseLog.create!(tenancy: "Secure (including flexible)",
tenancylength: 1)
}.to raise_error(ActiveRecord::RecordInvalid)
expect {
- CaseLog.create!(tenancy: "Fixed term – Secure",
+ CaseLog.create!(tenancy: "Secure (including flexible)",
tenancylength: 100)
}.to raise_error(ActiveRecord::RecordInvalid)
expect {
- CaseLog.create!(tenancy: "Fixed term – Secure",
+ CaseLog.create!(tenancy: "Secure (including flexible)",
tenancylength: nil)
}.not_to raise_error
expect {
- CaseLog.create!(tenancy: "Fixed term – Secure",
+ CaseLog.create!(tenancy: "Secure (including flexible)",
tenancylength: 2)
}.not_to raise_error
end
@@ -294,12 +294,12 @@ RSpec.describe Form, type: :model do
it "must not be provided if tenancy type is not other" do
expect {
- CaseLog.create!(tenancy: "Fixed term – Secure",
+ CaseLog.create!(tenancy: "Secure (including flexible)",
tenancyother: "the other reason provided")
}.to raise_error(ActiveRecord::RecordInvalid)
expect {
- CaseLog.create!(tenancy: "Fixed term – Secure",
+ CaseLog.create!(tenancy: "Secure (including flexible)",
tenancyother: nil)
}.not_to raise_error
end
diff --git a/spec/requests/bulk_upload_controller_spec.rb b/spec/requests/bulk_upload_controller_spec.rb
new file mode 100644
index 000000000..6b9ab16e0
--- /dev/null
+++ b/spec/requests/bulk_upload_controller_spec.rb
@@ -0,0 +1,60 @@
+require "rails_helper"
+
+RSpec.describe BulkUploadController, type: :request do
+ let(:url) { "/case_logs/bulk_upload" }
+
+ describe "GET #show" do
+ before do
+ get url, params: {}
+ end
+
+ it "returns a success response" do
+ expect(response).to be_successful
+ end
+
+ it "returns a page with a file upload form" do
+ expect(response.body).to match(/