Browse Source

Merge branch 'CLDC-571-allow-users-to-login-and-change-password' of https://github.com/communitiesuk/mhclg-data-collection-beta into CLDC-571-allow-users-to-login-and-change-password

pull/81/head
Matthew Phelan 3 years ago
parent
commit
f0fcf146b1
  1. 2
      .env.example
  2. 3
      Gemfile
  3. 5
      Gemfile.lock
  4. 15
      README.md
  5. 2
      app/admin/case_logs.rb
  6. 455
      app/constants/db_enums.rb
  7. 6
      app/controllers/case_logs_controller.rb
  8. 5
      app/controllers/users/passwords_controller.rb
  9. 14
      app/javascript/controllers/soft_validations_controller.js
  10. 70
      app/models/case_log.rb
  11. 8
      app/validations/financial_validations.rb
  12. 6
      app/validations/household_validations.rb
  13. 2
      app/validations/soft_validations.rb
  14. 2
      app/views/devise/mailer/reset_password_instructions.html.erb
  15. 22
      app/views/devise/passwords/edit.html.erb
  16. 3
      app/views/form/_validation_override_question.html.erb
  17. 14
      config/environments/development.rb
  18. 2
      config/environments/test.rb
  19. 94
      config/forms/2021_2022.json
  20. 112
      config/forms/schema/2021_2022.json
  21. 129
      config/forms/schema/generic.json
  22. 6
      config/initializers/devise.rb
  23. 15
      db/migrate/20211108091320_change_checkbox_types.rb
  24. 57
      db/migrate/20211108134601_further_core_migrations.rb
  25. 21
      db/migrate/20211110140928_add_mrc_dates.rb
  26. 7
      db/migrate/20211111143319_add_other_members_column.rb
  27. 7
      db/migrate/20211112105348_add_incref_field.rb
  28. 38
      db/schema.rb
  29. 54
      docs/api/DLUHC-CORE-Data.v1.json
  30. 51
      lib/tasks/form_definition.rake
  31. 49
      spec/controllers/case_logs_controller_spec.rb
  32. 4
      spec/factories/case_log.rb
  33. 12
      spec/features/case_log_spec.rb
  34. 6
      spec/features/user_spec.rb
  35. 33
      spec/fixtures/complete_case_log.json
  36. 26
      spec/fixtures/forms/test_form.json
  37. 48
      spec/fixtures/forms/test_validator.json
  38. 4
      spec/helpers/check_answers_helper_spec.rb
  39. 2
      spec/helpers/tasklist_helper_spec.rb
  40. 18
      spec/models/case_log_spec.rb
  41. 2
      spec/models/form_handler_spec.rb
  42. 2
      spec/models/form_spec.rb

2
.env.example

@ -1,2 +1,4 @@
DB_USERNAME=postgres-user DB_USERNAME=postgres-user
DB_PASSWORD=postgres-password DB_PASSWORD=postgres-password
CORE_EMAIL_USERNAME=email@example.com
CORE_EMAIL_PASSWORD=password123

3
Gemfile

@ -29,6 +29,9 @@ gem "discard"
gem "activeadmin" gem "activeadmin"
# Admin charts # Admin charts
gem "chartkick" gem "chartkick"
# Json Schema
gem "json-schema"
gem "uk_postcode"
# Authentication # Authentication
gem "devise" gem "devise"

5
Gemfile.lock

@ -203,6 +203,8 @@ GEM
rails-dom-testing (>= 1, < 3) rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0) railties (>= 4.2.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json-schema (2.8.1)
addressable (>= 2.4)
kaminari (1.2.1) kaminari (1.2.1)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1) kaminari-actionview (= 1.2.1)
@ -366,6 +368,7 @@ GEM
rails (>= 6.0.0) rails (>= 6.0.0)
tzinfo (2.0.4) tzinfo (2.0.4)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uk_postcode (2.1.6)
unicode-display_width (2.1.0) unicode-display_width (2.1.0)
view_component (2.39.0) view_component (2.39.0)
activesupport (>= 5.0.0, < 8.0) activesupport (>= 5.0.0, < 8.0)
@ -409,6 +412,7 @@ DEPENDENCIES
govuk_design_system_formbuilder govuk_design_system_formbuilder
hotwire-rails hotwire-rails
jbuilder (~> 2.7) jbuilder (~> 2.7)
json-schema
listen (~> 3.3) listen (~> 3.3)
overcommit (>= 0.37.0) overcommit (>= 0.37.0)
pg (~> 1.1) pg (~> 1.1)
@ -428,6 +432,7 @@ DEPENDENCIES
selenium-webdriver selenium-webdriver
simplecov simplecov
tzinfo-data tzinfo-data
uk_postcode
web-console (>= 4.1.0) web-console (>= 4.1.0)
webpacker (~> 5.0) webpacker (~> 5.0)

15
README.md

@ -120,6 +120,7 @@ The JSON should follow the structure:
"[snake_case_question_name_string]": { "[snake_case_question_name_string]": {
"header": String, "header": String,
"hint_text": String, "hint_text": String,
"check_answer_label": String,
"type": "text" / "numeric" / "radio" / "checkbox" / "date", "type": "text" / "numeric" / "radio" / "checkbox" / "date",
"min": Integer, // numeric only "min": Integer, // numeric only
"max": Integer, // numeric only "max": Integer, // numeric only
@ -133,6 +134,10 @@ The JSON should follow the structure:
"[snake_case_question_to_enable_2_name_string]": ["condition-that-enables"] "[snake_case_question_to_enable_2_name_string]": ["condition-that-enables"]
} }
} }
},
"conditional_route_to": {
"[page_name_to_route_to]": {"question_name": "expected_answer"},
"[page_name_to_route_to]": {"question_name": "expected_answer"}
} }
} }
} }
@ -155,6 +160,16 @@ Assumptions made by the format:
- Radio question answer option selected matches one of conditional e.g. ["answer-options-1-string", "answer-option-3-string"] - Radio question answer option selected matches one of conditional e.g. ["answer-options-1-string", "answer-option-3-string"]
- Numeric question value matches condition e.g. [">2"], ["<7"] or ["== 6"] - Numeric question value matches condition e.g. [">2"], ["<7"] or ["== 6"]
## JSON Form Validation against Schema
To validate the form JSON against the schema you can run:
`rake form_definition:validate`
This will validate all forms in:
directories = ["config/forms", "spec/fixtures/forms"]
against the schema in (config/forms/schema/generic.json)
## Useful documentation (external dependencies) ## Useful documentation (external dependencies)
### GOV.UK Design System Form Builder for Rails ### GOV.UK Design System Form Builder for Rails

2
app/admin/case_logs.rb

@ -2,7 +2,7 @@ ActiveAdmin.register CaseLog do
# See permitted parameters documentation: # See permitted parameters documentation:
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters # https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
permit_params do permit_params do
permitted = %i[status tenant_code age1 sex1 tenant_ethnic_group tenant_nationality previous_housing_situation armed_forces ecstat1 hhmemb relat2 age2 sex2 ecstat2 relat3 age3 sex3 ecstat3 relat4 age4 sex4 ecstat4 relat5 age5 sex5 ecstat5 relat6 age6 sex6 ecstat6 relat7 age7 person_7_gender ecstat7 relat8 age8 sex8 ecstat8 homelessness reason_for_leaving_last_settled_home benefit_cap_spare_room_subsidy armed_forces_active armed_forces_injured armed_forces_partner medical_conditions pregnancy accessibility_requirements condition_effects tenancy_code tenancy_start_date starter_tenancy fixed_term_tenancy tenancy_type letting_type letting_provider property_location previous_postcode property_relet property_vacancy_reason property_reference property_unit_type property_building_type property_number_of_bedrooms property_void_date property_major_repairs property_major_repairs_date property_number_of_times_relet property_wheelchair_accessible net_income net_income_frequency net_income_uc_proportion housing_benefit rent_frequency basic_rent service_charge personal_service_charge support_charge total_charge outstanding_amount time_lived_in_la time_on_la_waiting_list previous_la property_postcode reasonable_preference reasonable_preference_reason cbl_letting chr_letting cap_letting outstanding_rent_or_charges other_reason_for_leaving_last_settled_home accessibility_requirements_fully_wheelchair_accessible_housing accessibility_requirements_wheelchair_access_to_essential_rooms accessibility_requirements_level_access_housing accessibility_requirements_other_disability_requirements accessibility_requirements_no_disability_requirements accessibility_requirements_do_not_know accessibility_requirements_prefer_not_to_say condition_effects_vision condition_effects_hearing condition_effects_mobility condition_effects_dexterity condition_effects_stamina condition_effects_learning condition_effects_memory condition_effects_mental_health condition_effects_social_or_behavioral condition_effects_other condition_effects_prefer_not_to_say reasonable_preference_reason_homeless reasonable_preference_reason_unsatisfactory_housing reasonable_preference_reason_medical_grounds reasonable_preference_reason_avoid_hardship reasonable_preference_reason_do_not_know other_tenancy_type override_net_income_validation net_income_known] permitted = %i[status tenant_code age1 sex1 tenant_ethnic_group tenant_nationality previous_housing_situation armed_forces ecstat1 other_hhmemb relat2 age2 sex2 ecstat2 relat3 age3 sex3 ecstat3 relat4 age4 sex4 ecstat4 relat5 age5 sex5 ecstat5 relat6 age6 sex6 ecstat6 relat7 age7 person_7_gender ecstat7 relat8 age8 sex8 ecstat8 homelessness reason benefit_cap_spare_room_subsidy armed_forces_active armed_forces_injured armed_forces_partner medical_conditions pregnancy accessibility_requirements condition_effects tenancy_code tenancy_start_date starter_tenancy fixed_term_tenancy tenancy_type letting_type letting_provider la previous_postcode property_relet property_vacancy_reason property_reference property_unit_type property_building_type property_number_of_bedrooms property_void_date majorrepairs mrcdate property_number_of_times_relet property_wheelchair_accessible net_income net_income_frequency net_income_uc_proportion hb rent_frequency basic_rent service_charge personal_service_charge support_charge total_charge tshortfall time_lived_in_la time_on_la_waiting_list prevloc property_postcode reasonable_preference reasonable_preference_reason cbl_letting chr_letting cap_letting hbrentshortfall other_reason accessibility_requirements_fully_wheelchair_accessible_housing accessibility_requirements_wheelchair_access_to_essential_rooms accessibility_requirements_level_access_housing accessibility_requirements_other_disability_requirements accessibility_requirements_no_disability_requirements accessibility_requirements_do_not_know accessibility_requirements_prefer_not_to_say condition_effects_vision condition_effects_hearing condition_effects_mobility condition_effects_dexterity condition_effects_stamina condition_effects_learning condition_effects_memory condition_effects_mental_health condition_effects_social_or_behavioral condition_effects_other condition_effects_prefer_not_to_say reasonable_preference_reason_homeless reasonable_preference_reason_unsatisfactory_housing reasonable_preference_reason_medical_grounds reasonable_preference_reason_avoid_hardship reasonable_preference_reason_do_not_know other_tenancy_type override_net_income_validation net_income_known]
permitted permitted
end end

455
app/constants/db_enums.rb

