Browse Source

Merge branch 'main' into CLDC-3654-Copy-changes-sales-property-information-questions

pull/2694/head
Manny Dinssa 9 months ago committed by GitHub
parent
commit
e8147059e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      app/models/form/lettings/pages/declaration.rb
  2. 2
      app/models/form/lettings/pages/location.rb
  3. 2
      app/models/form/lettings/pages/location_search.rb
  4. 2
      app/models/form/lettings/pages/rent_type.rb
  5. 1
      app/models/form/lettings/pages/scheme.rb
  6. 2
      app/models/form/lettings/pages/type_of_access_needs.rb
  7. 3
      app/models/form/lettings/questions/armedforces.rb
  8. 3
      app/models/form/lettings/questions/condition_effects.rb
  9. 2
      app/models/form/lettings/questions/created_by_id.rb
  10. 3
      app/models/form/lettings/questions/housingneeds.rb
  11. 4
      app/models/form/lettings/questions/housingneeds_other.rb
  12. 4
      app/models/form/lettings/questions/housingneeds_type.rb
  13. 3
      app/models/form/lettings/questions/illness.rb
  14. 3
      app/models/form/lettings/questions/irproduct_other.rb
  15. 3
      app/models/form/lettings/questions/leftreg.rb
  16. 11
      app/models/form/lettings/questions/location_id.rb
  17. 12
      app/models/form/lettings/questions/location_id_search.rb
  18. 2
      app/models/form/lettings/questions/managing_organisation.rb
  19. 3
      app/models/form/lettings/questions/needs_type.rb
  20. 3
      app/models/form/lettings/questions/preg_occ.rb
  21. 3
      app/models/form/lettings/questions/property_reference.rb
  22. 19
      app/models/form/lettings/questions/renewal.rb
  23. 3
      app/models/form/lettings/questions/rent_type.rb
  24. 3
      app/models/form/lettings/questions/reservist.rb
  25. 11
      app/models/form/lettings/questions/scheme_id.rb
  26. 2
      app/models/form/lettings/questions/stock_owner.rb
  27. 2
      app/models/form/lettings/questions/tenancy_start_date.rb
  28. 3
      app/models/form/lettings/questions/tenant_code.rb
  29. 1
      app/models/form/lettings/subsections/household_needs.rb
  30. 80
      app/models/validations/sales/sale_information_validations.rb
  31. 43
      config/locales/en.yml
  32. 58
      config/locales/forms/2023/lettings/household_needs.en.yml
  33. 81
      config/locales/forms/2023/lettings/setup.en.yml
  34. 58
      config/locales/forms/2024/lettings/household_needs.en.yml
  35. 87
      config/locales/forms/2024/lettings/setup.en.yml
  36. 125
      config/locales/validations/sales/sale_information.en.yml
  37. 2
      spec/features/form/check_answers_page_lettings_logs_spec.rb
  38. 2
      spec/features/lettings_log_spec.rb
  39. 8
      spec/models/validations/sales/sale_information_validations_spec.rb
  40. 7
      spec/requests/form_controller_spec.rb
  41. 2
      spec/requests/lettings_logs_controller_spec.rb
  42. 4
      spec/requests/locations_controller_spec.rb

1
app/models/form/lettings/pages/declaration.rb

@ -2,7 +2,6 @@ class Form::Lettings::Pages::Declaration < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "declaration" @id = "declaration"
@header = "Ministry of Housing, Communities and Local Government privacy notice"
end end
def questions def questions

2
app/models/form/lettings/pages/location.rb

@ -8,7 +8,7 @@ class Form::Lettings::Pages::Location < ::Form::Page
"scheme_has_large_number_of_locations?" => false, "scheme_has_large_number_of_locations?" => false,
}, },
] ]
@header = "Location" @copy_key = "lettings.setup.location_id.less_than_twenty"
@next_unresolved_page_id = :check_answers @next_unresolved_page_id = :check_answers
end end

2
app/models/form/lettings/pages/location_search.rb

@ -8,7 +8,7 @@ class Form::Lettings::Pages::LocationSearch < ::Form::Page
"scheme_has_large_number_of_locations?" => true, "scheme_has_large_number_of_locations?" => true,
}, },
] ]
@header = "Location" @copy_key = "lettings.setup.location_id.twenty_or_more"
@next_unresolved_page_id = :check_answers @next_unresolved_page_id = :check_answers
end end

2
app/models/form/lettings/pages/rent_type.rb

@ -2,7 +2,7 @@ class Form::Lettings::Pages::RentType < ::Form::Page
def initialize(_id, hsh, subsection) def initialize(_id, hsh, subsection)
super("rent_type", hsh, subsection) super("rent_type", hsh, subsection)
@derived = true @derived = true
@header = "Rent Type" @copy_key = "lettings.setup.rent_type"
end end
def questions def questions

1
app/models/form/lettings/pages/scheme.rb

@ -6,7 +6,6 @@ class Form::Lettings::Pages::Scheme < ::Form::Page
"needstype" => 2, "needstype" => 2,
}, },
] ]
@header = "Scheme"
@next_unresolved_page_id = "location" @next_unresolved_page_id = "location"
end end

2
app/models/form/lettings/pages/type_of_access_needs.rb

@ -2,7 +2,7 @@ class Form::Lettings::Pages::TypeOfAccessNeeds < ::Form::Page
def initialize(id, hsh, subsection) def initialize(id, hsh, subsection)
super super
@id = "type_of_access_needs" @id = "type_of_access_needs"
@header = "Disabled access needs" @copy_key = "lettings.household_needs.housingneeds_type"
@depends_on = [{ "housingneeds" => 1 }] @depends_on = [{ "housingneeds" => 1 }]
end end

3
app/models/form/lettings/questions/armedforces.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::Armedforces < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "armedforces" @id = "armedforces"
@check_answer_label = "Household links to UK armed forces"
@header = "Does anybody in the household have any links to the UK armed forces?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = "This excludes national service.<br><br>If there are several people in the household with links to the UK armed forces, you should answer for the regular. If there’s no regular, answer for the reserve. If there’s no reserve, answer for the spouse or civil partner."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

3
app/models/form/lettings/questions/condition_effects.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::ConditionEffects < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "condition_effects" @id = "condition_effects"
@check_answer_label = "How is person affected by condition or illness"
@header = "How is the person affected by their condition or illness?"
@type = "checkbox" @type = "checkbox"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = "Select all that apply."
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

2
app/models/form/lettings/questions/created_by_id.rb

@ -4,8 +4,6 @@ class Form::Lettings::Questions::CreatedById < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "assigned_to_id" @id = "assigned_to_id"
@check_answer_label = "Log owner"
@header = "Which user are you creating this log for?"
@derived = true @derived = true
@type = "select" @type = "select"
end end

3
app/models/form/lettings/questions/housingneeds.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::Housingneeds < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "housingneeds" @id = "housingneeds"
@check_answer_label = "Anybody with disabled access needs"
@header = "Does anybody in the household have any disabled access needs?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

4
app/models/form/lettings/questions/housingneeds_other.rb

@ -2,11 +2,9 @@ class Form::Lettings::Questions::HousingneedsOther < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "housingneeds_other" @id = "housingneeds_other"
@check_answer_label = "Other disabled access needs" @copy_key = "lettings.household_needs.housingneeds_type.housingneeds_other"
@header = "Do they have any other disabled access needs?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

