">
+
<% remove_other_page_errors(@log, @page) %>
<%= f.govuk_error_summary %>
@@ -59,6 +59,8 @@
<% end %>
<%= f.hidden_field :page, value: @page.id %>
+ <%= f.hidden_field :interruption_page_id, value: @interruption_page_id %>
+ <%= f.hidden_field :interruption_page_referrer_type, value: @interruption_page_referrer_type %>
<% if !@page.interruption_screen? && if request.query_parameters["referrer"] != "check_answers" %>
diff --git a/app/views/form/review.html.erb b/app/views/form/review.html.erb
index 0e63875d9..e619da2f8 100644
--- a/app/views/form/review.html.erb
+++ b/app/views/form/review.html.erb
@@ -23,7 +23,7 @@
<%= subsection.label %>
- <%= render partial: "form/check_answers_summary_list", locals: { subsection: } %>
+ <%= render partial: "form/check_answers_summary_list", locals: { subsection:, questions: total_applicable_questions(subsection, @log, current_user), referrer: "check_answers" } %>
<% end %>
diff --git a/config/forms/2022_2023.json b/config/forms/2022_2023.json
index c3b4e3341..5a65e6f0e 100644
--- a/config/forms/2022_2023.json
+++ b/config/forms/2022_2023.json
@@ -789,7 +789,10 @@
"title_text": {
"translation": "soft_validations.void_date.title_text"
},
- "informative_text": {},
+ "informative_text": {
+ "translation": "soft_validations.void_date.hint_text",
+ "arguments": []
+ },
"questions": {
"void_date_value_check": {
"check_answer_label": "Void date confirmation",
@@ -814,7 +817,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["voiddate", "startdate"]
},
"property_major_repairs": {
"header": "",
@@ -898,7 +902,10 @@
"title_text": {
"translation": "soft_validations.major_repairs_date.title_text"
},
- "informative_text": {},
+ "informative_text": {
+ "translation": "soft_validations.major_repairs_date.hint_text",
+ "arguments": []
+ },
"questions": {
"major_repairs_date_value_check": {
"check_answer_label": "Major repairs date confirmation",
@@ -923,7 +930,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["mrcdate", "startdate"]
}
},
"displayed_in_tasklist": [
@@ -1237,7 +1245,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_lead_hhmemb_value_check": {
"depends_on": [
@@ -1280,7 +1289,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"lead_tenant_age": {
"header": "",
@@ -1379,7 +1389,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_lead_age_value_check": {
"depends_on": [
@@ -1422,7 +1433,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"lead_tenant_gender_identity": {
"header": "",
@@ -1500,7 +1512,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_lead_value_check": {
"depends_on": [
@@ -1543,7 +1556,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"lead_tenant_ethnic_group": {
"header": "",
@@ -1839,27 +1853,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_1",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_1",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_1",
- "label": false,
+ "key": "age1",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -1885,7 +1885,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat1", "age1"]
},
"lead_tenant_over_retirement_value_check": {
"depends_on": [
@@ -1943,7 +1944,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat1", "sex1", "age1"]
},
"person_2_known": {
"header": "You’ve given us the details for 1 person in the household",
@@ -2123,7 +2125,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_2_age_value_check": {
"depends_on": [
@@ -2167,7 +2170,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_2_gender_identity": {
"header": "",
@@ -2246,7 +2250,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_2_value_check": {
"depends_on": [
@@ -2290,7 +2295,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_2_working_situation": {
"header": "",
@@ -2377,27 +2383,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_2",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_2",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_2",
- "label": false,
+ "key": "age2",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -2423,7 +2415,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat2", "age2"]
},
"person_2_over_retirement_value_check": {
"depends_on": [
@@ -2481,7 +2474,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat2", "sex2", "age2"]
},
"person_3_known": {
"header": "You’ve given us the details for 2 people in the household",
@@ -2658,7 +2652,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_3_age_value_check": {
"depends_on": [
@@ -2702,7 +2697,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_3_gender_identity": {
"header": "",
@@ -2781,7 +2777,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_3_value_check": {
"depends_on": [
@@ -2825,7 +2822,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_3_working_situation": {
"header": "",
@@ -2912,27 +2910,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_3",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_3",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_3",
- "label": false,
+ "key": "age3",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -2958,7 +2942,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat3", "age3"]
},
"person_3_over_retirement_value_check": {
"depends_on": [
@@ -3016,7 +3001,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat3", "sex3", "age3"]
},
"person_4_known": {
"header": "You’ve given us the details for 3 people in the household",
@@ -3190,7 +3176,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_4_age_value_check": {
"depends_on": [
@@ -3234,7 +3221,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_4_gender_identity": {
"header": "",
@@ -3313,7 +3301,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_4_value_check": {
"depends_on": [
@@ -3357,7 +3346,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_4_working_situation": {
"header": "",
@@ -3444,27 +3434,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_4",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_4",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_4",
- "label": false,
+ "key": "age4",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -3490,7 +3466,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat4", "age4"]
},
"person_4_over_retirement_value_check": {
"depends_on": [
@@ -3548,7 +3525,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat4", "sex4", "age4"]
},
"person_5_known": {
"header": "You’ve given us the details for 4 people in the household",
@@ -3719,7 +3697,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_5_age_value_check": {
"depends_on": [
@@ -3763,7 +3742,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_5_gender_identity": {
"header": "",
@@ -3842,7 +3822,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_5_value_check": {
"depends_on": [
@@ -3886,7 +3867,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_5_working_situation": {
"header": "",
@@ -3973,27 +3955,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_5",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_5",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_5",
- "label": false,
+ "key": "age5",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -4019,7 +3987,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat5", "age5"]
},
"person_5_over_retirement_value_check": {
"depends_on": [
@@ -4077,7 +4046,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat5", "sex5", "age5"]
},
"person_6_known": {
"header": "You’ve given us the details for 5 people in the household",
@@ -4245,7 +4215,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_6_age_value_check": {
"depends_on": [
@@ -4289,7 +4260,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_6_gender_identity": {
"header": "",
@@ -4368,7 +4340,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_6_value_check": {
"depends_on": [
@@ -4412,7 +4385,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_6_working_situation": {
"header": "",
@@ -4499,27 +4473,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_6",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_6",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_6",
- "label": false,
+ "key": "age6",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -4545,7 +4505,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat6", "age6"]
},
"person_6_over_retirement_value_check": {
"depends_on": [
@@ -4603,7 +4564,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat6", "sex6", "age6"]
},
"person_7_known": {
"header": "You’ve given us the details for 6 people in the household",
@@ -4768,7 +4730,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_7_age_value_check": {
"depends_on": [
@@ -4812,7 +4775,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_7_gender_identity": {
"header": "",
@@ -4891,7 +4855,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_7_value_check": {
"depends_on": [
@@ -4935,7 +4900,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_7_working_situation": {
"header": "",
@@ -5022,27 +4988,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_7",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_7",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_7",
- "label": false,
+ "key": "age7",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -5068,7 +5020,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat7", "age7"]
},
"person_7_over_retirement_value_check": {
"depends_on": [
@@ -5126,7 +5079,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat7", "sex7", "age7"]
},
"person_8_known": {
"header": "You’ve given us the details for 7 people in the household",
@@ -5288,7 +5242,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_8_age_value_check": {
"depends_on": [
@@ -5332,7 +5287,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_8_gender_identity": {
"header": "",
@@ -5411,7 +5367,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_person_8_value_check": {
"depends_on": [
@@ -5455,7 +5412,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"person_8_working_situation": {
"header": "",
@@ -5542,27 +5500,13 @@
"translation": "soft_validations.retirement.min.title",
"arguments": [
{
- "key": "retirement_age_for_person_8",
- "label": false,
- "i18n_template": "age"
- }
- ]
- },
- "informative_text": {
- "translation": "soft_validations.retirement.min.hint_text",
- "arguments": [
- {
- "key": "plural_gender_for_person_8",
- "label": false,
- "i18n_template": "gender"
- },
- {
- "key": "retirement_age_for_person_8",
- "label": false,
+ "key": "age8",
+ "label": true,
"i18n_template": "age"
}
]
},
+ "informative_text": {},
"questions": {
"retirement_value_check": {
"check_answer_label": "Retirement confirmation",
@@ -5588,7 +5532,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat8", "age8"]
},
"person_8_over_retirement_value_check": {
"depends_on": [
@@ -5646,7 +5591,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat8", "sex8", "age8"]
}
}
},
@@ -5828,7 +5774,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8"]
},
"females_in_soft_age_range_in_pregnant_household_value_check": {
"depends_on": [
@@ -5880,7 +5827,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["preg_occ", "sex1", "sex2", "sex3", "sex4", "sex5", "sex6", "sex7", "sex8", "age1", "age2", "age3", "age4", "age5", "age6", "age7", "age8"]
},
"access_needs_exist": {
"header": "",
@@ -7351,23 +7299,30 @@
}
],
"title_text": {
- "translation": "soft_validations.net_income.title_text"
- },
- "informative_text": {
- "translation": "soft_validations.net_income.hint_text",
- "arguments": [
+ "translation": "soft_validations.net_income.title_text",
+ "arguments" :[
{
- "key": "ecstat1",
+ "key": "incfreq",
"label": true,
- "i18n_template": "ecstat1"
+ "i18n_template": "incfreq"
},
{
- "key": "earnings",
- "label": true,
+ "key": "field_formatted_as_currency",
+ "arguments_for_key": "earnings",
"i18n_template": "earnings"
}
]
},
+ "informative_text": {
+ "translation": "soft_validations.net_income.hint_text",
+ "arguments": [
+ {
+ "key": "net_income_higher_or_lower_text",
+ "label": false,
+ "i18n_template": "net_income_higher_or_lower_text"
+ }
+ ]
+ },
"questions": {
"net_income_value_check": {
"check_answer_label": "Net income confirmation",
@@ -7392,7 +7347,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["incfreq", "earnings", "ecstat1"]
},
"housing_benefit": {
"header": "",
@@ -7754,7 +7710,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["chcharge", "is_carehome"]
},
"rent_weekly": {
"header": "Household rent and charges",
@@ -8268,8 +8225,8 @@
}
]
},
- "header": "This rent is lower than expected for this property type, in this area. Check:",
- "hint_text": "
- the decimal point is not missing (£X.XX)
- the frequency is correct, for example weekly, monthly
- the rent type is correct, for example affordable or social rent
Are you sure this is correct?
",
+ "header": "Are you sure this is correct?",
+ "hint_text": "This is lower than we would expect. Check:
- the decimal point
- the frequency, for example every week or every calendar month
- the rent type is correct, for example affordable or social rent
",
"type": "interruption_screen",
"answer_options": {
"0": {
@@ -8280,7 +8237,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["brent", "startdate", "la", "beds", "rent_type", "needstype"]
},
"max_rent_value_check": {
"depends_on": [
@@ -8312,8 +8270,8 @@
}
]
},
- "header": "This rent is higher than expected for this property type, in this area. Check:",
- "hint_text": "
- the decimal point is not missing (£X.XX)
- the frequency is correct, for example weekly, monthly
- the rent type is correct, for example affordable or social rent
Are you sure this is correct?
",
+ "header": "Are you sure this is correct?",
+ "hint_text": "This is higher than we would expect. Check:
- the decimal point
- the frequency, for example every week or every calendar month
- the rent type is correct, for example affordable or social rent
",
"type": "interruption_screen",
"answer_options": {
"0": {
@@ -8324,7 +8282,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["brent", "startdate", "la", "beds", "rent_type", "needstype"]
},
"outstanding": {
"header": "",
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 4cf4e2c0e..53565b753 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -532,44 +532,54 @@ en:
soft_validations:
net_income:
- title_text: "Net income is outside the expected range based on the lead tenant’s working situation"
- hint_text: "
You told us the lead tenant’s working situation is: %{ecstat1}.
The household income you have entered is %{earnings}.
"
+ title_text: "You told us the lead tenant’s income is %{earnings} %{incfreq}."
+ hint_text: "This is %{net_income_higher_or_lower_text} than we would expect for their working situation."
in_soft_min_range:
message: "Net income is lower than expected based on the lead tenant’s working situation. Are you sure this is correct?"
in_soft_max_range:
message: "Net income is higher than expected based on the lead tenant’s working situation. Are you sure this is correct?"
income:
- under_soft_min_for_economic_status: "You said income was %{income}, which is below this working situation's minimum (%{minimum})"
+ under_soft_min_for_economic_status:
+ title_text: "You told us income was %{income}."
+ hint_text: "This is less than we would expect for someone in this working situation."
rent:
outside_range_title: "You told us the rent is %{brent}"
min_hint_text: "The minimum rent expected for this type of property in this local authority is %{soft_min_for_period}."
max_hint_text: "The maximum rent expected for this type of property in this local authority is %{soft_max_for_period}."
purchase_price:
title_text: "You told us the purchase price is %{value}"
- hint_text: "The %{min_or_max} purchase price expected for this type of property in this local authority is %{soft_min_or_soft_max}"
+ hint_text: "This is %{higher_or_lower} than we would expect"
retirement:
min:
- title: "You told us this person is under %{age} and retired"
+ title: "You told us this person is aged %{age} years and retired."
hint_text: "The minimum expected retirement age for %{gender} in England is %{age}."
max:
title: "You told us this person is %{age} or over and not retired"
hint_text: "The minimum expected retirement age for %{gender} in England is %{age}."
extra_borrowing:
- title: "The mortgage and deposit are higher than the purchase minus the discount"
+ title_text: "You told us that the mortgage and deposit total is %{mortgage_and_deposit_total}"
+ hint_text: "This is higher than the purchase price minus the discount."
pregnancy:
title: "You told us somebody in the household is pregnant"
no_females: "You also told us there are no female tenants living at the property."
females_not_in_soft_age_range: "You also told us that any female tenants living at the property are in the following age ranges:
- under 16 years old
- over 50 years old
"
major_repairs_date:
- title_text: "You told us the time between the start of the tenancy and the major repairs completion date is more than 2 years"
+ title_text: "You told us the property has been vacant for 2 years."
+ hint_text: "This is higher than we would expect."
void_date:
- title_text: "You told us the time between the start of the tenancy and the void date is more than 2 years"
+ title_text: "You told us that the property has been vacant for more than 2 years."
+ hint_text: "This is higher than we would expect."
shared_ownership_deposit:
- title_text: "Mortgage, deposit and cash discount total should equal %{expected_shared_ownership_deposit_value}"
- old_persons_shared_ownership: "At least one buyer should be aged over 64 for Older persons’ shared ownership scheme"
- staircase_bought_seems_high: "You said %{percentage}% was bought in this staircasing transaction, which seems high. Are you sure?"
+ title_text: "You told us that the mortgage, deposit and discount add up to %{mortgage_deposit_and_discount_total}"
+ old_persons_shared_ownership:
+ title_text: "You told us the buyer is using the Older Persons Shared Ownership scheme."
+ hint_text: "At least one buyer must be aged 65 years and over to use this scheme."
+ staircase_bought_seems_high:
+ title_text: "You told us that %{percentage}% was bought in this staircasing transaction."
+ hint_text: "Most staircasing transactions are less than 50%"
monthly_charges_over_soft_max:
- title_text: "The amount of monthly charges is high for this type of property and sale type"
+ title_text: "You told us that the monthly charges were %{mscharge}."
+ hint_text: "This is higher than we would expect."
student_not_child:
title_text: "You told us this person is a student aged beween 16 and 19"
discounted_sale_value:
@@ -578,11 +588,28 @@ en:
care_home_charges:
title_text: "Care home charges should be provided if this is a care home accommodation"
buyer1_livein_wrong_for_ownership_type:
- title_text: "You told us that buyer 1 will not live in the property. For %{ownership_scheme} types, the buyer usually lives in the property."
+ title_text: "You told us that buyer 1 will not live in the property."
+ hint_text: " For %{ownership_scheme} types, the buyer usually lives in the property."
buyer2_livein_wrong_for_ownership_type:
- title_text: "You told us that buyer 2 will not live in the property. For %{ownership_scheme} types, the buyer usually lives in the property."
+ title_text: "You told us that buyer 2 will not live in the property."
+ hint_text: " For %{ownership_scheme} types, the buyer usually lives in the property."
percentage_discount_value:
- title_text: "You told us that the percentage discount was %{discount}. This seems high for this type of property."
+ title_text: "You told us that the percentage discount is %{discount}."
+ hint_text: "This is higher than we would expect."
+ savings:
+ title_text: "You told us the buyer’s savings were %{savings}."
+ hint_text: "This is higher than we would expect."
+ deposit:
+ title_text: "You told us the buyer’s deposit was %{deposit} and their savings were %{savings}."
+ hint_text: "The deposit amount is higher than we would expect for the amount of savings they have."
+ grant:
+ title_text: "You told us that the grant amount is %{grant}"
+ hint_text: "Loans, grants and subsidies are usually between £9,000 and £16,000."
+ wheelchair:
+ title_text: "You told us that someone in the household uses a wheelchair."
+ mortgage:
+ title_text: "You told us that the mortgage amount is %{mortgage}"
+ hint_text: "This is more than 5 times the income, which is higher than we would expect."
devise:
two_factor_authentication:
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 8bf9f4a45..842f55e14 100644
--- a/spec/components/check_answers_summary_list_card_component_spec.rb
+++ b/spec/components/check_answers_summary_list_card_component_spec.rb
@@ -62,13 +62,21 @@ RSpec.describe CheckAnswersSummaryListCardComponent, type: :component do
context "when in 23/24 collection" do
context "when given a set of questions" do
- let(:user) { build(:user) }
- let(:log) { build(:lettings_log, :completed, age2: 99, startdate: Time.zone.local(2023, 5, 1)) }
+ let(:user) { create(:user) }
+ let(:log) { create(:lettings_log, :completed, age2: 99, startdate: Time.zone.local(2023, 5, 1)) }
let(:subsection_id) { "household_characteristics" }
let(:subsection) { log.form.get_subsection(subsection_id) }
let(:questions) { subsection.applicable_questions(log) }
- it "renders a summary list card includinq question numbers for the answers to those questions" do
+ around do |example|
+ Timecop.freeze(Time.zone.local(2023, 5, 1))
+ Singleton.__init__(FormHandler)
+ example.run
+ Timecop.return
+ Singleton.__init__(FormHandler)
+ end
+
+ it "renders a summary list card including question numbers for the answers to those questions" do
expect(rendered).to have_content(questions.first.answer_label(log))
expect(rendered).to have_content("Q32 - Lead tenant’s age")
end
diff --git a/spec/features/form/validations_spec.rb b/spec/features/form/validations_spec.rb
index 12e5bb657..420114129 100644
--- a/spec/features/form/validations_spec.rb
+++ b/spec/features/form/validations_spec.rb
@@ -138,28 +138,62 @@ RSpec.describe "validations" do
let(:income_over_soft_limit) { 750 }
let(:income_under_soft_limit) { 700 }
- it "prompts the user to confirm the value is correct with an interruption screen" do
+ before do
visit("/lettings-logs/#{lettings_log.id}/net-income")
fill_in("lettings-log-earnings-field", with: income_over_soft_limit)
choose("lettings-log-incfreq-1-field", allow_label_click: true)
click_button("Save and continue")
+ end
+
+ it "prompts the user to confirm the value is correct with an interruption screen" do
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
- expect(page).to have_content("Net income is outside the expected range based on the lead tenant’s working situation")
- expect(page).to have_content("You told us the lead tenant’s working situation is: full-time – 30 hours or more")
- expect(page).to have_content("The household income you have entered is £750.00 every week")
- choose("lettings-log-net-income-value-check-0-field", allow_label_click: true)
- click_button("Save and continue")
+ expect(page).to have_content("You told us the lead tenant’s income is £750.00 weekly.")
+ expect(page).to have_content("This is higher than we would expect for their working situation.")
+ click_button("Confirm and continue")
expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-uc-proportion")
end
- it "returns the user to the previous question if they do not confirm the value as correct on the interruption screen" do
- visit("/lettings-logs/#{lettings_log.id}/net-income")
- fill_in("lettings-log-earnings-field", with: income_over_soft_limit)
+ it "allows to fix the questions that trigger the soft validation" do
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
+ expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen").twice
+ expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
+ click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen", match: :first)
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen")
+ fill_in("lettings-log-earnings-field", with: income_under_soft_limit)
choose("lettings-log-incfreq-1-field", allow_label_click: true)
click_button("Save and continue")
- choose("lettings-log-net-income-value-check-1-field", allow_label_click: true)
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
+ expect(page).not_to have_content("You told us the lead tenant’s income is £750.00 weekly.")
+ expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
+ end
+
+ it "allows to fix the questions from different sections" do
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
+ expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen").twice
+ expect(page).to have_link("Change", href: "/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
+ click_link("Change", href: "/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/person-1-working-situation?referrer=interruption_screen")
+ choose("lettings-log-ecstat1-10-field", allow_label_click: true)
+ click_button("Save and continue")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check")
+ expect(page).to have_css(".govuk-notification-banner.govuk-notification-banner--success")
+ end
+
+ it "returns the user back to the check_your_answers after fixing a validation from check_your_anwers" do
+ lettings_log.update!(earnings: income_over_soft_limit, incfreq: 1)
+ visit("/lettings-logs/#{lettings_log.id}/income-and-benefits/check-answers")
+ click_link("Answer", href: "/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers")
+ click_link("Change", href: "/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen", match: :first)
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income?referrer=interruption_screen")
+ fill_in("lettings-log-earnings-field", with: income_under_soft_limit)
+ choose("lettings-log-incfreq-1-field", allow_label_click: true)
click_button("Save and continue")
- expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/net-income-value-check?referrer=check_answers")
+ click_button("Confirm and continue")
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/income-and-benefits/check-answers")
end
end
end
diff --git a/spec/fixtures/forms/2021_2022.json b/spec/fixtures/forms/2021_2022.json
index 139ac8399..5a400d095 100644
--- a/spec/fixtures/forms/2021_2022.json
+++ b/spec/fixtures/forms/2021_2022.json
@@ -226,7 +226,8 @@
"operand": 50
}
}
- ]
+ ],
+ "interruption_screen_question_ids": ["age1", "ecstat1"]
},
"person_2_working_situation": {
"header": "",
@@ -740,27 +741,43 @@
}
],
"title_text": {
- "translation": "soft_validations.net_income.title_text"
- },
- "informative_text": {
- "translation": "soft_validations.net_income.hint_text",
+ "translation": "soft_validations.net_income.title_text",
"arguments": [
{
- "key": "ecstat1",
+ "key": "incfreq",
"label": true,
- "i18n_template": "ecstat1"
+ "i18n_template": "incfreq"
},
{
- "key": "earnings",
- "label": true,
+ "key": "field_formatted_as_currency",
+ "arguments_for_key": "earnings",
"i18n_template": "earnings"
}
]
},
+ "informative_text": {
+ "translation": "soft_validations.net_income.hint_text",
+ "arguments": [
+ {
+ "key": "net_income_higher_or_lower_text",
+ "label": false,
+ "i18n_template": "net_income_higher_or_lower_text"
+ }
+ ]
+ },
"questions": {
"net_income_value_check": {
"check_answer_label": "Net income soft validation",
- "hidden_in_check_answers": true,
+ "hidden_in_check_answers": {
+ "depends_on": [
+ {
+ "net_income_value_check": 0
+ },
+ {
+ "net_income_value_check": 1
+ }
+ ]
+ },
"header": "Are you sure this is correct?",
"type": "interruption_screen",
"answer_options": {
@@ -772,7 +789,8 @@
}
}
}
- }
+ },
+ "interruption_screen_question_ids": ["ecstat1", "incfreq", "earnings"]
},
"net_income_uc_proportion": {
"questions": {
diff --git a/spec/helpers/form_page_helper_spec.rb b/spec/helpers/form_page_helper_spec.rb
new file mode 100644
index 000000000..aeded6f8d
--- /dev/null
+++ b/spec/helpers/form_page_helper_spec.rb
@@ -0,0 +1,28 @@
+require "rails_helper"
+
+RSpec.describe FormPageHelper do
+ describe "#action_href" do
+ let(:lettings_log) { FactoryBot.create(:lettings_log) }
+ let(:sales_log) { FactoryBot.create(:sales_log) }
+
+ context "with a lettings log" do
+ it "has an update answer link href helper" do
+ expect(action_href(lettings_log, "net_income")).to eq("/lettings-logs/#{lettings_log.id}/net-income?referrer=check_answers")
+ end
+
+ it "returns a correct referrer in the url" do
+ expect(action_href(lettings_log, "retirement_value_check", "interruption_screen")).to eq("/lettings-logs/#{lettings_log.id}/retirement-value-check?referrer=interruption_screen")
+ end
+ end
+
+ context "with a sales log" do
+ it "has an update answer link href helper" do
+ expect(action_href(sales_log, "buyer_1_age")).to eq("/sales-logs/#{sales_log.id}/buyer-1-age?referrer=check_answers")
+ end
+
+ it "returns a correct referrer in the url" do
+ expect(action_href(sales_log, "grant_value_check", "interruption_screen")).to eq("/sales-logs/#{sales_log.id}/grant-value-check?referrer=interruption_screen")
+ end
+ end
+ end
+end
diff --git a/spec/helpers/interruption_screen_helper_spec.rb b/spec/helpers/interruption_screen_helper_spec.rb
index 33553f39f..6d9122f27 100644
--- a/spec/helpers/interruption_screen_helper_spec.rb
+++ b/spec/helpers/interruption_screen_helper_spec.rb
@@ -34,14 +34,9 @@ RSpec.describe InterruptionScreenHelper do
"translation" => "soft_validations.net_income.hint_text",
"arguments" => [
{
- "key" => "ecstat1",
- "label" => true,
- "i18n_template" => "ecstat1",
- },
- {
- "key" => "earnings",
- "label" => true,
- "i18n_template" => "earnings",
+ "key" => "net_income_higher_or_lower_text",
+ "label" => false,
+ "i18n_template" => "net_income_higher_or_lower_text",
},
],
}
@@ -49,8 +44,7 @@ RSpec.describe InterruptionScreenHelper do
.to eq(
I18n.t(
"soft_validations.net_income.hint_text",
- ecstat1: lettings_log.form.get_question("ecstat1", lettings_log).answer_label(lettings_log).downcase,
- earnings: lettings_log.form.get_question("earnings", lettings_log).answer_label(lettings_log),
+ net_income_higher_or_lower_text: "higher",
),
)
end
@@ -241,4 +235,13 @@ RSpec.describe InterruptionScreenHelper do
end
end
end
+
+ describe "soft_validation_affected_questions" do
+ let(:question) { lettings_log.form.get_question("retirement_value_check", lettings_log) }
+
+ it "returns a list of questions affected by the soft validation" do
+ expect(soft_validation_affected_questions(question, lettings_log).count).to eq(2)
+ expect(soft_validation_affected_questions(question, lettings_log).map(&:id)).to eq(%w[age1 ecstat1])
+ end
+ end
end
diff --git a/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb b/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb
index c81538146..6d2b8f7a8 100644
--- a/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/care_home_charges_value_check_spec.rb
@@ -41,4 +41,8 @@ RSpec.describe Form::Lettings::Pages::CareHomeChargesValueCheck, type: :model do
it "has the correct informative_text" do
expect(page.informative_text).to eq("")
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[chcharge is_carehome])
+ end
end
diff --git a/spec/models/form/lettings/pages/max_rent_value_check_spec.rb b/spec/models/form/lettings/pages/max_rent_value_check_spec.rb
index 1d712d886..5d803252b 100644
--- a/spec/models/form/lettings/pages/max_rent_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/max_rent_value_check_spec.rb
@@ -34,4 +34,8 @@ RSpec.describe Form::Lettings::Pages::MaxRentValueCheck, type: :model do
it "has the correct informative_text" do
expect(page.informative_text).to eq({ "arguments" => [{ "arguments_for_key" => "soft_max_for_period", "i18n_template" => "soft_max_for_period", "key" => "field_formatted_as_currency" }], "translation" => "soft_validations.rent.max_hint_text" })
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[brent startdate la beds rent_type needstype])
+ end
end
diff --git a/spec/models/form/lettings/pages/min_rent_value_check_spec.rb b/spec/models/form/lettings/pages/min_rent_value_check_spec.rb
index 818599b06..e4b6b5478 100644
--- a/spec/models/form/lettings/pages/min_rent_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/min_rent_value_check_spec.rb
@@ -52,4 +52,8 @@ RSpec.describe Form::Lettings::Pages::MinRentValueCheck, type: :model do
],
})
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[brent startdate la beds rent_type needstype])
+ end
end
diff --git a/spec/models/form/lettings/pages/net_income_value_check_spec.rb b/spec/models/form/lettings/pages/net_income_value_check_spec.rb
index c43eb3381..f07ec84ab 100644
--- a/spec/models/form/lettings/pages/net_income_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/net_income_value_check_spec.rb
@@ -28,12 +28,12 @@ RSpec.describe Form::Lettings::Pages::NetIncomeValueCheck, type: :model do
end
it "has the correct title_text" do
- expect(page.title_text).to eq({ "translation" => "soft_validations.net_income.title_text" })
+ expect(page.title_text).to eq({ "translation" => "soft_validations.net_income.title_text", "arguments" => [{ "i18n_template" => "incfreq", "key" => "incfreq", "label" => true }, { "arguments_for_key" => "earnings", "i18n_template" => "earnings", "key" => "field_formatted_as_currency" }] })
end
it "has the correct informative_text" do
expect(page.informative_text).to eq({
- "arguments" => [{ "arguments_for_key" => "ecstat1", "i18n_template" => "ecstat1", "key" => "field_formatted_as_currency" }, { "arguments_for_key" => "earnings", "i18n_template" => "earnings", "key" => "field_formatted_as_currency" }],
+ "arguments" => [{ "i18n_template" => "net_income_higher_or_lower_text", "key" => "net_income_higher_or_lower_text", "label" => false }],
"translation" => "soft_validations.net_income.hint_text",
})
end
diff --git a/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb b/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb
index a2a6dd20f..929009ce7 100644
--- a/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb
+++ b/spec/models/form/lettings/pages/person_under_retirement_value_check_spec.rb
@@ -39,8 +39,8 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_2",
- "label" => false,
+ "key" => "age2",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -48,21 +48,7 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo
end
it "has the correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_2",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_2",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
end
end
@@ -84,8 +70,8 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_3",
- "label" => false,
+ "key" => "age3",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -93,21 +79,7 @@ RSpec.describe Form::Lettings::Pages::PersonUnderRetirementValueCheck, type: :mo
end
it "has the correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_3",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_3",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
end
end
end
diff --git a/spec/models/form/question_spec.rb b/spec/models/form/question_spec.rb
index ede6636f7..751523d37 100644
--- a/spec/models/form/question_spec.rb
+++ b/spec/models/form/question_spec.rb
@@ -257,11 +257,6 @@ RSpec.describe Form::Question, type: :model do
expect(question.action_text(lettings_log)).to match(/Change/)
end
- it "has an update answer link href helper" do
- lettings_log.id = 1
- expect(question.action_href(lettings_log, page.id)).to eq("/lettings-logs/1/net-income?referrer=check_answers")
- end
-
context "when the question has an inferred answer" do
let(:section_id) { "tenancy_and_property" }
let(:subsection_id) { "property_information" }
diff --git a/spec/models/form/sales/pages/about_price_value_check_spec.rb b/spec/models/form/sales/pages/about_price_value_check_spec.rb
index 6c8d27463..881969703 100644
--- a/spec/models/form/sales/pages/about_price_value_check_spec.rb
+++ b/spec/models/form/sales/pages/about_price_value_check_spec.rb
@@ -45,10 +45,14 @@ RSpec.describe Form::Sales::Pages::AboutPriceValueCheck, type: :model do
"i18n_template" => "soft_min_or_soft_max",
},
{
- "key" => "purchase_price_min_or_max_text",
- "i18n_template" => "min_or_max",
+ "key" => "purchase_price_higher_or_lower_text",
+ "i18n_template" => "higher_or_lower",
},
],
})
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[value beds la])
+ end
end
diff --git a/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb b/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
index f770bd651..8aa6ef233 100644
--- a/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
+++ b/spec/models/form/sales/pages/buyer1_income_value_check_spec.rb
@@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::Buyer1IncomeValueCheck, type: :model do
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
+
+ it "is has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[ecstat1 income1])
+ end
end
diff --git a/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb b/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb
index 9d00b086b..22d1433bc 100644
--- a/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb
+++ b/spec/models/form/sales/pages/buyer_live_in_value_check_spec.rb
@@ -43,6 +43,10 @@ RSpec.describe Form::Sales::Pages::BuyerLiveInValueCheck, type: :model do
})
end
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[ownershipsch buy1livein])
+ end
+
context "with buyer 2" do
let(:person_index) { 2 }
@@ -60,5 +64,9 @@ RSpec.describe Form::Sales::Pages::BuyerLiveInValueCheck, type: :model do
"arguments" => [{ "key" => "ownership_scheme", "label" => false, "i18n_template" => "ownership_scheme" }],
})
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[ownershipsch buy2livein])
+ end
end
end
diff --git a/spec/models/form/sales/pages/deposit_value_check_spec.rb b/spec/models/form/sales/pages/deposit_value_check_spec.rb
index 91e1cccc5..9f8c99966 100644
--- a/spec/models/form/sales/pages/deposit_value_check_spec.rb
+++ b/spec/models/form/sales/pages/deposit_value_check_spec.rb
@@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::DepositValueCheck, type: :model do
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
+
+ it "is has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[savings deposit])
+ end
end
diff --git a/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb b/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb
index d086542dc..ce0d7be8e 100644
--- a/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb
+++ b/spec/models/form/sales/pages/discounted_sale_value_check_spec.rb
@@ -49,4 +49,8 @@ RSpec.describe Form::Sales::Pages::DiscountedSaleValueCheck, type: :model do
},
])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[value deposit ownershipsch mortgage mortgageused discount grant type])
+ end
end
diff --git a/spec/models/form/sales/pages/handover_date_check_spec.rb b/spec/models/form/sales/pages/handover_date_check_spec.rb
index 6bd3f3874..cd358a2b8 100644
--- a/spec/models/form/sales/pages/handover_date_check_spec.rb
+++ b/spec/models/form/sales/pages/handover_date_check_spec.rb
@@ -44,4 +44,8 @@ RSpec.describe Form::Sales::Pages::HandoverDateCheck, type: :model do
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
+
+ it "is has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[hodate saledate])
+ end
end
diff --git a/spec/models/form/sales/pages/household_wheelchair_check_spec.rb b/spec/models/form/sales/pages/household_wheelchair_check_spec.rb
index 4eb6182d9..20d0a325f 100644
--- a/spec/models/form/sales/pages/household_wheelchair_check_spec.rb
+++ b/spec/models/form/sales/pages/household_wheelchair_check_spec.rb
@@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::HouseholdWheelchairCheck, type: :model do
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
+
+ it "is has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[disabled wheel])
+ end
end
diff --git a/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb b/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb
index ac378dc82..5add5d8e7 100644
--- a/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb
+++ b/spec/models/form/sales/pages/monthly_charges_value_check_spec.rb
@@ -38,11 +38,15 @@ RSpec.describe Form::Sales::Pages::MonthlyChargesValueCheck, type: :model do
it "has correct title_text" do
expect(page.title_text).to eq({
"translation" => "soft_validations.monthly_charges_over_soft_max.title_text",
- "arguments" => [],
+ "arguments" => [{ "arguments_for_key" => "mscharge", "i18n_template" => "mscharge", "key" => "field_formatted_as_currency" }],
})
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({})
+ expect(page.informative_text).to eq({ "arguments" => [], "translation" => "soft_validations.monthly_charges_over_soft_max.hint_text" })
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[type mscharge proptype])
end
end
diff --git a/spec/models/form/sales/pages/mortgage_value_check_spec.rb b/spec/models/form/sales/pages/mortgage_value_check_spec.rb
index 8c7375f81..f33525652 100644
--- a/spec/models/form/sales/pages/mortgage_value_check_spec.rb
+++ b/spec/models/form/sales/pages/mortgage_value_check_spec.rb
@@ -36,6 +36,10 @@ RSpec.describe Form::Sales::Pages::MortgageValueCheck, type: :model do
])
end
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[mortgage inc1mort inc2mort jointpur income1 income2 inc1mort inc2mort])
+ end
+
context "when checking buyer 2" do
let(:index) { 2 }
diff --git a/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb b/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb
index 05b6f7734..d9c4beed9 100644
--- a/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb
+++ b/spec/models/form/sales/pages/old_persons_shared_ownership_value_check_spec.rb
@@ -33,12 +33,16 @@ RSpec.describe Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck, type: :m
it "has the correct title_text" do
expect(page.title_text).to eq({
- "translation" => "soft_validations.old_persons_shared_ownership",
+ "translation" => "soft_validations.old_persons_shared_ownership.title_text",
"arguments" => [],
})
end
it "has the correct informative_text" do
- expect(page.informative_text).to eq({})
+ expect(page.informative_text).to eq({ "arguments" => [], "translation" => "soft_validations.old_persons_shared_ownership.hint_text" })
+ end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[type jointpur age1 age2])
end
end
diff --git a/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb b/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb
index d933e3278..6b1fc36a4 100644
--- a/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb
+++ b/spec/models/form/sales/pages/percentage_discount_value_check_spec.rb
@@ -31,7 +31,10 @@ RSpec.describe Form::Sales::Pages::PercentageDiscountValueCheck, type: :model do
end
it "has the correct informative_text" do
- expect(page.informative_text).to eq({})
+ expect(page.informative_text).to eq({
+ "translation" => "soft_validations.percentage_discount_value.hint_text",
+ "arguments" => [],
+ })
end
it "is interruption screen page" do
@@ -45,4 +48,8 @@ RSpec.describe Form::Sales::Pages::PercentageDiscountValueCheck, type: :model do
},
])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[discount proptype])
+ end
end
diff --git a/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb b/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb
index 3499cda9f..c0d1b2734 100644
--- a/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb
+++ b/spec/models/form/sales/pages/person_student_not_child_value_check_spec.rb
@@ -39,6 +39,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model
expect(page.questions.map(&:id)).to eq(%w[student_not_child_value_check])
end
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[relat2 ecstat2 age2])
+ end
+
context "with person 2" do
let(:person_index) { 2 }
let(:page_id) { "person_2_student_not_child_value_check" }
@@ -50,6 +54,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_2_student_not_child?" => true }])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[relat2 ecstat2 age2])
+ end
end
context "with person 3" do
@@ -63,6 +71,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_3_student_not_child?" => true }])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[relat3 ecstat3 age3])
+ end
end
context "with person 4" do
@@ -76,6 +88,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_4_student_not_child?" => true }])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[relat4 ecstat4 age4])
+ end
end
context "with person 5" do
@@ -89,6 +105,10 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_5_student_not_child?" => true }])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[relat5 ecstat5 age5])
+ end
end
context "with person 6" do
@@ -102,5 +122,9 @@ RSpec.describe Form::Sales::Pages::PersonStudentNotChildValueCheck, type: :model
it "has correct depends_on" do
expect(page.depends_on).to eq([{ "person_6_student_not_child?" => true }])
end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[relat6 ecstat6 age6])
+ end
end
end
diff --git a/spec/models/form/sales/pages/retirement_value_check_spec.rb b/spec/models/form/sales/pages/retirement_value_check_spec.rb
index 76599d982..b09760ff5 100644
--- a/spec/models/form/sales/pages/retirement_value_check_spec.rb
+++ b/spec/models/form/sales/pages/retirement_value_check_spec.rb
@@ -46,8 +46,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_1",
- "label" => false,
+ "key" => "age1",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -55,21 +55,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_1",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_1",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[age1 ecstat1])
end
end
@@ -94,8 +84,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_2",
- "label" => false,
+ "key" => "age2",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -103,21 +93,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_2",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_2",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[age2 ecstat2])
end
end
@@ -142,8 +122,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_3",
- "label" => false,
+ "key" => "age3",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -151,21 +131,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_3",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_3",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[age3 ecstat3])
end
end
@@ -190,8 +160,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_4",
- "label" => false,
+ "key" => "age4",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -199,21 +169,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_4",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_4",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[age4 ecstat4])
end
end
@@ -238,8 +198,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_5",
- "label" => false,
+ "key" => "age5",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -247,21 +207,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_5",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_5",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[age5 ecstat5])
end
end
@@ -286,8 +236,8 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
"translation" => "soft_validations.retirement.min.title",
"arguments" => [
{
- "key" => "retirement_age_for_person_6",
- "label" => false,
+ "key" => "age6",
+ "label" => true,
"i18n_template" => "age",
},
],
@@ -295,21 +245,11 @@ RSpec.describe Form::Sales::Pages::RetirementValueCheck, type: :model do
end
it "has correct informative_text" do
- expect(page.informative_text).to eq({
- "translation" => "soft_validations.retirement.min.hint_text",
- "arguments" => [
- {
- "key" => "plural_gender_for_person_6",
- "label" => false,
- "i18n_template" => "gender",
- },
- {
- "key" => "retirement_age_for_person_6",
- "label" => false,
- "i18n_template" => "age",
- },
- ],
- })
+ expect(page.informative_text).to eq({})
+ end
+
+ it "has correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[age6 ecstat6])
end
end
end
diff --git a/spec/models/form/sales/pages/savings_value_check_spec.rb b/spec/models/form/sales/pages/savings_value_check_spec.rb
index 80606afb5..cfff9c74d 100644
--- a/spec/models/form/sales/pages/savings_value_check_spec.rb
+++ b/spec/models/form/sales/pages/savings_value_check_spec.rb
@@ -34,4 +34,8 @@ RSpec.describe Form::Sales::Pages::SavingsValueCheck, type: :model do
it "is interruption screen page" do
expect(page.interruption_screen?).to eq(true)
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[savings])
+ end
end
diff --git a/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb b/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb
index 45744cde4..9a184a6fc 100644
--- a/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb
+++ b/spec/models/form/sales/pages/shared_ownership_deposit_value_check_spec.rb
@@ -34,11 +34,15 @@ RSpec.describe Form::Sales::Pages::SharedOwnershipDepositValueCheck, type: :mode
it "has the correct title_text" do
expect(page.title_text).to eq({
"translation" => "soft_validations.shared_ownership_deposit.title_text",
- "arguments" => [{ "arguments_for_key" => "expected_shared_ownership_deposit_value", "i18n_template" => "expected_shared_ownership_deposit_value", "key" => "field_formatted_as_currency" }],
+ "arguments" => [{ "arguments_for_key" => "mortgage_deposit_and_discount_total", "i18n_template" => "mortgage_deposit_and_discount_total", "key" => "field_formatted_as_currency" }],
})
end
it "has the correct informative_text" do
expect(page.informative_text).to eq({})
end
+
+ it "has the correct interruption_screen_question_ids" do
+ expect(page.interruption_screen_question_ids).to eq(%w[mortgage mortgageused cashdis type deposit value equity])
+ end
end
diff --git a/spec/models/form/sales/questions/household_wheelchair_check_spec.rb b/spec/models/form/sales/questions/household_wheelchair_check_spec.rb
index 92f80fdb1..d203ee444 100644
--- a/spec/models/form/sales/questions/household_wheelchair_check_spec.rb
+++ b/spec/models/form/sales/questions/household_wheelchair_check_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Form::Sales::Questions::HouseholdWheelchairCheck, type: :model do
end
it "has the correct header" do
- expect(question.header).to eq("Are you sure? You said previously that somebody in household uses a wheelchair")
+ expect(question.header).to eq("You told us that someone in the household uses a wheelchair.")
end
it "has the correct check_answer_label" do
diff --git a/spec/models/validations/soft_validations_spec.rb b/spec/models/validations/soft_validations_spec.rb
index b51a084f5..acffc9cac 100644
--- a/spec/models/validations/soft_validations_spec.rb
+++ b/spec/models/validations/soft_validations_spec.rb
@@ -84,25 +84,9 @@ RSpec.describe Validations::SoftValidations do
describe "retirement soft validations" do
context "when the tenant is retired but under the expected retirement age" do
- context "when the tenant is female" do
- it "shows the interruption screen" do
- record.update!(age1: 43, sex1: "F", ecstat1: 5)
- expect(record.person_1_retired_under_soft_min_age?).to be true
- end
- end
-
- context "when the tenant is male" do
- it "shows the interruption screen" do
- record.update!(age1: 43, sex1: "M", ecstat1: 5)
- expect(record.person_1_retired_under_soft_min_age?).to be true
- end
- end
-
- context "when the tenant is non-binary" do
- it "shows the interruption screen" do
- record.update!(age1: 43, sex1: "X", ecstat1: 5)
- expect(record.person_1_retired_under_soft_min_age?).to be true
- end
+ it "shows the interruption screen" do
+ record.update!(age1: 43, ecstat1: 5)
+ expect(record.person_1_retired_under_soft_min_age?).to be true
end
end
@@ -130,25 +114,9 @@ RSpec.describe Validations::SoftValidations do
end
context "when the tenant prefers not to say what their economic status is but is under the expected retirement age" do
- context "when the tenant is female" do
- it "does not show the interruption screen" do
- record.update!(age1: 43, sex1: "F", ecstat1: 10)
- expect(record.person_1_retired_under_soft_min_age?).to be false
- end
- end
-
- context "when the tenant is male" do
- it "does not show the interruption screen" do
- record.update!(age1: 43, sex1: "M", ecstat1: 10)
- expect(record.person_1_retired_under_soft_min_age?).to be false
- end
- end
-
- context "when the tenant is non-binary" do
- it "does not show the interruption screen" do
- record.update!(age1: 43, sex1: "X", ecstat1: 10)
- expect(record.person_1_retired_under_soft_min_age?).to be false
- end
+ it "does not show the interruption screen" do
+ record.update!(age1: 43, ecstat1: 10)
+ expect(record.person_1_retired_under_soft_min_age?).to be false
end
end
diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb
index de2e0b571..5d9042c7c 100644
--- a/spec/requests/form_controller_spec.rb
+++ b/spec/requests/form_controller_spec.rb
@@ -532,6 +532,56 @@ RSpec.describe FormController, type: :request do
end
end
end
+
+ context "when the question was accessed from an interruption screen (soft validation)" do
+ let(:params) do
+ {
+ id: lettings_log.id,
+ lettings_log: {
+ page: page_id,
+ age1: 20,
+ interruption_page_id: "retirement_value_check",
+ },
+ }
+ end
+
+ before do
+ post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}?referrer=interruption_screen", params:
+ end
+
+ it "redirects back to the soft validation page" do
+ expect(response).to redirect_to("/lettings-logs/#{lettings_log.id}/retirement-value-check")
+ end
+
+ it "displays a success banner" do
+ follow_redirect!
+ follow_redirect!
+ expect(response.body).to include("You have successfully updated lead tenant’s age")
+ end
+ end
+
+ context "when requesting a soft validation page for validation that isn't triggering" do
+ before do
+ get "/lettings-logs/#{lettings_log.id}/retirement-value-check", headers: headers.merge({ "HTTP_REFERER" => referrer })
+ end
+
+ context "when the referrer header has interruption_screen" do
+ let(:referrer) { "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}?referrer=interruption_screen" }
+
+ it "routes to the soft validation page" do
+ expect(response.body).to include("Make sure these answers are correct:")
+ end
+ end
+
+ context "when the referrer header does not have interruption screen" do
+ let(:referrer) { "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}" }
+
+ it "skips the soft validation page" do
+ follow_redirect!
+ expect(response.body).not_to include("Make sure these answers are correct:")
+ end
+ end
+ end
end
context "with checkbox questions" do
From 4e489b53003ef7fef18c4c741ba38a43a0c2e987 Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Thu, 4 May 2023 17:26:42 +0100
Subject: [PATCH 04/14] CLDC-2288 ensure derived answers cleared when renewal
changed from yes to no (#1596)
* add a validation to prevent an inconsistent combination of values and tests for this validation
* show related method in diff
* remove comment
* extract reusable logic from SalesLogVariables to independent module
* update lettings log tests around derivations related to renewal
* refactor derivation logic to share functionality with sales logs where possible
* remove some tests which are now duplicated above, refactor tests using Jack's wonderful :change suggestion, add in tests about logic deriving vacdays that was not previously covered
* minor changes after tech review
---
.../lettings_log_variables.rb | 55 +-
.../derived_variables/sales_log_variables.rb | 30 +-
app/models/derived_variables/shared_logic.rb | 27 +
app/models/lettings_log.rb | 19 -
config/locales/en.yml | 6 +-
spec/models/lettings_log_spec.rb | 574 +++++++-----------
6 files changed, 318 insertions(+), 393 deletions(-)
create mode 100644 app/models/derived_variables/shared_logic.rb
diff --git a/app/models/derived_variables/lettings_log_variables.rb b/app/models/derived_variables/lettings_log_variables.rb
index aae49422d..937dd7c2e 100644
--- a/app/models/derived_variables/lettings_log_variables.rb
+++ b/app/models/derived_variables/lettings_log_variables.rb
@@ -1,4 +1,6 @@
module DerivedVariables::LettingsLogVariables
+ include DerivedVariables::SharedLogic
+
# renttype and unitletas values are different for intermediate rent (3 for renttype and 4 for unitletas)
RENT_TYPE_MAPPING = {
0 => 1, # "Social Rent" => "Social Rent"
@@ -35,10 +37,12 @@ module DerivedVariables::LettingsLogVariables
end
def set_derived_fields!
+ clear_inapplicable_derived_values!
+ set_encoded_derived_values!(DEPENDENCIES)
+
if rsnvac.present?
self.newprop = has_first_let_vacancy_reason? ? 1 : 2
end
- self.incref = 1 if net_income_refused?
self.renttype = RENT_TYPE_MAPPING[rent_type]
self.lettype = get_lettype
self.totchild = get_totchild
@@ -69,15 +73,9 @@ module DerivedVariables::LettingsLogVariables
self.nocharge = household_charge&.zero? ? 1 : 0
if is_renewal?
self.underoccupation_benefitcap = 2 if collection_start_year == 2021
- self.referral = 1
- self.waityear = 2
- self.offered = 0
self.voiddate = startdate
- self.first_time_property_let_as_social_housing = 0
- self.rsnvac = 14
self.unitletas = form.start_date.year >= 2023 ? UNITLETAS_MAPPING_23_24[rent_type] : UNITLETAS_MAPPING[rent_type]
if is_general_needs?
- # fixed term
self.prevten = 32 if managing_organisation&.provider_type == "PRP"
self.prevten = 30 if managing_organisation&.provider_type == "LA"
end
@@ -109,6 +107,49 @@ module DerivedVariables::LettingsLogVariables
private
+ DEPENDENCIES = [
+ {
+ conditions: {
+ renewal: 1,
+ },
+ derived_values: {
+ referral: 1,
+ waityear: 2,
+ offered: 0,
+ rsnvac: 14,
+ first_time_property_let_as_social_housing: 0,
+ },
+ },
+ {
+ conditions: {
+ net_income_known: 2,
+ },
+ derived_values: {
+ incref: 1,
+ },
+ },
+ ].freeze
+
+ def clear_inapplicable_derived_values!
+ reset_invalidated_derived_values!(DEPENDENCIES)
+ if (startdate_changed? || renewal_changed?) && (renewal_was == 1 && startdate_was&.between?(Time.zone.local(2021, 4, 1), Time.zone.local(2022, 3, 31)))
+ self.underoccupation_benefitcap = nil
+ end
+ if renewal_changed? && renewal_was == 1
+ self.voiddate = nil
+ self.unitletas = nil
+ end
+ if %w[PRP LA].include?(managing_organisation&.provider_type) &&
+ (needstype_changed? || renewal_changed?) &&
+ needstype_was == 1 && renewal_was == 1
+ self.prevten = nil
+ end
+ if needstype_changed? && needstype_was == 2
+ self.wchair = nil
+ self.location_id = nil
+ end
+ end
+
def get_totelder
ages = [age1, age2, age3, age4, age5, age6, age7, age8]
ages.count { |x| !x.nil? && x >= 60 }
diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb
index 9151a72f5..c30c62b9a 100644
--- a/app/models/derived_variables/sales_log_variables.rb
+++ b/app/models/derived_variables/sales_log_variables.rb
@@ -1,6 +1,8 @@
module DerivedVariables::SalesLogVariables
+ include DerivedVariables::SharedLogic
+
def set_derived_fields!
- reset_invalidated_derived_values!
+ reset_invalidated_derived_values!(DEPENDENCIES)
self.ethnic = 17 if ethnic_refused?
self.mscharge = nil if no_monthly_leasehold_charges?
@@ -30,7 +32,7 @@ module DerivedVariables::SalesLogVariables
self.uprn_known = 0
end
- set_encoded_derived_values!
+ set_encoded_derived_values!(DEPENDENCIES)
end
private
@@ -73,30 +75,6 @@ private
},
].freeze
- def reset_invalidated_derived_values!
- DEPENDENCIES.each do |dependency|
- any_conditions_changed = dependency[:conditions].any? { |attribute, _value| send("#{attribute}_changed?") }
- next unless any_conditions_changed
-
- previously_in_derived_state = dependency[:conditions].all? { |attribute, value| send("#{attribute}_was") == value }
- next unless previously_in_derived_state
-
- dependency[:derived_values].each do |derived_attribute, _derived_value|
- Rails.logger.debug("Cleared derived #{derived_attribute} value")
- send("#{derived_attribute}=", nil)
- end
- end
- end
-
- def set_encoded_derived_values!
- DEPENDENCIES.each do |dependency|
- derivation_applies = dependency[:conditions].all? { |attribute, value| send(attribute) == value }
- if derivation_applies
- dependency[:derived_values].each { |attribute, value| send("#{attribute}=", value) }
- end
- end
- end
-
def number_of_household_members
return unless hholdcount.present? && jointpur.present?
diff --git a/app/models/derived_variables/shared_logic.rb b/app/models/derived_variables/shared_logic.rb
new file mode 100644
index 000000000..c326cb8c9
--- /dev/null
+++ b/app/models/derived_variables/shared_logic.rb
@@ -0,0 +1,27 @@
+module DerivedVariables::SharedLogic
+private
+
+ def reset_invalidated_derived_values!(dependencies)
+ dependencies.each do |dependency|
+ any_conditions_changed = dependency[:conditions].any? { |attribute, _value| send("#{attribute}_changed?") }
+ next unless any_conditions_changed
+
+ previously_in_derived_state = dependency[:conditions].all? { |attribute, value| send("#{attribute}_was") == value }
+ next unless previously_in_derived_state
+
+ dependency[:derived_values].each do |derived_attribute, _derived_value|
+ Rails.logger.debug("Cleared derived #{derived_attribute} value")
+ send("#{derived_attribute}=", nil)
+ end
+ end
+ end
+
+ def set_encoded_derived_values!(dependencies)
+ dependencies.each do |dependency|
+ derivation_applies = dependency[:conditions].all? { |attribute, value| send(attribute) == value }
+ if derivation_applies
+ dependency[:derived_values].each { |attribute, value| send("#{attribute}=", value) }
+ end
+ end
+ end
+end
diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb
index 2d91e2ad9..6fce43f42 100644
--- a/app/models/lettings_log.rb
+++ b/app/models/lettings_log.rb
@@ -531,24 +531,6 @@ class LettingsLog < Log
private
- def reset_derived_questions
- dependent_questions = { waityear: [{ key: :renewal, value: 0 }],
- referral: [{ key: :renewal, value: 0 }],
- rsnvac: [{ key: :renewal, value: 0 }],
- underoccupation_benefitcap: [{ key: :renewal, value: 0 }],
- wchair: [{ key: :needstype, value: 1 }],
- location_id: [{ key: :needstype, value: 1 }] }
-
- dependent_questions.each do |dependent, conditions|
- condition_key = conditions.first[:key]
- condition_value = conditions.first[:value]
- if public_send("#{condition_key}_changed?") && condition_value == public_send(condition_key) && !public_send("#{dependent}_changed?")
- Rails.logger.debug("Cleared derived #{dependent} value")
- self[dependent] = nil
- end
- end
- end
-
def reset_invalid_unresolved_log_fields!
return unless unresolved?
@@ -581,7 +563,6 @@ private
reset_invalid_unresolved_log_fields!
reset_scheme
- reset_derived_questions
end
def dynamically_not_required
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 53565b753..0e2500dce 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -539,7 +539,7 @@ en:
in_soft_max_range:
message: "Net income is higher than expected based on the lead tenant’s working situation. Are you sure this is correct?"
income:
- under_soft_min_for_economic_status:
+ under_soft_min_for_economic_status:
title_text: "You told us income was %{income}."
hint_text: "This is less than we would expect for someone in this working situation."
rent:
@@ -571,10 +571,10 @@ en:
hint_text: "This is higher than we would expect."
shared_ownership_deposit:
title_text: "You told us that the mortgage, deposit and discount add up to %{mortgage_deposit_and_discount_total}"
- old_persons_shared_ownership:
+ old_persons_shared_ownership:
title_text: "You told us the buyer is using the Older Persons Shared Ownership scheme."
hint_text: "At least one buyer must be aged 65 years and over to use this scheme."
- staircase_bought_seems_high:
+ staircase_bought_seems_high:
title_text: "You told us that %{percentage}% was bought in this staircasing transaction."
hint_text: "Most staircasing transactions are less than 50%"
monthly_charges_over_soft_max:
diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb
index 05da0a5a8..5e661fe16 100644
--- a/spec/models/lettings_log_spec.rb
+++ b/spec/models/lettings_log_spec.rb
@@ -268,13 +268,6 @@ RSpec.describe LettingsLog do
expect(record_from_db["mrcdate"].year).to eq(2021)
end
- it "correctly derives and saves partial and full major property void date" do
- record_from_db = ActiveRecord::Base.connection.execute("select voiddate from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["voiddate"].day).to eq(3)
- expect(record_from_db["voiddate"].month).to eq(3)
- expect(record_from_db["voiddate"].year).to eq(2021)
- end
-
it "correctly derives and saves incref" do
record_from_db = ActiveRecord::Base.connection.execute("select incref from lettings_logs where id=#{lettings_log.id}").to_a[0]
expect(record_from_db["incref"]).to eq(1)
@@ -1504,334 +1497,329 @@ RSpec.describe LettingsLog do
expect(record_from_db["has_benefits"]).to eq(1)
end
- context "when it is a renewal" do
+ context "when updating values that derive vacdays" do
+ let(:lettings_log) { create(:lettings_log, startdate:) }
+
+ context "when start date is set" do
+ let(:startdate) { Time.zone.now }
+
+ it "correctly derives vacdays when voiddate is set" do
+ day_count = 3
+ expect { lettings_log.update!(voiddate: startdate - day_count.days) }.to change(lettings_log, :vacdays).to day_count
+ expect { lettings_log.update!(voiddate: nil) }.to change(lettings_log, :vacdays).from(day_count).to nil
+ end
+
+ it "correctly derives vacdays when mrcdate is set" do
+ day_count = 3
+ expect { lettings_log.update!(mrcdate: startdate - day_count.days) }.to change(lettings_log, :vacdays).to day_count
+ expect { lettings_log.update!(mrcdate: nil) }.to change(lettings_log, :vacdays).from(day_count).to nil
+ end
+ end
+
+ context "when start date is not set" do
+ let(:startdate) { nil }
+
+ it "correctly derives vacdays when voiddate is set" do
+ day_count = 3
+ lettings_log.update!(voiddate: Time.zone.now - day_count.days)
+ expect(lettings_log.vacdays).to be nil
+ end
+
+ it "correctly derives vacdays when mrcdate is set" do
+ day_count = 3
+ lettings_log.update!(mrcdate: Time.zone.now - day_count.days)
+ expect(lettings_log.vacdays).to be nil
+ end
+ end
+ end
+
+ context "when updating renewal" do
let!(:lettings_log) do
described_class.create({
managing_organisation: owning_organisation,
owning_organisation:,
created_by: created_by_user,
- renewal: 1,
startdate: Time.zone.local(2021, 4, 10),
created_at: Time.utc(2022, 2, 8, 16, 52, 15),
})
end
- it "correctly derives and saves waityear" do
- record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["waityear"]).to eq(2)
- expect(lettings_log["waityear"]).to eq(2)
+ it "correctly derives the length of time on local authority waiting list" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :waityear).to 2
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :waityear).from(2).to nil
end
- it "correctly derives and saves underoccupation_benefitcap if year is 2021" do
- record_from_db = ActiveRecord::Base.connection.execute("select underoccupation_benefitcap from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["underoccupation_benefitcap"]).to eq(2)
- expect(lettings_log["underoccupation_benefitcap"]).to eq(2)
+ it "correctly derives the number of times previously offered since becoming available" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :offered).to 0
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :offered).from(0).to nil
end
- it "correctly derives and saves prevten" do
- lettings_log.managing_organisation.update!({ provider_type: "PRP" })
- lettings_log.update!({ needstype: 1 })
-
- record_from_db = ActiveRecord::Base.connection.execute("select prevten from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["prevten"]).to eq(32)
- expect(lettings_log["prevten"]).to eq(32)
+ it "correctly derives referral if the letting is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :referral).to 1
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :referral).from(1).to nil
+ end
- lettings_log.managing_organisation.update!({ provider_type: "LA" })
- lettings_log.update!({ needstype: 1 })
+ it "correctly derives voiddate if the letting is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :voiddate).to lettings_log.startdate
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :voiddate).from(lettings_log.startdate).to nil
+ end
- record_from_db = ActiveRecord::Base.connection.execute("select prevten from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["prevten"]).to eq(30)
- expect(lettings_log["prevten"]).to eq(30)
+ it "correctly derives first_time_property_let_as_social_housing and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :first_time_property_let_as_social_housing).to 0
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :first_time_property_let_as_social_housing).from(0).to nil
end
- it "correctly derives and saves referral" do
- record_from_db = ActiveRecord::Base.connection.execute("select referral from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["referral"]).to eq(1)
- expect(lettings_log["referral"]).to eq(1)
+ it "correctly derives vacancy reason and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :rsnvac).to 14
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :rsnvac).from(14).to nil
end
- it "correctly derives and saves vacdays" do
- record_from_db = ActiveRecord::Base.connection.execute("select vacdays from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["vacdays"]).to eq(0)
- expect(lettings_log["vacdays"]).to eq(0)
+ it "derives vacdays as 0 if log is renewal" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :vacdays).to 0
end
- it "correctly derives and saves first_time_property_let_as_social_housing" do
- record_from_db = ActiveRecord::Base.connection.execute(
- "select first_time_property_let_as_social_housing" \
- " from lettings_logs where id=#{lettings_log.id}",
- ).to_a[0]
- expect(record_from_db["first_time_property_let_as_social_housing"]).to eq(0)
- expect(lettings_log["first_time_property_let_as_social_housing"]).to eq(0)
+ it "correctly derives underoccupation_benefitcap if log is a renewal from 2021/22" do
+ lettings_log.update!(renewal: 1)
+ expect(lettings_log.underoccupation_benefitcap).to be 2
end
- it "derives vacancy reason as relet" do
- record_from_db = ActiveRecord::Base.connection.execute("select rsnvac from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["rsnvac"]).to eq(14)
- expect(lettings_log["rsnvac"]).to eq(14)
+ it "clears underoccupation_benefitcap if log is no longer a renewal" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :underoccupation_benefitcap).to 2
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :underoccupation_benefitcap).from(2).to nil
end
- context "when deriving renttype and unitletas" do
- before do
- Timecop.freeze(Time.zone.local(2022, 1, 1))
- allow(FeatureToggle).to receive(:startdate_two_week_validation_enabled?).and_return(false)
- lettings_log.update!(rent_type:, irproduct_other: "other")
- end
+ it "clears underoccupation_benefitcap if log is no longer in 2021/22" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :underoccupation_benefitcap).to 2
+ Timecop.return
+ expect { lettings_log.update!(startdate: Time.zone.local(2023, 1, 1)) }.to change(lettings_log, :underoccupation_benefitcap).from(2).to nil
+ end
- after do
- Timecop.unfreeze
- end
+ context "when the log is general needs" do
+ context "and the managing organisation is a private registered provider" do
+ before do
+ lettings_log.managing_organisation.update!(provider_type: "PRP")
+ lettings_log.update!(needstype: 1, renewal: 1)
+ end
- context "when the rent_type is Social Rent (0)" do
- let(:rent_type) { 0 }
+ it "correctly derives prevten" do
+ expect(lettings_log.prevten).to be 32
+ end
- it "derives and saves renttype and unitletas as Social rent(1)" do
- record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.renttype).to eq(1)
- expect(record_from_db["renttype"]).to eq(1)
+ it "clears prevten if the log is marked as supported housing" do
+ lettings_log.update!(needstype: 2)
+ expect(lettings_log.prevten).to be nil
end
- it "derives and saves unitletas as Social rent(1)" do
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(1)
- expect(record_from_db["unitletas"]).to eq(1)
+ it "clears prevten if renewal is update to no" do
+ lettings_log.update!(renewal: 0)
+ expect(lettings_log.prevten).to be nil
end
+ end
- context "and it is a 23/24 form" do
- before do
- Timecop.freeze(Time.zone.local(2023, 5, 1))
- end
+ context "and the managing organisation is a local authority" do
+ before do
+ lettings_log.managing_organisation.update!(provider_type: "LA")
+ lettings_log.update!(needstype: 1, renewal: 1)
+ end
- after do
- Timecop.unfreeze
- end
+ it "correctly derives prevten" do
+ expect(lettings_log.prevten).to be 30
+ end
- around do |example|
- Timecop.freeze(Time.zone.local(2023, 5, 1)) do
- Singleton.__init__(FormHandler)
- example.run
- end
- Timecop.return
- Singleton.__init__(FormHandler)
- end
+ it "clears prevten if the log is marked as supported housing" do
+ expect { lettings_log.update!(needstype: 2) }.to change(lettings_log, :prevten).to nil
+ end
- it "derives and saves unitletas as Social rent(1)" do
- lettings_log.update!(startdate: Time.zone.local(2023, 5, 1))
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(1)
- expect(record_from_db["unitletas"]).to eq(1)
- end
+ it "clears prevten if renewal is update to no" do
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :prevten).to nil
end
end
+ end
- context "when the rent_type is Affordable Rent(1)" do
- let(:rent_type) { 1 }
+ context "and updating rent_type" do
+ let(:irproduct_other) { nil }
- it "derives and saves renttype as Affordable Rent(2)" do
- record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.renttype).to eq(2)
- expect(record_from_db["renttype"]).to eq(2)
+ around do |example|
+ Timecop.freeze(now) do
+ Singleton.__init__(FormHandler)
+ lettings_log.update!(rent_type:, irproduct_other:, startdate: now)
+ example.run
end
+ end
- it "derives and saves unitletas as Affordable Rent(2)" do
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(2)
- expect(record_from_db["unitletas"]).to eq(2)
- end
+ context "when collection year is 2022/23 or earlier" do
+ let(:now) { Time.zone.local(2023, 1, 1) }
- context "and it is a 23/24 form" do
- before do
- Timecop.freeze(Time.zone.local(2023, 5, 1))
- end
+ context "when rent_type is Social Rent" do
+ let(:rent_type) { 0 }
- after do
- Timecop.unfreeze
+ it "derives the most recent let type as Social Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 1
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(1).to nil
end
+ end
- around do |example|
- Timecop.freeze(Time.zone.local(2023, 5, 1)) do
- Singleton.__init__(FormHandler)
- example.run
- end
- Timecop.return
- Singleton.__init__(FormHandler)
- end
+ context "when rent_type is Affordable Rent" do
+ let(:rent_type) { 1 }
- it "derives and saves unitletas as Affordable Rent basis(2)" do
- lettings_log.update!(startdate: Time.zone.local(2023, 5, 1))
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(2)
- expect(record_from_db["unitletas"]).to eq(2)
+ it "derives the most recent let type as Affordable Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 2
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(2).to nil
end
end
- end
- context "when the rent_type is London Affordable Rent(2)" do
- let(:rent_type) { 2 }
+ context "when rent_type is London Affordable Rent" do
+ let(:rent_type) { 2 }
- it "derives and saves renttype as London Affordable Rent(2)" do
- record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.renttype).to eq(2)
- expect(record_from_db["renttype"]).to eq(2)
+ it "derives the most recent let type as Affordable Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 2
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(2).to nil
+ end
end
- it "derives and saves unitletas as London Affordable Rent(2)" do
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(2)
- expect(record_from_db["unitletas"]).to eq(2)
- end
+ context "when rent_type is Rent to Buy" do
+ let(:rent_type) { 3 }
- context "and it is a 23/24 form" do
- before do
- Timecop.freeze(Time.zone.local(2023, 5, 1))
+ it "derives the most recent let type as Intermediate Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 4
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(4).to nil
end
+ end
- after do
- Timecop.unfreeze
- end
+ context "when rent_type is London Living Rent" do
+ let(:rent_type) { 4 }
- around do |example|
- Timecop.freeze(Time.zone.local(2023, 5, 1)) do
- Singleton.__init__(FormHandler)
- example.run
- end
- Timecop.return
- Singleton.__init__(FormHandler)
+ it "derives the most recent let type as Intermediate Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 4
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(4).to nil
end
+ end
+
+ context "when rent_type is Other intermediate rent product" do
+ let(:rent_type) { 5 }
+ let(:irproduct_other) { "Rent first" }
- it "derives and saves unitletas as London Affordable Rent basis(5)" do
- lettings_log.update!(startdate: Time.zone.local(2023, 5, 1))
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(5)
- expect(record_from_db["unitletas"]).to eq(5)
+ it "derives the most recent let type as Intermediate Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 4
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(4).to nil
end
end
end
- context "when the rent_type is Rent to Buy(3)" do
- let(:rent_type) { 3 }
+ context "when collection year is 2023/24 or later" do
+ let(:now) { Time.zone.local(2024, 1, 1) }
- it "derives and saves renttype as Intermediate Rent(3)" do
- record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.renttype).to eq(3)
- expect(record_from_db["renttype"]).to eq(3)
- end
+ context "when rent_type is Social Rent" do
+ let(:rent_type) { 0 }
- it "derives and saves unitletas as Intermediate Rent(4)" do
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(4)
- expect(record_from_db["unitletas"]).to eq(4)
+ it "derives the most recent let type as Social Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 1
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(1).to nil
+ end
end
- context "and it is a 23/24 form" do
- before do
- Timecop.freeze(Time.zone.local(2023, 5, 1))
- end
+ context "when rent_type is Affordable Rent" do
+ let(:rent_type) { 1 }
- after do
- Timecop.unfreeze
+ it "derives the most recent let type as Affordable Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 2
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(2).to nil
end
+ end
- around do |example|
- Timecop.freeze(Time.zone.local(2023, 5, 1)) do
- Singleton.__init__(FormHandler)
- example.run
- end
- Timecop.return
- Singleton.__init__(FormHandler)
- end
+ context "when rent_type is London Affordable Rent" do
+ let(:rent_type) { 2 }
- it "derives and saves unitletas as Rent to Buy basis(6)" do
- lettings_log.update!(startdate: Time.zone.local(2023, 5, 1))
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(6)
- expect(record_from_db["unitletas"]).to eq(6)
+ it "derives the most recent let type as London Affordable Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 5
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(5).to nil
end
end
- end
- context "when the rent_type is London Living Rent(4)" do
- let(:rent_type) { 4 }
+ context "when rent_type is Rent to Buy" do
+ let(:rent_type) { 3 }
- it "derives and saves renttype as Intermediate Rent(3)" do
- record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.renttype).to eq(3)
- expect(record_from_db["renttype"]).to eq(3)
- end
-
- it "derives and saves unitletas as Intermediate Rent(4)" do
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(4)
- expect(record_from_db["unitletas"]).to eq(4)
+ it "derives the most recent let type as Rent to Buy basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 6
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(6).to nil
+ end
end
- context "and it is a 23/24 form" do
- before do
- Timecop.freeze(Time.zone.local(2023, 5, 1))
- end
+ context "when rent_type is London Living Rent" do
+ let(:rent_type) { 4 }
- after do
- Timecop.unfreeze
+ it "derives the most recent let type as London Living Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 7
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(7).to nil
end
+ end
- around do |example|
- Timecop.freeze(Time.zone.local(2023, 5, 1)) do
- Singleton.__init__(FormHandler)
- example.run
- end
- Timecop.return
- Singleton.__init__(FormHandler)
- end
+ context "when rent_type is Other intermediate rent product" do
+ let(:rent_type) { 5 }
+ let(:irproduct_other) { "Rent first" }
- it "derives and saves unitletas as London Living Rent basis(7)" do
- lettings_log.update!(startdate: Time.zone.local(2023, 5, 1))
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(7)
- expect(record_from_db["unitletas"]).to eq(7)
+ it "derives the most recent let type as Another Intermediate Rent basis if it is a renewal and clears it if it is not" do
+ expect { lettings_log.update!(renewal: 1) }.to change(lettings_log, :unitletas).to 8
+ expect { lettings_log.update!(renewal: 0) }.to change(lettings_log, :unitletas).from(8).to nil
end
end
end
+ end
+ end
+
+ context "when updating rent type" do
+ let(:irproduct_other) { nil }
+
+ before do
+ lettings_log.update!(rent_type:, irproduct_other:)
+ end
+
+ context "when rent_type is Social Rent" do
+ let(:rent_type) { 0 }
+
+ it "derives renttype as Social Rent" do
+ expect(lettings_log.renttype).to be 1
+ end
+ end
- context "when the rent_type is Other intermediate rent product(5)" do
- let(:rent_type) { 5 }
+ context "when rent_type is Affordable Rent" do
+ let(:rent_type) { 1 }
- it "derives and saves renttype as Intermediate Rent(3)" do
- record_from_db = ActiveRecord::Base.connection.execute("select renttype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.renttype).to eq(3)
- expect(record_from_db["renttype"]).to eq(3)
- end
+ it "derives renttype as Affordable Rent" do
+ expect(lettings_log.renttype).to be 2
+ end
+ end
- it "derives and saves unitletas as Intermediate Rent(4)" do
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(4)
- expect(record_from_db["unitletas"]).to eq(4)
- end
+ context "when rent_type is London Affordable Rent" do
+ let(:rent_type) { 2 }
- context "and it is a 23/24 form" do
- before do
- Timecop.freeze(Time.zone.local(2023, 5, 1))
- end
+ it "derives renttype as Affordable Rent" do
+ expect(lettings_log.renttype).to be 2
+ end
+ end
- after do
- Timecop.unfreeze
- end
+ context "when rent_type is Rent to Buy" do
+ let(:rent_type) { 3 }
- around do |example|
- Timecop.freeze(Time.zone.local(2023, 5, 1)) do
- Singleton.__init__(FormHandler)
- example.run
- end
- Timecop.return
- Singleton.__init__(FormHandler)
- end
+ it "derives renttype as Intermediate Rent" do
+ expect(lettings_log.renttype).to be 3
+ end
+ end
- it "derives and saves unitletas as Other intermediate rent basis(8)" do
- lettings_log.update!(startdate: Time.zone.local(2023, 5, 1))
- record_from_db = ActiveRecord::Base.connection.execute("select unitletas from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(lettings_log.unitletas).to eq(8)
- expect(record_from_db["unitletas"]).to eq(8)
- end
- end
+ context "when rent_type is London Living Rent" do
+ let(:rent_type) { 4 }
+
+ it "derives renttype as Intermediate Rent" do
+ expect(lettings_log.renttype).to be 3
+ end
+ end
+
+ context "when rent_type is Other intermediate rent product" do
+ let(:rent_type) { 5 }
+ let(:irproduct_other) { "Rent first" }
+
+ it "derives renttype as Intermediate Rent" do
+ expect(lettings_log.renttype).to be 3
end
end
end
@@ -2168,37 +2156,6 @@ RSpec.describe LettingsLog do
expect(record_from_db["wchair"]).to eq(1)
end
end
-
- context "and renewal" do
- before do
- Timecop.freeze(Time.zone.local(2022, 4, 2))
- end
-
- after do
- Timecop.unfreeze
- end
-
- let(:scheme) { create(:scheme) }
- let(:location) { create(:location, scheme:) }
- let!(:supported_housing_lettings_log) do
- described_class.create!({
- managing_organisation: owning_organisation,
- owning_organisation:,
- created_by: created_by_user,
- needstype: 2,
- scheme_id: scheme.id,
- location_id: location.id,
- renewal: 1,
- startdate: Time.zone.local(2022, 4, 2),
- created_at: Time.utc(2022, 2, 8, 16, 52, 15),
- })
- end
-
- it "correctly infers and saves the renewal date" do
- record_from_db = ActiveRecord::Base.connection.execute("SELECT voiddate from lettings_logs where id=#{supported_housing_lettings_log.id}").to_a[0]
- expect(record_from_db["voiddate"].to_i).to eq(supported_housing_lettings_log.startdate.to_i)
- end
- end
end
context "when saving accessibility needs" do
@@ -2434,77 +2391,18 @@ RSpec.describe LettingsLog do
end
end
- context "when it changes from a renewal to not a renewal" do
- let(:lettings_log) { create(:lettings_log) }
-
- it "resets inferred waityear value" do
- lettings_log.update!({ renewal: 1 })
-
- record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["waityear"]).to eq(2)
- expect(lettings_log["waityear"]).to eq(2)
-
- lettings_log.update!({ renewal: 0 })
- record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["waityear"]).to eq(nil)
- expect(lettings_log["waityear"]).to eq(nil)
- end
-
- it "resets inferred vacancy reason value" do
- vacancy_reason = "rsnvac"
-
- lettings_log.update!({ renewal: 1 })
-
- record_from_db = ActiveRecord::Base.connection.execute("select #{vacancy_reason} from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db[vacancy_reason]).to eq(14)
- expect(lettings_log[vacancy_reason]).to eq(14)
-
- lettings_log.update!({ renewal: 0 })
- record_from_db = ActiveRecord::Base.connection.execute("select #{vacancy_reason} from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db[vacancy_reason]).to eq(nil)
- expect(lettings_log[vacancy_reason]).to eq(nil)
- end
- end
-
context "when it changes from a supported housing to not a supported housing" do
let(:location) { create(:location, mobility_type: "A", postcode: "SW1P 4DG") }
let(:lettings_log) { create(:lettings_log, location:) }
it "resets inferred wchair value" do
- lettings_log.update!({ needstype: 2 })
-
- record_from_db = ActiveRecord::Base.connection.execute("select wchair from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["wchair"]).to eq(2)
- expect(lettings_log["wchair"]).to eq(2)
-
- lettings_log.update!({ needstype: 1 })
- record_from_db = ActiveRecord::Base.connection.execute("select needstype from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["wchair"]).to eq(nil)
- expect(lettings_log["wchair"]).to eq(nil)
+ expect { lettings_log.update!(needstype: 2) }.to change(lettings_log, :wchair).to(2)
+ expect { lettings_log.update!(needstype: 1) }.to change(lettings_log, :wchair).from(2).to(nil)
end
it "resets location" do
- lettings_log.update!({ needstype: 2 })
-
- record_from_db = ActiveRecord::Base.connection.execute("select location_id from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["location_id"]).to eq(location.id)
- expect(lettings_log["location_id"]).to eq(location.id)
- lettings_log.update!({ needstype: 1 })
- record_from_db = ActiveRecord::Base.connection.execute("select location_id from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["location_id"]).to eq(nil)
- expect(lettings_log["location_id"]).to eq(nil)
- end
- end
-
- context "when it is not a renewal" do
- let(:lettings_log) { create(:lettings_log) }
-
- it "saves waityear value" do
- lettings_log.update!({ renewal: 0, waityear: 2 })
-
- record_from_db = ActiveRecord::Base.connection.execute("select waityear from lettings_logs where id=#{lettings_log.id}").to_a[0]
- expect(record_from_db["waityear"]).to eq(2)
- expect(lettings_log["waityear"]).to eq(2)
+ lettings_log.update!(needstype: 2)
+ expect { lettings_log.update!(needstype: 1) }.to change(lettings_log, :location_id).from(location.id).to(nil)
end
end
From 99cb26b93471d5de2f8f0cb37e15509a522ee76a Mon Sep 17 00:00:00 2001
From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com>
Date: Thu, 4 May 2023 17:57:41 +0100
Subject: [PATCH 05/14] CLDC-2296 Display soft validation errors on bulk upload
(#1594)
* Add affected_question_ids to pregnancy check
* Update is_referrer_interruption_screen? check and naming
* Use interruption_screen_question_ids to set soft validation errors on relevant fields
* Add soft validations to sales bulk upload
* Add soft validations to lettings logs 23/24 bulk upload
* Add errors for optional soft validations
* Only add soft validations once
* Import helper methods
* Update test based on new validation messages
* Rebase fix
---
.../lettings/year2022/row_parser.rb | 19 +++++++
.../lettings/year2023/row_parser.rb | 20 ++++++++
.../bulk_upload/sales/year2022/row_parser.rb | 19 +++++++
.../lettings/year2022/row_parser_spec.rb | 20 ++++++++
.../lettings/year2023/row_parser_spec.rb | 49 +++++++++++++++++++
.../sales/year2022/row_parser_spec.rb | 20 ++++++++
6 files changed, 147 insertions(+)
diff --git a/app/services/bulk_upload/lettings/year2022/row_parser.rb b/app/services/bulk_upload/lettings/year2022/row_parser.rb
index 9d273dab5..80b65c133 100644
--- a/app/services/bulk_upload/lettings/year2022/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2022/row_parser.rb
@@ -1,6 +1,7 @@
class BulkUpload::Lettings::Year2022::RowParser
include ActiveModel::Model
include ActiveModel::Attributes
+ include InterruptionScreenHelper
QUESTIONS = {
field_1: "What is the letting type?",
@@ -335,6 +336,7 @@ class BulkUpload::Lettings::Year2022::RowParser
validate :validate_declaration_acceptance, on: :after_log
validate :validate_valid_radio_option, on: :before_log
+ validate :validate_incomplete_soft_validations, on: :after_log
def self.question_for_field(field)
QUESTIONS[field]
@@ -716,6 +718,23 @@ private
end
end
+ def validate_incomplete_soft_validations
+ routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) }
+ routed_to_soft_validation_questions.each do |question|
+ next unless question
+ next if question.completed?(log)
+
+ question.page.interruption_screen_question_ids.each do |interruption_screen_question_id|
+ field_mapping_for_errors[interruption_screen_question_id.to_sym].each do |field|
+ unless errors.any? { |e| e.options[:category] == :soft_validation && field_mapping_for_errors[interruption_screen_question_id.to_sym].include?(e.attribute) }
+ error_message = [display_title_text(question.page.title_text, log), display_informative_text(question.page.informative_text, log)].reject(&:empty?).join(". ")
+ errors.add(field, message: error_message, category: :soft_validation)
+ end
+ end
+ end
+ end
+ end
+
def setup_question?(question)
log.form.setup_sections[0].subsections[0].questions.include?(question)
end
diff --git a/app/services/bulk_upload/lettings/year2023/row_parser.rb b/app/services/bulk_upload/lettings/year2023/row_parser.rb
index 4393d1af2..46ff0ff12 100644
--- a/app/services/bulk_upload/lettings/year2023/row_parser.rb
+++ b/app/services/bulk_upload/lettings/year2023/row_parser.rb
@@ -1,6 +1,7 @@
class BulkUpload::Lettings::Year2023::RowParser
include ActiveModel::Model
include ActiveModel::Attributes
+ include InterruptionScreenHelper
QUESTIONS = {
field_1: "Which organisation owns this property?",
@@ -339,6 +340,8 @@ class BulkUpload::Lettings::Year2023::RowParser
validate :validate_uprn_exists_if_any_key_adddress_fields_are_blank, on: :after_log
+ validate :validate_incomplete_soft_validations, on: :after_log
+
def self.question_for_field(field)
QUESTIONS[field]
end
@@ -457,6 +460,23 @@ private
end
end
+ def validate_incomplete_soft_validations
+ routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) }
+ routed_to_soft_validation_questions.each do |question|
+ next unless question
+ next if question.completed?(log)
+
+ question.page.interruption_screen_question_ids.each do |interruption_screen_question_id|
+ field_mapping_for_errors[interruption_screen_question_id.to_sym].each do |field|
+ unless errors.any? { |e| field_mapping_for_errors[interruption_screen_question_id.to_sym].include?(e.attribute) }
+ error_message = [display_title_text(question.page.title_text, log), display_informative_text(question.page.informative_text, log)].reject(&:empty?).join(". ")
+ errors.add(field, message: error_message, category: :soft_validation)
+ end
+ end
+ end
+ end
+ end
+
def duplicate_check_fields
%w[
startdate
diff --git a/app/services/bulk_upload/sales/year2022/row_parser.rb b/app/services/bulk_upload/sales/year2022/row_parser.rb
index b08db8585..e53b42ecb 100644
--- a/app/services/bulk_upload/sales/year2022/row_parser.rb
+++ b/app/services/bulk_upload/sales/year2022/row_parser.rb
@@ -1,6 +1,7 @@
class BulkUpload::Sales::Year2022::RowParser
include ActiveModel::Model
include ActiveModel::Attributes
+ include InterruptionScreenHelper
QUESTIONS = {
field_1: "What is the purchaser code?",
@@ -283,6 +284,7 @@ class BulkUpload::Sales::Year2022::RowParser
validate :validate_created_by_exists, on: :after_log
validate :validate_created_by_related, on: :after_log
validate :validate_relevant_collection_window, on: :after_log
+ validate :validate_incomplete_soft_validations, on: :after_log
def self.question_for_field(field)
QUESTIONS[field]
@@ -944,4 +946,21 @@ private
errors.add(:field_4, I18n.t("validations.date.outside_collection_window"))
end
end
+
+ def validate_incomplete_soft_validations
+ routed_to_soft_validation_questions = log.form.questions.filter { |q| q.type == "interruption_screen" && q.page.routed_to?(log, nil) }
+ routed_to_soft_validation_questions.each do |question|
+ next unless question
+ next if question.completed?(log)
+
+ question.page.interruption_screen_question_ids.each do |interruption_screen_question_id|
+ field_mapping_for_errors[interruption_screen_question_id.to_sym].each do |field|
+ unless errors.any? { |e| e.options[:category] == :soft_validation && field_mapping_for_errors[interruption_screen_question_id.to_sym].include?(e.attribute) }
+ error_message = [display_title_text(question.page.title_text, log), display_informative_text(question.page.informative_text, log)].reject(&:empty?).join(". ")
+ errors.add(field, message: error_message, category: :soft_validation)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
index dd9bf4a5b..25fc64cf9 100644
--- a/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2022/row_parser_spec.rb
@@ -954,6 +954,26 @@ RSpec.describe BulkUpload::Lettings::Year2022::RowParser do
end
end
end
+
+ describe "soft validations" do
+ context "when soft validation is triggered" do
+ let(:attributes) { setup_section_params.merge({ field_12: 22, field_35: 5 }) }
+
+ it "adds an error to the relevant fields" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_12 }).to be_present
+ expect(soft_validation_errors.find { |e| e.attribute == :field_35 }).to be_present
+ end
+
+ it "populates with correct error message" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_12 }.message).to eql("You told us this person is aged 22 years and retired.")
+ expect(soft_validation_errors.find { |e| e.attribute == :field_35 }.message).to eql("You told us this person is aged 22 years and retired.")
+ end
+ end
+ end
end
describe "#log" do
diff --git a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
index 58b216e43..f98eed6d0 100644
--- a/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
+++ b/spec/services/bulk_upload/lettings/year2023/row_parser_spec.rb
@@ -31,6 +31,18 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
before do
create(:organisation_relationship, parent_organisation: owning_org, child_organisation: managing_org)
+
+ LaRentRange.create!(
+ ranges_rent_id: "1",
+ la: "E09000008",
+ beds: 1,
+ lettype: 3,
+ soft_min: 12.41,
+ soft_max: 118.85,
+ hard_min: 9.87,
+ hard_max: 200.99,
+ start_year: 2023,
+ )
end
around do |example|
@@ -984,6 +996,43 @@ RSpec.describe BulkUpload::Lettings::Year2023::RowParser do
end
end
end
+
+ describe "soft validations" do
+ context "when soft validation is triggered" do
+ let(:attributes) { setup_section_params.merge({ field_46: 22, field_50: 5 }) }
+
+ it "adds an error to the relevant fields" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_46 }).to be_present
+ expect(soft_validation_errors.find { |e| e.attribute == :field_50 }).to be_present
+ end
+
+ it "populates with correct error message" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_46 }.message).to eql("You told us this person is aged 22 years and retired.")
+ expect(soft_validation_errors.find { |e| e.attribute == :field_50 }.message).to eql("You told us this person is aged 22 years and retired.")
+ end
+ end
+
+ context "when soft validation is triggered and not required" do
+ let(:attributes) { setup_section_params.merge({ field_128: 120, field_126: 1, field_32: 1, field_4: 1, field_5: "3", field_25: "E09000008" }) }
+
+ it "adds an error to the relevant fields" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_128 }).to be_present
+ end
+
+ it "populates with correct error message" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.count { |e| e.attribute == :field_128 }).to be(1)
+ expect(soft_validation_errors.find { |e| e.attribute == :field_128 }.message).to eql("You told us the rent is £120.00 every week. The maximum rent expected for this type of property in this local authority is ££118.85 every week.")
+ end
+ end
+ end
end
describe "#log" do
diff --git a/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb
index bd5520ea7..8c0a4cd45 100644
--- a/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb
+++ b/spec/services/bulk_upload/sales/year2022/row_parser_spec.rb
@@ -520,5 +520,25 @@ RSpec.describe BulkUpload::Sales::Year2022::RowParser do
end
end
end
+
+ describe "soft validations" do
+ context "when soft validation is triggered" do
+ let(:attributes) { valid_attributes.merge({ field_7: 22, field_24: 5 }) }
+
+ it "adds an error to the relevant fields" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_7 }).to be_present
+ expect(soft_validation_errors.find { |e| e.attribute == :field_24 }).to be_present
+ end
+
+ it "populates with correct error message" do
+ soft_validation_errors = parser.errors.select { |e| e.options[:category] == :soft_validation }
+
+ expect(soft_validation_errors.find { |e| e.attribute == :field_7 }.message).to eql("You told us this person is aged 22 years and retired.")
+ expect(soft_validation_errors.find { |e| e.attribute == :field_24 }.message).to eql("You told us this person is aged 22 years and retired.")
+ end
+ end
+ end
end
end
From 6bf582a9a69f9a62f349f85b22e410192501481d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 9 May 2023 09:43:10 +0100
Subject: [PATCH 06/14] Bump nunjucks from 3.2.3 to 3.2.4 (#1577)
Bumps [nunjucks](https://github.com/mozilla/nunjucks) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/mozilla/nunjucks/releases)
- [Changelog](https://github.com/mozilla/nunjucks/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mozilla/nunjucks/compare/v3.2.3...v3.2.4)
---
updated-dependencies:
- dependency-name: nunjucks
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 1fcfee856..9b7da0c6b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4272,9 +4272,9 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
nunjucks@^3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.3.tgz#1b33615247290e94e28263b5d855ece765648a31"
- integrity sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.4.tgz#f0878eef528ce7b0aa35d67cc6898635fd74649e"
+ integrity sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==
dependencies:
a-sync-waterfall "^1.0.0"
asap "^2.0.3"
From 42aa07cc39f27ba507f6e000db168cc2475bfda7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 9 May 2023 09:44:10 +0100
Subject: [PATCH 07/14] Bump webpack from 5.74.0 to 5.76.0 (#1414)
Bumps [webpack](https://github.com/webpack/webpack) from 5.74.0 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.74.0...v5.76.0)
---
updated-dependencies:
- dependency-name: webpack
dependency-type: direct:production
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
package.json | 2 +-
yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 8b6a0dbe1..707c3c3fe 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
"sass": "^1.49.9",
"sass-loader": "^12.6.0",
"unfetch": "^4.2.0",
- "webpack": "^5.70.0",
+ "webpack": "^5.76.0",
"webpack-cli": "^4.9.2",
"webpack-remove-empty-scripts": "^0.7.3"
},
diff --git a/yarn.lock b/yarn.lock
index 9b7da0c6b..77b4f6c41 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5955,10 +5955,10 @@ webpack-sources@^3.2.3:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
-webpack@^5.70.0:
- version "5.74.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.74.0.tgz#02a5dac19a17e0bb47093f2be67c695102a55980"
- integrity sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==
+webpack@^5.76.0:
+ version "5.76.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c"
+ integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==
dependencies:
"@types/eslint-scope" "^3.7.3"
"@types/estree" "^0.0.51"
From 621289abb70b7effee7b6ad9b23f5110fdf992e1 Mon Sep 17 00:00:00 2001
From: Phil Lee
Date: Tue, 9 May 2023 11:12:12 +0100
Subject: [PATCH 08/14] remove chart.js (#1606)
# Context
- Removing dependencies that are not used
# Changes
- Remove chart.js which appears to be part of activeadmin and chartkick which seemed to have been removed
---
package.json | 1 -
webpack.config.js | 1 -
yarn.lock | 5 -----
3 files changed, 7 deletions(-)
diff --git a/package.json b/package.json
index 707c3c3fe..b4f225668 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,6 @@
"accessible-autocomplete": "^2.0.3",
"babel-loader": "^8.2.3",
"babel-plugin-macros": "^3.1.0",
- "chart.js": "^3.6.0",
"copy-webpack-plugin": "^10.2.4",
"core-js": "^3.21.1",
"css-loader": "^6.7.1",
diff --git a/webpack.config.js b/webpack.config.js
index c027f2dd2..d2f1d8993 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -23,7 +23,6 @@ module.exports = {
path.resolve(__dirname, 'node_modules/@hotwired/stimulus'),
path.resolve(__dirname, 'node_modules/@stimulus/polyfills'),
path.resolve(__dirname, 'node_modules/@rails/actioncable'),
- path.resolve(__dirname, 'node_modules/chartjs'),
path.resolve(__dirname, 'app/frontend')
],
use: ['babel-loader']
diff --git a/yarn.lock b/yarn.lock
index 77b4f6c41..272885f74 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1964,11 +1964,6 @@ character-parser@^2.2.0:
dependencies:
is-regex "^1.0.3"
-chart.js@^3.6.0:
- version "3.9.1"
- resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.9.1.tgz#3abf2c775169c4c71217a107163ac708515924b8"
- integrity sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==
-
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
From 7d4a8f90eb0d44481e3dd273a16d643675c4c70b Mon Sep 17 00:00:00 2001
From: kosiakkatrina <54268893+kosiakkatrina@users.noreply.github.com>
Date: Tue, 9 May 2023 11:19:13 +0100
Subject: [PATCH 09/14] Update import to handle validations (#1605)
* Clear all the charges if the error is on tcharge
* Round earnings value upon import
* Rounds savings to the nearest 10
* Clear income on over_hard_max_for_london validation
* Refactor sale import validations to be consistent with lettings
* Extract charges attributes into a variable
---
.../sales/financial_validations.rb | 4 +-
.../imports/lettings_logs_import_service.rb | 19 +++--
.../imports/sales_logs_import_service.rb | 55 ++++++------
.../lettings_logs_import_service_spec.rb | 62 ++++++++++++++
.../imports/sales_logs_import_service_spec.rb | 85 +++++++++++++++++--
5 files changed, 180 insertions(+), 45 deletions(-)
diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb
index c3b1446c9..8e54758bd 100644
--- a/app/models/validations/sales/financial_validations.rb
+++ b/app/models/validations/sales/financial_validations.rb
@@ -7,7 +7,7 @@ module Validations::Sales::FinancialValidations
relevant_fields = %i[income1 ownershipsch uprn la postcode_full]
if record.london_property? && record.income1 > 90_000
- relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_london") }
+ relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_london, message: I18n.t("validations.financial.income.over_hard_max_for_london") }
elsif record.property_not_in_london? && record.income1 > 80_000
relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_outside_london, message: I18n.t("validations.financial.income.over_hard_max_for_outside_london") }
end
@@ -18,7 +18,7 @@ module Validations::Sales::FinancialValidations
relevant_fields = %i[income2 ownershipsch uprn la postcode_full]
if record.london_property? && record.income2 > 90_000
- relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_london") }
+ relevant_fields.each { |field| record.errors.add field, :over_hard_max_for_london, message: I18n.t("validations.financial.income.over_hard_max_for_london") }
elsif record.property_not_in_london? && record.income2 > 80_000
relevant_fields.each { |field| record.errors.add field, I18n.t("validations.financial.income.over_hard_max_for_outside_london") }
end
diff --git a/app/services/imports/lettings_logs_import_service.rb b/app/services/imports/lettings_logs_import_service.rb
index 1bc98925a..b1487a0b7 100644
--- a/app/services/imports/lettings_logs_import_service.rb
+++ b/app/services/imports/lettings_logs_import_service.rb
@@ -105,7 +105,7 @@ module Imports
attributes["hb"] = unsafe_string_as_integer(xml_doc, "Q6Ben")
attributes["benefits"] = unsafe_string_as_integer(xml_doc, "Q7Ben")
- attributes["earnings"] = safe_string_as_decimal(xml_doc, "Q8Money")
+ attributes["earnings"] = safe_string_as_decimal(xml_doc, "Q8Money")&.round
attributes["net_income_known"] = net_income_known(xml_doc, attributes["earnings"])
attributes["incfreq"] = unsafe_string_as_integer(xml_doc, "Q8a")
@@ -284,11 +284,14 @@ module Imports
end
def rescue_validation_or_raise(lettings_log, attributes, previous_status, exception)
+ charges_attributes = %w[brent scharge pscharge supcharg tcharge]
+
# Blank out all invalid fields for in-progress logs
if %w[saved submitted-invalid].include?(previous_status)
lettings_log.errors.each do |error|
@logger.warn("Log #{lettings_log.old_id}: Removing field #{error.attribute} from log triggering validation: #{error.type}")
attributes.delete(error.attribute.to_s)
+ charges_attributes.each { |attribute| attributes.delete(attribute) } if error.attribute == :tcharge
end
@logs_overridden << lettings_log.old_id
return save_lettings_log(attributes, previous_status)
@@ -306,16 +309,16 @@ module Imports
%i[earnings over_hard_max] => %w[ecstat1],
%i[tshortfall no_outstanding_charges] => %w[tshortfall hbrentshortfall],
%i[beds outside_the_range] => %w[beds],
- %i[tcharge complete_1_of_3] => %w[brent scharge pscharge supcharg tcharge],
- %i[scharge under_min] => %w[brent scharge pscharge supcharg tcharge],
+ %i[tcharge complete_1_of_3] => charges_attributes,
+ %i[scharge under_min] => charges_attributes,
%i[tshortfall must_be_positive] => %w[tshortfall tshortfall_known],
%i[referral referral_invalid] => %w[referral],
- %i[pscharge outside_the_range] => %w[brent scharge pscharge supcharg tcharge],
- %i[supcharg outside_the_range] => %w[brent scharge pscharge supcharg tcharge],
- %i[scharge outside_the_range] => %w[brent scharge pscharge supcharg tcharge],
+ %i[pscharge outside_the_range] => charges_attributes,
+ %i[supcharg outside_the_range] => charges_attributes,
+ %i[scharge outside_the_range] => charges_attributes,
%i[location_id not_active] => %w[location_id scheme_id],
- %i[tcharge under_10] => %w[brent scharge pscharge supcharg tcharge],
- %i[brent over_hard_max] => %w[brent scharge pscharge supcharg tcharge],
+ %i[tcharge under_10] => charges_attributes,
+ %i[brent over_hard_max] => charges_attributes,
}
(2..8).each do |person|
diff --git a/app/services/imports/sales_logs_import_service.rb b/app/services/imports/sales_logs_import_service.rb
index 8702a8846..565c16512 100644
--- a/app/services/imports/sales_logs_import_service.rb
+++ b/app/services/imports/sales_logs_import_service.rb
@@ -70,7 +70,7 @@ module Imports
attributes["inc1mort"] = unsafe_string_as_integer(xml_doc, "Q2Person1Mortgage")
attributes["income2"] = safe_string_as_integer(xml_doc, "Q2Person2Income")
attributes["income2nk"] = income_known(unsafe_string_as_integer(xml_doc, "P2IncKnown"))
- attributes["savings"] = safe_string_as_integer(xml_doc, "Q3Savings")
+ attributes["savings"] = safe_string_as_integer(xml_doc, "Q3Savings")&.round(-1)
attributes["savingsnk"] = savings_known(xml_doc)
attributes["prevown"] = unsafe_string_as_integer(xml_doc, "Q4PrevOwnedProperty")
attributes["mortgage"] = safe_string_as_decimal(xml_doc, "CALCMORT")
@@ -217,39 +217,38 @@ module Imports
attributes.delete("ppcodenk") if error.attribute == :ppostcode_full
end
@logs_overridden << sales_log.old_id
- save_sales_log(attributes, previous_status)
- elsif sales_log.errors.of_kind?(:postcode_full, :postcodes_not_matching)
- @logger.warn("Log #{sales_log.old_id}: Removing previous postcode known and previous postcode as the postcode is invalid")
- @logs_overridden << sales_log.old_id
- attributes.delete("ppcodenk")
- attributes.delete("ppostcode_full")
- save_sales_log(attributes, previous_status)
- elsif sales_log.errors.of_kind?(:exdate, :over_a_year_from_saledate)
- @logger.warn("Log #{sales_log.old_id}: Removing exchange date as the exchange date is invalid")
- @logs_overridden << sales_log.old_id
- attributes.delete("exdate")
- save_sales_log(attributes, previous_status)
- elsif sales_log.errors.of_kind?(:income1, :over_hard_max_for_outside_london)
- @logger.warn("Log #{sales_log.old_id}: Removing income1 as the income1 is invalid")
- @logs_overridden << sales_log.old_id
- attributes.delete("income1")
- save_sales_log(attributes, previous_status)
- elsif sales_log.errors.of_kind?(:equity, :over_max) || sales_log.errors.of_kind?(:equity, :under_min)
- @logger.warn("Log #{sales_log.old_id}: Removing equity as the equity is invalid")
+ return save_sales_log(attributes, previous_status)
+ end
+
+ errors = {
+ %i[postcode_full postcodes_not_matching] => %w[ppcodenk ppostcode_full],
+ %i[exdate over_a_year_from_saledate] => %w[exdate],
+ %i[income1 over_hard_max_for_outside_london] => %w[income1],
+ %i[income1 over_hard_max_for_london] => %w[income1],
+ %i[income2 over_hard_max_for_london] => %w[income2],
+ %i[equity over_max] => %w[equity],
+ %i[equity under_min] => %w[equity],
+ %i[mortgage cannot_be_0] => %w[mortgage],
+ }
+
+ errors.each do |(error, fields)|
+ next unless sales_log.errors.of_kind?(*error)
+
+ attribute, _type = error
+ fields.each do |field|
+ @logger.warn("Log #{sales_log.old_id}: Removing #{field} with error: #{sales_log.errors[attribute].sort.join(', ')}")
+ attributes.delete(field)
+ end
@logs_overridden << sales_log.old_id
- attributes.delete("equity")
- save_sales_log(attributes, previous_status)
- elsif sales_log.errors.of_kind?(:postcode_full, :wrong_format)
+ return save_sales_log(attributes, previous_status)
+ end
+
+ if sales_log.errors.of_kind?(:postcode_full, :wrong_format)
@logger.warn("Log #{sales_log.old_id}: Removing postcode as the postcode is invalid")
@logs_overridden << sales_log.old_id
attributes.delete("postcode_full")
attributes["pcodenk"] = attributes["la"].present? ? 1 : nil
save_sales_log(attributes, previous_status)
- elsif sales_log.errors.of_kind?(:mortgage, :cannot_be_0)
- @logger.warn("Log #{sales_log.old_id}: Removing mortgage because it cannot be 0")
- @logs_overridden << sales_log.old_id
- attributes.delete("mortgage")
- save_sales_log(attributes, previous_status)
elsif sales_log.errors.of_kind?(:uprn, :uprn_error)
@logger.warn("Log #{sales_log.old_id}: Setting uprn_known to no with error: #{sales_log.errors[:uprn].join(', ')}")
@logs_overridden << sales_log.old_id
diff --git a/spec/services/imports/lettings_logs_import_service_spec.rb b/spec/services/imports/lettings_logs_import_service_spec.rb
index ec41ba366..6132890a1 100644
--- a/spec/services/imports/lettings_logs_import_service_spec.rb
+++ b/spec/services/imports/lettings_logs_import_service_spec.rb
@@ -1173,6 +1173,68 @@ RSpec.describe Imports::LettingsLogsImportService do
end
end
+ context "and an error is added to tcharge for in progress log" do
+ let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" }
+ let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) }
+ let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) }
+
+ before do
+ lettings_log_xml.at_xpath("//meta:status").content = "saved"
+ lettings_log_xml.at_xpath("//xmlns:Q18ai").content = "1"
+ lettings_log_xml.at_xpath("//xmlns:Q18aii").content = "2"
+ lettings_log_xml.at_xpath("//xmlns:Q18aiii").content = "3"
+ lettings_log_xml.at_xpath("//xmlns:Q18aiv").content = "3"
+ lettings_log_xml.at_xpath("//xmlns:Q18av").content = "9"
+ end
+
+ it "intercepts the relevant validation error" do
+ expect(logger).to receive(:warn).with("Log 00d2343e-d5fa-4c89-8400-ec3854b0f2b4: Removing field tcharge from log triggering validation: under_10")
+ lettings_log_service.send(:create_log, lettings_log_xml)
+ end
+
+ it "clears out the invalid answers" do
+ allow(logger).to receive(:warn)
+
+ lettings_log_service.send(:create_log, lettings_log_xml)
+ lettings_log = LettingsLog.find_by(old_id: lettings_log_id)
+
+ expect(lettings_log).not_to be_nil
+ expect(lettings_log.tcharge).to be_nil
+ expect(lettings_log.brent).to be_nil
+ expect(lettings_log.scharge).to be_nil
+ expect(lettings_log.pscharge).to be_nil
+ expect(lettings_log.supcharg).to be_nil
+ end
+ end
+
+ context "and the earnings is not a whole number" do
+ let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" }
+ let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) }
+ let(:lettings_log_xml) { Nokogiri::XML(lettings_log_file) }
+
+ before do
+ lettings_log_xml.at_xpath("//meta:status").content = "submitted"
+ lettings_log_xml.at_xpath("//xmlns:Q8a").content = "1 Weekly"
+ lettings_log_xml.at_xpath("//xmlns:Q8Money").content = 100.59
+ lettings_log_xml.at_xpath("//xmlns:Q8Refused").content = ""
+ end
+
+ it "does not error" do
+ expect { lettings_log_service.send(:create_log, lettings_log_xml) }
+ .not_to raise_error
+ end
+
+ it "rounds the earnings value" do
+ allow(logger).to receive(:warn)
+
+ lettings_log_service.send(:create_log, lettings_log_xml)
+ lettings_log = LettingsLog.find_by(old_id: lettings_log_id)
+
+ expect(lettings_log).not_to be_nil
+ expect(lettings_log.earnings).to eq(101)
+ end
+ end
+
context "when setting location fields for 23/24 logs" do
let(:lettings_log_id) { "00d2343e-d5fa-4c89-8400-ec3854b0f2b4" }
let(:lettings_log_file) { open_file(fixture_directory, lettings_log_id) }
diff --git a/spec/services/imports/sales_logs_import_service_spec.rb b/spec/services/imports/sales_logs_import_service_spec.rb
index c06b7bf42..f169dbd78 100644
--- a/spec/services/imports/sales_logs_import_service_spec.rb
+++ b/spec/services/imports/sales_logs_import_service_spec.rb
@@ -528,7 +528,7 @@ RSpec.describe Imports::SalesLogsImportService do
end
it "intercepts the relevant validation error" do
- expect(logger).to receive(:warn).with(/Log shared_ownership_sales_log: Removing equity as the equity is invalid/)
+ expect(logger).to receive(:warn).with(/Removing equity with error: The maximum initial equity stake is 75%/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
@@ -552,7 +552,7 @@ RSpec.describe Imports::SalesLogsImportService do
end
it "intercepts the relevant validation error" do
- expect(logger).to receive(:warn).with(/Log shared_ownership_sales_log: Removing equity as the equity is invalid/)
+ expect(logger).to receive(:warn).with(/Removing equity with error: The minimum initial equity stake for this type of shared ownership sale is 25%/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
@@ -631,7 +631,8 @@ RSpec.describe Imports::SalesLogsImportService do
end
it "intercepts the relevant validation error" do
- expect(logger).to receive(:warn).with(/Removing previous postcode known and previous postcode as the postcode is invalid/)
+ expect(logger).to receive(:warn).with(/Removing ppcodenk with error: Buyer's last accommodation and discounted ownership postcodes must match, Last settled accommodation and discounted ownership property postcodes must match/)
+ expect(logger).to receive(:warn).with(/Removing ppostcode_full with error: Buyer's last accommodation and discounted ownership postcodes must match, Last settled accommodation and discounted ownership property postcodes must match/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
@@ -701,7 +702,7 @@ RSpec.describe Imports::SalesLogsImportService do
end
it "intercepts the relevant validation error" do
- expect(logger).to receive(:warn).with(/Removing exchange date as the exchange date is invalid/)
+ expect(logger).to receive(:warn).with(/Removing exdate with error: Contract exchange date must be less than 1 year before sale completion date/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
@@ -726,7 +727,7 @@ RSpec.describe Imports::SalesLogsImportService do
end
it "intercepts the relevant validation error" do
- expect(logger).to receive(:warn).with(/Removing mortgage because it cannot be 0/)
+ expect(logger).to receive(:warn).with(/Removing mortgage with error: Mortgage amount must be at least £1, Mortgage value cannot be £0 if a mortgage was used for the purchase of this property/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
@@ -742,7 +743,7 @@ RSpec.describe Imports::SalesLogsImportService do
end
end
- context "and it has an invalid income" do
+ context "and it has an invalid income 1" do
let(:sales_log_id) { "shared_ownership_sales_log" }
before do
@@ -751,7 +752,7 @@ RSpec.describe Imports::SalesLogsImportService do
end
it "intercepts the relevant validation error" do
- expect(logger).to receive(:warn).with(/Removing income1 as the income1 is invalid/)
+ expect(logger).to receive(:warn).with(/Removing income1 with error: Income must be £80,000 or lower for properties outside London local authority/)
expect { sales_log_service.send(:create_log, sales_log_xml) }
.not_to raise_error
end
@@ -767,6 +768,56 @@ RSpec.describe Imports::SalesLogsImportService do
end
end
+ context "and it has an invalid income 1 for london" do
+ let(:sales_log_id) { "shared_ownership_sales_log" }
+
+ before do
+ sales_log_xml.at_xpath("//xmlns:Q2Person1Income").content = "95000"
+ sales_log_xml.at_xpath("//xmlns:Q14ONSLACode").content = "E09000012"
+ end
+
+ it "intercepts the relevant validation error" do
+ expect(logger).to receive(:warn).with(/Removing income1 with error: Income must be £90,000 or lower for properties within a London local authority/)
+ expect { sales_log_service.send(:create_log, sales_log_xml) }
+ .not_to raise_error
+ end
+
+ it "clears out the invalid answers" do
+ allow(logger).to receive(:warn)
+
+ sales_log_service.send(:create_log, sales_log_xml)
+ sales_log = SalesLog.find_by(old_id: sales_log_id)
+
+ expect(sales_log).not_to be_nil
+ expect(sales_log.income1).to be_nil
+ end
+ end
+
+ context "and it has an invalid income 2 for london" do
+ let(:sales_log_id) { "shared_ownership_sales_log" }
+
+ before do
+ sales_log_xml.at_xpath("//xmlns:Q2Person2Income").content = "95000"
+ sales_log_xml.at_xpath("//xmlns:Q14ONSLACode").content = "E09000012"
+ end
+
+ it "intercepts the relevant validation error" do
+ expect(logger).to receive(:warn).with(/Removing income2 with error: Combined income must be £90,000 or lower for properties within a London local authority, Income must be £90,000 or lower for properties within a London local authority/)
+ expect { sales_log_service.send(:create_log, sales_log_xml) }
+ .not_to raise_error
+ end
+
+ it "clears out the invalid answers" do
+ allow(logger).to receive(:warn)
+
+ sales_log_service.send(:create_log, sales_log_xml)
+ sales_log = SalesLog.find_by(old_id: sales_log_id)
+
+ expect(sales_log).not_to be_nil
+ expect(sales_log.income2).to be_nil
+ end
+ end
+
context "when inferring default answers for completed sales logs" do
context "when the armedforcesspouse is not answered" do
let(:sales_log_id) { "discounted_ownership_sales_log" }
@@ -817,6 +868,26 @@ RSpec.describe Imports::SalesLogsImportService do
end
end
+ context "when the savings is given not to the nearest 10" do
+ let(:sales_log_id) { "discounted_ownership_sales_log" }
+
+ before do
+ sales_log_xml.at_xpath("//xmlns:Q3Savings").content = "10013"
+ allow(logger).to receive(:warn).and_return(nil)
+ end
+
+ it "does not error" do
+ expect { sales_log_service.send(:create_log, sales_log_xml) }.not_to raise_error
+ end
+
+ it "sets savings to the nearest 10" do
+ sales_log_service.send(:create_log, sales_log_xml)
+
+ sales_log = SalesLog.find_by(old_id: sales_log_id)
+ expect(sales_log&.savings).to be(10_010)
+ end
+ end
+
context "when inferring age known" do
let(:sales_log_id) { "discounted_ownership_sales_log" }
From 40cf0c10c2bbe07b6393c3e98b9c0a871a61eca0 Mon Sep 17 00:00:00 2001
From: Arthur Campbell <51094020+arfacamble@users.noreply.github.com>
Date: Tue, 9 May 2023 11:50:38 +0100
Subject: [PATCH 10/14] CLDC-1633 build feature csv download of sales logs
(#1568)
* create a method on the FormHandler that returns the sales form questions for all years in the order that they appear in the form
* update csv email job to accomodate sales log export as well as lettings
add to tests to reflec the changes made
* write tests to cover the desired functionality of the SalesLogCsvService
* create the SalesLogCsvService
create a necessary method on the log to enable submission method to be included on the csv
derive values for the two halves of previous postcode for export
* add relevant links in the UI and pipe everything together in controllers
amend organisations controller to have flexibility to download logs of either type
add necessary methods to sales log controller, raising shared method to logs controller
update routing for amendments and additions
extract helper method to build urls for downloading logs within an organisation
* correct various linter complaints and tech review suggestions
* minor amendment to add old_id and reorder early columns
* undo my 'clever' refactor that broke things
* refactoring of csv service after some tech review and some UI testing in review app
* update tests to include a test of a full export and all values in teh csv
* correct minor routing error to ensure correct url is shown and tab selected after requesting csv email
* update organisations controller requests spec file to cover new functionality and make a minor amendment to authentication scope in the controller after error found in testing
* write request tests for the new functionality in the sales log controller, define authorisation in the controller
* minor correction after rubocop's kind suggestion'
* various corrections from first pass at PO, tech review, linter, etc
* refactor :ordered_sales_questions_for_all_years
* first pass at implementing flexible code-based form fixtures for testing
* second pass
* refactor all tests of :ordered_sales_questions_for_all_years to use new factories
* some refactoring in the testing of the csv service
* use that fact that params is always available in controllers and don't pass it around, inline some methods calls
* correct minor bug to ensure that "Return to logs" link returns to the correct index page
* remove reminder comments
* write further tests on the manipulation of questions into the csv headers, update factories of form constituents to allow the creation of forms with richer questions
* fix linter complaints
* minor alterations after rebase to account for changes made on other branches
* refactor after code review
* tweak fixtures after rebase containing alterations to the factory defaults
---
app/controllers/lettings_logs_controller.rb | 19 +-
app/controllers/logs_controller.rb | 4 +
app/controllers/organisations_controller.rb | 36 +++-
app/controllers/sales_logs_controller.rb | 21 ++
app/helpers/logs_helper.rb | 39 ++--
app/jobs/email_csv_job.rb | 19 +-
.../derived_variables/sales_log_variables.rb | 3 +-
app/models/form_handler.rb | 26 ++-
app/models/log.rb | 4 +
app/services/csv/sales_log_csv_service.rb | 107 ++++++++++
app/views/logs/_log_list.html.erb | 2 +-
app/views/logs/csv_confirmation.html.erb | 2 +-
app/views/logs/index.html.erb | 4 +-
app/views/organisations/logs.html.erb | 4 +-
config/routes.rb | 13 +-
spec/factories/form.rb | 25 +++
spec/factories/page.rb | 21 ++
spec/factories/question.rb | 6 +
spec/factories/sales_log.rb | 3 +-
spec/factories/section.rb | 16 ++
spec/factories/subsection.rb | 20 ++
.../files/sales_logs_csv_export_codes.csv | 2 +
.../files/sales_logs_csv_export_labels.csv | 2 +
spec/jobs/email_csv_job_spec.rb | 39 +++-
spec/models/form_handler_spec.rb | 51 +++++
.../requests/organisations_controller_spec.rb | 189 ++++++++++++++++--
spec/requests/sales_logs_controller_spec.rb | 184 ++++++++++++++++-
.../csv/sales_log_csv_service_spec.rb | 189 ++++++++++++++++++
28 files changed, 966 insertions(+), 84 deletions(-)
create mode 100644 app/services/csv/sales_log_csv_service.rb
create mode 100644 spec/factories/form.rb
create mode 100644 spec/factories/page.rb
create mode 100644 spec/factories/question.rb
create mode 100644 spec/factories/section.rb
create mode 100644 spec/factories/subsection.rb
create mode 100644 spec/fixtures/files/sales_logs_csv_export_codes.csv
create mode 100644 spec/fixtures/files/sales_logs_csv_export_labels.csv
create mode 100644 spec/services/csv/sales_log_csv_service_spec.rb
diff --git a/app/controllers/lettings_logs_controller.rb b/app/controllers/lettings_logs_controller.rb
index c97f5f45d..169ff42e3 100644
--- a/app/controllers/lettings_logs_controller.rb
+++ b/app/controllers/lettings_logs_controller.rb
@@ -7,11 +7,6 @@ class LettingsLogsController < LogsController
before_action :extract_bulk_upload_from_session_filters, only: [:index]
before_action :redirect_if_bulk_upload_resolved, only: [:index]
- def authenticate_scope!
- codes_only_export = codes_only_export?(params)
- head :unauthorized and return unless current_user.support? || !codes_only_export
- end
-
def index
respond_to do |format|
format.html do
@@ -86,19 +81,13 @@ class LettingsLogsController < LogsController
def download_csv
unpaginated_filtered_logs = filtered_logs(current_user.lettings_logs, search_term, @session_filters)
- codes_only = codes_only_export?(params)
-
- render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path, codes_only: }
- end
- def codes_only_export?(params)
- params.require(:codes_only) == "true"
+ render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path, codes_only: codes_only_export? }
end
def email_csv
all_orgs = params["organisation_select"] == "all"
- codes_only_export = params.require(:codes_only) == "true"
- EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export)
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export?)
redirect_to csv_confirmation_lettings_logs_path
end
@@ -124,6 +113,10 @@ class LettingsLogsController < LogsController
private
+ def authenticate_scope!
+ head :unauthorized and return if codes_only_export? && !current_user.support?
+ end
+
def redirect_if_bulk_upload_resolved
if @bulk_upload && @bulk_upload.lettings_logs.in_progress.count.zero?
redirect_to resume_bulk_upload_lettings_result_path(@bulk_upload)
diff --git a/app/controllers/logs_controller.rb b/app/controllers/logs_controller.rb
index 603608241..12156d5aa 100644
--- a/app/controllers/logs_controller.rb
+++ b/app/controllers/logs_controller.rb
@@ -28,6 +28,10 @@ private
end
end
+ def codes_only_export?
+ params.require(:codes_only) == "true"
+ end
+
def post_create_redirect_url
raise "implement in sub class"
end
diff --git a/app/controllers/organisations_controller.rb b/app/controllers/organisations_controller.rb
index 70943063f..b430136ae 100644
--- a/app/controllers/organisations_controller.rb
+++ b/app/controllers/organisations_controller.rb
@@ -6,8 +6,8 @@ class OrganisationsController < ApplicationController
before_action :authenticate_user!
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
- before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_csv download_csv]
- before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_csv download_csv]
+ before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
+ before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
def index
redirect_to organisation_path(current_user.organisation) unless current_user.support?
@@ -99,23 +99,23 @@ class OrganisationsController < ApplicationController
@pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = organisation_logs.size
+ @log_type = :lettings
render "logs", layout: "application"
end
end
end
- def download_csv
+ def download_lettings_csv
organisation_logs = LettingsLog.visible.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
codes_only = params.require(:codes_only) == "true"
- render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: logs_email_csv_organisation_path, codes_only: }
+ render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: lettings_logs_email_csv_organisation_path, codes_only: }
end
- def email_csv
- codes_only_export = params.require(:codes_only) == "true"
- EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export)
- redirect_to logs_csv_confirmation_organisation_path
+ def email_lettings_csv
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export?)
+ redirect_to lettings_logs_csv_confirmation_organisation_path
end
def sales_logs
@@ -128,6 +128,7 @@ class OrganisationsController < ApplicationController
@pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = organisation_logs.size
+ @log_type = :sales
render "logs", layout: "application"
end
@@ -137,6 +138,19 @@ class OrganisationsController < ApplicationController
end
end
+ def download_sales_csv
+ organisation_logs = SalesLog.visible.where(owning_organisation_id: @organisation.id)
+ unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
+ codes_only = params.require(:codes_only) == "true"
+
+ render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: sales_logs_email_csv_organisation_path, codes_only: }
+ end
+
+ def email_sales_csv
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export?, "sales")
+ redirect_to sales_logs_csv_confirmation_organisation_path
+ end
+
def merge_request
@merge_request = MergeRequest.new
end
@@ -147,12 +161,16 @@ private
params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no)
end
+ def codes_only_export?
+ params.require(:codes_only) == "true"
+ end
+
def search_term
params["search"]
end
def authenticate_scope!
- if %w[create new lettings_logs download_csv email_csv].include? action_name
+ if %w[create new lettings_logs sales_logs download_lettings_csv email_lettings_csv email_sales_csv download_sales_csv].include? action_name
head :unauthorized and return unless current_user.support?
elsif current_user.organisation != @organisation && !current_user.support?
render_not_found
diff --git a/app/controllers/sales_logs_controller.rb b/app/controllers/sales_logs_controller.rb
index 155f606ab..5e9777a3c 100644
--- a/app/controllers/sales_logs_controller.rb
+++ b/app/controllers/sales_logs_controller.rb
@@ -1,6 +1,7 @@
class SalesLogsController < LogsController
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
+ before_action :authenticate_scope!, only: %i[download_csv email_csv]
def create
super { SalesLog.new(log_params) }
@@ -36,6 +37,20 @@ class SalesLogsController < LogsController
end
end
+ def download_csv
+ unpaginated_filtered_logs = filtered_logs(current_user.sales_logs, search_term, @session_filters)
+
+ render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_sales_logs_path, codes_only: codes_only_export? }
+ end
+
+ def email_csv
+ all_orgs = params["organisation_select"] == "all"
+ EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export?, "sales")
+ redirect_to csv_confirmation_sales_logs_path
+ end
+
+ def csv_confirmation; end
+
def post_create_redirect_url(log)
sales_log_url(log)
end
@@ -43,4 +58,10 @@ class SalesLogsController < LogsController
def permitted_log_params
params.require(:sales_log).permit(SalesLog.editable_fields)
end
+
+private
+
+ def authenticate_scope!
+ head :unauthorized and return if codes_only_export? && !current_user.support?
+ end
end
diff --git a/app/helpers/logs_helper.rb b/app/helpers/logs_helper.rb
index aa132afd6..f525f4600 100644
--- a/app/helpers/logs_helper.rb
+++ b/app/helpers/logs_helper.rb
@@ -1,10 +1,8 @@
module LogsHelper
def log_type_for_controller(controller)
- case controller.class.to_s
- when "LettingsLogsController"
- "lettings"
- when "SalesLogsController"
- "sales"
+ case controller.class.name
+ when "LettingsLogsController" then "lettings"
+ when "SalesLogsController" then "sales"
else
raise "Log type not found for #{controller.class}"
end
@@ -12,10 +10,8 @@ module LogsHelper
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:)
+ when "lettings" then bulk_upload_lettings_log_path(id:)
+ when "sales" then bulk_upload_sales_log_path(id:)
end
end
@@ -26,16 +22,29 @@ module LogsHelper
def search_label_for_controller(controller)
case log_type_for_controller(controller)
- when "lettings"
- "Search by log ID, tenant code, property reference or postcode"
- when "sales"
- "Search by log ID, purchaser code or postcode"
+ when "lettings" then "Search by log ID, tenant code, property reference or postcode"
+ when "sales" then "Search by log ID, purchaser code or postcode"
end
end
- def csv_download_url_for_controller(controller_type:, search:, codes_only:)
- case log_type_for_controller(controller_type)
+ def csv_download_url_for_controller(controller:, search:, codes_only:)
+ case log_type_for_controller(controller)
when "lettings" then csv_download_lettings_logs_path(search:, codes_only:)
+ when "sales" then csv_download_sales_logs_path(search:, codes_only:)
+ end
+ end
+
+ def logs_path_for_controller(controller)
+ case log_type_for_controller(controller)
+ when "lettings" then lettings_logs_path
+ when "sales" then sales_logs_path
+ end
+ end
+
+ def csv_download_url_by_log_type(log_type, organisation, search:, codes_only:)
+ case log_type
+ when :lettings then lettings_logs_csv_download_organisation_path(organisation, search:, codes_only:)
+ when :sales then sales_logs_csv_download_organisation_path(organisation, search:, codes_only:)
end
end
end
diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb
index f313c87b8..63acdbaaa 100644
--- a/app/jobs/email_csv_job.rb
+++ b/app/jobs/email_csv_job.rb
@@ -5,14 +5,23 @@ class EmailCsvJob < ApplicationJob
EXPIRATION_TIME = 3.hours.to_i
- def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
- unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible
- filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
+ def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false, log_type = "lettings") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
+ case log_type
+ when "lettings"
+ unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible
+ filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
+ csv_string = filtered_logs.to_csv(user, codes_only_export:)
+ when "sales"
+ unfiltered_logs = organisation.present? && user.support? ? SalesLog.visible.where(owning_organisation_id: organisation.id) : user.sales_logs.visible
+ filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
+ export_type = codes_only_export ? "codes" : "labels"
+ csv_string = Csv::SalesLogCsvService.new(export_type:).prepare_csv(filtered_logs)
+ end
- filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv"
+ filename = "#{[log_type, 'logs', organisation&.name, Time.zone.now].compact.join('-')}.csv"
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"])
- storage_service.write_file(filename, BYTE_ORDER_MARK + filtered_logs.to_csv(user, codes_only_export:))
+ storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)
diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb
index c30c62b9a..308a62bb8 100644
--- a/app/models/derived_variables/sales_log_variables.rb
+++ b/app/models/derived_variables/sales_log_variables.rb
@@ -17,7 +17,8 @@ module DerivedVariables::SalesLogVariables
self.hoyear = hodate.year
end
self.deposit = value if outright_sale? && mortgage_not_used?
- self.pcode1, self.pcode2 = postcode_full.split(" ") if postcode_full.present?
+ self.pcode1, self.pcode2 = postcode_full.split if postcode_full.present?
+ self.ppostc1, self.ppostc2 = ppostcode_full.split if ppostcode_full.present?
self.totchild = total_child
self.totadult = total_adult + total_elder
self.hhmemb = number_of_household_members
diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb
index ee84c3e32..cdebdc679 100644
--- a/app/models/form_handler.rb
+++ b/app/models/form_handler.rb
@@ -40,6 +40,28 @@ class FormHandler
}
end
+ def ordered_sales_questions_for_all_years
+ sales_forms = forms.filter { |name, _form| name.end_with? "sales" }.values
+ ordered_questions = sales_forms.pop.questions.uniq(&:id)
+ question_ids = ordered_questions.map(&:id)
+ all_questions_from_previous_forms = sales_forms.flat_map(&:questions)
+ deprecated_questions_by_preceding_question_id(question_ids, all_questions_from_previous_forms).each do |preceding_question_id, deprecated_question|
+ index_of_preceding_question = ordered_questions.index { |q| q.id == preceding_question_id }
+ ordered_questions.insert(index_of_preceding_question + 1, deprecated_question)
+ end
+ ordered_questions
+ end
+
+ def deprecated_questions_by_preceding_question_id(current_form_question_ids, all_questions_from_previous_forms)
+ deprecated_questions = {}
+ all_questions_from_previous_forms.each_cons(2) do |preceding_question, question|
+ next if current_form_question_ids.include?(question.id) || deprecated_questions.values.map(&:id).include?(question.id)
+
+ deprecated_questions[preceding_question.id] = question
+ end
+ deprecated_questions
+ end
+
def lettings_forms
forms = {}
directories.each do |directory|
@@ -95,9 +117,9 @@ class FormHandler
forms.count { |form| now.between?(form.start_date, form.end_date) } > 1
end
- def use_fake_forms!
+ def use_fake_forms!(fake_forms = nil)
@directories = ["spec/fixtures/forms"]
- @forms = get_all_forms
+ @forms = fake_forms || get_all_forms
end
def use_real_forms!
diff --git a/app/models/log.rb b/app/models/log.rb
index 4ee748eda..807b45c8b 100644
--- a/app/models/log.rb
+++ b/app/models/log.rb
@@ -136,6 +136,10 @@ class Log < ApplicationRecord
format_as_currency(field_value)
end
+ def creation_method
+ bulk_upload_id ? "bulk upload" : "single log"
+ end
+
private
# Handle logs that are older than previous collection start date
diff --git a/app/services/csv/sales_log_csv_service.rb b/app/services/csv/sales_log_csv_service.rb
new file mode 100644
index 000000000..a9573df41
--- /dev/null
+++ b/app/services/csv/sales_log_csv_service.rb
@@ -0,0 +1,107 @@
+module Csv
+ class SalesLogCsvService
+ def initialize(export_type:)
+ @export_type = export_type
+ @attributes = sales_log_attributes
+ end
+
+ def prepare_csv(logs)
+ CSV.generate(headers: true) do |csv|
+ csv << @attributes
+
+ logs.find_each do |log|
+ csv << @attributes.map { |attribute| value(attribute, log) }
+ end
+ end
+ end
+
+ private
+
+ ATTRIBUTES_OF_RELATED_OBJECTS = {
+ day: %i[saledate day],
+ month: %i[saledate month],
+ year: %i[saledate year],
+ is_dpo: %i[created_by is_dpo],
+ created_by_name: %i[created_by name],
+ owning_organisation_name: %i[owning_organisation name],
+ }.freeze
+
+ FIELDS_ALWAYS_EXPORTED_AS_CODES = %w[
+ la
+ prevloc
+ ].freeze
+
+ FIELDS_ALWAYS_EXPORTED_AS_LABELS = {
+ "la_label" => "la",
+ "prevloc_label" => "prevloc",
+ }.freeze
+
+ DATE_FIELDS = %w[
+ created_at
+ updated_at
+ ].freeze
+
+ def value(attribute, log)
+ if ATTRIBUTES_OF_RELATED_OBJECTS.key? attribute.to_sym
+ call_chain = ATTRIBUTES_OF_RELATED_OBJECTS[attribute.to_sym]
+ call_chain.reduce(log) { |object, next_call| object&.public_send(next_call) }
+ elsif FIELDS_ALWAYS_EXPORTED_AS_CODES.include? attribute
+ log.send(attribute)
+ elsif FIELDS_ALWAYS_EXPORTED_AS_LABELS.key? attribute
+ attribute = FIELDS_ALWAYS_EXPORTED_AS_LABELS[attribute]
+ field_value = log.send(attribute)
+ get_label(field_value, attribute, log)
+ elsif DATE_FIELDS.include? attribute
+ log.send(attribute)&.iso8601
+ else
+ value = log.public_send(attribute)
+ case @export_type
+ when "codes"
+ value
+ when "labels"
+ answer_label = get_label(value, attribute, log)
+ answer_label || label_if_boolean_value(value) || value
+ end
+ end
+ end
+
+ def get_label(value, attribute, log)
+ log.form
+ .get_question(attribute, log)
+ &.label_from_value(value)
+ end
+
+ def label_if_boolean_value(value)
+ return "Yes" if value == true
+ return "No" if value == false
+ end
+
+ ATTRIBUTE_MAPPINGS = {
+ "saledate" => %w[day month year],
+ "exdate" => %w[exday exmonth exyear],
+ "hodate" => %w[hoday homonth hoyear],
+ "postcode_full" => %w[pcode1 pcode2],
+ "ppostcode_full" => %w[ppostc1 ppostc2],
+ "la" => %w[la la_label],
+ "prevloc" => %w[prevloc prevloc_label],
+ "created_by_id" => %w[created_by_name],
+ "owning_organisation_id" => %w[owning_organisation_name],
+ }.freeze
+
+ def sales_log_attributes
+ ordered_questions = FormHandler.instance.ordered_sales_questions_for_all_years
+ ordered_questions.reject! { |q| q.id.match?(/((?
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %>
- <% if logs&.first&.lettings? %>
+ <% if logs&.any? %>
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4" %>
<% if @current_user.support? %>
<%= govuk_link_to "Download (CSV, codes only)", csv_codes_only_download_url, type: "text/csv" %>
diff --git a/app/views/logs/csv_confirmation.html.erb b/app/views/logs/csv_confirmation.html.erb
index 90f90813c..847be9ebf 100644
--- a/app/views/logs/csv_confirmation.html.erb
+++ b/app/views/logs/csv_confirmation.html.erb
@@ -9,7 +9,7 @@
Open your email inbox and click the link to download your CSV file.
- <%= govuk_link_to "Return to logs", lettings_logs_path %>
+ <%= govuk_link_to "Return to logs", logs_path_for_controller(controller) %>