@ -20,7 +20,7 @@ module DbEnums
"Not seeking work" => 6, "Not seeking work" => 6,
"Full-time student" => 7, "Full-time student" => 7,
"Unable to work because of long term sick or disability" => 8, "Unable to work because of long term sick or disability" => 8,
"Child under 16" => 100, "Child under 16" => 9,
"Other" => 0, "Other" => 0,
"Prefer not to say" => 10, "Prefer not to say" => 10,
} }
@ -63,7 +63,6 @@ module DbEnums
"Yes" => 1, "Yes" => 1,
"No" => 2, "No" => 2,
"Do not know" => 3, "Do not know" => 3,
"Prefer not to say" => 100,
} }
end end
@ -91,6 +90,8 @@ module DbEnums
"Bulgaria" => 14, "Bulgaria" => 14,
"Romania" => 15, "Romania" => 15,
"Ireland" => 17, "Ireland" => 17,
"Slovenia" => 10,
"Croatia" => 16,
"Other EU Economic Area (EEA country)" => 11, "Other EU Economic Area (EEA country)" => 11,
"Any other country" => 12, "Any other country" => 12,
"Prefer not to say" => 13, "Prefer not to say" => 13,
@ -155,6 +156,14 @@ module DbEnums
} }
end end
def self.polar_with_unknown
{
"No" => 2,
"Yes" => 1,
"Do not know" => 3,
}
end
def self.tenancy def self.tenancy
{ {
"Fixed term – Secure" => 1, "Fixed term – Secure" => 1,
@ -213,6 +222,13 @@ module DbEnums
} }
end end
def self.override_soft_validation
{
"No" => 0,
"Yes" => 1,
}
end
def self.benefits def self.benefits
{ {
"All" => 1, "All" => 1,
@ -249,4 +265,439 @@ module DbEnums
"Do not know" => 6, "Do not know" => 6,
} }
end end
def self.housing_benefit
{
"Housing Benefit, but not Universal Credit" => 1,
"Universal Credit with housing element, but not Housing Benefit" => 6,
"Universal Credit without housing element and no Housing Benefit" => 7,
"Universal Credit and Housing Benefit" => 8,
"Not Housing Benefit or Universal Credit" => 9,
"Do not know" => 3,
"Prefer not to say" => 100,
}
end
def self.reason
{
"Permanently decanted from another property owned by this landlord" => 1,
"Left home country as a refugee" => 2,
"Loss of tied accommodation" => 4,
"Domestic abuse" => 7,
"(Non violent) relationship breakdown with partner" => 8,
"Asked to leave by family or friends" => 9,
"Racial harassment" => 10,
"Other problems with neighbours" => 11,
"Property unsuitable because of overcrowding" => 12,
"End of assured shorthold tenancy - no fault" => 40,
"End of assured shorthold tenancy - tenant's fault" => 41,
"End of fixed term tenancy - no fault" => 42,
"End of fixed term tenancy - tenant's fault" => 43,
"Repossession" => 34,
"Under occupation - offered incentive to downsize" => 29,
"Under occupation - no incentive" => 30,
"Property unsuitable because of ill health / disability" => 13,
"Property unsuitable because of poor condition" => 14,
"Couldn't afford fees attached to renewing the tenancy" => 35,
"Couldn't afford increase in rent" => 36,
"Couldn't afford rent or mortgage - welfare reforms" => 37,
"Couldn't afford rent or mortgage - employment" => 38,
"Couldn't afford rent or mortgage - other" => 39,
"To move nearer to family / friends / school" => 16,
"To move nearer to work" => 17,
"To move to accomodation with support" => 18,
"To move to independent accomodation" => 19,
"Hate crime" => 31,
"Death of household member in last settled accomodation" => 46,
"Discharged from prison" => 44,
"Discharged from long stay hospital or similar institution" => 45,
"Other" => 20,
"Do not know" => 28,
"Prefer not to say" => 100,
}
end
def self.la
{
"Hartlepool" => "E06000001",
"Na h-Eileanan Siar" => "S12000013",
"Middlesbrough" => "E06000002",
"Redcar and Cleveland" => "E06000003",
"Stockton-on-Tees" => "E06000004",
"Darlington" => "E06000005",
"Halton" => "E06000006",
"Warrington" => "E06000007",
"Blackburn with Darwen" => "E06000008",
"Blackpool" => "E06000009",
"Kingston upon Hull, City of" => "E06000010",
"East Riding of Yorkshire" => "E06000011",
"North East Lincolnshire" => "E06000012",
"North Lincolnshire" => "E06000013",
"York" => "E06000014",
"Derby" => "E06000015",
"Leicester" => "E06000016",
"Rutland" => "E06000017",
"Nottingham" => "E06000018",
"Herefordshire, County of" => "E06000019",
"Telford and Wrekin" => "E06000020",
"Stoke-on-Trent" => "E06000021",
"Bath and North East Somerset" => "E06000022",
"Bristol, City of" => "E06000023",
"North Somerset" => "E06000024",
"South Gloucestershire" => "E06000025",
"Plymouth" => "E06000026",
"Torbay" => "E06000027",
"Swindon" => "E06000030",
"Peterborough" => "E06000031",
"Luton" => "E06000032",
"Southend-on-Sea" => "E06000033",
"Thurrock" => "E06000034",
"Medway" => "E06000035",
"Bracknell Forest" => "E06000036",
"West Berkshire" => "E06000037",
"Reading" => "E06000038",
"Slough" => "E06000039",
"Windsor and Maidenhead" => "E06000040",
"Wokingham" => "E06000041",
"Milton Keynes" => "E06000042",
"Brighton and Hove" => "E06000043",
"Portsmouth" => "E06000044",
"Southampton" => "E06000045",
"Isle of Wight" => "E06000046",
"County Durham" => "E06000047",
"Cheshire East" => "E06000049",
"Cheshire West and Chester" => "E06000050",
"Shropshire" => "E06000051",
"Cornwall" => "E06000052",
"Isles of Scilly" => "E06000053",
"Wiltshire" => "E06000054",
"Bedford" => "E06000055",
"Central Bedfordshire" => "E06000056",
"Northumberland" => "E06000057",
"Bournemouth, Christchurch and Poole" => "E06000058",
"North Warwickshire" => "E07000218",
"Nuneaton and Bedworth" => "E07000219",
"Rugby" => "E07000220",
"Stratford-on-Avon" => "E07000221",
"Warwick" => "E07000222",
"Adur" => "E07000223",
"Arun" => "E07000224",
"Chichester" => "E07000225",
"Crawley" => "E07000226",
"Horsham" => "E07000227",
"Mid Sussex" => "E07000228",
"Worthing" => "E07000229",
"Bromsgrove" => "E07000234",
"Malvern Hills" => "E07000235",
"Redditch" => "E07000236",
"Worcester" => "E07000237",
"Wychavon" => "E07000238",
"Wyre Forest" => "E07000239",
"St Albans" => "E07000240",
"Welwyn Hatfield" => "E07000241",
"East Hertfordshire" => "E07000242",
"Stevenage" => "E07000243",
"East Suffolk" => "E07000244",
"West Suffolk" => "E07000245",
"Somerset West and Taunton" => "E07000246",
"Bolton" => "E08000001",
"Bury" => "E08000002",
"Manchester" => "E08000003",
"Oldham" => "E08000004",
"Rochdale" => "E08000005",
"Salford" => "E08000006",
"Stockport" => "E08000007",
"Tameside" => "E08000008",
"Trafford" => "E08000009",
"Wigan" => "E08000010",
"Knowsley" => "E08000011",
"Liverpool" => "E08000012",
"St. Helens" => "E08000013",
"Sefton" => "E08000014",
"Wirral" => "E08000015",
"Barnsley" => "E08000016",
"Doncaster" => "E08000017",
"Rotherham" => "E08000018",
"Sheffield" => "E08000019",
"Newcastle upon Tyne" => "E08000021",
"North Tyneside" => "E08000022",
"South Tyneside" => "E08000023",
"Sunderland" => "E08000024",
"Birmingham" => "E08000025",
"Coventry" => "E08000026",
"Dudley" => "E08000027",
"Sandwell" => "E08000028",
"Solihull" => "E08000029",
"Walsall" => "E08000030",
"Dorset" => "E06000059",
"Wolverhampton" => "E08000031",
"Falkirk" => "S12000014",
"Highland" => "S12000017",
"Inverclyde" => "S12000018",
"Midlothian" => "S12000019",
"Moray" => "S12000020",
"North Ayrshire" => "S12000021",
"Orkney Islands" => "S12000023",
"Scottish Borders" => "S12000026",
"Shetland Islands" => "S12000027",
"South Ayrshire" => "S12000028",
"South Lanarkshire" => "S12000029",
"Stirling" => "S12000030",
"Aberdeen City" => "S12000033",
"Aberdeenshire" => "S12000034",
"Argyll and Bute" => "S12000035",
"City of Edinburgh" => "S12000036",
"Renfrewshire" => "S12000038",
"West Dunbartonshire" => "S12000039",
"West Lothian" => "S12000040",
"Angus" => "S12000041",
"Dundee City" => "S12000042",
"East Dunbartonshire" => "S12000045",
"Buckinghamshire" => "E06000060",
"Fife" => "S12000047",
"Cambridge" => "E07000008",
"Perth and Kinross" => "S12000048",
"East Cambridgeshire" => "E07000009",
"Glasgow City" => "S12000049",
"Fenland" => "E07000010",
"North Lanarkshire" => "S12000050",
"Huntingdonshire" => "E07000011",
"Isle of Anglesey" => "W06000001",
"South Cambridgeshire" => "E07000012",
"Gwynedd" => "W06000002",
"Allerdale" => "E07000026",
"Conwy" => "W06000003",
"Barrow-in-Furness" => "E07000027",
"Denbighshire" => "W06000004",
"Carlisle" => "E07000028",
"Flintshire" => "W06000005",
"Copeland" => "E07000029",
"Wrexham" => "W06000006",
"Eden" => "E07000030",
"Ceredigion" => "W06000008",
"South Lakeland" => "E07000031",
"Pembrokeshire" => "W06000009",
"Amber Valley" => "E07000032",
"Carmarthenshire" => "W06000010",
"Bolsover" => "E07000033",
"Swansea" => "W06000011",
"Chesterfield" => "E07000034",
"Neath Port Talbot" => "W06000012",
"Derbyshire Dales" => "E07000035",
"Bridgend" => "W06000013",
"Erewash" => "E07000036",
"Vale of Glamorgan" => "W06000014",
"High Peak" => "E07000037",
"Cardiff" => "W06000015",
"North East Derbyshire" => "E07000038",
"Rhondda Cynon Taf" => "W06000016",
"South Derbyshire" => "E07000039",
"Caerphilly" => "W06000018",
"East Devon" => "E07000040",
"Blaenau Gwent" => "W06000019",
"Exeter" => "E07000041",
"Torfaen" => "W06000020",
"Mid Devon" => "E07000042",
"Monmouthshire" => "W06000021",
"North Devon" => "E07000043",
"Newport" => "W06000022",
"South Hams" => "E07000044",
"Powys" => "W06000023",
"Teignbridge" => "E07000045",
"Merthyr Tydfil" => "W06000024",
"Torridge" => "E07000046",
"West Devon" => "E07000047",
"Eastbourne" => "E07000061",
"Hastings" => "E07000062",
"Lewes" => "E07000063",
"Rother" => "E07000064",
"Wealden" => "E07000065",
"Basildon" => "E07000066",
"Braintree" => "E07000067",
"Brentwood" => "E07000068",
"Castle Point" => "E07000069",
"Chelmsford" => "E07000070",
"Colchester" => "E07000071",
"Epping Forest" => "E07000072",
"Harlow" => "E07000073",
"Maldon" => "E07000074",
"Rochford" => "E07000075",
"Tendring" => "E07000076",
"Uttlesford" => "E07000077",
"Cheltenham" => "E07000078",
"Cotswold" => "E07000079",
"Forest of Dean" => "E07000080",
"Gloucester" => "E07000081",
"Stroud" => "E07000082",
"Tewkesbury" => "E07000083",
"Basingstoke and Deane" => "E07000084",
"East Hampshire" => "E07000085",
"King’s Lynn and West Norfolk" => "E07000146",
"Eastleigh" => "E07000086",
"North Norfolk" => "E07000147",
"Norwich" => "E07000148",
"South Norfolk" => "E07000149",
"Corby" => "E07000150",
"Daventry" => "E07000151",
"East Northamptonshire" => "E07000152",
"Kettering" => "E07000153",
"Northampton" => "E07000154",
"South Northamptonshire" => "E07000155",
"Wellingborough" => "E07000156",
"Craven" => "E07000163",
"Hambleton" => "E07000164",
"Harrogate" => "E07000165",
"Richmondshire" => "E07000166",
"Ryedale" => "E07000167",
"Scarborough" => "E07000168",
"Selby" => "E07000169",
"Ashfield" => "E07000170",
"Bassetlaw" => "E07000171",
"Broxtowe" => "E07000172",
"Gedling" => "E07000173",
"Mansfield" => "E07000174",
"Newark and Sherwood" => "E07000175",
"Rushcliffe" => "E07000176",
"Cherwell" => "E07000177",
"Oxford" => "E07000178",
"South Oxfordshire" => "E07000179",
"Vale of White Horse" => "E07000180",
"West Oxfordshire" => "E07000181",
"Mendip" => "E07000187",
"Sedgemoor" => "E07000188",
"South Somerset" => "E07000189",
"Cannock Chase" => "E07000192",
"East Staffordshire" => "E07000193",
"Lichfield" => "E07000194",
"Newcastle-under-Lyme" => "E07000195",
"South Staffordshire" => "E07000196",
"Stafford" => "E07000197",
"Staffordshire Moorlands" => "E07000198",
"Tamworth" => "E07000199",
"Babergh" => "E07000200",
"Ipswich" => "E07000202",
"Mid Suffolk" => "E07000203",
"Elmbridge" => "E07000207",
"Epsom and Ewell" => "E07000208",
"Guildford" => "E07000209",
"Mole Valley" => "E07000210",
"Reigate and Banstead" => "E07000211",
"Runnymede" => "E07000212",
"Spelthorne" => "E07000213",
"Surrey Heath" => "E07000214",
"Tandridge" => "E07000215",
"Waverley" => "E07000216",
"Woking" => "E07000217",
"Fareham" => "E07000087",
"Gosport" => "E07000088",
"Hart" => "E07000089",
"Havant" => "E07000090",
"New Forest" => "E07000091",
"Rushmoor" => "E07000092",
"Test Valley" => "E07000093",
"Winchester" => "E07000094",
"Broxbourne" => "E07000095",
"Dacorum" => "E07000096",
"Hertsmere" => "E07000098",
"North Hertfordshire" => "E07000099",
"Three Rivers" => "E07000102",
"Watford" => "E07000103",
"Ashford" => "E07000105",
"Canterbury" => "E07000106",
"Dartford" => "E07000107",
"Dover" => "E07000108",
"Gravesham" => "E07000109",
"Maidstone" => "E07000110",
"Sevenoaks" => "E07000111",
"Folkestone and Hythe" => "E07000112",
"Swale" => "E07000113",
"Thanet" => "E07000114",
"Tonbridge and Malling" => "E07000115",
"Tunbridge Wells" => "E07000116",
"Burnley" => "E07000117",
"Chorley" => "E07000118",
"Fylde" => "E07000119",
"Hyndburn" => "E07000120",
"Lancaster" => "E07000121",
"Pendle" => "E07000122",
"Preston" => "E07000123",
"Ribble Valley" => "E07000124",
"Rossendale" => "E07000125",
"South Ribble" => "E07000126",
"West Lancashire" => "E07000127",
"Wyre" => "E07000128",
"Blaby" => "E07000129",
"Charnwood" => "E07000130",
"Harborough" => "E07000131",
"Hinckley and Bosworth" => "E07000132",
"Melton" => "E07000133",
"North West Leicestershire" => "E07000134",
"Oadby and Wigston" => "E07000135",
"Boston" => "E07000136",
"East Lindsey" => "E07000137",
"Lincoln" => "E07000138",
"North Kesteven" => "E07000139",
"South Holland" => "E07000140",
"South Kesteven" => "E07000141",
"West Lindsey" => "E07000142",
"Breckland" => "E07000143",
"Broadland" => "E07000144",
"Great Yarmouth" => "E07000145",
"Bradford" => "E08000032",
"Calderdale" => "E08000033",
"Kirklees" => "E08000034",
"Leeds" => "E08000035",
"Wakefield" => "E08000036",
"Gateshead" => "E08000037",
"City of London" => "E09000001",
"Barking and Dagenham" => "E09000002",
"Barnet" => "E09000003",
"Bexley" => "E09000004",
"Brent" => "E09000005",
"Bromley" => "E09000006",
"Camden" => "E09000007",
"Croydon" => "E09000008",
"Ealing" => "E09000009",
"Enfield" => "E09000010",
"Greenwich" => "E09000011",
"Hackney" => "E09000012",
"Hammersmith and Fulham" => "E09000013",
"Haringey" => "E09000014",
"Harrow" => "E09000015",
"Havering" => "E09000016",
"Hillingdon" => "E09000017",
"Hounslow" => "E09000018",
"Islington" => "E09000019",
"Kensington and Chelsea" => "E09000020",
"Kingston upon Thames" => "E09000021",
"Lambeth" => "E09000022",
"Lewisham" => "E09000023",
"Merton" => "E09000024",
"Newham" => "E09000025",
"Redbridge" => "E09000026",
"Richmond upon Thames" => "E09000027",
"Southwark" => "E09000028",
"Sutton" => "E09000029",
"Tower Hamlets" => "E09000030",
"Waltham Forest" => "E09000031",
"Wandsworth" => "E09000032",
"Westminster" => "E09000033",
"Antrim and Newtownabbey" => "N09000001",
"Armagh City, Banbridge and Craigavon" => "N09000002",
"Belfast" => "N09000003",
"Causeway Coast and Glens" => "N09000004",
"Derry City and Strabane" => "N09000005",
"Fermanagh and Omagh" => "N09000006",
"Lisburn and Castlereagh" => "N09000007",
"Mid and East Antrim" => "N09000008",
"Mid Ulster" => "N09000009",
"Newry, Mourne and Down" => "N09000010",
"Ards and North Down" => "N09000011",
"Clackmannanshire" => "S12000005",
"Dumfries and Galloway" => "S12000006",
"East Ayrshire" => "S12000008",
"East Lothian" => "S12000010",
"East Renfrewshire" => "S12000011",
}
end
end end