4
app/models/form/lettings/questions/housingneeds_type.rb

@ -2,11 +2,9 @@ class Form::Lettings::Questions::HousingneedsType < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "housingneeds_type" @id = "housingneeds_type"
@check_answer_label = "Disabled access needs" @copy_key = "lettings.household_needs.housingneeds_type.housingneeds_type"
@header = "What type of access needs do they have?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

3
app/models/form/lettings/questions/illness.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::Illness < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "illness" @id = "illness"
@check_answer_label = "Anybody in household with physical or mental health condition"
@header = "Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

3
app/models/form/lettings/questions/irproduct_other.rb

@ -2,8 +2,7 @@ class Form::Lettings::Questions::IrproductOther < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "irproduct_other" @id = "irproduct_other"
@check_answer_label = "Product name" @copy_key = "lettings.setup.rent_type.irproduct_other"
@header = "Name of rent product"
@type = "text" @type = "text"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?
end end

3
app/models/form/lettings/questions/leftreg.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::Leftreg < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "leftreg" @id = "leftreg"
@check_answer_label = "Person still serving in UK armed forces"
@header = "Is the person still serving in the UK armed forces?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

11
app/models/form/lettings/questions/location_id.rb

@ -2,8 +2,7 @@ class Form::Lettings::Questions::LocationId < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "location_id" @id = "location_id"
@check_answer_label = "Location" @copy_key = "lettings.setup.location_id.less_than_twenty"
@header = header_text
@type = "radio" @type = "radio"
@answer_options = answer_options @answer_options = answer_options
@inferred_answers = { @inferred_answers = {
@ -56,13 +55,5 @@ private
false false
end end
def header_text
if form.start_date && form.start_date.year >= 2023
"Which location is this letting for?"
else
"Which location is this log for?"
end
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 10, 2024 => 5 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2023 => 10, 2024 => 5 }.freeze
end end

12
app/models/form/lettings/questions/location_id_search.rb

@ -2,10 +2,8 @@ class Form::Lettings::Questions::LocationIdSearch < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "location_id" @id = "location_id"
@check_answer_label = "Location"
@header = header_text
@hint_text = '<div class="govuk-inset-text">This scheme has 20 or more locations.</div>Enter postcode or address.'
@type = "select" @type = "select"
@copy_key = "lettings.setup.location_id.twenty_or_more"
@answer_options = answer_options @answer_options = answer_options
@inferred_answers = { @inferred_answers = {
"location.name": { "location.name": {
@ -52,13 +50,5 @@ private
false false
end end
def header_text
if form.start_date && form.start_date.year >= 2023
"Which location is this letting for?"
else
"Which location is this log for?"
end
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 10, 2024 => 5 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2023 => 10, 2024 => 5 }.freeze
end end

2
app/models/form/lettings/questions/managing_organisation.rb

@ -2,8 +2,6 @@ class Form::Lettings::Questions::ManagingOrganisation < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "managing_organisation_id" @id = "managing_organisation_id"
@check_answer_label = "Managing agent"
@header = "Which organisation manages this letting?"
@derived = true @derived = true
@type = "select" @type = "select"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?

3
app/models/form/lettings/questions/needs_type.rb

@ -2,9 +2,6 @@ class Form::Lettings::Questions::NeedsType < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "needstype" @id = "needstype"
@check_answer_label = "Needs type"
@header = "What is the needs type?"
@hint_text = "General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing can include direct access hostels, group homes, residential care and nursing homes."
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?

3
app/models/form/lettings/questions/preg_occ.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::PregOcc < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "preg_occ" @id = "preg_occ"
@check_answer_label = "Anybody in household pregnant"
@header = "Is anybody in the household pregnant?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

3
app/models/form/lettings/questions/property_reference.rb

@ -2,9 +2,6 @@ class Form::Lettings::Questions::PropertyReference < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "propcode" @id = "propcode"
@check_answer_label = "Property reference"
@header = "What is the property reference?"
@hint_text = "This is how you usually refer to this property on your own systems."
@type = "text" @type = "text"
@width = 10 @width = 10
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?

19
app/models/form/lettings/questions/renewal.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::Renewal < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "renewal" @id = "renewal"
@check_answer_label = "Property renewal"
@header = header_text
@type = "radio" @type = "radio"
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@hint_text = hint_text
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?
end end
@ -16,21 +13,5 @@ class Form::Lettings::Questions::Renewal < ::Form::Question
} }
.freeze .freeze
def header_text
if form.start_year_after_2024?
"Is this letting a renewal of social housing to the same tenant in the same property?"
else
"Is this letting a renewal?"
end
end
def hint_text
if form.start_year_after_2024?
"If the property was previously being used as temporary accommodation, then answer 'no'"
else
"A renewal is a letting to the same tenant in the same property. If the property was previously being used as temporary accommodation, then answer 'no'"
end
end
QUESTION_NUMBER_FROM_YEAR = { 2023 => 4, 2024 => 6 }.freeze QUESTION_NUMBER_FROM_YEAR = { 2023 => 4, 2024 => 6 }.freeze
end end

3
app/models/form/lettings/questions/rent_type.rb

@ -2,8 +2,7 @@ class Form::Lettings::Questions::RentType < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "rent_type" @id = "rent_type"
@check_answer_label = "Rent type" @copy_key = "lettings.setup.rent_type.rent_type"
@header = "What is the rent type?"
@type = "radio" @type = "radio"
@top_guidance_partial = form.start_year_after_2024? ? "rent_type_definitions_2024" : "rent_type_definitions" @top_guidance_partial = form.start_year_after_2024? ? "rent_type_definitions_2024" : "rent_type_definitions"
@answer_options = form.start_year_after_2024? ? ANSWER_OPTIONS_2024 : ANSWER_OPTIONS @answer_options = form.start_year_after_2024? ? ANSWER_OPTIONS_2024 : ANSWER_OPTIONS

3
app/models/form/lettings/questions/reservist.rb

@ -2,11 +2,8 @@ class Form::Lettings::Questions::Reservist < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "reservist" @id = "reservist"
@check_answer_label = "Person seriously injured or ill as result of serving in UK armed forces"
@header = "Was the person seriously injured or ill as a result of serving in the UK armed forces?"
@type = "radio" @type = "radio"
@check_answers_card_number = 0 @check_answers_card_number = 0
@hint_text = ""
@answer_options = ANSWER_OPTIONS @answer_options = ANSWER_OPTIONS
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max]
end end

11
app/models/form/lettings/questions/scheme_id.rb

@ -1,8 +1,6 @@
class Form::Lettings::Questions::SchemeId < ::Form::Question class Form::Lettings::Questions::SchemeId < ::Form::Question
def initialize(_id, hsh, page) def initialize(_id, hsh, page)
super("scheme_id", hsh, page) super("scheme_id", hsh, page)
@check_answer_label = "Scheme name"
@header = "What scheme is this log for?"
@type = "select" @type = "select"
@answer_options = answer_options @answer_options = answer_options
@top_guidance_partial = "finding_scheme" @top_guidance_partial = "finding_scheme"
@ -48,15 +46,6 @@ class Form::Lettings::Questions::SchemeId < ::Form::Question
lettings_log.form.get_question("postcode_full", nil).label_from_value(lettings_log.postcode_full) unless lettings_log.scheme_has_multiple_locations? lettings_log.form.get_question("postcode_full", nil).label_from_value(lettings_log.postcode_full) unless lettings_log.scheme_has_multiple_locations?
end end
def hint_text
if form.start_year_after_2024?
"Enter postcode or scheme name.<br><br>
A supported housing scheme provides shared or self-contained housing for a particular client group, for example younger or vulnerable people."
else
"Enter postcode or scheme name"
end
end
private private
def supported_housing_selected?(lettings_log) def supported_housing_selected?(lettings_log)

2
app/models/form/lettings/questions/stock_owner.rb

@ -2,8 +2,6 @@ class Form::Lettings::Questions::StockOwner < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "owning_organisation_id" @id = "owning_organisation_id"
@check_answer_label = "Stock owner"
@header = "Which organisation owns this property?"
@derived = true @derived = true
@type = "select" @type = "select"
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?

2
app/models/form/lettings/questions/tenancy_start_date.rb

@ -2,8 +2,6 @@ class Form::Lettings::Questions::TenancyStartDate < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "startdate" @id = "startdate"
@check_answer_label = "Tenancy start date"
@header = "What is the tenancy start date?"
@type = "date" @type = "date"
@unresolved_hint_text = "Some scheme details have changed, and now this log needs updating. Check that the tenancy start date is correct." @unresolved_hint_text = "Some scheme details have changed, and now this log needs updating. Check that the tenancy start date is correct."
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?

3
app/models/form/lettings/questions/tenant_code.rb

@ -2,9 +2,6 @@ class Form::Lettings::Questions::TenantCode < ::Form::Question
def initialize(id, hsh, page) def initialize(id, hsh, page)
super super
@id = "tenancycode" @id = "tenancycode"
@check_answer_label = "Tenant code"
@header = "What is the tenant code?"
@hint_text = "This is how you usually refer to this tenancy on your own systems."
@type = "text" @type = "text"
@width = 10 @width = 10
@question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present? @question_number = QUESTION_NUMBER_FROM_YEAR[form.start_date.year] || QUESTION_NUMBER_FROM_YEAR[QUESTION_NUMBER_FROM_YEAR.keys.max] if form.start_date.present?

1
app/models/form/lettings/subsections/household_needs.rb

@ -2,6 +2,7 @@ class Form::Lettings::Subsections::HouseholdNeeds < ::Form::Subsection
def initialize(id, hsh, section) def initialize(id, hsh, section)
super super
@id = "household_needs" @id = "household_needs"
@copy_key = "lettings.household_needs.housingneeds_type"
@label = "Household needs" @label = "Household needs"
@depends_on = [{ "non_location_setup_questions_completed?" => true }] @depends_on = [{ "non_location_setup_questions_completed?" => true }]
end end

80
app/models/validations/sales/sale_information_validations.rb

@ -8,13 +8,13 @@ module Validations::Sales::SaleInformationValidations
return if record.saledate.blank? return if record.saledate.blank?
if record.hodate > record.saledate if record.hodate > record.saledate
record.errors.add :hodate, I18n.t("validations.sale_information.hodate.must_be_before_saledate") record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_before_saledate")
record.errors.add :saledate, I18n.t("validations.sale_information.saledate.must_be_after_hodate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_after_hodate")
end end
if record.saledate - record.hodate >= 3.years && record.form.start_year_after_2024? if record.saledate - record.hodate >= 3.years && record.form.start_year_after_2024?
record.errors.add :hodate, I18n.t("validations.sale_information.hodate.must_be_less_than_3_years_from_saledate") record.errors.add :hodate, I18n.t("validations.sales.sale_information.hodate.must_be_less_than_3_years_from_saledate")
record.errors.add :saledate, I18n.t("validations.sale_information.saledate.must_be_less_than_3_years_from_hodate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_3_years_from_hodate")
end end
end end
@ -22,13 +22,13 @@ module Validations::Sales::SaleInformationValidations
return unless record.exdate && record.saledate return unless record.exdate && record.saledate
if record.exdate > record.saledate if record.exdate > record.saledate
record.errors.add :exdate, I18n.t("validations.sale_information.exdate.must_be_before_saledate") record.errors.add :exdate, I18n.t("validations.sales.sale_information.exdate.must_be_before_saledate")
record.errors.add :saledate, I18n.t("validations.sale_information.saledate.must_be_after_exdate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_after_exdate")
end end
if record.saledate - record.exdate >= 1.year if record.saledate - record.exdate >= 1.year
record.errors.add :exdate, I18n.t("validations.sale_information.exdate.must_be_less_than_1_year_from_saledate") record.errors.add :exdate, I18n.t("validations.sales.sale_information.exdate.must_be_less_than_1_year_from_saledate")
record.errors.add :saledate, I18n.t("validations.sale_information.saledate.must_be_less_than_1_year_from_exdate") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.must_be_less_than_1_year_from_exdate")
end end
end end
@ -36,8 +36,8 @@ module Validations::Sales::SaleInformationValidations
return unless record.fromprop && record.frombeds return unless record.fromprop && record.frombeds
if record.frombeds != 1 && record.fromprop == 2 if record.frombeds != 1 && record.fromprop == 2
record.errors.add :frombeds, I18n.t("validations.sale_information.previous_property_type.property_type_bedsit") record.errors.add :frombeds, I18n.t("validations.sales.sale_information.frombeds.previous_property_type_bedsit")
record.errors.add :fromprop, I18n.t("validations.sale_information.previous_property_type.property_type_bedsit") record.errors.add :fromprop, I18n.t("validations.sales.sale_information.fromprop.previous_property_type_bedsit")
end end
end end
@ -54,7 +54,7 @@ module Validations::Sales::SaleInformationValidations
deposit_and_grant_sentence = record.grant.present? ? ", cash deposit (#{record.field_formatted_as_currency('deposit')}), and grant (#{record.field_formatted_as_currency('grant')})" : " and cash deposit (#{record.field_formatted_as_currency('deposit')})" deposit_and_grant_sentence = record.grant.present? ? ", cash deposit (#{record.field_formatted_as_currency('deposit')}), and grant (#{record.field_formatted_as_currency('grant')})" : " and cash deposit (#{record.field_formatted_as_currency('deposit')})"
discount_sentence = record.discount.present? ? " (#{record.field_formatted_as_currency('value')}) subtracted by the sum of the full purchase price (#{record.field_formatted_as_currency('value')}) multiplied by the percentage discount (#{record.discount}%)" : "" discount_sentence = record.discount.present? ? " (#{record.field_formatted_as_currency('value')}) subtracted by the sum of the full purchase price (#{record.field_formatted_as_currency('value')}) multiplied by the percentage discount (#{record.discount}%)" : ""
%i[mortgageused mortgage value deposit ownershipsch discount grant].each do |field| %i[mortgageused mortgage value deposit ownershipsch discount grant].each do |field|
record.errors.add field, I18n.t("validations.sale_information.discounted_ownership_value", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.discounted_ownership_value",
mortgage: record.mortgage&.positive? ? " (#{record.field_formatted_as_currency('mortgage')})" : "", mortgage: record.mortgage&.positive? ? " (#{record.field_formatted_as_currency('mortgage')})" : "",
deposit_and_grant_sentence:, deposit_and_grant_sentence:,
mortgage_deposit_and_grant_total: record.field_formatted_as_currency("mortgage_deposit_and_grant_total"), mortgage_deposit_and_grant_total: record.field_formatted_as_currency("mortgage_deposit_and_grant_total"),
@ -72,13 +72,13 @@ module Validations::Sales::SaleInformationValidations
if over_tolerance?(record.mortgage_and_deposit_total, record.value, 1) if over_tolerance?(record.mortgage_and_deposit_total, record.value, 1)
%i[mortgageused mortgage value deposit].each do |field| %i[mortgageused mortgage value deposit].each do |field|
record.errors.add field, I18n.t("validations.sale_information.outright_sale_value", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.outright_sale_value",
mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"),
mortgage: record.mortgage&.positive? ? " (#{record.field_formatted_as_currency('mortgage')})" : "", mortgage: record.mortgage&.positive? ? " (#{record.field_formatted_as_currency('mortgage')})" : "",
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value")).html_safe value: record.field_formatted_as_currency("value")).html_safe
end end
record.errors.add :ownershipsch, :skip_bu_error, message: I18n.t("validations.sale_information.outright_sale_value", record.errors.add :ownershipsch, :skip_bu_error, message: I18n.t("validations.sales.sale_information.ownershipsch.outright_sale_value",
mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"),
mortgage: record.mortgage&.positive? ? " (#{record.field_formatted_as_currency('mortgage')})" : "", mortgage: record.mortgage&.positive? ? " (#{record.field_formatted_as_currency('mortgage')})" : "",
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
@ -90,8 +90,8 @@ module Validations::Sales::SaleInformationValidations
return unless record.mrent && record.ownershipsch && record.type return unless record.mrent && record.ownershipsch && record.type
if record.shared_ownership_scheme? && !record.old_persons_shared_ownership? && record.mrent > 9999 if record.shared_ownership_scheme? && !record.old_persons_shared_ownership? && record.mrent > 9999
record.errors.add :mrent, I18n.t("validations.sale_information.monthly_rent.higher_than_expected") record.errors.add :mrent, I18n.t("validations.sales.sale_information.mrent.monthly_rent_higher_than_expected")
record.errors.add :type, I18n.t("validations.sale_information.monthly_rent.higher_than_expected") record.errors.add :type, I18n.t("validations.sales.sale_information.type.monthly_rent_higher_than_expected")
end end
end end
@ -100,7 +100,7 @@ module Validations::Sales::SaleInformationValidations
return unless record.grant && (record.type == 8 || record.type == 21) return unless record.grant && (record.type == 8 || record.type == 21)
unless record.grant.between?(9_000, 16_000) unless record.grant.between?(9_000, 16_000)
record.errors.add :grant, I18n.t("validations.sale_information.grant.out_of_range") record.errors.add :grant, I18n.t("validations.sales.sale_information.grant.out_of_range")
end end
end end
@ -118,8 +118,8 @@ module Validations::Sales::SaleInformationValidations
end end
if max_stairbought && record.stairbought > max_stairbought if max_stairbought && record.stairbought > max_stairbought
record.errors.add :stairbought, I18n.t("validations.sale_information.stairbought.over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record)) record.errors.add :stairbought, I18n.t("validations.sales.sale_information.stairbought.stairbought_over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record))
record.errors.add :type, I18n.t("validations.sale_information.stairbought.over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record)) record.errors.add :type, I18n.t("validations.sales.sale_information.type.stairbought_over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record))
end end
end end
@ -129,11 +129,11 @@ module Validations::Sales::SaleInformationValidations
if record.london_property? && record.discount_value > 136_400 if record.london_property? && record.discount_value > 136_400
%i[discount value la postcode_full uprn].each do |field| %i[discount value la postcode_full uprn].each do |field|
record.errors.add field, I18n.t("validations.sale_information.value.over_discounted_london_max", discount_value: record.field_formatted_as_currency("discount_value")) record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.value_over_discounted_london_max", discount_value: record.field_formatted_as_currency("discount_value"))
end end
elsif record.property_not_in_london? && record.discount_value > 102_400 elsif record.property_not_in_london? && record.discount_value > 102_400
%i[discount value la postcode_full uprn].each do |field| %i[discount value la postcode_full uprn].each do |field|
record.errors.add field, I18n.t("validations.sale_information.value.over_discounted_max", discount_value: record.field_formatted_as_currency("discount_value")) record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.value_over_discounted_max", discount_value: record.field_formatted_as_currency("discount_value"))
end end
end end
end end
@ -170,7 +170,7 @@ module Validations::Sales::SaleInformationValidations
if over_tolerance?(record.mortgage_deposit_and_discount_total, record.expected_shared_ownership_deposit_value, 1) if over_tolerance?(record.mortgage_deposit_and_discount_total, record.expected_shared_ownership_deposit_value, 1)
%i[mortgage value deposit cashdis equity].each do |field| %i[mortgage value deposit cashdis equity].each do |field|
record.errors.add field, I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_used_socialhomebuy", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.non_staircasing_mortgage.mortgage_used_socialhomebuy",
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
@ -179,7 +179,7 @@ module Validations::Sales::SaleInformationValidations
mortgage_deposit_and_discount_total: record.field_formatted_as_currency("mortgage_deposit_and_discount_total"), mortgage_deposit_and_discount_total: record.field_formatted_as_currency("mortgage_deposit_and_discount_total"),
expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_used_socialhomebuy", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.non_staircasing_mortgage.mortgage_used_socialhomebuy",
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
@ -191,7 +191,7 @@ module Validations::Sales::SaleInformationValidations
elsif record.mortgage_not_used? elsif record.mortgage_not_used?
if over_tolerance?(record.deposit_and_discount_total, record.expected_shared_ownership_deposit_value, 1) if over_tolerance?(record.deposit_and_discount_total, record.expected_shared_ownership_deposit_value, 1)
%i[mortgageused value deposit cashdis equity].each do |field| %i[mortgageused value deposit cashdis equity].each do |field|
record.errors.add field, I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_not_used_socialhomebuy", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.non_staircasing_mortgage.mortgage_not_used_socialhomebuy",
deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"), deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"),
expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value"), expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
@ -199,7 +199,7 @@ module Validations::Sales::SaleInformationValidations
cashdis: record.field_formatted_as_currency("cashdis"), cashdis: record.field_formatted_as_currency("cashdis"),
equity: "#{record.equity}%").html_safe equity: "#{record.equity}%").html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_not_used_socialhomebuy", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.non_staircasing_mortgage.mortgage_not_used_socialhomebuy",
deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"), deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"),
expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value"), expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
@ -216,7 +216,7 @@ module Validations::Sales::SaleInformationValidations
if over_tolerance?(record.mortgage_and_deposit_total, record.expected_shared_ownership_deposit_value, 1) if over_tolerance?(record.mortgage_and_deposit_total, record.expected_shared_ownership_deposit_value, 1)
%i[mortgage value deposit equity].each do |field| %i[mortgage value deposit equity].each do |field|
record.errors.add field, I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_used", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.non_staircasing_mortgage.mortgage_used",
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
@ -224,7 +224,7 @@ module Validations::Sales::SaleInformationValidations
mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"),
expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_used", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.non_staircasing_mortgage.mortgage_used",
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
@ -235,12 +235,12 @@ module Validations::Sales::SaleInformationValidations
elsif record.mortgage_not_used? elsif record.mortgage_not_used?
if over_tolerance?(record.deposit, record.expected_shared_ownership_deposit_value, 1) if over_tolerance?(record.deposit, record.expected_shared_ownership_deposit_value, 1)
%i[mortgageused value deposit equity].each do |field| %i[mortgageused value deposit equity].each do |field|
record.errors.add field, I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_not_used", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.non_staircasing_mortgage.mortgage_not_used",
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.non_staircasing_mortgage.mortgage_not_used", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.non_staircasing_mortgage.mortgage_not_used",
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe expected_shared_ownership_deposit_value: record.field_formatted_as_currency("expected_shared_ownership_deposit_value")).html_safe
@ -256,7 +256,7 @@ module Validations::Sales::SaleInformationValidations
if over_tolerance?(record.mortgage_deposit_and_discount_total, record.stairbought_part_of_value, 1) if over_tolerance?(record.mortgage_deposit_and_discount_total, record.stairbought_part_of_value, 1)
%i[mortgage value deposit cashdis stairbought].each do |field| %i[mortgage value deposit cashdis stairbought].each do |field|
record.errors.add field, I18n.t("validations.sale_information.staircasing_mortgage.mortgage_used_socialhomebuy", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.staircasing_mortgage.mortgage_used_socialhomebuy",
mortgage_deposit_and_discount_total: record.field_formatted_as_currency("mortgage_deposit_and_discount_total"), mortgage_deposit_and_discount_total: record.field_formatted_as_currency("mortgage_deposit_and_discount_total"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"), stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"),
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
@ -265,7 +265,7 @@ module Validations::Sales::SaleInformationValidations
cashdis: record.field_formatted_as_currency("cashdis"), cashdis: record.field_formatted_as_currency("cashdis"),
stairbought: "#{record.stairbought}%").html_safe stairbought: "#{record.stairbought}%").html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.staircasing_mortgage.mortgage_used_socialhomebuy", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_used_socialhomebuy",
mortgage_deposit_and_discount_total: record.field_formatted_as_currency("mortgage_deposit_and_discount_total"), mortgage_deposit_and_discount_total: record.field_formatted_as_currency("mortgage_deposit_and_discount_total"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"), stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"),
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
@ -276,7 +276,7 @@ module Validations::Sales::SaleInformationValidations
end end
elsif over_tolerance?(record.deposit_and_discount_total, record.stairbought_part_of_value, 1) elsif over_tolerance?(record.deposit_and_discount_total, record.stairbought_part_of_value, 1)
%i[mortgageused value deposit cashdis stairbought].each do |field| %i[mortgageused value deposit cashdis stairbought].each do |field|
record.errors.add field, I18n.t("validations.sale_information.staircasing_mortgage.mortgage_not_used_socialhomebuy", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.staircasing_mortgage.mortgage_not_used_socialhomebuy",
deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"), deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"), stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
@ -284,7 +284,7 @@ module Validations::Sales::SaleInformationValidations
cashdis: record.field_formatted_as_currency("cashdis"), cashdis: record.field_formatted_as_currency("cashdis"),
stairbought: "#{record.stairbought}%").html_safe stairbought: "#{record.stairbought}%").html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.staircasing_mortgage.mortgage_not_used_socialhomebuy", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_not_used_socialhomebuy",
deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"), deposit_and_discount_total: record.field_formatted_as_currency("deposit_and_discount_total"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"), stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
@ -300,14 +300,14 @@ module Validations::Sales::SaleInformationValidations
if over_tolerance?(record.mortgage_and_deposit_total, record.stairbought_part_of_value, 1) if over_tolerance?(record.mortgage_and_deposit_total, record.stairbought_part_of_value, 1)
%i[mortgage value deposit stairbought].each do |field| %i[mortgage value deposit stairbought].each do |field|
record.errors.add field, I18n.t("validations.sale_information.staircasing_mortgage.mortgage_used", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.staircasing_mortgage.mortgage_used",
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value")).html_safe stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value")).html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.staircasing_mortgage.mortgage_used", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_used",
mortgage: record.field_formatted_as_currency("mortgage"), mortgage: record.field_formatted_as_currency("mortgage"),
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"), mortgage_and_deposit_total: record.field_formatted_as_currency("mortgage_and_deposit_total"),
@ -316,12 +316,12 @@ module Validations::Sales::SaleInformationValidations
end end
elsif over_tolerance?(record.deposit, record.stairbought_part_of_value, 1) elsif over_tolerance?(record.deposit, record.stairbought_part_of_value, 1)
%i[mortgageused value deposit stairbought].each do |field| %i[mortgageused value deposit stairbought].each do |field|
record.errors.add field, I18n.t("validations.sale_information.staircasing_mortgage.mortgage_not_used", record.errors.add field, I18n.t("validations.sales.sale_information.#{field}.staircasing_mortgage.mortgage_not_used",
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value")).html_safe stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value")).html_safe
end end
record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sale_information.staircasing_mortgage.mortgage_not_used", record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.staircasing_mortgage.mortgage_not_used",
deposit: record.field_formatted_as_currency("deposit"), deposit: record.field_formatted_as_currency("deposit"),
value: record.field_formatted_as_currency("value"), value: record.field_formatted_as_currency("value"),
stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value")).html_safe stairbought_part_of_value: record.field_formatted_as_currency("stairbought_part_of_value")).html_safe
@ -336,15 +336,15 @@ module Validations::Sales::SaleInformationValidations
end end
if record.outright_sale? && record.saledate && !record.form.start_year_after_2024? if record.outright_sale? && record.saledate && !record.form.start_year_after_2024?
record.errors.add :mortgageused, I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?") record.errors.add :mortgageused, I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?")
record.errors.add :saledate, I18n.t("validations.financial.mortgage_used.year") record.errors.add :saledate, I18n.t("validations.sales.sale_information.saledate.mortgage_used_year")
end end
if record.shared_ownership_scheme? && record.is_not_staircasing? if record.shared_ownership_scheme? && record.is_not_staircasing?
record.errors.add :mortgageused, I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?") record.errors.add :mortgageused, I18n.t("validations.invalid_option", question: "was a mortgage used for the purchase of this property?")
record.errors.add :staircase, I18n.t("validations.financial.mortgage_used.staircasing") record.errors.add :staircase, I18n.t("validations.sales.sale_information.staircase.mortgage_used_value")
end end
if record.stairowned && !record.stairowned_100? if record.stairowned && !record.stairowned_100?
record.errors.add :stairowned, I18n.t("validations.sale_information.stairowned.mortgageused_dont_know") record.errors.add :stairowned, I18n.t("validations.sales.sale_information.stairowned.mortgageused_dont_know")
record.errors.add :mortgageused, I18n.t("validations.sale_information.stairowned.mortgageused_dont_know") record.errors.add :mortgageused, I18n.t("validations.sales.sale_information.mortgageused.mortgageused_dont_know")
end end
end end

43
config/locales/en.yml

@ -476,9 +476,6 @@ en:
over_max: "The maximum initial equity stake is %{max_equity}%." over_max: "The maximum initial equity stake is %{max_equity}%."
over_stairowned_minus_stairbought: "The initial equity stake is %{equity}% and the percentage owned in total minus the percentage bought is %{staircase_difference}%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the %{buyer_owns} minus the percentage bought." over_stairowned_minus_stairbought: "The initial equity stake is %{equity}% and the percentage owned in total minus the percentage bought is %{staircase_difference}%. In a staircasing transaction, the equity stake purchased cannot be larger than the percentage the %{buyer_owns} minus the percentage bought."
mortgage: "Mortgage value cannot be £0 if a mortgage was used for the purchase of this property." mortgage: "Mortgage value cannot be £0 if a mortgage was used for the purchase of this property."
mortgage_used:
year: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for the selected year."
staircasing: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for staircasing transactions."
shared_ownership_deposit: "The %{mortgage_deposit_and_discount_error_fields} added together is %{mortgage_deposit_and_discount_total}. The value times the equity percentage is %{value_times_equity}. These figures should be the same." shared_ownership_deposit: "The %{mortgage_deposit_and_discount_error_fields} added together is %{mortgage_deposit_and_discount_total}. The value times the equity percentage is %{value_times_equity}. These figures should be the same."
household: household:
@ -637,46 +634,6 @@ en:
before_deactivation: "This location was deactivated on %{date}. The reactivation date must be on or after deactivation date." before_deactivation: "This location was deactivated on %{date}. The reactivation date must be on or after deactivation date."
deactivation: deactivation:
during_deactivated_period: "The location is already deactivated during this date, please enter a different date." during_deactivated_period: "The location is already deactivated during this date, please enter a different date."
sale_information:
proplen:
social_homebuy: "Social HomeBuy buyers should not have lived here before."
rent_to_buy: "Rent to Buy buyers should not have lived here before."
hodate:
must_be_before_saledate: "Practical completion or handover date must be before sale completion date."
must_be_less_than_3_years_from_saledate: "Practical completion or handover date must be less than 3 years before sale completion date."
exdate:
must_be_before_saledate: "Contract exchange date must be before sale completion date."
must_be_less_than_1_year_from_saledate: "Contract exchange date must be less than 1 year before sale completion date."
saledate:
must_be_after_exdate: "Sale completion date must be after contract exchange date."
must_be_less_than_1_year_from_exdate: "Sale completion date must be less than 1 year after contract exchange date."
must_be_less_than_3_years_from_hodate: "Sale completion date must be less than 3 years after practical completion or handover date."
must_be_after_hodate: "Sale completion date must be after practical completion or handover date."
previous_property_type:
property_type_bedsit: "A bedsit cannot have more than 1 bedroom."
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
outright_sale_value: "The mortgage%{mortgage} and cash deposit (%{deposit}) when added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price is %{value}.</br></br>These two amounts should be the same."
monthly_rent:
higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00."
grant:
out_of_range: "Loan, grants or subsidies must be between £9,000 and £16,000."
stairbought:
over_max: "The percentage bought in this staircasing transaction cannot be higher than %{max_stairbought}% for %{type} sales."
value:
over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."
non_staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
stairowned:
mortgageused_dont_know: "The percentage owned has to be 100% if the mortgage used is 'Don’t know'"
merge_request: merge_request:
organisation_part_of_another_merge: "This organisation is part of another merge - select a different one." organisation_part_of_another_merge: "This organisation is part of another merge - select a different one."
organisation_not_selected: "Select an organisation from the search list." organisation_not_selected: "Select an organisation from the search list."