6
app/controllers/case_logs_controller.rb

@ -105,6 +105,12 @@ private
form = FormHandler.instance.get_form("2021_2022") form = FormHandler.instance.get_form("2021_2022")
form.expected_responses_for_page(page).each_with_object({}) do |(question_key, question_info), result| form.expected_responses_for_page(page).each_with_object({}) do |(question_key, question_info), result|
question_params = params["case_log"][question_key] question_params = params["case_log"][question_key]
if question_info["type"] == "date"
day = params["case_log"]["#{question_key}(3i)"]
month = params["case_log"]["#{question_key}(2i)"]
year = params["case_log"]["#{question_key}(1i)"]
result[question_key] = Date.new(year.to_i, month.to_i, day.to_i)
end
next unless question_params next unless question_params
if %w[checkbox validation_override].include?(question_info["type"]) if %w[checkbox validation_override].include?(question_info["type"])

5
app/controllers/users/passwords_controller.rb

@ -1,5 +1,4 @@
class Users::PasswordsController < Devise::PasswordsController class Users::PasswordsController < Devise::PasswordsController
def reset_confirmation def reset_confirmation
@email = params["email"] @email = params["email"]
render "devise/confirmations/reset" render "devise/confirmations/reset"
@ -12,9 +11,9 @@ class Users::PasswordsController < Devise::PasswordsController
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
end end
protected protected
def after_sending_reset_password_instructions_path_for(resource) def after_sending_reset_password_instructions_path_for(_resource)
confirmations_reset_path(email: params.dig("user", "email")) if is_navigational_format? confirmations_reset_path(email: params.dig("user", "email")) if is_navigational_format?
end end
end end

14
app/javascript/controllers/soft_validations_controller.js

@ -5,8 +5,13 @@ export default class extends Controller {
initialize() { initialize() {
let url = window.location.href + "/soft_validations" let url = window.location.href + "/soft_validations"
this.fetch_retry(url, { headers: { accept: "application/json" } }, 2)
}
fetch_retry(url, options, n) {
let self = this
let div = this.overrideTarget let div = this.overrideTarget
fetch(url, { headers: { accept: "application/json" } }) fetch(url, options)
.then(response => response.json()) .then(response => response.json())
.then((response) => { .then((response) => {
if(response["show"]){ if(response["show"]){
@ -22,7 +27,10 @@ export default class extends Controller {
button.checked = false button.checked = false
}) })
} }
} })
) .catch(function(error) {
if (n === 1) throw error
return self.fetch_retry(url, options, n - 1)
})
} }
} }

70
app/models/case_log.rb