58
config/locales/forms/2023/lettings/household_needs.en.yml

@ -0,0 +1,58 @@
en:
forms:
2023:
lettings:
household_needs:
armedforces:
page_header: ""
check_answer_label: "Household links to UK armed forces"
hint_text: "This excludes national service.<br><br>If there are several people in the household with links to the UK armed forces, you should answer for the regular. If there’s no regular, answer for the reserve. If there’s no reserve, answer for the spouse or civil partner."
question_text: "Does anybody in the household have any links to the UK armed forces?"
leftreg:
page_header: ""
check_answer_label: "Person still serving in UK armed forces"
hint_text: ""
question_text: "Is the person still serving in the UK armed forces?"
reservist:
page_header: ""
check_answer_label: "Person seriously injured or ill as result of serving in UK armed forces"
hint_text: ""
question_text: "Was the person seriously injured or ill as a result of serving in the UK armed forces?"
preg_occ:
page_header: ""
check_answer_label: "Anybody in household pregnant"
hint_text: ""
question_text: "Is anybody in the household pregnant?"
housingneeds:
page_header: ""
check_answer_label: "Anybody with disabled access needs"
hint_text: ""
question_text: "Does anybody in the household have any disabled access needs?"
housingneeds_type:
page_header: "Disabled access needs"
housingneeds_type:
check_answer_label: "Disabled access needs"
hint_text: ""
question_text: "What type of access needs do they have?"
housingneeds_other:
check_answer_label: "Other disabled access needs"
hint_text: ""
question_text: "Do they have any other disabled access needs?"
illness:
page_header: ""
check_answer_label: "Anybody in household with physical or mental health condition"
hint_text: ""
question_text: "Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?"
condition_effects:
page_header: ""
check_answer_label: "How is person affected by condition or illness"
hint_text: "Select all that apply."
question_text: "How is the person affected by their condition or illness?"

81
config/locales/forms/2023/lettings/setup.en.yml

@ -0,0 +1,81 @@
en:
forms:
2023:
lettings:
setup:
owning_organisation_id:
page_header: ""
check_answer_label: "Stock owner"
hint_text: ""
question_text: "Which organisation owns this property?"
managing_organisation_id:
page_header: ""
check_answer_label: "Managing agent"
hint_text: ""
question_text: "Which organisation manages this letting?"
assigned_to_id:
page_header: ""
check_answer_label: "Log owner"
hint_text: ""
question_text: "Which user are you creating this log for?"
needstype:
page_header: ""
check_answer_label: "Needs type"
hint_text: "General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing can include direct access hostels, group homes, residential care and nursing homes."
question_text: "What is the needs type?"
scheme_id:
page_header: "Scheme"
check_answer_label: "Scheme name"
hint_text: "Enter postcode or scheme name"
question_text: "What scheme is this log for?"
location_id:
less_than_twenty:
page_header: "Location"
check_answer_label: "Location"
hint_text: ""
question_text: "Which location is this letting for?"
twenty_or_more:
page_header: "Location"
check_answer_label: "Location"
hint_text: '<div class="govuk-inset-text">This scheme has 20 or more locations.</div>Enter postcode or address.'
question_text: "Which location is this letting for?"
renewal:
page_header: ""
check_answer_label: "Property renewal"
hint_text: "A renewal is a letting to the same tenant in the same property. If the property was previously being used as temporary accommodation, then answer 'no'."
question_text: "Is this letting a renewal?"
startdate:
page_header: ""
check_answer_label: "Tenancy start date"
hint_text: ""
question_text: "What is the tenancy start date?"
rent_type:
page_header: "Rent Type"
rent_type:
check_answer_label: "Rent type"
hint_text: ""
question_text: "What is the rent type?"
irproduct_other:
check_answer_label: "Product name"
hint_text: ""
question_text: "Name of rent product"
tenancycode:
page_header: ""
check_answer_label: "Tenant code"
hint_text: "This is how you usually refer to this tenancy on your own systems."
question_text: "What is the tenant code?"
propcode:
page_header: ""
check_answer_label: "Property reference"
hint_text: "This is how you usually refer to this property on your own systems."
question_text: "What is the property reference?"