@ -65,6 +65,7 @@ class CaseLog < ApplicationRecord
enum leftreg: DbEnums.leftreg, _suffix: true enum leftreg: DbEnums.leftreg, _suffix: true
enum illness: DbEnums.illness, _suffix: true enum illness: DbEnums.illness, _suffix: true
enum preg_occ: DbEnums.pregnancy, _suffix: true enum preg_occ: DbEnums.pregnancy, _suffix: true
enum override_net_income_validation: DbEnums.override_soft_validation, _suffix: true
enum housingneeds_a: DbEnums.polar, _suffix: true enum housingneeds_a: DbEnums.polar, _suffix: true
enum housingneeds_b: DbEnums.polar, _suffix: true enum housingneeds_b: DbEnums.polar, _suffix: true
enum housingneeds_c: DbEnums.polar, _suffix: true enum housingneeds_c: DbEnums.polar, _suffix: true
@ -100,7 +101,14 @@ class CaseLog < ApplicationRecord
enum period: DbEnums.period, _suffix: true enum period: DbEnums.period, _suffix: true
enum layear: DbEnums.latime, _suffix: true enum layear: DbEnums.latime, _suffix: true
enum lawaitlist: DbEnums.latime, _suffix: true enum lawaitlist: DbEnums.latime, _suffix: true
enum reasonpref: DbEnums.polar2, _suffix: true enum reasonpref: DbEnums.polar_with_unknown, _suffix: true
enum reason: DbEnums.reason, _suffix: true
enum la: DbEnums.la, _suffix: true
enum prevloc: DbEnums.la, _suffix: true
enum majorrepairs: DbEnums.polar, _suffix: true
enum hb: DbEnums.housing_benefit, _suffix: true
enum hbrentshortfall: DbEnums.polar_with_unknown, _suffix: true
enum property_relet: DbEnums.polar, _suffix: true
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
@ -131,12 +139,64 @@ class CaseLog < ApplicationRecord
end end
end end
def postcode
if property_postcode.present?
UKPostcode.parse(property_postcode).outcode
end
end
def postcod2
if property_postcode.present?
UKPostcode.parse(property_postcode).incode
end
end
def ppostc1
if previous_postcode.present?
UKPostcode.parse(previous_postcode).outcode
end
end
def ppostc2
if previous_postcode.present?
UKPostcode.parse(previous_postcode).incode
end
end
def hhmemb
other_hhmemb.presence
end
def applicable_income_range def applicable_income_range
return unless ecstat1 return unless ecstat1
IncomeRange::ALLOWED[ecstat1.to_sym] IncomeRange::ALLOWED[ecstat1.to_sym]
end end
def mrcday
if mrcdate.present?
mrcdate.day
end
end
def mrcmonth
if mrcdate.present?
mrcdate.month
end
end
def mrcyear
if mrcdate.present?
mrcdate.year
end
end
def incref
if net_income_known == "Prefer not to say"
1
end
end
private private
def update_status! def update_status!
@ -162,7 +222,7 @@ private
dynamically_not_required = [] dynamically_not_required = []
if reason_for_leaving_last_settled_home != "Other" if reason != "Other"
dynamically_not_required << "other_reason_for_leaving_last_settled_home" dynamically_not_required << "other_reason_for_leaving_last_settled_home"
end end
@ -187,7 +247,7 @@ private
dynamically_not_required << "incfreq" dynamically_not_required << "incfreq"
end end
start_range = (hhmemb || 0) + 2 start_range = (other_hhmemb || 0) + 2
(start_range..8).each do |n| (start_range..8).each do |n|
dynamically_not_required << "age#{n}" dynamically_not_required << "age#{n}"
dynamically_not_required << "sex#{n}" dynamically_not_required << "sex#{n}"
@ -195,6 +255,10 @@ private
dynamically_not_required << "ecstat#{n}" dynamically_not_required << "ecstat#{n}"
end end
if net_income_known != "Prefer not to say"
dynamically_not_required << "incref"
end
required.delete_if { |key, _value| dynamically_not_required.include?(key) } required.delete_if { |key, _value| dynamically_not_required.include?(key) }
end end
end end

8
app/validations/financial_validations.rb

@ -2,11 +2,11 @@ module FinancialValidations
# Validations methods need to be called 'validate_<page_name>' to run on model save # Validations methods need to be called 'validate_<page_name>' to run on model save
# or 'validate_' to run on submit as well # or 'validate_' to run on submit as well
def validate_outstanding_rent_amount(record) def validate_outstanding_rent_amount(record)
if record.outstanding_rent_or_charges == "Yes" && record.outstanding_amount.blank? if record.hbrentshortfall == "Yes" && record.tshortfall.blank?
record.errors.add :outstanding_amount, "You must answer the oustanding amout question if you have outstanding rent or charges." record.errors.add :tshortfall, "You must answer the oustanding amout question if you have outstanding rent or charges."
end end
if record.outstanding_rent_or_charges == "No" && record.outstanding_amount.present? if record.hbrentshortfall == "No" && record.tshortfall.present?
record.errors.add :outstanding_amount, "You must not answer the oustanding amout question if you don't have outstanding rent or charges." record.errors.add :tshortfall, "You must not answer the oustanding amout question if you don't have outstanding rent or charges."
end end
end end

6
app/validations/household_validations.rb

@ -16,11 +16,11 @@ module HouseholdValidations
end end
def validate_other_reason_for_leaving_last_settled_home(record) def validate_other_reason_for_leaving_last_settled_home(record)
validate_other_field(record, "reason_for_leaving_last_settled_home", "other_reason_for_leaving_last_settled_home") validate_other_field(record, "reason", "other_reason_for_leaving_last_settled_home")
end end
def validate_reason_for_leaving_last_settled_home(record) def validate_reason_for_leaving_last_settled_home(record)
if record.reason_for_leaving_last_settled_home == "Do not know" && record.underoccupation_benefitcap != "Do not know" if record.reason == "Do not know" && record.underoccupation_benefitcap != "Do not know"
record.errors.add :underoccupation_benefitcap, "must be do not know if tenant’s main reason for leaving is do not know" record.errors.add :underoccupation_benefitcap, "must be do not know if tenant’s main reason for leaving is do not know"
end end
end end
@ -80,7 +80,7 @@ module HouseholdValidations
record.errors.add :unittype_gn, "A bedsit can only have one bedroom" record.errors.add :unittype_gn, "A bedsit can only have one bedroom"
end end
if !record.hhmemb.nil? && record.hhmemb.positive? && (record.unittype_gn.include?("Shared") && !record.beds.to_i.between?(1, 7)) if !record.other_hhmemb.nil? && record.other_hhmemb.positive? && (record.unittype_gn.include?("Shared") && !record.beds.to_i.between?(1, 7))
record.errors.add :unittype_gn, "A shared house must have 1 to 7 bedrooms" record.errors.add :unittype_gn, "A shared house must have 1 to 7 bedrooms"
end end

2
app/validations/soft_validations.rb

@ -8,7 +8,7 @@ module SoftValidations
end end
def soft_errors_overridden? def soft_errors_overridden?
!public_send(soft_errors.keys.first).zero? if soft_errors.present? public_send(soft_errors.keys.first) == "Yes" if soft_errors.present?
end end
private private

2
app/views/devise/mailer/reset_password_instructions.html.erb

@ -2,5 +2,7 @@
<p>Someone has requested a link to change your password. You can do this through the link below.</p> <p>Someone has requested a link to change your password. You can do this through the link below.</p>
<p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
<p>If you didn't request this, please ignore this email.</p> <p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p> <p>Your password won't change until you access the link above and create a new one.</p>

22
app/views/devise/passwords/edit.html.erb

@ -1,25 +1,19 @@
<h2>Change your password</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">Reset your password</h1>
<%= render "devise/shared/error_messages", resource: resource %> <%= render "devise/shared/error_messages", resource: resource %>
<%= f.hidden_field :reset_password_token %> <%= f.hidden_field :reset_password_token %>
<div class="field"> <div class="govuk-form-group">
<%= f.label :password, "New password" %><br /> <%= f.label :password, "New password", class: "govuk-label" %>
<% if @minimum_password_length %> <% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em><br /> <div class="govuk-hint">Your password must be at least 8 characters and hard to guess.</div>
<% end %> <% end %>
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %> <%= f.password_field :password, autofocus: true, autocomplete: "new-password", class: "govuk-input" %>
</div> </div>
<div class="field"> <%= f.submit "Reset password", class: "govuk-button" %>
<%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div> </div>
<div class="actions">
<%= f.submit "Change my password" %>
</div> </div>
<% end %> <% end %>
<%= render "devise/shared/links" %>

3
app/views/form/_validation_override_question.html.erb

@ -8,7 +8,8 @@
hint: { text: "soft-validations-placeholder-hint-text" } do %> hint: { text: "soft-validations-placeholder-hint-text" } do %>
<%= f.govuk_check_box page_info["soft_validations"]&.keys&.first, page_info["soft_validations"]&.keys&.first, <%= f.govuk_check_box page_info["soft_validations"]&.keys&.first, page_info["soft_validations"]&.keys&.first,
label: { text: "Yes" } label: { text: "Yes" },
checked: @case_log[page_info["soft_validations"]&.keys&.first] == "Yes"
%> %>
<% end %> <% end %>
</div> </div>

14
config/environments/development.rb

@ -40,6 +40,20 @@ Rails.application.configure do
config.action_mailer.default_url_options = { host: "localhost", port: 3000 } config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
config.action_mailer.perform_deliveries = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.gmail.com",
port: 465,
domain: "gmail.com",
user_name: ENV["CORE_EMAIL_USERNAME"],
password: ENV["CORE_EMAIL_PASSWORD"],
authentication: "plain",
enable_starttls_auto: true,
ssl: true,
}
# Print deprecation notices to the Rails logger. # Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log config.active_support.deprecation = :log

2
config/environments/test.rb

@ -37,6 +37,8 @@ Rails.application.configure do
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
# Tell Action Mailer not to deliver emails to the real world. # Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the # The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array. # ActionMailer::Base.deliveries array.

94
config/forms/2021_2022.json