58
config/locales/forms/2024/lettings/household_needs.en.yml

@ -0,0 +1,58 @@
en:
forms:
2024:
lettings:
household_needs:
armedforces:
page_header: ""
check_answer_label: "Household links to UK armed forces"
hint_text: "This excludes national service.<br><br>If there are several people in the household with links to the UK armed forces, you should answer for the regular. If there’s no regular, answer for the reserve. If there’s no reserve, answer for the spouse or civil partner."
question_text: "Does anybody in the household have any links to the UK armed forces?"
leftreg:
page_header: ""
check_answer_label: "Person still serving in UK armed forces"
hint_text: ""
question_text: "Is the person still serving in the UK armed forces?"
reservist:
page_header: ""
check_answer_label: "Person seriously injured or ill as result of serving in UK armed forces"
hint_text: ""
question_text: "Was the person seriously injured or ill as a result of serving in the UK armed forces?"
preg_occ:
page_header: ""
check_answer_label: "Anybody in household pregnant"
hint_text: ""
question_text: "Is anybody in the household pregnant?"
housingneeds:
page_header: ""
check_answer_label: "Anybody with disabled access needs"
hint_text: ""
question_text: "Does anybody in the household have any disabled access needs?"
housingneeds_type:
page_header: "Disabled access needs"
housingneeds_type:
check_answer_label: "Disabled access needs"
hint_text: ""
question_text: "What type of access needs do they have?"
housingneeds_other:
check_answer_label: "Other disabled access needs"
hint_text: ""
question_text: "Do they have any other disabled access needs?"
illness:
page_header: ""
check_answer_label: "Anybody in household with physical or mental health condition"
hint_text: ""
question_text: "Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?"
condition_effects:
page_header: ""
check_answer_label: "How is person affected by condition or illness"
hint_text: "Select all that apply."
question_text: "How is the person affected by their condition or illness?"

87
config/locales/forms/2024/lettings/setup.en.yml

@ -0,0 +1,87 @@
en:
forms:
2024:
lettings:
setup:
owning_organisation_id:
page_header: ""
check_answer_label: "Stock owner"
hint_text: ""
question_text: "Which organisation owns this property?"
managing_organisation_id:
page_header: ""
check_answer_label: "Managing agent"
hint_text: ""
question_text: "Which organisation manages this letting?"
assigned_to_id:
page_header: ""
check_answer_label: "Log owner"
hint_text: ""
question_text: "Which user are you creating this log for?"
needstype:
page_header: ""
check_answer_label: "Needs type"
hint_text: "General needs housing includes both self-contained and shared housing without support or specific adaptations. Supported housing can include direct access hostels, group homes, residential care and nursing homes."
question_text: "What is the needs type?"
scheme_id:
page_header: "Scheme"
check_answer_label: "Scheme name"
hint_text: "Enter postcode or scheme name.<br><br>A supported housing scheme provides shared or self-contained housing for a particular client group, for example younger or vulnerable people."
question_text: "What scheme is this log for?"
location_id:
less_than_twenty:
page_header: "Location"
check_answer_label: "Location"
hint_text: ""
question_text: "Which location is this letting for?"
twenty_or_more:
page_header: "Location"
check_answer_label: "Location"
hint_text: '<div class="govuk-inset-text">This scheme has 20 or more locations.</div>Enter postcode or address.'
question_text: "Which location is this letting for?"
renewal:
page_header: ""
check_answer_label: "Property renewal"
hint_text: "If the property was previously being used as temporary accommodation, then answer 'no'."
question_text: "Is this letting a renewal of social housing to the same tenant in the same property?"
startdate:
page_header: ""
check_answer_label: "Tenancy start date"
hint_text: ""
question_text: "What is the tenancy start date?"
rent_type:
page_header: "Rent Type"
rent_type:
check_answer_label: "Rent type"
hint_text: ""
question_text: "What is the rent type?"
irproduct_other:
check_answer_label: "Product name"
hint_text: ""
question_text: "Name of rent product"
tenancycode:
page_header: ""
check_answer_label: "Tenant code"
hint_text: "This is how you usually refer to this tenancy on your own systems."
question_text: "What is the tenant code?"
propcode:
page_header: ""
check_answer_label: "Property reference"
hint_text: "This is how you usually refer to this property on your own systems."
question_text: "What is the property reference?"
declaration:
page_header: "Ministry of Housing, Communities and Local Government privacy notice"
check_answer_label: "Tenant has seen the privacy notice"
hint_text: ""
question_text: "Declaration"

125
config/locales/validations/sales/sale_information.en.yml

@ -0,0 +1,125 @@
en:
validations:
sales:
sale_information:
ownershipsch:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
outright_sale_value: "The mortgage%{mortgage} and cash deposit (%{deposit}) when added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price is %{value}.</br></br>These two amounts should be the same."
hodate:
must_be_before_saledate: "Practical completion or handover date must be before sale completion date."
must_be_less_than_3_years_from_saledate: "Practical completion or handover date must be less than 3 years before sale completion date."
saledate:
must_be_after_hodate: "Sale completion date must be after practical completion or handover date."
must_be_less_than_3_years_from_hodate: "Sale completion date must be less than 3 years after practical completion or handover date."
must_be_after_exdate: "Sale completion date must be after contract exchange date."
must_be_less_than_1_year_from_exdate: "Sale completion date must be less than 1 year after contract exchange date."
mortgage_used_year: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for the selected year."
exdate:
must_be_before_saledate: "Contract exchange date must be before sale completion date."
must_be_less_than_1_year_from_saledate: "Contract exchange date must be less than 1 year before sale completion date."
fromprop:
previous_property_type_bedsit: "A bedsit cannot have more than 1 bedroom."
frombeds:
previous_property_type_bedsit: "A bedsit cannot have more than 1 bedroom."
mortgageused:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
outright_sale_value: "The mortgage%{mortgage} and cash deposit (%{deposit}) when added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price is %{value}.</br></br>These two amounts should be the same."
mortgageused_dont_know: "The percentage owned has to be 100% if the mortgage used is 'Don’t know'"
non_staircasing_mortgage:
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
outright_sale_value: "The mortgage%{mortgage} and cash deposit (%{deposit}) when added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price is %{value}.</br></br>These two amounts should be the same."
non_staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
value:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
outright_sale_value: "The mortgage%{mortgage} and cash deposit (%{deposit}) when added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price is %{value}.</br></br>These two amounts should be the same."
value_over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
value_over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."
non_staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
deposit:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
outright_sale_value: "The mortgage%{mortgage} and cash deposit (%{deposit}) when added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price is %{value}.</br></br>These two amounts should be the same."
non_staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
cashdis:
non_staircasing_mortgage:
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
equity:
non_staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
discount:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
value_over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
value_over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."
grant:
discounted_ownership_value: "The mortgage%{mortgage}%{deposit_and_grant_sentence} added together is %{mortgage_deposit_and_grant_total}.</br></br>The full purchase price%{discount_sentence} is %{value_with_discount}.</br></br>These two amounts should be the same."
out_of_range: "Loan, grants or subsidies must be between £9,000 and £16,000."
mrent:
monthly_rent_higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00."
type:
monthly_rent_higher_than_expected: "Basic monthly rent must be between £0.00 and £9,999.00."
stairbought_over_max: "The percentage bought in this staircasing transaction cannot be higher than %{max_stairbought}% for %{type} sales."
non_staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage equity stake purchased (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{equity}) is %{expected_shared_ownership_deposit_value}.</br></br>These two amounts should be the same."
staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
stairbought:
stairbought_over_max: "The percentage bought in this staircasing transaction cannot be higher than %{max_stairbought}% for %{type} sales."
staircasing_mortgage:
mortgage_used: "The mortgage (%{mortgage}) and cash deposit (%{deposit}) added together is %{mortgage_and_deposit_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used: "The cash deposit is %{deposit}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_used_socialhomebuy: "The mortgage amount (%{mortgage}), cash deposit (%{deposit}), and cash discount (%{cashdis}) added together is %{mortgage_deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
mortgage_not_used_socialhomebuy: "The cash deposit (%{deposit}) and cash discount (%{cashdis}) added together is %{deposit_and_discount_total}.</br></br>The full purchase price (%{value}) multiplied by the percentage bought (%{stairbought}) is %{stairbought_part_of_value}.</br></br>These two amounts should be the same."
stairowned:
mortgageused_dont_know: "The percentage owned has to be 100% if the mortgage used is 'Don’t know'"
staircase:
mortgage_used_value: "You must answer either ‘yes’ or ‘no’ to the question ‘was a mortgage used’ for staircasing transactions."
la:
value_over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
value_over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."
uprn:
value_over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
value_over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."
postcode_full:
value_over_discounted_london_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £136,400 for properties in London."
value_over_discounted_max: "The percentage discount multiplied by the purchase price is %{discount_value}. This figure should not be more than £102,400 for properties outside of London."