@ -104,7 +104,7 @@
"header": "About this log", "header": "About this log",
"description": "", "description": "",
"questions": { "questions": {
"tenancy_start_date": { "startdate": {
"check_answer_label": "When is the tenancy start date?", "check_answer_label": "When is the tenancy start date?",
"header": "What is the tenancy start date?", "header": "What is the tenancy start date?",
"hint_text": "For example, 27 3 2007", "hint_text": "For example, 27 3 2007",
@ -112,7 +112,7 @@
} }
} }
}, },
"letting_type": { "rent_type": {
"header": "About this log", "header": "About this log",
"description": "", "description": "",
"questions": { "questions": {
@ -273,21 +273,23 @@
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
"0": "UK national resident in UK", "0": "UK national resident in UK",
"1": "A current or former reserve in the UK Armed Forces (exc. National Service)", "1": "UK national returning from residence overseas",
"2": "UK national returning from residence overseas", "2": "Czech Republic",
"3": "Czech Republic", "3": "Estonia",
"4": "Estonia", "4": "Hungary",
"5": "Hungary", "5": "Latvia",
"6": "Latvia", "6": "Lithuania",
"7": "Lithuania", "7": "Poland",
"8": "Poland", "8": "Slovakia",
"9": "Slovakia", "9": "Bulgaria",
"10": "Bulgaria", "10": "Romania",
"11": "Romania", "11": "Ireland",
"12": "Ireland", "12": "Slovakia",
"13": "Other EU Economic Area (EEA country)", "13":"Slovenia",
"14": "Any other country", "14": "Croatia",
"15": "Prefer not to say" "15": "Other EU Economic Area (EEA country)",
"16": "Any other country",
"17": "Prefer not to say"
} }
} }
} }
@ -315,13 +317,22 @@
"10": "Prefer not to say" "10": "Prefer not to say"
} }
} }
},
"soft_validations": {
"override_net_income_validation": {
"check_answer_label": "Net income confirmed?",
"type": "validation_override",
"answer_options": {
"override_net_income_validation": "Yes"
}
}
} }
}, },
"household_number_of_other_members": { "household_number_of_other_members": {
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"hhmemb": { "other_hhmemb": {
"check_answer_label": "Number of Other Household Members", "check_answer_label": "Number of Other Household Members",
"header": "How many other people are there in the household?", "header": "How many other people are there in the household?",
"hint_text": "The maximum number of others is 7", "hint_text": "The maximum number of others is 7",
@ -787,7 +798,7 @@
"header": "Leaving their last settled home", "header": "Leaving their last settled home",
"description": "", "description": "",
"questions": { "questions": {
"reason_for_leaving_last_settled_home": { "reason": {
"header": "What is the tenant’s main reason for leaving?", "header": "What is the tenant’s main reason for leaving?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
@ -925,8 +936,7 @@
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No", "1": "No",
"2": "Do not know", "2": "Prefer not to say"
"3": "Prefer not to say"
} }
} }
} }
@ -990,9 +1000,7 @@
"illness_type_6": "Memory", "illness_type_6": "Memory",
"illness_type_7": "Mental health - such as depression, anxiety, schizophrenia or bipolar", "illness_type_7": "Mental health - such as depression, anxiety, schizophrenia or bipolar",
"illness_type_9": "Socially or behaviourally - such as those associated with autism spectral disorder (ASD) including Aspergers’ or attention deficit hyperactivity disorder (ADHD))", "illness_type_9": "Socially or behaviourally - such as those associated with autism spectral disorder (ASD) including Aspergers’ or attention deficit hyperactivity disorder (ADHD))",
"illness_type_10": "Other", "illness_type_10": "Other"
"divider": true,
"condition_effects_prefer_not_to_say": "Prefer not to say"
} }
} }
} }
@ -1019,18 +1027,6 @@
} }
} }
}, },
"tenancy_start_date": {
"header": "",
"description": "",
"questions": {
"startdate": {
"check_answer_label": "When is the tenancy start date?",
"header": "What is the tenancy start date?",
"hint_text": "For example, 27 3 2007",
"type": "date"
}
}
},
"starter_tenancy": { "starter_tenancy": {
"header": "", "header": "",
"description": "", "description": "",
@ -1135,7 +1131,7 @@
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"property_location": { "la": {
"check_answer_label": "Property Location", "check_answer_label": "Property Location",
"header": "Property location", "header": "Property location",
"hint_text": "", "hint_text": "",
@ -1518,7 +1514,7 @@
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"property_reference": { "propcode": {
"check_answer_label": "What’s the property reference?", "check_answer_label": "What’s the property reference?",
"header": "What's the property reference?", "header": "What's the property reference?",
"hint_text": "", "hint_text": "",
@ -1567,7 +1563,7 @@
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"property_major_repairs": { "majorrepairs": {
"check_answer_label": "Were major repairs carried out during the void period?", "check_answer_label": "Were major repairs carried out during the void period?",
"header": "Were any major repairs completed during the void period?", "header": "Were any major repairs completed during the void period?",
"hint_text": "", "hint_text": "",
@ -1577,10 +1573,10 @@
"1": "No" "1": "No"
}, },
"conditional_for": { "conditional_for": {
"property_major_repairs_date": ["Yes"] "mrcdate": ["Yes"]
} }
}, },
"property_major_repairs_date": { "mrcdate": {
"check_answer_label": "What was the major repairs completion date?", "check_answer_label": "What was the major repairs completion date?",
"header": "What was the major repairs completion date?", "header": "What was the major repairs completion date?",
"hint_text": "For example, 27 3 2007", "hint_text": "For example, 27 3 2007",
@ -1700,7 +1696,7 @@
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"housing_benefit": { "hb": {
"check_answer_label": "Universal Credit & Housing Benefit", "check_answer_label": "Universal Credit & Housing Benefit",
"header": "Is the tenant likely to be in receipt of any of these housing-related benefits?", "header": "Is the tenant likely to be in receipt of any of these housing-related benefits?",
"hint_text": "", "hint_text": "",
@ -1813,20 +1809,21 @@
"step": 1, "step": 1,
"readonly": true "readonly": true
}, },
"outstanding_rent_or_charges": { "hbrentshortfall": {
"check_answer_label": "After housing benefit and/or housing element of UC payment is received, will there be an outstanding amount for basic rent and/or benefit eligible charges?", "check_answer_label": "After housing benefit and/or housing element of UC payment is received, will there be an outstanding amount for basic rent and/or benefit eligible charges?",
"header": "After housing benefit and/or housing element of UC payment is received, will there be an outstanding amount for basic rent and/or benefit eligible charges?", "header": "After housing benefit and/or housing element of UC payment is received, will there be an outstanding amount for basic rent and/or benefit eligible charges?",
"hint_text": "", "hint_text": "",
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No" "1": "No",
"2": "Do not know"
}, },
"conditional_for": { "conditional_for": {
"outstanding_amount": ["Yes"] "tshortfall": ["Yes"]
} }
}, },
"outstanding_amount": { "tshortfall": {
"check_answer_label": "Outstanding amount", "check_answer_label": "Outstanding amount",
"header": "What do you expect the amount to be?", "header": "What do you expect the amount to be?",
"hint_text": "If the amount is unknown you can estimate", "hint_text": "If the amount is unknown you can estimate",
@ -1894,7 +1891,7 @@
"header": "", "header": "",
"description": "", "description": "",
"questions": { "questions": {
"previous_la": { "prevloc": {
"check_answer_label": "The LA in which household lived immediately before this letting\t", "check_answer_label": "The LA in which household lived immediately before this letting\t",
"header": "Which local authority area did the household live in immediately before this letting?", "header": "Which local authority area did the household live in immediately before this letting?",
"hint_text": "Includes temporary accommodation", "hint_text": "Includes temporary accommodation",
@ -2240,7 +2237,8 @@
"type": "radio", "type": "radio",
"answer_options": { "answer_options": {
"0": "Yes", "0": "Yes",
"1": "No" "1": "No",
"2": "Do not know"
}, },
"conditional_for": { "conditional_for": {
"reasonable_preference_reason": ["Yes"] "reasonable_preference_reason": ["Yes"]

112
config/forms/schema/2021_2022.json

@ -0,0 +1,112 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$id": "https://example.com/product.schema.json",
"title": "Form",
"description": "A form",
"type": "object",
"required": ["form_type", "start_year", "end_year", "sections"],
"properties": {
"form_type": {
"description": "",
"type": "string"
},
"start_year": {
"description": "",
"type": "integer"
},
"end_year": {
"description": "",
"type": "integer"
},
"sections": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "",
"type": "object",
"properties": {
"label": {
"description": "",
"type": "string"
},
"subsections": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "",
"type": "object",
"required": ["label"],
"properties": {
"label": {
"description": "",
"type": "string"
},
"pages": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "",
"type": "object",
"properties": {
"header": {
"description": "",
"type": "string"
},
"description": {
"description": "",
"type": "string"
},
"questions": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "",
"type": "object",
"required": ["header", "check_answer_label"],
"properties": {
"check_answer_label": {
"description": "",
"type": "string"
},
"header": {
"description": "",
"type": "string"
},
"type": {
"description": "",
"type": "string"
},
"hint_text": {
"description": "",
"type": "string"
},
"answer_options": {
"description": "",
"type": "object"
},
"conditional_for": {
"description": "",
"type": "object"
}
}
}
}
}
},
"minProperties": 1
}
}
}
},
"minProperties": 1
}
}
}
},
"minProperties": 2
}
},
"minProperties": 1
}
}
}

129
config/forms/schema/generic.json

@ -0,0 +1,129 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$id": "https://example.com/product.schema.json",
"title": "Form",
"description": "A form",
"type": "object",
"required": ["form_type", "start_year", "end_year", "sections"],
"properties": {
"form_type": {
"description": "",
"type": "string"
},
"start_year": {
"description": "",
"type": "integer"
},
"end_year": {
"description": "",
"type": "integer"
},
"sections": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "Section Name",
"type": "object",
"properties": {
"label": {
"description": "",
"type": "string"
},
"subsections": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "SubSection Name",
"type": "object",
"required": ["label"],
"properties": {
"label": {
"description": "",
"type": "string"
},
"pages": {
"type": "object",
"patternProperties": {
"^(?!(conditional_route_to))[a-z_]+$": {
"description": "Page Name",
"type": "object",
"required": ["header", "questions"],
"properties": {
"header": {
"description": "",
"type": "string"
},
"description": {
"description": "",
"type": "string"
},
"questions": {
"type": "object",
"patternProperties": {
"[a-z_]+": {
"description": "Question Name",
"type": "object",
"required": ["header", "type"],
"properties": {
"header": {
"description": "",
"type": "string"
},
"type": {
"description": "",
"type": "string"
},
"check_answer_label": {
"description": "",
"type": "string",
"optional": "true"
}
},
"additionalProperties": {
"hint_text": {
"optional": "true",
"description": "",
"type": "string"
},
"answer_options": {
"optional": "true",
"description": "",
"type": "object"
},
"check_answer_label": {
"description": "",
"type": "string"
},
"conditional_for": {
"description": "",
"type": "object"
}
},
"minProperties": 1
}
}
}
},
"additionalProperties": {
"conditional_route_to": {
"description": "",
"type": "object"
}
},
"minProperties": 1
}
}
}
},
"minProperties": 1
}
}
}
},
"minProperties": 2
}
},
"minProperties": 1
}
}
}

6
config/initializers/devise.rb

@ -24,7 +24,7 @@ Devise.setup do |config|
# Configure the e-mail address which will be shown in Devise::Mailer, # Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class # note that it will be overwritten if you use your own mailer class
# with default "from" parameter. # with default "from" parameter.
config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" config.mailer_sender = ENV["CORE_EMAIL_USERNAME"]
# Configure the class responsible to send e-mails. # Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer' # config.mailer = 'Devise::Mailer'
@ -178,7 +178,7 @@ Devise.setup do |config|
# ==> Configuration for :validatable # ==> Configuration for :validatable
# Range for password length. # Range for password length.
config.password_length = 6..128 config.password_length = 8..128
# Email regex used to validate email formats. It simply asserts that # Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly # one (and only one) @ exists in the given string. This is mainly
@ -224,7 +224,7 @@ Devise.setup do |config|
# Time interval you can reset your password with a reset password key. # Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to # Don't put a too small interval or your users won't have the time to
# change their passwords. # change their passwords.
config.reset_password_within = 6.hours config.reset_password_within = 3.hours
# When set to false, does not sign a user in automatically after their password is # When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset. # reset. Defaults to true, so a user is signed in automatically after a reset.

15
db/migrate/20211108091320_change_checkbox_types.rb

@ -0,0 +1,15 @@
class ChangeCheckboxTypes < ActiveRecord::Migration[6.1]
def up
change_table :case_logs, bulk: true do |t|
t.change :accessibility_requirements_prefer_not_to_say, "integer USING accessibility_requirements_prefer_not_to_say::integer"
t.change :condition_effects_prefer_not_to_say, "integer USING condition_effects_prefer_not_to_say::integer"
end
end
def down
change_table :case_logs, bulk: true do |t|
t.change :accessibility_requirements_prefer_not_to_say, "boolean USING accessibility_requirements_prefer_not_to_say::boolean"
t.change :condition_effects_prefer_not_to_say, "boolean USING condition_effects_prefer_not_to_say::boolean"
end
end
end

57
db/migrate/20211108134601_further_core_migrations.rb

@ -0,0 +1,57 @@
class FurtherCoreMigrations < ActiveRecord::Migration[6.1]
def up
change_table :case_logs, bulk: true do |t|
t.remove :condition_effects_prefer_not_to_say
t.remove :reason_for_leaving_last_settled_home
t.column :reason, :integer
t.remove :property_reference
t.column :propcode, :string
t.remove :property_major_repairs
t.column :majorrepairs, :integer
t.remove :property_location
t.column :la, :string
t.remove :previous_la
t.column :prevloc, :string
t.remove :housing_benefit
t.column :hb, :integer
t.remove :outstanding_rent_or_charges
t.column :hbrentshortfall, :integer
t.remove :outstanding_amount
t.column :tshortfall, :integer
t.column :postcode, :string
t.column :postcod2, :string
t.column :ppostc1, :string
t.column :ppostc2, :string
t.remove :property_relet
t.column :property_relet, :integer
end
end
def down
change_table :case_logs, bulk: true do |t|
t.column :condition_effects_prefer_not_to_say, :integer
t.column :reason_for_leaving_last_settled_home, :string
t.remove :reason
t.column :property_reference, :string
t.remove :propcode
t.column :property_major_repairs, :string
t.remove :majorrepairs
t.column :property_location, :string
t.remove :la
t.column :previous_la, :string
t.remove :prevloc
t.column :housing_benefit, :string
t.remove :hb
t.column :outstanding_rent_or_charges, :string
t.remove :hbrentshortfall
t.column :outstanding_amount, :string
t.remove :tshortfall
t.remove :postcode
t.remove :postcod2
t.remove :ppostc1
t.remove :ppostc2
t.remove :property_relet
t.column :property_relet, :string
end
end
end

21
db/migrate/20211110140928_add_mrc_dates.rb

@ -0,0 +1,21 @@
class AddMrcDates < ActiveRecord::Migration[6.1]
def up
change_table :case_logs, bulk: true do |t|
t.remove :property_major_repairs_date
t.column :mrcdate, :datetime
t.column :mrcday, :integer
t.column :mrcmonth, :integer
t.column :mrcyear, :integer
end
end
def down
change_table :case_logs, bulk: true do |t|
t.column :property_major_repairs_date, :string
t.remove :mrcdate
t.remove :mrcday
t.remove :mrcmonth
t.remove :mrcyear
end
end
end

7
db/migrate/20211111143319_add_other_members_column.rb

@ -0,0 +1,7 @@
class AddOtherMembersColumn < ActiveRecord::Migration[6.1]
def change
change_table :case_logs, bulk: true do |t|
t.column :other_hhmemb, :integer
end
end
end

7
db/migrate/20211112105348_add_incref_field.rb

@ -0,0 +1,7 @@
class AddIncrefField < ActiveRecord::Migration[6.1]
def change
change_table :case_logs, bulk: true do |t|
t.column :incref, :integer
end
end
end

38
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_11_05_164644) do ActiveRecord::Schema.define(version: 2021_11_12_105348) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -57,7 +57,6 @@ ActiveRecord::Schema.define(version: 2021_11_05_164644) do
t.string "sex8" t.string "sex8"
t.integer "ecstat8" t.integer "ecstat8"
t.integer "homeless" t.integer "homeless"
t.string "reason_for_leaving_last_settled_home"
t.integer "underoccupation_benefitcap" t.integer "underoccupation_benefitcap"
t.integer "leftreg" t.integer "leftreg"
t.integer "reservist" t.integer "reservist"
@ -73,40 +72,31 @@ ActiveRecord::Schema.define(version: 2021_11_05_164644) do
t.integer "tenancy" t.integer "tenancy"
t.string "lettype" t.string "lettype"
t.integer "landlord" t.integer "landlord"
t.string "property_location"
t.string "previous_postcode" t.string "previous_postcode"
t.string "property_relet"
t.integer "rsnvac" t.integer "rsnvac"
t.string "property_reference"
t.integer "unittype_gn" t.integer "unittype_gn"
t.string "property_building_type" t.string "property_building_type"
t.integer "beds" t.integer "beds"
t.string "property_void_date" t.string "property_void_date"
t.string "property_major_repairs"
t.string "property_major_repairs_date"
t.integer "offered" t.integer "offered"
t.integer "wchair" t.integer "wchair"
t.integer "earnings" t.integer "earnings"
t.integer "incfreq" t.integer "incfreq"
t.integer "benefits" t.integer "benefits"
t.string "housing_benefit"
t.integer "period" t.integer "period"
t.integer "brent" t.integer "brent"
t.integer "scharge" t.integer "scharge"
t.integer "pscharge" t.integer "pscharge"
t.integer "supcharg" t.integer "supcharg"
t.integer "tcharge" t.integer "tcharge"
t.string "outstanding_amount"
t.integer "layear" t.integer "layear"
t.integer "lawaitlist" t.integer "lawaitlist"
t.string "previous_la"
t.string "property_postcode" t.string "property_postcode"
t.integer "reasonpref" t.integer "reasonpref"
t.string "reasonable_preference_reason" t.string "reasonable_preference_reason"
t.integer "cbl" t.integer "cbl"
t.integer "chr" t.integer "chr"
t.integer "cap" t.integer "cap"
t.string "outstanding_rent_or_charges"
t.string "other_reason_for_leaving_last_settled_home" t.string "other_reason_for_leaving_last_settled_home"
t.integer "housingneeds_a" t.integer "housingneeds_a"
t.integer "housingneeds_b" t.integer "housingneeds_b"
@ -114,7 +104,7 @@ ActiveRecord::Schema.define(version: 2021_11_05_164644) do
t.integer "housingneeds_f" t.integer "housingneeds_f"
t.integer "housingneeds_g" t.integer "housingneeds_g"
t.integer "housingneeds_h" t.integer "housingneeds_h"
t.boolean "accessibility_requirements_prefer_not_to_say" t.integer "accessibility_requirements_prefer_not_to_say"
t.integer "illness_type_1" t.integer "illness_type_1"
t.integer "illness_type_2" t.integer "illness_type_2"
t.integer "illness_type_3" t.integer "illness_type_3"
@ -125,7 +115,6 @@ ActiveRecord::Schema.define(version: 2021_11_05_164644) do
t.integer "illness_type_7" t.integer "illness_type_7"
t.integer "illness_type_9" t.integer "illness_type_9"
t.integer "illness_type_10" t.integer "illness_type_10"
t.boolean "condition_effects_prefer_not_to_say"
t.integer "rp_homeless" t.integer "rp_homeless"
t.integer "rp_insan_unsat" t.integer "rp_insan_unsat"
t.integer "rp_medwel" t.integer "rp_medwel"
@ -133,8 +122,6 @@ ActiveRecord::Schema.define(version: 2021_11_05_164644) do
t.integer "rp_dontknow" t.integer "rp_dontknow"
t.datetime "discarded_at" t.datetime "discarded_at"
t.string "tenancyother" t.string "tenancyother"
t.integer "override_net_income_validation"
t.string "net_income_known"
t.string "gdpr_acceptance" t.string "gdpr_acceptance"
t.string "gdpr_declined" t.string "gdpr_declined"
t.string "property_owner_organisation" t.string "property_owner_organisation"
@ -146,6 +133,27 @@ ActiveRecord::Schema.define(version: 2021_11_05_164644) do
t.string "needs_type" t.string "needs_type"
t.string "sale_completion_date" t.string "sale_completion_date"
t.string "purchaser_code" t.string "purchaser_code"
t.integer "override_net_income_validation"
t.string "net_income_known"
t.integer "reason"
t.string "propcode"
t.integer "majorrepairs"
t.string "la"
t.string "prevloc"
t.integer "hb"
t.integer "hbrentshortfall"
t.integer "tshortfall"
t.string "postcode"
t.string "postcod2"
t.string "ppostc1"
t.string "ppostc2"
t.integer "property_relet"
t.datetime "mrcdate"
t.integer "mrcday"
t.integer "mrcmonth"
t.integer "mrcyear"
t.integer "other_hhmemb"
t.integer "incref"
t.index ["discarded_at"], name: "index_case_logs_on_discarded_at" t.index ["discarded_at"], name: "index_case_logs_on_discarded_at"
end end

54
docs/api/DLUHC-CORE-Data.v1.json