2
spec/features/form/check_answers_page_lettings_logs_spec.rb

@ -167,14 +167,12 @@ RSpec.describe "Lettings Log Check Answers Page" do
it "displays inferred postcode with the location id" do it "displays inferred postcode with the location id" do
lettings_log.update!(location:) lettings_log.update!(location:)
visit("/lettings-logs/#{id}/setup/check-answers") visit("/lettings-logs/#{id}/setup/check-answers")
expect(page).to have_content("Location")
expect(page).to have_content(location.name) expect(page).to have_content(location.name)
end end
it "displays inferred postcode with the location_admin_district" do it "displays inferred postcode with the location_admin_district" do
lettings_log.update!(location:) lettings_log.update!(location:)
visit("/lettings-logs/#{id}/setup/check-answers") visit("/lettings-logs/#{id}/setup/check-answers")
expect(page).to have_content("Location")
expect(page).to have_content(location.location_admin_district) expect(page).to have_content(location.location_admin_district)
end end
end end

2
spec/features/lettings_log_spec.rb

@ -243,7 +243,7 @@ RSpec.describe "Lettings Log Features" do
context "when completing the setup lettings log section", :aggregate_failure do context "when completing the setup lettings log section", :aggregate_failure do
before do before do
Timecop.freeze(Time.zone.local(2023, 3, 3)) Timecop.freeze(Time.zone.local(2023, 4, 3))
Singleton.__init__(FormHandler) Singleton.__init__(FormHandler)
end end

8
spec/models/validations/sales/sale_information_validations_spec.rb

@ -197,8 +197,8 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
it "does add an error if it's a bedsit" do it "does add an error if it's a bedsit" do
sale_information_validator.validate_previous_property_unit_type(record) sale_information_validator.validate_previous_property_unit_type(record)
expect(record.errors["fromprop"]).to include(I18n.t("validations.sale_information.previous_property_type.property_type_bedsit")) expect(record.errors["fromprop"]).to include(I18n.t("validations.sales.sale_information.fromprop.previous_property_type_bedsit"))
expect(record.errors["frombeds"]).to include(I18n.t("validations.sale_information.previous_property_type.property_type_bedsit")) expect(record.errors["frombeds"]).to include(I18n.t("validations.sales.sale_information.frombeds.previous_property_type_bedsit"))
end end
end end
end end
@ -648,8 +648,8 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
it "adds an error" do it "adds an error" do
sale_information_validator.validate_basic_monthly_rent(record) sale_information_validator.validate_basic_monthly_rent(record)
expect(record.errors[:mrent]).to include(I18n.t("validations.sale_information.monthly_rent.higher_than_expected")) expect(record.errors[:mrent]).to include(I18n.t("validations.sales.sale_information.mrent.monthly_rent_higher_than_expected"))
expect(record.errors[:type]).to include(I18n.t("validations.sale_information.monthly_rent.higher_than_expected")) expect(record.errors[:type]).to include(I18n.t("validations.sales.sale_information.type.monthly_rent_higher_than_expected"))
end end
end end
end end

7
spec/requests/form_controller_spec.rb

@ -735,14 +735,19 @@ RSpec.describe FormController, type: :request do
end end
before do before do
Timecop.freeze(Time.zone.local(2023, 12, 1))
organisation.stock_owners << stock_owner organisation.stock_owners << stock_owner
organisation.managing_agents << managing_organisation organisation.managing_agents << managing_organisation
organisation.managing_agents << managing_organisation_too organisation.managing_agents << managing_organisation_too
organisation.reload organisation.reload
lettings_log.update!(owning_organisation: stock_owner, assigned_to: user, managing_organisation: organisation) lettings_log.update!(owning_organisation: stock_owner, assigned_to: user, managing_organisation: organisation, startdate: Time.zone.local(2023, 5, 1))
lettings_log.reload lettings_log.reload
end end
after do
Timecop.unfreeze
end
it "re-renders the same page with errors if validation fails" do it "re-renders the same page with errors if validation fails" do
post "/lettings-logs/#{lettings_log.id}/managing-organisation", params: params post "/lettings-logs/#{lettings_log.id}/managing-organisation", params: params
expect(page).to have_content("There is a problem") expect(page).to have_content("There is a problem")

2
spec/requests/lettings_logs_controller_spec.rb

@ -1538,7 +1538,7 @@ RSpec.describe LettingsLogsController, type: :request do
end end
context "when viewing a specific log affected by deactivated location" do context "when viewing a specific log affected by deactivated location" do
let!(:affected_lettings_log) { FactoryBot.create(:lettings_log, unresolved: true, assigned_to: user, needstype: 2) } let!(:affected_lettings_log) { FactoryBot.create(:lettings_log, unresolved: true, assigned_to: user, needstype: 2, startdate: Time.zone.local(2024, 4, 1)) }
let(:headers) { { "Accept" => "text/html" } } let(:headers) { { "Accept" => "text/html" } }
before do before do

4
spec/requests/locations_controller_spec.rb

@ -180,7 +180,7 @@ RSpec.describe LocationsController, type: :request do
context "when signed in as a data coordinator user" do context "when signed in as a data coordinator user" do
let(:user) { create(:user, :data_coordinator) } let(:user) { create(:user, :data_coordinator) }
let(:scheme) { create(:scheme, owning_organisation: user.organisation) } let(:scheme) { create(:scheme, owning_organisation: user.organisation, service_name: "Some name") }
let!(:locations) { create_list(:location, 3, scheme:, startdate: Time.zone.local(2022, 4, 1)) } let!(:locations) { create_list(:location, 3, scheme:, startdate: Time.zone.local(2022, 4, 1)) }
before do before do
@ -288,7 +288,7 @@ RSpec.describe LocationsController, type: :request do
it "has search in the title" do it "has search in the title" do
expected_title = CGI.escapeHTML("#{scheme.service_name} (1 location matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK") expected_title = CGI.escapeHTML("#{scheme.service_name} (1 location matching ‘#{search_param}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
expect(page).to have_title(expected_title) expect(page.title).to eq(expected_title)
end end
end end

Loading…
Cancel
Save