@ -263,7 +263,7 @@
"prevten": "Private sector tenancy", "prevten": "Private sector tenancy",
"armed_forces": "Yes - a regular", "armed_forces": "Yes - a regular",
"ecstat1": "Full-time - 30 hours or more", "ecstat1": "Full-time - 30 hours or more",
"hhmemb": 7, "other_hhmemb": 7,
"relat2": "Partner", "relat2": "Partner",
"age2": 32, "age2": 32,
"sex2": "Male", "sex2": "Male",
@ -293,7 +293,7 @@
"sex8": "Prefer not to say", "sex8": "Prefer not to say",
"ecstat8": "Child under 16", "ecstat8": "Child under 16",
"homeless": "No", "homeless": "No",
"reason_for_leaving_last_settled_home": "Other problems with neighbours", "reason": "Other problems with neighbours",
"underoccupation_benefitcap": "No", "underoccupation_benefitcap": "No",
"leftreg": "No", "leftreg": "No",
"reservist": "No", "reservist": "No",
@ -309,7 +309,7 @@
"tenancy": "Fixed term – Secure", "tenancy": "Fixed term – Secure",
"lettype": "Affordable Rent - General Needs", "lettype": "Affordable Rent - General Needs",
"landlord": "This landlord", "landlord": "This landlord",
"property_location": "Barnet", "la": "Barnet",
"previous_postcode": "NW1 5TY", "previous_postcode": "NW1 5TY",
"property_relet": "No", "property_relet": "No",
"rsnvac": "Relet - tenant abandoned property", "rsnvac": "Relet - tenant abandoned property",
@ -318,31 +318,31 @@
"property_building_type": "dummy", "property_building_type": "dummy",
"beds": 3, "beds": 3,
"property_void_date": "03/11/2019", "property_void_date": "03/11/2019",
"property_major_repairs": "Yes", "majorrepairs": "Yes",
"property_major_repairs_date": "05/05/2020", "mrcdate": "05/05/2020",
"offered": 2, "offered": 2,
"wchair": true, "wchair": true,
"earnings": 1000, "earnings": 1000,
"incfreq": "Monthly", "incfreq": "Monthly",
"benefits": "Some", "benefits": "Some",
"housing_benefit": "Universal Credit with housing element, but not Housing Benefit", "hb": "Universal Credit with housing element, but not Housing Benefit",
"period": "Weekly", "period": "Weekly",
"brent": 200, "brent": 200,
"scharge": 50, "scharge": 50,
"pscharge": 40, "pscharge": 40,
"supcharg": 35, "supcharg": 35,
"tcharge": 325, "tcharge": 325,
"outstanding_amount": "Yes", "tshortfall": "Yes",
"layear": "1 to 2 years", "layear": "1 to 2 years",
"lawaitlist": "Less than 1 year", "lawaitlist": "Less than 1 year",
"previous_la": "Ashford", "prevloc": "Ashford",
"property_postcode": "SE2 6RT", "property_postcode": "SE2 6RT",
"reasonpref": "Yes", "reasonpref": "Yes",
"reasonable_preference_reason": "dummy", "reasonable_preference_reason": "dummy",
"cbl": true, "cbl": true,
"chr": false, "chr": false,
"cap": false, "cap": false,
"outstanding_rent_or_charges": 25, "hbrentshortfall": "Yes",
"other_reason_for_leaving_last_settled_home": "Other reason", "other_reason_for_leaving_last_settled_home": "Other reason",
"housingneeds_a": true, "housingneeds_a": true,
"housingneeds_b": false, "housingneeds_b": false,
@ -459,7 +459,7 @@
"Prefer not to say" "Prefer not to say"
] ]
}, },
"hhmemb": { "other_hhmemb": {
"type": "number", "type": "number",
"minimum": 0, "minimum": 0,
"maximum": 7 "maximum": 7
@ -727,7 +727,7 @@
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
"reason_for_leaving_last_settled_home": { "reason": {
"type": "string", "type": "string",
"minLength": 1, "minLength": 1,
"enum": [ "enum": [
@ -836,7 +836,7 @@
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
"property_location": { "la": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
@ -871,11 +871,11 @@
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
"property_major_repairs": { "majorrepairs": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
"property_major_repairs_date": { "mrcdate": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
@ -902,7 +902,7 @@
"minLength": 1, "minLength": 1,
"enum": ["All", "Some", "None", "Do not know"] "enum": ["All", "Some", "None", "Do not know"]
}, },
"housing_benefit": { "hb": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
@ -925,7 +925,7 @@
"tcharge": { "tcharge": {
"type": "number" "type": "number"
}, },
"outstanding_amount": { "tshortfall": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
@ -937,7 +937,7 @@
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
"previous_la": { "prevloc": {
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
@ -962,7 +962,7 @@
"cap": { "cap": {
"type": "boolean" "type": "boolean"
}, },
"outstanding_rent_or_charges": { "hbrentshortfall": {
"type": "number" "type": "number"
}, },
"other_reason_for_leaving_last_settled_home": { "other_reason_for_leaving_last_settled_home": {
@ -1052,7 +1052,7 @@
"prevten", "prevten",
"armed_forces", "armed_forces",
"ecstat1", "ecstat1",
"hhmemb", "other_hhmemb",
"relat2", "relat2",
"age2", "age2",
"sex2", "sex2",
@ -1082,7 +1082,7 @@
"sex8", "sex8",
"ecstat8", "ecstat8",
"homeless", "homeless",
"reason_for_leaving_last_settled_home", "reason",
"underoccupation_benefitcap", "underoccupation_benefitcap",
"leftreg", "leftreg",
"reservist", "reservist",
@ -1098,7 +1098,7 @@
"tenancy", "tenancy",
"lettype", "lettype",
"landlord", "landlord",
"property_location", "la",
"previous_postcode", "previous_postcode",
"property_relet", "property_relet",
"rsnvac", "rsnvac",
@ -1107,31 +1107,31 @@
"property_building_type", "property_building_type",
"beds", "beds",
"property_void_date", "property_void_date",
"property_major_repairs", "majorrepairs",
"property_major_repairs_date", "mrcdate",
"offered", "offered",
"wchair", "wchair",
"earnings", "earnings",
"incfreq", "incfreq",
"benefits", "benefits",
"housing_benefit", "hb",
"period", "period",
"brent", "brent",
"scharge", "scharge",
"pscharge", "pscharge",
"supcharg", "supcharg",
"tcharge", "tcharge",
"outstanding_amount", "tshortfall",
"layear", "layear",
"lawaitlist", "lawaitlist",
"previous_la", "prevloc",
"property_postcode", "property_postcode",
"reasonpref", "reasonpref",
"reasonable_preference_reason", "reasonable_preference_reason",
"cbl", "cbl",
"chr", "chr",
"cap", "cap",
"outstanding_rent_or_charges", "hbrentshortfall",
"other_reason_for_leaving_last_settled_home", "other_reason_for_leaving_last_settled_home",
"housingneeds_a", "housingneeds_a",
"housingneeds_b", "housingneeds_b",

51
lib/tasks/form_definition.rake

@ -0,0 +1,51 @@
require "json"
require "json-schema"
# rubocop:disable Lint/ShadowingOuterLocalVariable
def get_all_form_paths(directories)
form_paths = []
directories.each do |directory|
Dir.glob("#{directory}/*.json").each do |form_path|
form_paths.push(form_path)
end
end
form_paths
end
namespace :form_definition do
desc "Validate JSON against Generic Form Schema"
task validate: :environment do
puts Rails.root.to_s
path = "config/forms/schema/generic.json"
file = File.open(path)
schema = JSON.parse(file.read)
metaschema = JSON::Validator.validator_for_name("draft4").metaschema
puts path
if JSON::Validator.validate(metaschema, schema)
puts "schema valid"
else
puts "schema not valid"
return
end
directories = ["config/forms", "spec/fixtures/forms"]
# directories = ["config/forms"]
get_all_form_paths(directories).each do |path|
puts path
file = File.open(path)
data = JSON.parse(file.read)
puts JSON::Validator.fully_validate(schema, data, strict: true)
begin
JSON::Validator.validate!(schema, data)
rescue JSON::Schema::ValidationError => e
e.message
end
end
end
end
# rubocop:enable Lint/ShadowingOuterLocalVariable

49
spec/controllers/case_logs_controller_spec.rb

@ -152,6 +152,55 @@ RSpec.describe CaseLogsController, type: :controller do
expect(response).to redirect_to("/case_logs/#{id}/conditional_question_no_page") expect(response).to redirect_to("/case_logs/#{id}/conditional_question_no_page")
end end
end end
context "partition postcode" do
let(:case_log_with_postcode) do
{
property_postcode: "M1 1AE",
previous_postcode: "M2 2AE",
page: "property_postcode",
}
end
it "saves full and partial postcodes" do
post :submit_form, params: { id: id, case_log: case_log_with_postcode }
case_log.reload
expect(case_log.property_postcode).to eq("M1 1AE")
expect(case_log.postcode).to eq("M1")
expect(case_log.postcod2).to eq("1AE")
end
it "saves full and partial previous postcodes" do
post :submit_form, params: { id: id, case_log: case_log_with_postcode }
case_log.reload
expect(case_log.previous_postcode).to eq("M2 2AE")
expect(case_log.ppostc1).to eq("M2")
expect(case_log.ppostc2).to eq("2AE")
end
end
context "partition date" do
let(:case_log_with_date) do
{
"mrcdate(1i)": "2021",
"mrcdate(2i)": "05",
"mrcdate(3i)": "04",
page: "major_repairs_date",
}
end
it "saves full and partial dates" do
post :submit_form, params: { id: id, case_log: case_log_with_date }
case_log.reload
expect(case_log.mrcdate.day).to eq(4)
expect(case_log.mrcdate.month).to eq(5)
expect(case_log.mrcdate.year).to eq(2021)
expect(case_log.mrcday).to eq(4)
expect(case_log.mrcmonth).to eq(5)
expect(case_log.mrcyear).to eq(2021)
end
end
end end
describe "get_next_page_path" do describe "get_next_page_path" do

4
spec/factories/case_log.rb

@ -4,8 +4,8 @@ FactoryBot.define do
trait :in_progress do trait :in_progress do
status { 1 } status { 1 }
tenant_code { "TH356" } tenant_code { "TH356" }
property_postcode { "SW2 6HI" } property_postcode { "P0 5ST" }
previous_postcode { "P0 5ST" } previous_postcode { "SW2 6HI" }
age1 { "17" } age1 { "17" }
end end
trait :completed do trait :completed do

12
spec/features/case_log_spec.rb

@ -13,7 +13,7 @@ RSpec.describe "Form Features" do
tenant_code: { type: "text", answer: "BZ737", path: "tenant_code" }, tenant_code: { type: "text", answer: "BZ737", path: "tenant_code" },
age1: { type: "numeric", answer: 25, path: "person_1_age" }, age1: { type: "numeric", answer: 25, path: "person_1_age" },
sex1: { type: "radio", answer: "Female", path: "person_1_gender" }, sex1: { type: "radio", answer: "Female", path: "person_1_gender" },
hhmemb: { type: "numeric", answer: 2, path: "household_number_of_other_members" }, other_hhmemb: { type: "numeric", answer: 2, path: "household_number_of_other_members" },
} }
def fill_in_number_question(case_log_id, question, value, path) def fill_in_number_question(case_log_id, question, value, path)
@ -29,7 +29,7 @@ RSpec.describe "Form Features" do
click_button("Save and continue") click_button("Save and continue")
choose("case-log-benefits-all-field") choose("case-log-benefits-all-field")
click_button("Save and continue") click_button("Save and continue")
choose("case-log-housing-benefit-housing-benefit-but-not-universal-credit-field") choose("case-log-hb-housing-benefit-but-not-universal-credit-field")
click_button("Save and continue") click_button("Save and continue")
end end
@ -179,8 +179,8 @@ RSpec.describe "Form Features" do
end end
it "displays number answers in inputs if they are already saved" do it "displays number answers in inputs if they are already saved" do
visit("/case_logs/#{id}/previous_postcode") visit("/case_logs/#{id}/property_postcode")
expect(page).to have_field("case-log-previous-postcode-field", with: "P0 5ST") expect(page).to have_field("case-log-property-postcode-field", with: "P0 5ST")
end end
it "displays text answers in inputs if they are already saved" do it "displays text answers in inputs if they are already saved" do
@ -269,7 +269,7 @@ RSpec.describe "Form Features" do
let(:last_question_for_subsection) { "household_number_of_other_members" } let(:last_question_for_subsection) { "household_number_of_other_members" }
it "redirects to the check answers page when answering the last question and clicking save and continue" do it "redirects to the check answers page when answering the last question and clicking save and continue" do
fill_in_number_question(id, "hhmemb", 0, last_question_for_subsection) fill_in_number_question(id, "other_hhmemb", 0, last_question_for_subsection)
expect(page).to have_current_path("/case_logs/#{id}/#{subsection}/check_answers") expect(page).to have_current_path("/case_logs/#{id}/#{subsection}/check_answers")
end end
@ -435,7 +435,7 @@ RSpec.describe "Form Features" do
fill_in("case-log-earnings-field", with: income_under_soft_limit) fill_in("case-log-earnings-field", with: income_under_soft_limit)
click_button("Save and continue") click_button("Save and continue")
click_link(text: "Back") click_link(text: "Back")
expect(page).not_to have_content("Are you sure this is correct?") expect(page).to have_no_content("Are you sure this is correct?")
end end
it "does not clear the confirmation question if the page is returned to using the back button and the amount is still over the soft limit", js: true do it "does not clear the confirmation question if the page is returned to using the back button and the amount is still over the soft limit", js: true do

6
spec/features/user_spec.rb

@ -43,5 +43,11 @@ RSpec.describe "User Features" do
click_button("Send email") click_button("Send email")
expect(page).to have_current_path("/confirmations/reset?email=idontexist%40example.com") expect(page).to have_current_path("/confirmations/reset?email=idontexist%40example.com")
end end
it " is sent a reset password email" do
visit("/users/password/new")
fill_in("user_email", with: "test@example.com")
expect { click_button("Send email") }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end end
end end

33
spec/fixtures/complete_case_log.json vendored

@ -8,7 +8,8 @@
"prevten": "Private sector tenancy", "prevten": "Private sector tenancy",
"armed_forces": "Yes - a regular", "armed_forces": "Yes - a regular",
"ecstat1": "Full-time - 30 hours or more", "ecstat1": "Full-time - 30 hours or more",
"hhmemb": 7, "other_hhmemb": 7,
"hhmemb": 8,
"relat2": "Partner", "relat2": "Partner",
"age2": 32, "age2": 32,
"sex2": "Male", "sex2": "Male",
@ -38,7 +39,7 @@
"sex8": "Prefer not to say", "sex8": "Prefer not to say",
"ecstat8": "Child under 16", "ecstat8": "Child under 16",
"homeless": "Yes - other homelessness", "homeless": "Yes - other homelessness",
"reason_for_leaving_last_settled_home": "Other problems with neighbours", "reason": 1,
"underoccupation_benefitcap": "No", "underoccupation_benefitcap": "No",
"leftreg": "No - they left up to 5 years ago", "leftreg": "No - they left up to 5 years ago",
"reservist": "No", "reservist": "No",
@ -54,8 +55,8 @@
"tenancy": "Fixed term – Secure", "tenancy": "Fixed term – Secure",
"lettype": "Affordable Rent - General Needs", "lettype": "Affordable Rent - General Needs",
"landlord": "This landlord", "landlord": "This landlord",
"property_location": "Barnet", "la": "Barnet",
"previous_postcode": "NW1 5TY", "property_postcode": "NW1 5TY",
"property_relet": "No", "property_relet": "No",
"rsnvac": "Relet - tenant abandoned property", "rsnvac": "Relet - tenant abandoned property",
"property_reference": "P9876", "property_reference": "P9876",
@ -63,15 +64,18 @@
"property_building_type": "dummy", "property_building_type": "dummy",
"beds": 3, "beds": 3,
"property_void_date": "03/11/2019", "property_void_date": "03/11/2019",
"property_major_repairs": "Yes", "majorrepairs": "Yes",
"property_major_repairs_date": "05/05/2020", "mrcdate": "05/05/2020",
"mrcday": 5,
"mrcmonth": 5,
"mrcyear": 2020,
"offered": 2, "offered": 2,
"wchair": "Yes", "wchair": "Yes",
"net_income_known": "Yes", "net_income_known": "Yes",
"earnings": 0, "earnings": 0,
"incfreq": null, "incfreq": null,
"benefits": "Some", "benefits": "Some",
"housing_benefit": "Universal Credit with housing element, but not Housing Benefit", "hb": "Universal Credit with housing element, but not Housing Benefit",
"period": "Fortnightly", "period": "Fortnightly",
"brent": 200, "brent": 200,
"scharge": 50, "scharge": 50,
@ -81,14 +85,15 @@
"outstanding_amount": "Yes", "outstanding_amount": "Yes",
"layear": "1 to 2 years", "layear": "1 to 2 years",
"lawaitlist": "Less than 1 year", "lawaitlist": "Less than 1 year",
"previous_la": "Ashford", "prevloc": "Ashford",
"property_postcode": "SE2 6RT", "previous_postcode": "SE2 6RT",
"reasonpref": "Yes", "reasonpref": "Yes",
"reasonable_preference_reason": "dummy", "reasonable_preference_reason": "dummy",
"cbl": "Yes", "cbl": "Yes",
"chr": "Yes", "chr": "Yes",
"cap": "No", "cap": "No",
"outstanding_rent_or_charges": 25, "hbrentshortfall": "Yes",
"tshortfall": 12,
"other_reason_for_leaving_last_settled_home": null, "other_reason_for_leaving_last_settled_home": null,
"housingneeds_a": "Yes", "housingneeds_a": "Yes",
"housingneeds_b": "No", "housingneeds_b": "No",
@ -125,6 +130,12 @@
"intermediate_rent_product_name": "", "intermediate_rent_product_name": "",
"needs_type": "", "needs_type": "",
"sale_completion_date": "", "sale_completion_date": "",
"purchaser_code": "" "purchaser_code": "",
"propcode": "123",
"majorrepairs": "Yes",
"postcode": "a1",
"postcod2": "w3",
"ppostc1": "w3",
"ppostc2": "w3"
} }
} }

26
spec/fixtures/forms/test_form.json vendored

@ -45,7 +45,7 @@
}, },
"household_number_of_other_members": { "household_number_of_other_members": {
"questions": { "questions": {
"hhmemb": { "other_hhmemb": {
"check_answer_label": "Number of Other Household Members", "check_answer_label": "Number of Other Household Members",
"header": "How many other people are there in the household?", "header": "How many other people are there in the household?",
"hint_text": "The maximum number of others is 1", "hint_text": "The maximum number of others is 1",
@ -348,7 +348,7 @@
}, },
"housing_benefit": { "housing_benefit": {
"questions": { "questions": {
"housing_benefit": { "hb": {
"check_answer_label": "Universal Credit & Housing Benefit", "check_answer_label": "Universal Credit & Housing Benefit",
"header": "Is the tenant likely to be in receipt of any of these housing-related benefits?", "header": "Is the tenant likely to be in receipt of any of these housing-related benefits?",
"type": "radio", "type": "radio",
@ -494,14 +494,30 @@
} }
} }
}, },
"previous_postcode": { "property_postcode": {
"questions": { "questions": {
"previous_postcode": { "property_postcode": {
"check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation", "check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation",
"header": "Postcode for the previous accommodation", "header": "Postcode for the previous accommodation",
"hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed", "hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed",
"type": "text", "type": "text",
"conditional_for": { "faake_key": "fake_condition" } "conditional_for": { "faake_key": "fake_condition" }
},
"previous_postcode": {
"check_answer_label": "Postcode of previous accomodation if the household has moved from settled accommodation",
"header": "Postcode for the previous accommodation",
"hint_text": "If the household has moved from settled accommodation immediately prior to being re-housed",
"type": "text"
}
}
},
"major_repairs_date": {
"questions": {
"mrcdate": {
"check_answer_label": "What was the major repairs completion date?",
"header": "What was the major repairs completion date?",
"hint_text": "For example, 27 3 2007",
"type": "date"
} }
} }
} }
@ -529,4 +545,4 @@
} }
} }
} }
} }

48
spec/fixtures/forms/test_validator.json vendored

@ -0,0 +1,48 @@
{
"form_type": "lettings",
"start_year": 2021,
"end_year": 2022,
"sections": {
"household": {
"label": "About the household",
"subsections": {
"household_characteristics": {
"label": "Household characteristics",
"ShouldThrowError": "Shouldn't be here but what you gonna do?",
"pages": {
"tenant_code": {
"header": "",
"description": "",
"ShouldThrowError": "Shouldn't be here but what you gonna do?",
"questions": {
"tenant_code": {
"check_answer_label": "Tenant code",
"header": "What is the tenant code?",
"description": "",
"type": "text"
}
},
"conditional_route_to": {"test": "Yes"}
},
"conditional_route_to": {"test": "Yes"},
"person_1_age": {
"header": "",
"description": "",
"questions": {
"person_1_age": {
"check_answer_label": "Tenant's age",
"header": "What is the tenant's age?",
"hint_text": "",
"type": "numeric",
"min": 0,
"max": 120,
"step": 1
}
}
}
}
}
}
}
}
}

4
spec/helpers/check_answers_helper_spec.rb

@ -6,7 +6,7 @@ RSpec.describe CheckAnswersHelper do
FactoryBot.create( FactoryBot.create(
:case_log, :case_log,
:in_progress, :in_progress,
hhmemb: 1, other_hhmemb: 1,
relat2: "Partner", relat2: "Partner",
) )
end end
@ -127,7 +127,7 @@ RSpec.describe CheckAnswersHelper do
it "returns total questions" do it "returns total questions" do
result = total_questions(subsection, case_log, form) result = total_questions(subsection, case_log, form)
expect(result.class).to eq(Hash) expect(result.class).to eq(Hash)
expected_keys = %w[earnings incfreq benefits housing_benefit] expected_keys = %w[earnings incfreq benefits hb]
expect(result.keys).to eq(expected_keys) expect(result.keys).to eq(expected_keys)
end end

2
spec/helpers/tasklist_helper_spec.rb

@ -33,7 +33,7 @@ RSpec.describe TasklistHelper do
case_log["earnings"] = "value" case_log["earnings"] = "value"
case_log["incfreq"] = "Weekly" case_log["incfreq"] = "Weekly"
case_log["benefits"] = "All" case_log["benefits"] = "All"
case_log["housing_benefit"] = "Do not know" case_log["hb"] = "Do not know"
status = get_subsection_status("income_and_benefits", case_log, income_and_benefits_questions) status = get_subsection_status("income_and_benefits", case_log, income_and_benefits_questions)
expect(status).to eq(:completed) expect(status).to eq(:completed)

18
spec/models/case_log_spec.rb

@ -60,7 +60,7 @@ RSpec.describe Form, type: :model do
context "reason for leaving last settled home validation" do context "reason for leaving last settled home validation" do
it "Reason for leaving must be don't know if reason for leaving settled home (Q9a) is don't know." do it "Reason for leaving must be don't know if reason for leaving settled home (Q9a) is don't know." do
expect { expect {
CaseLog.create!(reason_for_leaving_last_settled_home: "Do not know", CaseLog.create!(reason: "Do not know",
underoccupation_benefitcap: "Yes - benefit cap") underoccupation_benefitcap: "Yes - benefit cap")
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
@ -68,14 +68,14 @@ RSpec.describe Form, type: :model do
context "other reason for leaving last settled home validation" do context "other reason for leaving last settled home validation" do
it "must be provided if main reason for leaving last settled home was given as other" do it "must be provided if main reason for leaving last settled home was given as other" do
expect { expect {
CaseLog.create!(reason_for_leaving_last_settled_home: "Other", CaseLog.create!(reason: "Other",
other_reason_for_leaving_last_settled_home: nil) other_reason_for_leaving_last_settled_home: nil)
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
it "must not be provided if the main reason for leaving settled home is not other" do it "must not be provided if the main reason for leaving settled home is not other" do
expect { expect {
CaseLog.create!(reason_for_leaving_last_settled_home: "Repossession", CaseLog.create!(reason: "Repossession",
other_reason_for_leaving_last_settled_home: "the other reason provided") other_reason_for_leaving_last_settled_home: "the other reason provided")
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
@ -109,7 +109,7 @@ RSpec.describe Form, type: :model do
expect { expect {
CaseLog.create!(unittype_gn: "Shared bungalow", CaseLog.create!(unittype_gn: "Shared bungalow",
beds: 8, beds: 8,
hhmemb: 1) other_hhmemb: 1)
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
@ -117,7 +117,7 @@ RSpec.describe Form, type: :model do
expect { expect {
CaseLog.create!(unittype_gn: "Shared bungalow", CaseLog.create!(unittype_gn: "Shared bungalow",
beds: 4, beds: 4,
hhmemb: 0) other_hhmemb: 0)
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
@ -139,15 +139,15 @@ RSpec.describe Form, type: :model do
context "outstanding rent or charges validation" do context "outstanding rent or charges validation" do
it "must be anwered if answered yes to outstanding rent or charges" do it "must be anwered if answered yes to outstanding rent or charges" do
expect { expect {
CaseLog.create!(outstanding_rent_or_charges: "Yes", CaseLog.create!(hbrentshortfall: "Yes",
outstanding_amount: nil) tshortfall: nil)
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
it "must be not be anwered if answered no to outstanding rent or charges" do it "must be not be anwered if answered no to outstanding rent or charges" do
expect { expect {
CaseLog.create!(outstanding_rent_or_charges: "No", CaseLog.create!(hbrentshortfall: "No",
outstanding_amount: 99) tshortfall: 99)
}.to raise_error(ActiveRecord::RecordInvalid) }.to raise_error(ActiveRecord::RecordInvalid)
end end
end end

2
spec/models/form_handler_spec.rb

@ -15,7 +15,7 @@ RSpec.describe FormHandler do
form_handler = FormHandler.instance form_handler = FormHandler.instance
form = form_handler.get_form("test_form") form = form_handler.get_form("test_form")
expect(form).to be_a(Form) expect(form).to be_a(Form)
expect(form.all_pages.count).to eq(22) expect(form.all_pages.count).to eq(23)
end end
end end

2
spec/models/form_spec.rb

@ -39,7 +39,7 @@ RSpec.describe Form, type: :model do
it "returns all questions for subsection" do it "returns all questions for subsection" do
result = form.questions_for_subsection(subsection) result = form.questions_for_subsection(subsection)
expect(result.length).to eq(4) expect(result.length).to eq(4)
expect(result.keys).to eq(%w[earnings incfreq benefits housing_benefit]) expect(result.keys).to eq(%w[earnings incfreq benefits hb])
end end
end end
end end

Loading…
Cancel
Save