The following groups of cells might have conflicting data. Check the answers and fix any incorrect data.
If the answers are correct, fix the critical errors and reupload the file. You'll need to confirm that the following data is correct when the file only contains potential errors.
+
The following groups of cells might have conflicting data. Check the answers and fix any incorrect data.
If the answers are correct, fix the critical errors and upload the file again. You'll need to confirm that the following data is correct when the file only contains potential errors.
<%= govuk_table(html_attributes: { class: "no-bottom-border" }) do |table| %>
<%= table.with_head do |head| %>
<% head.with_row do |row| %>
diff --git a/app/components/primary_navigation_component.html.erb b/app/components/primary_navigation_component.html.erb
index 9cad3bc64..032c02b81 100644
--- a/app/components/primary_navigation_component.html.erb
+++ b/app/components/primary_navigation_component.html.erb
@@ -1,17 +1,5 @@
-
+<%= govuk_service_navigation(navigation_id: "primary-navigation", classes: "app-service-navigation") do |sn|
+ items.each do |item|
+ sn.with_navigation_item(text: item[:text], href: item[:href], classes: "", current: item[:current])
+ end
+ end %>
diff --git a/app/components/search_component.html.erb b/app/components/search_component.html.erb
index 5e3eb4ae3..62dca0ec2 100644
--- a/app/components/search_component.html.erb
+++ b/app/components/search_component.html.erb
@@ -1,4 +1,4 @@
-<%= form_with model: @user, url: path(current_user), method: "get", local: true do |f| %>
+<%= form_with url: path(current_user), method: "get", local: true do |f| %>
Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.
-
Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.
-
Use the <%= govuk_link_to "Lettings bulk upload Specification (2024 to 2025)", @form.specification_path %> to check your data is in the correct format.
-
Username field: To assign a log to someone else, enter the email address they use to log into CORE.
-
If you have reordered the headers, keep the headers in the file.
-
-
- <%= govuk_inset_text(text: "You can upload both general needs and supported housing logs in the same file for 2024 to 2025 data.") %>
+ <%= govuk_list [
+ "Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. Leave column A blank - the bulk upload fields start in column B.",
+ "Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.",
+ "Use the #{govuk_link_to 'Lettings bulk upload Specification (2024 to 2025)', @form.specification_path} to check your data is in the correct format.".html_safe,
+ "Username field: To assign a log to someone else, enter the email address they use to log into CORE.".html_safe,
+ "If you have reordered the headers, keep the headers in the file.",
+ ], type: :bullet %>
Save your file
-
-
Save your file as a CSV.
-
Your file should now be ready to upload.
-
+ <%= govuk_list ["Save your file as a CSV.", "Your file should now be ready to upload."], type: :bullet %>
<%= f.govuk_submit class: "govuk-!-margin-top-7" %>
<% end %>
diff --git a/app/views/bulk_upload_lettings_resume/deletion_report.html.erb b/app/views/bulk_upload_lettings_resume/deletion_report.html.erb
index a13979dfd..d3533a130 100644
--- a/app/views/bulk_upload_lettings_resume/deletion_report.html.erb
+++ b/app/views/bulk_upload_lettings_resume/deletion_report.html.erb
@@ -28,6 +28,6 @@
<%= form_with model: @form, scope: :form, url: page_bulk_upload_lettings_resume_path(@bulk_upload, "confirm"), method: :patch do |f| %>
<%= f.govuk_submit "Clear this data and upload the logs" %>
- <%= govuk_button_link_to "I have fixed these errors and I want to reupload the file", start_bulk_upload_lettings_logs_path, secondary: true %>
+ <%= govuk_button_link_to "I have fixed these errors and I want to upload the file again", start_bulk_upload_lettings_logs_path, secondary: true %>
<% end %>
diff --git a/app/views/bulk_upload_lettings_resume/fix_choice.html.erb b/app/views/bulk_upload_lettings_resume/fix_choice.html.erb
index 383cdbfa1..225fb07bf 100644
--- a/app/views/bulk_upload_lettings_resume/fix_choice.html.erb
+++ b/app/views/bulk_upload_lettings_resume/fix_choice.html.erb
@@ -24,17 +24,9 @@
<%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %>
You may find it easier to fix the errors in the CSV file if:
-
-
you have a lot of errors
-
the CSV file is formatted incorrectly and you can see where the errors are
-
you need to fix multiple errors at once
-
+ <%= govuk_list ["you have a lot of errors", "the CSV file is formatted incorrectly and you can see where the errors are", "you need to fix multiple errors at once"], type: :bullet %>
You may find it easier to fix the errors on the CORE site if:
-
-
you need to see the data in context
-
you have a smaller file, with a few errors
-
you are not sure where the errors are
-
+ <%= govuk_list ["you need to see the data in context", "you have a smaller file, with a few errors", "you are not sure where the errors are"], type: :bullet %>
<% end %>
<%= f.govuk_collection_radio_buttons :choice,
diff --git a/app/views/bulk_upload_lettings_soft_validations_check/confirm.html.erb b/app/views/bulk_upload_lettings_soft_validations_check/confirm.html.erb
index 90f5a00b3..e17b55130 100644
--- a/app/views/bulk_upload_lettings_soft_validations_check/confirm.html.erb
+++ b/app/views/bulk_upload_lettings_soft_validations_check/confirm.html.erb
@@ -5,7 +5,7 @@
Upload lettings logs in bulk (<%= @bulk_upload.year_combo %>)
-
You have chosen to upload all logs from this bulk upload.
+
Are you sure you want to upload all logs from this bulk upload?
There are 8 rows of content in the templates. These rows are called the ‘headers’. They contain the CORE form questions and guidance about which questions are required and how to format your answers.
Create your file
-
-
Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. The bulk upload fields start at column B. Leave column A blank.
-
Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.
-
Use the <%= govuk_link_to "Sales bulk upload Specification (2024 to 2025)", @form.specification_path %> to check your data is in the correct format.
-
Username field: To assign a log to someone else, enter the email address they use to log into CORE.
-
If you have reordered the headers, keep the headers in the file.
-
+ <%= govuk_list [
+ "Fill in the template with data from your housing management system. Your data should go below the headers, with one row per log. The bulk upload fields start at column B. Leave column A blank.",
+ "Make sure each column of your data aligns with the matching headers above. You may need to reorder your data.",
+ "Use the #{govuk_link_to 'Sales bulk upload Specification (2024 to 2025)', @form.specification_path} to check your data is in the correct format.".html_safe,
+ "Username field: To assign a log to someone else, enter the email address they use to log into CORE.".html_safe,
+ "If you have reordered the headers, keep the headers in the file.",
+ ], type: :bullet %>
Save your file
-
-
Save your file as a CSV.
-
Your file should now be ready to upload.
-
+ <%= govuk_list ["Save your file as a CSV.", "Your file should now be ready to upload."], type: :bullet %>
<%= f.govuk_submit %>
<% end %>
diff --git a/app/views/bulk_upload_sales_resume/deletion_report.html.erb b/app/views/bulk_upload_sales_resume/deletion_report.html.erb
index bcc044439..e9013a4e2 100644
--- a/app/views/bulk_upload_sales_resume/deletion_report.html.erb
+++ b/app/views/bulk_upload_sales_resume/deletion_report.html.erb
@@ -28,6 +28,6 @@
<%= form_with model: @form, scope: :form, url: page_bulk_upload_sales_resume_path(@bulk_upload, "confirm"), method: :patch do |f| %>
<%= f.govuk_submit "Clear this data and upload the logs" %>
- <%= govuk_button_link_to "I have fixed these errors and I want to reupload the file", start_bulk_upload_sales_logs_path, secondary: true %>
+ <%= govuk_button_link_to "I have fixed these errors and I want to upload the file again", start_bulk_upload_sales_logs_path, secondary: true %>
<% end %>
<%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %>
-
You may find it easier to fix the errors in the CSV file if:
-
-
you have a lot of errors
-
the CSV file is formatted incorrectly and you can see where the errors are
-
you need to fix multiple errors at once
-
+
You may find it easier to fix the errors in the CSV file if:
+ <%= govuk_list ["you have a lot of errors", "the CSV file is formatted incorrectly and you can see where the errors are", "you need to fix multiple errors at once"], type: :bullet %>
You may find it easier to fix the errors on the CORE site if:
-
-
you need to see the data in context
-
you have a smaller file, with a few errors
-
you are not sure where the errors are
-
+ <%= govuk_list ["you need to see the data in context", "you have a smaller file, with a few errors", "you are not sure where the errors are"], type: :bullet %>
<% end %>
<%= f.govuk_collection_radio_buttons :choice,
diff --git a/app/views/bulk_upload_sales_soft_validations_check/confirm.html.erb b/app/views/bulk_upload_sales_soft_validations_check/confirm.html.erb
index 2b4a6e981..5c81c179d 100644
--- a/app/views/bulk_upload_sales_soft_validations_check/confirm.html.erb
+++ b/app/views/bulk_upload_sales_soft_validations_check/confirm.html.erb
@@ -5,7 +5,7 @@
Upload sales logs in bulk (<%= @bulk_upload.year_combo %>)
-
You have chosen to upload all logs from this bulk upload.
+
Are you sure you want to upload all logs from this bulk upload?
diff --git a/app/views/bulk_upload_shared/_moved_user_banner.html.erb b/app/views/bulk_upload_shared/_moved_user_banner.html.erb
index 9ab97022e..220405cc5 100644
--- a/app/views/bulk_upload_shared/_moved_user_banner.html.erb
+++ b/app/views/bulk_upload_shared/_moved_user_banner.html.erb
@@ -4,9 +4,9 @@
This error report is out of date.
<% if current_user.id == @bulk_upload.moved_user_id %>
- You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.
+ You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.
<% else %>
- Some logs in this upload are assigned to <%= @bulk_upload.moved_user_name %>, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.
+ Some logs in this upload are assigned to <%= @bulk_upload.moved_user_name %>, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.
<% end %>
<% end %>
<% end %>
diff --git a/app/views/bulk_upload_shared/guidance.html.erb b/app/views/bulk_upload_shared/guidance.html.erb
index 4cb8b76b4..54e8063f0 100644
--- a/app/views/bulk_upload_shared/guidance.html.erb
+++ b/app/views/bulk_upload_shared/guidance.html.erb
@@ -34,12 +34,7 @@
The bulk upload templates contain 8 rows of ‘headers’ with information about how to fill in the template, including:
<% end %>
-
-
the CORE form questions and their field numbers
-
each field’s valid responses
-
if/when certain fields can be left blank
-
which fields are used to check for duplicate logs
-
+ <%= govuk_list ["the CORE form questions and their field numbers", "each field’s valid responses", "if/when certain fields can be left blank", "which fields are used to check for duplicate logs"], type: :bullet %>
You can paste your data below the headers or copy the headers and insert them above the data in your file. The bulk upload fields start at column B. Leave column A blank.
Make sure that each column of data aligns with the corresponding question in the headers. We recommend ordering your data to match the headers, but you can also reorder the headers to match your data. When processing the file, we check what each column of data represents based on the headers above.
@@ -80,7 +75,7 @@
Once you've saved your CSV file, you can upload it via a button at the top of the lettings and sales logs pages.
When your file is done processing, you will receive an email explaining your next steps. If all your data is valid, your logs will be created. If some data is invalid, you’ll receive an email with instructions about how to resolve the errors.
If your file has errors on fields 1 through 15 for lettings, or 1 through 18 for sales, you must fix these in the CSV. This is because we need to know these answers to validate the rest of the data. Any errors in these fields will be featured in the error report’s summary tab.
-
If none of your errors are in fields 1 through 15 for lettings, or 1 through 18 for sales, you can choose how to fix the errors. You can either fix them in the CSV and reupload, or create partially complete logs and answer the remaining questions on the CORE site. Any errors that affect a significant number of logs will be featured in the error report’s summary tab to help you decide.
+
If none of your errors are in fields 1 through 15 for lettings, or 1 through 18 for sales, you can choose how to fix the errors. You can either fix them in the CSV and upload the file again, or create partially complete logs and answer the remaining questions on the CORE site. Any errors that affect a significant number of logs will be featured in the error report’s summary tab to help you decide.
Google is not allowed to use or share our analytics data with anyone.
Google Analytics stores anonymised information about:
-
-
how you got to the service
-
the pages you visit on the service and how long you spend on them
-
any errors you see while using the service
-
+ <%= govuk_list ["how you got to the service", "the pages you visit on the service and how long you spend on them", "any errors you see while using the service"], type: :bullet %>
Regardless of the merge type, you’ll be asked for:
-
-
the organisations merging in CORE
-
the merge type: for example one organisation absorbing the rest, or them all creating a new organisation
-
to confirm or update the telephone number
-
the merge date
-
if user email addresses will change with this merge
-
+ <%= govuk_list [
+ "the organisations merging in CORE",
+ "the merge type: for example one organisation absorbing the rest, or them all creating a new organisation",
+ "to confirm or update the telephone number",
+ "the merge date",
+ "if user email addresses will change with this merge",
+ ], type: :bullet %>
If email addresses are changing
<%= govuk_inset_text(text: "Update all user email addresses on CORE as soon as they change, so that everyone can still access their CORE account. ") %>
@@ -30,14 +30,14 @@
If merging into a new organisation
You'll also be asked for the new organisation’s:
-
-
name
-
address
-
telephone number
-
housing provider type
-
Regulator of Social Housing registration number
-
held stock details
-
+ <%= govuk_list [
+ "name",
+ "address",
+ "telephone number",
+ "housing provider type",
+ "Regulator of Social Housing registration number",
+ "held stock details",
+ ], type: :bullet %>
<%= govuk_warning_text text: "You will not be able to submit your request without the above information. Do not start the form until you have obtained all of the information. " %>
diff --git a/app/views/organisation_relationships/add_managing_agent.html.erb b/app/views/organisation_relationships/add_managing_agent.html.erb
index 25c7c53a2..71e4b5abc 100644
--- a/app/views/organisation_relationships/add_managing_agent.html.erb
+++ b/app/views/organisation_relationships/add_managing_agent.html.erb
@@ -28,12 +28,10 @@
<%= govuk_button_link_to("Cancel", managing_agents_organisation_path(@organisation), secondary: true) %>
<%= govuk_details(summary_text: "Can't find the managing agent you're looking for?") do %>
-
-
Double check the spelling and try again
-
Type the first few letters to see the suggestions
-
If you still can't find it,
- <%= govuk_link_to("contact the MHCLG service desk", GlobalConstants::HELPDESK_URL, rel: "noreferrer noopener", target: "_blank") %>
-
-
+ <%= govuk_list [
+ "Double check the spelling and try again",
+ "Type the first few letters to see the suggestions",
+ "If you still can't find it, #{govuk_link_to('contact the MHCLG service desk', GlobalConstants::HELPDESK_URL, rel: 'noreferrer noopener', target: '_blank')}",
+ ], type: :bullet %>
<% end %>
<% end %>
diff --git a/app/views/organisation_relationships/add_stock_owner.html.erb b/app/views/organisation_relationships/add_stock_owner.html.erb
index 042442125..1bd522f54 100644
--- a/app/views/organisation_relationships/add_stock_owner.html.erb
+++ b/app/views/organisation_relationships/add_stock_owner.html.erb
@@ -28,12 +28,10 @@
<%= govuk_button_link_to("Cancel", stock_owners_organisation_path(@organisation), secondary: true) %>
<%= govuk_details(summary_text: "Can't find the stock owner you're looking for?") do %>
-
-
Double check the spelling and try again
-
Type the first few letters to see the suggestions
-
If you still can't find it,
- <%= govuk_link_to("contact the MHCLG service desk", GlobalConstants::HELPDESK_URL, rel: "noreferrer noopener", target: "_blank") %>
-
2.1. In this Agreement the following words and phrases shall have the following meanings, unless expressly stated to the contrary:
-
-
“Act” means the Data Protection Act 2018;
-
“Authorised Representatives” means the nominated lead officer representing each of the
- parties with delegated authority to handle the day-to-day matters arising from this Agreement;
-
“Data Subject” means social housing lettings tenants and participants in discounted sales where their data is reported via the CORE system.
-
“Data Controller” has the meaning in Article 4(7) of the GDPR and section 5(2) of the Act.
-
“Data Processor” has the meaning in Article 4(8) of the GDPR.
-
“Data Protection Legislation” means the Data Protection Act 2018 and all applicable laws and regulations relating to the processing of personal data and privacy, including where applicable the guidance and codes of practice issued by the Information Commissioner; it includes the General Data Protection Regulation (GDPR).
-
“Data” means the data supplied by the CORE data providers via the CORE system and the data that is calculated or derived via the CORE system based on that initial data;
-
“GDPR” means the General Data Protection Regulation.
-
“Parties” means the parties to this Agreement, namely MHCLG and the CORE data providers. CORE data providers include social housing providers and managing organisations that provide data on behalf of the social housing providers.
-
“Personal Data” has the meaning in Article 4(1) of the GDPR. “Processing” has the meaning in Article 4(2) of the GDPR.
-
“Request for Information” means a request for information or a request under the Freedom of Information Act 2000.
-
“Special category personal data” has the meaning in Article 9(1) of the GDPR. In this Agreement:
-
-
A. The masculine includes the feminine and neuter;
-
B. Person means a natural person;
-
C. The singular includes the plural and vice versa;
-
D. A reference to any statute, enactment, order, regulation or other similar instrument
- shall be construed as a reference to the statute, enactment, order, regulation or instrument as amended by any subsequent statute, enactment, order, regulation or instrument or as contained in any subsequent re-enactment.
-
-
-
-
+ <%= govuk_list [
+ "“Act” means the Data Protection Act 2018;",
+ "“Authorised Representatives” means the nominated lead officer representing each of the parties with delegated authority to handle the day-to-day matters arising from this Agreement;",
+ "“Data Subject” means social housing lettings tenants and participants in discounted sales where their data is reported via the CORE system.",
+ "“Data Controller” has the meaning in Article 4(7) of the GDPR and section 5(2) of the Act.",
+ "“Data Processor” has the meaning in Article 4(8) of the GDPR.",
+ "“Data Protection Legislation” means the Data Protection Act 2018 and all applicable laws and regulations relating to the processing of personal data and privacy, including where applicable the guidance and codes of practice issued by the Information Commissioner; it includes the General Data Protection Regulation (GDPR).",
+ "“Data” means the data supplied by the CORE data providers via the CORE system and the data that is calculated or derived via the CORE system based on that initial data;",
+ "“GDPR” means the General Data Protection Regulation.",
+ "“Parties” means the parties to this Agreement, namely MHCLG and the CORE data providers. CORE data providers include social housing providers and managing organisations that provide data on behalf of the social housing providers.",
+ "“Personal Data” has the meaning in Article 4(1) of the GDPR. “Processing” has the meaning in Article 4(2) of the GDPR.",
+ "“Request for Information” means a request for information or a request under the Freedom of Information Act 2000.",
+ "“Special category personal data” has the meaning in Article 9(1) of the GDPR. In this Agreement:
+ #{ govuk_list([
+ 'A. The masculine includes the feminine and neuter;',
+ 'B. Person means a natural person;',
+ 'C. The singular includes the plural and vice versa;',
+ 'D. A reference to any statute, enactment, order, regulation or other similar instrument shall be construed as a reference to the statute, enactment, order, regulation or instrument as amended by any subsequent statute, enactment, order, regulation or instrument or as contained in any subsequent re-enactment.',
+ ], type: :bullet)}".html_safe,
+ ], type: :bullet %>
2.2. Headings are included in this Agreement for ease of reference only and shall not affect the interpretation or construction of this Agreement.
2.3. References in this Agreement to Clauses, Paragraphs and Annexes are, unless otherwise provided, references to the Clauses, Paragraphs and Annexes of this Agreement.
2.4. In the event and to the extent only of any conflict or inconsistency between the provisions of this Agreement and the provisions of any document referred to or referenced herein, the provisions of this Agreement shall prevail.
@@ -95,11 +91,13 @@
8.5. All work carried out by MHCLG will follow appropriate security measures and procedures to ensure the protection of the data.
9. Protection of personal data
9.1. CORE data providers and MHCLG agree that they shall:
-
-
A. Implement appropriate technical and organisational measures to protect the Personal Data against unauthorised or unlawful Processing and against accidental loss, destruction, damage, alteration or disclosure. These measures shall ensure a level of security appropriate to the harm which might result from any unauthorised or unlawful Processing, accidental loss, destruction or damage to the Personal Data and having regard to the nature of the Personal Data which is to be protected;
-
B. Take reasonable steps to ensure the reliability of any personnel who have access to the Personal Data. MHCLG and Data providers will ensure such personnel will be a limited number of analysts assigned to the data collection.
-
+ <%= govuk_list [
+ "A. Implement appropriate technical and organisational measures to protect the Personal Data against unauthorised or unlawful Processing and against accidental loss, destruction, damage, alteration or disclosure. These measures shall ensure a level of security appropriate to the harm which might result from any unauthorised or unlawful Processing, accidental loss, destruction or damage to the Personal Data and having regard to the nature of the Personal Data which is to be protected;",
+ "B. Take reasonable steps to ensure the reliability of any personnel who have access to the Personal Data. MHCLG and Data providers will ensure such personnel will be a limited number of analysts assigned to the data collection.",
+],
+ type: :bullet %>
+
9.2. The data providers and MHCLG shall comply at all times with the Data Protection Legislation and shall ensure that they each perform their obligations under this agreement in full compliance with the Data Protection Legislation and any other applicable law, in particular the Human Rights Act 1998 and the common law duty of confidentiality.
9.3. CORE data providers should limit access to CORE to a small number of individuals who can be named on request. CORE access is limited to registered users only via password, but it is the responsibility of the CORE data providers to ensure that all individuals granted access to the datasets should be briefed on the legal requirements around handling and storing the Data from CORE.
10. Freedom of information
@@ -128,15 +126,10 @@
16.1. The Parties shall comply with all relevant legislation, regulations, orders, statutory instruments and any amendments or re-enactments thereof from the commencement of this agreement.
As witness of which the parties have set their hands on the day and year first above written
signed for and on behalf of the Data Protection Officer for <%= org_name_for_data_sharing_agreement(@data_protection_confirmation, current_user) %>, by:
Since your organisation recently merged, we’ve reviewed your schemes for possible duplicates.
These sets of schemes and locations might be duplicates because they have the same answers for certain fields.
What you need to do
-
-
Review each set of schemes or locations and decide if they are duplicates.
-
If they are, choose one to keep and deactivate the others on the date your organisation merged.
-
When you have resolved all duplicates, confirm below.
-
+ <%= govuk_list [
+ "Review each set of schemes or locations and decide if they are duplicates.",
+ "If they are, choose one to keep and deactivate the others on the date your organisation merged.",
+ "When you have resolved all duplicates, confirm below.",
+ ], type: :bullet %>
If you need help with this, <%= govuk_link_to "contact the helpdesk (opens in a new tab)", GlobalConstants::HELPDESK_URL, target: "#" %>.
<% if @duplicate_schemes.any? %>
@@ -43,17 +43,17 @@
These schemes have the same answers for the following fields:
-
-
Type of scheme
-
Registered under Care Standards Act 2000
-
Housing stock owned by
-
Support services provided by
-
Primary client group
-
Has another client group
-
Secondary client group
-
Level of support given
-
Intended length of stay
-
+ <%= govuk_list [
+ "Type of scheme",
+ "Registered under Care Standards Act 2000",
+ "Housing stock owned by",
+ "Support services provided by",
+ "Primary client group",
+ "Has another client group",
+ "Secondary client group",
+ "Level of support given",
+ "Intended length of stay",
+ ], type: :bullet %>
<% end %>
The links below open in a new tab.
@@ -68,13 +68,7 @@
<% @duplicate_schemes.each do |duplicate_set| %>
<% body.with_row do |row| %>
<% row.with_cell do %>
-
- <% duplicate_set.each do |scheme| %>
-
We have restructured the way we group supported housing scheme data.
On old CORE, a scheme’s data was split into management group and scheme. On new CORE, the data is split into scheme and location.
These are the main changes:
-
-
Schemes now store data about the stock owner and support provider. This was previously stored in management group.
-
Schemes still store data about the type of support, as they did in old CORE.
-
Schemes no longer store data about postcodes where support is provided. These are now in locations.
-
+ <%= govuk_list [
+ "Schemes now store data about the stock owner and support provider. This was previously stored in management group.",
+ "Schemes still store data about the type of support, as they did in old CORE.",
+ "Schemes no longer store data about postcodes where support is provided. These are now in locations.",
+ ], type: :bullet %>
This new structure means data coordinators only needs to fill in support details once.
How schemes are migrated from old CORE
If your organisation has migrated from old CORE to new CORE, your existing schemes are on the <%= govuk_link_to("schemes page", schemes_path) %>, with the same details.
diff --git a/app/views/schemes/confirm_secondary.html.erb b/app/views/schemes/confirm_secondary.html.erb
index 9b715c264..9feca87bc 100644
--- a/app/views/schemes/confirm_secondary.html.erb
+++ b/app/views/schemes/confirm_secondary.html.erb
@@ -1,7 +1,7 @@
<% content_for :title, "Does this scheme provide for another client group?" %>
<% content_for :before_content do %>
- <%= govuk_back_link(href: :back) %>
+ <%= govuk_back_link(href: scheme_back_button_path(@scheme, "confirm_secondary_client_group")) %>
<% end %>
<%= render partial: "organisations/headings", locals: { main: "Does this scheme provide for another client group?", sub: @scheme.service_name } %>
diff --git a/app/views/schemes/details.html.erb b/app/views/schemes/details.html.erb
index 4b23ab016..d1943cabe 100644
--- a/app/views/schemes/details.html.erb
+++ b/app/views/schemes/details.html.erb
@@ -1,7 +1,7 @@
<% content_for :title, "Create a new supported housing scheme" %>
<% content_for :before_content do %>
- <%= govuk_back_link(href: :back) %>
+ <%= govuk_back_link(href: scheme_back_button_path(@scheme, "details")) %>
<% end %>
<%= form_for(@scheme, method: :patch) do |f| %>
diff --git a/app/views/schemes/primary_client_group.html.erb b/app/views/schemes/primary_client_group.html.erb
index 55dc504b4..68da2f35c 100644
--- a/app/views/schemes/primary_client_group.html.erb
+++ b/app/views/schemes/primary_client_group.html.erb
@@ -1,13 +1,7 @@
<% content_for :title, "What client group is this scheme intended for?" %>
-<% if request.referer&.include?("new") %>
- <% back_button_path = scheme_details_path(@scheme) %>
-<% else %>
- <% back_button_path = :back %>
-<% end %>
-
<% content_for :before_content do %>
- <%= govuk_back_link(href: back_button_path) %>
+ <%= govuk_back_link(href: scheme_back_button_path(@scheme, "primary_client_group")) %>
<% end %>
<%= form_for(@scheme, method: :patch) do |f| %>
diff --git a/app/views/schemes/secondary_client_group.html.erb b/app/views/schemes/secondary_client_group.html.erb
index cd50283e3..8200702d2 100644
--- a/app/views/schemes/secondary_client_group.html.erb
+++ b/app/views/schemes/secondary_client_group.html.erb
@@ -1,7 +1,7 @@
<% content_for :title, "What is the other client group?" %>
<% content_for :before_content do %>
- <%= govuk_back_link(href: :back) %>
+ <%= govuk_back_link(href: scheme_back_button_path(@scheme, "secondary_client_group")) %>
<% end %>
<%= form_for(@scheme, method: :patch) do |f| %>
diff --git a/app/views/schemes/support.html.erb b/app/views/schemes/support.html.erb
index a5d30ed7f..eca72014f 100644
--- a/app/views/schemes/support.html.erb
+++ b/app/views/schemes/support.html.erb
@@ -1,7 +1,7 @@
<% content_for :title, "What support does this scheme provide?" %>
<% content_for :before_content do %>
- <%= govuk_back_link(href: :back) %>
+ <%= govuk_back_link(href: scheme_back_button_path(@scheme, "support")) %>
<% end %>
<%= form_for(@scheme, method: :patch) do |f| %>
diff --git a/app/views/start/guidance.html.erb b/app/views/start/guidance.html.erb
index 6fbd157f0..367c6f05f 100644
--- a/app/views/start/guidance.html.erb
+++ b/app/views/start/guidance.html.erb
@@ -21,46 +21,50 @@
<%= accordion.with_section(heading_text: "Types of lettings you should create logs for") do %>
You’ll need to create a log for:
-
-
Tenants in general needs housing allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. If fixed-term and social or affordable rent, only include tenancies of 2 years or more.
-
Tenants in supported housing (social housing, sheltered accommodation and care homes) allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. All supported housing tenancies should be reported regardless of length.
-
Starter tenancies provided by local authorities (LAs) and lettings with an introductory period provided by private registered providers (PRPs) should be completed in CORE at the beginning of the starter or introductory period. The tenancy type and length entered should be based on the tenancy the tenant will roll onto once the starter or introductory period has been completed. You do not need to submit another CORE log once the period has been completed.
-
Room moves within a shared housing unit that result in a different property type or support needs – this is classed as an internal transfer of an existing social tenant to another property.
-
Existing tenants who are issued with a new tenancy agreement when stock is acquired, transferred or permanently decanted.
-
Tenants under the Rough Sleepers Initiative or Rough Sleeping Accommodation Programme, where accommodation is permanent.
-
Households previously provided with temporary accommodation to meet a duty under the homelessness legislation who are allocated a tenancy as a settled home ending the duty (this may be the same property).
-
Refugees and asylum seekers who have been granted indefinite leave to remain, humanitarian protection or exceptional leave to remain.
-
Affordable Rent lettings – where up to 80% of market rent can be charged and a new supply agreement is signed.
-
London Affordable Rent lettings – a type of Affordable Rent available in London through the Greater London Authority (GLA).
-
Intermediate Rent lettings – where the rent must not exceed 80% of the current market rate (including any service charges).
-
Rent to Buy lettings – where a discount of up to 20% market rent is charged for a single rental period for a minimum of 5 years. After that period, the tenant is offered the chance to purchase the property (either shared ownership or outright) at full market value.
-
London Living Rent lettings – a type of Intermediate Rent available in London through the Greater London Authority (GLA).
-
+ <%= govuk_list [
+ "Tenants in general needs housing allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. If fixed-term and social or affordable rent, only include tenancies of 2 years or more.",
+ "Tenants in supported housing (social housing, sheltered accommodation and care homes) allocated a new letting. This includes tenants moving into the social rented sector from outside, existing social tenants moving between properties or landlords, and existing social tenants renewing lettings in the same property. All supported housing tenancies should be reported regardless of length.",
+ "Starter tenancies provided by local authorities (LAs) and lettings with an introductory period provided by private registered providers (PRPs) should be completed in CORE at the beginning of the starter or introductory period. The tenancy type and length entered should be based on the tenancy the tenant will roll onto once the starter or introductory period has been completed. You do not need to submit another CORE log once the period has been completed.",
+ "Room moves within a shared housing unit that result in a different property type or support needs – this is classed as an internal transfer of an existing social tenant to another property.",
+ "Existing tenants who are issued with a new tenancy agreement when stock is acquired, transferred or permanently decanted.",
+ "Tenants under the Rough Sleepers Initiative or Rough Sleeping Accommodation Programme, where accommodation is permanent.",
+ "Households previously provided with temporary accommodation to meet a duty under the homelessness legislation who are allocated a tenancy as a settled home ending the duty (this may be the same property).",
+ "Refugees and asylum seekers who have been granted indefinite leave to remain, humanitarian protection or exceptional leave to remain.",
+ "Affordable Rent lettings – where up to 80% of market rent can be charged and a new supply agreement is signed.",
+ "London Affordable Rent lettings – a type of Affordable Rent available in London through the Greater London Authority (GLA).",
+ "Intermediate Rent lettings – where the rent must not exceed 80% of the current market rate (including any service charges).",
+ "Rent to Buy lettings – where a discount of up to 20% market rent is charged for a single rental period for a minimum of 5 years. After that period, the tenant is offered the chance to purchase the property (either shared ownership or outright) at full market value.",
+ "London Living Rent lettings – a type of Intermediate Rent available in London through the Greater London Authority (GLA).",
+ ],
+ type: :bullet %>
<% end %>
<%= accordion.with_section(heading_text: "Types of lettings you should not create logs for") do %>
You don’t need to create a log for:
-
-
Temporary general needs housing with a fixed period of less than 2 years if they are social or affordable rent. (Temporary lettings for intermediate rent and supported housing should be recorded).
-
Starter tenancies or lettings with an introductory period that roll onto or convert into the main tenancy. The CORE log should be completed at the beginning of this period.
-
Changes from sole to joint or joint to sole tenancies, where the number of people in the household has not changed.
-
Moves within a shared housing unit resulting in the same support needs or property type, even if a new tenancy or licence agreement is issued.
-
Lettings where no new tenancy agreement is signed.
-
Where stock is acquired, transferred or permanently decanted and the existing tenants are not issued with a new tenancy agreement.
-
Mutual exchanges including lettings where registered provider tenants have exchanged homes, for example through the national HOMESWAP system.
-
Successions and assignments.
-
Demotion of a secure or assured tenancy, and any subsequent conversion of the demoted tenancy to a secure or assured tenancy.
-
Lettings made to asylum seekers who are awaiting a decision on their applications for asylum under the Immigration and Asylum Act 1999.
-
Non-social lettings, including market-rented properties, employer-provided housing where the employer provides financial support, homes for staff of social landlords linked to employment, homes social landlords manage for organisations who are not social landlords, homes social landlords own but lease in entirety to organisations who are not social landlords, and freehold housing with variable charges for services and communal facilities.
-
+ <%= govuk_list [
+ "Temporary general needs housing with a fixed period of less than 2 years if they are social or affordable rent. (Temporary lettings for intermediate rent and supported housing should be recorded).",
+ "Starter tenancies or lettings with an introductory period that roll onto or convert into the main tenancy. The CORE log should be completed at the beginning of this period.",
+ "Changes from sole to joint or joint to sole tenancies, where the number of people in the household has not changed.",
+ "Moves within a shared housing unit resulting in the same support needs or property type, even if a new tenancy or licence agreement is issued.",
+ "Lettings where no new tenancy agreement is signed.",
+ "Where stock is acquired, transferred or permanently decanted and the existing tenants are not issued with a new tenancy agreement.",
+ "Mutual exchanges including lettings where registered provider tenants have exchanged homes, for example through the national HOMESWAP system.",
+ "Successions and assignments.",
+ "Demotion of a secure or assured tenancy, and any subsequent conversion of the demoted tenancy to a secure or assured tenancy.",
+ "Lettings made to asylum seekers who are awaiting a decision on their applications for asylum under the Immigration and Asylum Act 1999.",
+ "Non-social lettings, including market-rented properties, employer-provided housing where the employer provides financial support, homes for staff of social landlords linked to employment, homes social landlords manage for organisations who are not social landlords, homes social landlords own but lease in entirety to organisations who are not social landlords, and freehold housing with variable charges for services and communal facilities.",
+ ],
+ type: :bullet %>
<% end %>
<%= accordion.with_section(heading_text: "What if someone is reluctant to answer any questions?") do %>
If a tenant or buyer is reluctant to answer questions as part of a log, you should explain that:
-
-
all information they provide is anonymous and will not affect their housing, benefits or other services they receive.
-
the data they provide is vital in helping to build a complete picture of social housing in England and is used to inform social housing policy.
-
+ <%= govuk_list [
+ "all information they provide is anonymous and will not affect their housing, benefits or other services they receive.",
+ "the data they provide is vital in helping to build a complete picture of social housing in England and is used to inform social housing policy.",
+ ],
+ type: :bullet %>
+
If a tenant or buyer is still unwilling or unable to answer questions, select the ‘Don’t know’ or ‘Tenant/person prefers not to say’ options.
<% end %>
<% end %>
diff --git a/app/views/users/_user_list.html.erb b/app/views/users/_user_list.html.erb
index 436c0def2..82a82b33b 100644
--- a/app/views/users/_user_list.html.erb
+++ b/app/views/users/_user_list.html.erb
@@ -1,7 +1,7 @@
<%= govuk_table do |table| %>
<%= table.with_caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
- <%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "users", filters_count: applied_filters_count(@filter_type))) %>
+ <%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "user", filters_count: applied_filters_count(@filter_type))) %>
<% if current_user.support? %>
<% query = searched.present? ? "?search=#{searched}" : nil %>
<%= govuk_link_to "Download (CSV)", "#{request.path}.csv#{query}", type: "text/csv", style: "white-space: nowrap" %>
diff --git a/bin/rubocop b/bin/rubocop
new file mode 100755
index 000000000..40330c0ff
--- /dev/null
+++ b/bin/rubocop
@@ -0,0 +1,8 @@
+#!/usr/bin/env ruby
+require "rubygems"
+require "bundler/setup"
+
+# explicit rubocop config increases performance slightly while avoiding config confusion.
+ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
+
+load Gem.bin_path("rubocop", "rubocop")
diff --git a/bin/setup b/bin/setup
index ec47b79b3..92bd38ead 100755
--- a/bin/setup
+++ b/bin/setup
@@ -1,11 +1,10 @@
#!/usr/bin/env ruby
require "fileutils"
-# path to your application root.
APP_ROOT = File.expand_path("..", __dir__)
def system!(*args)
- system(*args) || abort("\n== Command #{args} failed ==")
+ system(*args, exception: true)
end
FileUtils.chdir APP_ROOT do
diff --git a/config/application.rb b/config/application.rb
index 2cda05fea..cb8979a78 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -12,7 +12,6 @@ require "action_mailbox/engine"
# require "action_text/engine"
require "action_view/railtie"
# require "action_cable/engine"
-# require "sprockets/railtie"
# require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
@@ -22,8 +21,16 @@ Bundler.require(*Rails.groups)
module DataCollector
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
- config.load_defaults 7.0
+ config.load_defaults 7.2
+ # Please, add to the `ignore` list any other `lib` subdirectories that do
+ # not contain `.rb` files, or that should not be reloaded or eager loaded.
+ # Common ones are `templates`, `generators`, or `middleware`, for example.
+ config.autoload_lib(ignore: %w[assets tasks])
+
+ # it's strongly discouraged using add_autoload_paths_to_load_path, but rack_attack initializer can't load config files without it
+ config.add_autoload_paths_to_load_path = true
+
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
@@ -32,6 +39,8 @@ module DataCollector
config.time_zone = "London"
# config.eager_load_paths << Rails.root.join("extras")
+ # Don't generate system test files.
+ config.generators.system_tests = nil
config.exceptions_app = routes
config.active_job.queue_adapter = :sidekiq
diff --git a/config/environments/development.rb b/config/environments/development.rb
index f75643524..286c9ca30 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -6,7 +6,7 @@ Rails.application.configure do
# In the development environment your application's code is reloaded any time
# it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
- config.cache_classes = false
+ config.enable_reloading = true
# Do not eager load code on boot.
config.eager_load = false
@@ -14,7 +14,7 @@ Rails.application.configure do
# Show full error reports.
config.consider_all_requests_local = true
- # Enable server timing
+ # Enable server timing.
config.server_timing = true
# Enable/disable caching. By default caching is disabled.
@@ -24,9 +24,7 @@ Rails.application.configure do
config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :memory_store
- config.public_file_server.headers = {
- "Cache-Control" => "public, max-age=#{2.days.to_i}",
- }
+ config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
else
config.action_controller.perform_caching = false
@@ -39,6 +37,8 @@ Rails.application.configure do
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
+ # Disable caching for Action Mailer templates even if Action Controller
+ # caching is enabled.
config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
@@ -72,15 +72,20 @@ Rails.application.configure do
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
+ # Highlight code that enqueued background job in logs.
+ config.active_job.verbose_enqueue_logs = true
+
# Raises error for missing translations.
config.i18n.raise_on_missing_translations = true
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
- # Uncomment if you wish to allow Action Cable access from any origin.
- # config.action_cable.disable_request_forgery_protection = true
+ # Raise error when a before_action's only/except options reference missing actions.
+ # config.action_controller.raise_on_missing_callback_actions = true
+ # Apply autocorrection by RuboCop to files generated by `bin/rails generate`.
+ # config.generators.apply_rubocop_autocorrect_after_generate!
Faker::Config.locale = "en-GB"
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 55f442b3d..c337be49b 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -4,7 +4,7 @@ Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
- config.cache_classes = true
+ config.enable_reloading = false
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
@@ -13,15 +13,14 @@ Rails.application.configure do
config.eager_load = true
# Full error reports are disabled and caching is turned on.
- config.consider_all_requests_local = false
+ config.consider_all_requests_local = false
config.action_controller.perform_caching = true
- # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
- # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
+ # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
+ # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
# config.require_master_key = true
- # Disable serving static files from the `/public` folder by default since
- # Apache or NGINX already handles this.
+ # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
@@ -34,28 +33,38 @@ Rails.application.configure do
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
- # Mount Action Cable outside main process or domain.
- # config.action_cable.mount_path = nil
- # config.action_cable.url = "wss://example.com/cable"
- # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
+ # Assume all access to the app is happening through a SSL-terminating reverse proxy.
+ # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
+ # config.assume_ssl = true
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
- # config.force_ssl = true
+ config.force_ssl = true
- # Include generic and useful information about system operation, but avoid logging too much
- # information to avoid inadvertent exposure of personally identifiable information (PII).
- config.log_level = :info
+ # Skip http-to-https redirect for the default health check endpoint.
+ # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
+
+ # Log to STDOUT by default
+ config.logger = ActiveSupport::Logger.new($stdout)
+ .tap { |logger| logger.formatter = ::Logger::Formatter.new }
+ .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
+ # "info" includes generic and useful information about system operation, but avoids logging too much
+ # information to avoid inadvertent exposure of personally identifiable information (PII). If you
+ # want to log everything, set the level to "debug".
+ config.log_level = :info
+
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Use a real queuing backend for Active Job (and separate queues per environment).
- # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_adapter = :resque
# config.active_job.queue_name_prefix = "data_collector_production"
+ # Disable caching for Action Mailer templates even if Action Controller
+ # caching is enabled.
config.action_mailer.perform_caching = false
config.action_mailer.default_url_options = { host: ENV["APP_HOST"] }
@@ -98,37 +107,16 @@ Rails.application.configure do
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
- # Inserts middleware to perform automatic connection switching.
- # The `database_selector` hash is used to pass options to the DatabaseSelector
- # middleware. The `delay` is used to determine how long to wait after a write
- # to send a subsequent read to the primary.
- #
- # The `database_resolver` class is used by the middleware to determine which
- # database is appropriate to use based on the time delay.
- #
- # The `database_resolver_context` class is used by the middleware to set
- # timestamps for the last write to the primary. The resolver uses the context
- # class timestamps to determine how long to wait before reading from the
- # replica.
- #
- # By default Rails will store a last write timestamp in the session. The
- # DatabaseSelector middleware is designed as such you can define your own
- # strategy for connection switching and pass that into the middleware through
- # these configuration options.
- # config.active_record.database_selector = { delay: 2.seconds }
- # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
- # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
-
- # Inserts middleware to perform automatic shard swapping. The `shard_selector` hash
- # can be used to pass options to the `ShardSelector` middleware. The `lock` option is
- # used to determine whether shard swapping should be prohibited for the request.
- #
- # The `shard_resolver` option is used by the middleware to determine which shard
- # to switch to. The application must provide a mechanism for finding the shard name
- # in a proc. See guides for an example.
- # config.active_record.shard_selector = { lock: true }
- # config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }
+ # Only use :id for inspections in production.
+ config.active_record.attributes_for_inspect = [:id]
+ # Enable DNS rebinding protection and other `Host` header attacks.
+ # config.hosts = [
+ # "example.com", # Allow requests from example.com
+ # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
+ # ]
+ # Skip DNS rebinding protection for the default health check endpoint.
+ # config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
config.active_record.yaml_column_permitted_classes = [Time, BigDecimal]
diff --git a/config/environments/test.rb b/config/environments/test.rb
index aa3fe7bba..0d6b14147 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,5 +1,4 @@
require "active_support/core_ext/integer/time"
-require "faker"
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
@@ -9,26 +8,25 @@ require "faker"
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
- # Turn false under Spring and add config.action_view.cache_template_loading = true
- config.cache_classes = true
+ # While tests run files are not watched, reloading is not necessary.
+ config.enable_reloading = false
- # Eager loading loads your whole application. When running a single test locally,
- # this probably isn't necessary. It's a good idea to do in a continuous integration
- # system, or in some way before deploying your code.
+ # Eager loading loads your entire application. When running a single test locally,
+ # this is usually not necessary, and can slow down your test suite. However, it's
+ # recommended that you enable it in continuous integration systems to ensure eager
+ # loading is working properly before deploying your code.
config.eager_load = ENV["CI"].present?
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
- config.public_file_server.headers = {
- "Cache-Control" => "public, max-age=#{1.hour.to_i}",
- }
+ config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" }
# Show full error reports and disable caching.
- config.consider_all_requests_local = true
+ config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.cache_store = :null_store
- # Raise exceptions instead of rendering exception templates.
+ # Render exception templates for rescuable exceptions and raise for other exceptions.
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment.
@@ -37,16 +35,20 @@ Rails.application.configure do
# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
+ # Disable caching for Action Mailer templates even if Action Controller
+ # caching is enabled.
config.action_mailer.perform_caching = false
- config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
- config.action_mailer.default_options = { from: "test@gmail.com" }
-
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
+ # Unlike controllers, the mailer instance doesn't have any context about the
+ # incoming request so you'll need to provide the :host parameter yourself.
+ config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
+ config.action_mailer.default_options = { from: "test@gmail.com" }
+
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
@@ -61,6 +63,10 @@ Rails.application.configure do
# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
+
+ # Raise error when a before_action's only/except options reference missing actions.
+ # config.action_controller.raise_on_missing_callback_actions = true
+
Faker::Config.locale = "en-GB"
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
index e4e6a0e6c..ece129f2b 100644
--- a/config/initializers/assets.rb
+++ b/config/initializers/assets.rb
@@ -1 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = "1.0"
+
+# Add additional assets to the asset load path.
Rails.application.config.assets.paths << Rails.root.join("node_modules/@fortawesome/fontawesome-free/webfonts")
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index 3621f97f8..b3076b38f 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -1,8 +1,8 @@
# Be sure to restart your server when you modify this file.
-# Define an application-wide content security policy
-# For further information see the following documentation
-# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+# Define an application-wide content security policy.
+# See the Securing Rails Applications Guide for more information:
+# https://guides.rubyonrails.org/security.html#content-security-policy-header
# Rails.application.configure do
# config.content_security_policy do |policy|
@@ -16,11 +16,10 @@
# # policy.report_uri "/csp-violation-report-endpoint"
# end
#
-# # Generate session nonces for permitted importmap and inline scripts
+# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
-# config.content_security_policy_nonce_directives = %w(script-src)
+# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
-# # Report CSP violations to a specified URI. See:
-# # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
+# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end
diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb
index 928c8f671..86f38ad46 100644
--- a/config/initializers/filter_parameter_logging.rb
+++ b/config/initializers/filter_parameter_logging.rb
@@ -1,6 +1,8 @@
# Be sure to restart your server when you modify this file.
-# Configure sensitive parameters which will be filtered from the log file.
+# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
+# Use this to limit dissemination of sensitive information.
+# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += %i[
- passw secret token crypt salt certificate otp ssn reset_password_token
+ passw secret email secret token _key crypt salt certificate otp ssn reset_password_token
]
diff --git a/config/initializers/permissions_policy.rb b/config/initializers/permissions_policy.rb
index 00f64d71b..7db3b9577 100644
--- a/config/initializers/permissions_policy.rb
+++ b/config/initializers/permissions_policy.rb
@@ -1,11 +1,13 @@
+# Be sure to restart your server when you modify this file.
+
# Define an application-wide HTTP permissions policy. For further
-# information see https://developers.google.com/web/updates/2018/06/feature-policy
-#
-# Rails.application.config.permissions_policy do |f|
-# f.camera :none
-# f.gyroscope :none
-# f.microphone :none
-# f.usb :none
-# f.fullscreen :self
-# f.payment :self, "https://secure.example.com"
+# information see: https://developers.google.com/web/updates/2018/06/feature-policy
+
+# Rails.application.config.permissions_policy do |policy|
+# policy.camera :none
+# policy.gyroscope :none
+# policy.microphone :none
+# policy.usb :none
+# policy.fullscreen :self
+# policy.payment :self, "https://secure.example.com"
# end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 80711129e..7a95aa6fd 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -235,6 +235,7 @@ en:
organisation:
data_sharing_agreement_not_signed: "Your organisation must accept the Data Sharing Agreement before you can create any logs."
name_missing: "Enter the name of the organisation."
+ name_not_unique: "An organisation with this name already exists. Use the organisation that already exists or add a location or other identifier to the name. For example: Organisation name (City)."
provider_type_missing: "Select the organisation type."
stock_owner:
blank: "You must choose a stock owner."
@@ -361,7 +362,9 @@ en:
location:
postcode_blank: "Enter a postcode."
- units: "The units at this location must be a number."
+ units:
+ must_be_number: "The units at this location must be a number."
+ must_be_one_or_more: "Number of units must be at least 1."
type_of_unit: "Select the most common type of unit at this location."
mobility_standards: "Select the mobility standard for the majority of the units at this location."
startdate_invalid: "Enter a valid day, month and year when the first property became available at this location."
@@ -379,6 +382,10 @@ en:
organisation_part_of_another_merge: "This organisation is part of another merge - select a different one."
organisation_part_of_another_incomplete_merge: "Another merge request records %{organisation} as merging into %{absorbing_organisation} on %{merge_date}. Select another organisation or remove this organisation from the other merge request."
organisation_not_selected: "Select an organisation from the search list."
+ merge_request_id:
+ blank: "Select a merge request."
+ merging_organisation_id:
+ blank: "Select an organisation to merge."
soft_validations:
retirement:
diff --git a/config/locales/forms/2023/lettings/household_characteristics.en.yml b/config/locales/forms/2023/lettings/household_characteristics.en.yml
index 1f175adbc..3de0745b1 100644
--- a/config/locales/forms/2023/lettings/household_characteristics.en.yml
+++ b/config/locales/forms/2023/lettings/household_characteristics.en.yml
@@ -19,23 +19,23 @@ en:
page_header: ""
age1_known:
check_answer_label: ""
- hint_text: "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
+ hint_text: "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
question_text: "Do you know the lead tenant’s age?"
age1:
check_answer_label: "Lead tenant’s age"
hint_text: ""
question_text: "Age"
-
+
sex1:
page_header: ""
check_answer_label: "Lead tenant’s gender identity"
hint_text: "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
question_text: "Which of these best describes the lead tenant’s gender identity?"
-
+
ethnic_group:
page_header: ""
check_answer_label: "Lead tenant’s ethnic group"
- hint_text: "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
+ hint_text: "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
question_text: "What is the lead tenant’s ethnic group?"
ethnic:
@@ -97,13 +97,13 @@ en:
question_text: "Do you know person 2’s age?"
age2:
check_answer_label: "Person 2’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex2:
page_header: ""
check_answer_label: "Person 2’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 2’s gender identity?"
ecstat2:
@@ -132,13 +132,13 @@ en:
question_text: "Do you know person 3’s age?"
age3:
check_answer_label: "Person 3’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 3’s gender identity?"
ecstat3:
@@ -167,13 +167,13 @@ en:
question_text: "Do you know person 4’s age?"
age4:
check_answer_label: "Person 4’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 4’s gender identity?"
ecstat4:
@@ -202,13 +202,13 @@ en:
question_text: "Do you know person 5’s age?"
age5:
check_answer_label: "Person 5’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 5’s gender identity?"
ecstat5:
@@ -237,13 +237,13 @@ en:
question_text: "Do you know person 6’s age?"
age6:
check_answer_label: "Person 6’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 6’s gender identity?"
ecstat6:
@@ -272,13 +272,13 @@ en:
question_text: "Do you know person 7’s age?"
age7:
check_answer_label: "Person 7’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex7:
page_header: ""
check_answer_label: "Person 7’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 7’s gender identity?"
ecstat7:
@@ -307,17 +307,17 @@ en:
question_text: "Do you know person 8’s age?"
age8:
check_answer_label: "Person 8’s age"
- hint_text: ""
+ hint_text: ""
question_text: "Age"
sex8:
page_header: ""
check_answer_label: "Person 8’s gender identity"
- hint_text: ""
+ hint_text: ""
question_text: "Which of these best describes person 8’s gender identity?"
ecstat8:
page_header: ""
check_answer_label: "Person 8’s working situation"
hint_text: ""
- question_text: "Which of these best describes person 8’s working situation?"
\ No newline at end of file
+ question_text: "Which of these best describes person 8’s working situation?"
diff --git a/config/locales/forms/2023/lettings/income_and_benefits.en.yml b/config/locales/forms/2023/lettings/income_and_benefits.en.yml
index bc19c7954..dc5ee5a53 100644
--- a/config/locales/forms/2023/lettings/income_and_benefits.en.yml
+++ b/config/locales/forms/2023/lettings/income_and_benefits.en.yml
@@ -103,6 +103,6 @@ en:
hint_text: "You only need to give an approximate figure."
question_text: "Can you estimate the outstanding amount?"
tshortfall:
- check_answer_label: "Estimated outstanding amountt"
+ check_answer_label: "Estimated outstanding amount"
hint_text: "Also known as the ‘outstanding amount’."
question_text: "Estimated outstanding amount"
diff --git a/config/locales/forms/2023/sales/household_characteristics.en.yml b/config/locales/forms/2023/sales/household_characteristics.en.yml
index a49a817dc..ed03bc698 100644
--- a/config/locales/forms/2023/sales/household_characteristics.en.yml
+++ b/config/locales/forms/2023/sales/household_characteristics.en.yml
@@ -37,7 +37,7 @@ en:
check_answer_label: "Buyer 1’s age"
hint_text: ""
question_text: "Age"
-
+
sex1:
page_header: ""
check_answer_label: "Buyer 1’s gender identity"
@@ -49,7 +49,7 @@ en:
check_answer_label: "Buyer 1’s ethnic group"
hint_text: "Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest."
question_text: "What is buyer 1’s ethnic group?"
-
+
ethnic:
ethnic_background_black:
page_header: ""
@@ -103,9 +103,9 @@ en:
question_text: "What is buyer 2's relationship to buyer 1?"
person:
page_header: ""
- check_answer_label: "Person 2’s relationship to Buyer 1"
+ check_answer_label: "Person 2’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 2’s relationship to Buyer 1?"
+ question_text: "What is person 2’s relationship to buyer 1?"
age2:
buyer:
@@ -139,14 +139,14 @@ en:
page_header: ""
check_answer_label: "Person 2’s gender identity"
hint_text: ""
- question_text: "Which of these best describes Person 2’s gender identity?"
+ question_text: "Which of these best describes person 2’s gender identity?"
ethnic_group2:
page_header: ""
check_answer_label: "Buyer 2’s ethnic group"
hint_text: ""
question_text: "What is buyer 2’s ethnic group?"
-
+
ethnicbuy2:
ethnic_background_black:
page_header: ""
@@ -179,7 +179,7 @@ en:
check_answer_label: "Buyer 2’s nationality"
hint_text: ""
question_text: "What is buyer 2’s nationality?"
-
+
ecstat2:
buyer:
page_header: ""
@@ -190,14 +190,14 @@ en:
page_header: ""
check_answer_label: "Person 2’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 2’s working situation?"
+ question_text: "Which of these best describes person 2’s working situation?"
buy2livein:
page_header: ""
check_answer_label: "Will buyer 2 live in the property?"
hint_text: ""
question_text: "Will buyer 2 live in the property?"
-
+
hholdcount:
joint_purchase:
page_header: ""
@@ -224,9 +224,9 @@ en:
relat3:
page_header: ""
- check_answer_label: "Person 3’s relationship to Buyer 1"
+ check_answer_label: "Person 3’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 3’s relationship to Buyer 1?"
+ question_text: "What is person 3’s relationship to buyer 1?"
age3:
page_header: ""
@@ -238,18 +238,18 @@ en:
check_answer_label: "Person 3’s age"
hint_text: ""
question_text: "Age"
-
+
sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity"
hint_text: ""
- question_text: "Which of these best describes Person 3’s gender identity?"
+ question_text: "Which of these best describes person 3’s gender identity?"
ecstat3:
page_header: ""
check_answer_label: "Person 3’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 3’s working situation?"
+ question_text: "Which of these best describes person 3’s working situation?"
details_known_4:
page_header: ""
@@ -259,9 +259,9 @@ en:
relat4:
page_header: ""
- check_answer_label: "Person 4’s relationship to Buyer 1"
+ check_answer_label: "Person 4’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 4’s relationship to Buyer 1?"
+ question_text: "What is person 4’s relationship to buyer 1?"
age4:
page_header: ""
@@ -273,18 +273,18 @@ en:
check_answer_label: "Person 4’s age"
hint_text: ""
question_text: "Age"
-
+
sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity"
hint_text: ""
- question_text: "Which of these best describes Person 4’s gender identity?"
+ question_text: "Which of these best describes person 4’s gender identity?"
ecstat4:
page_header: ""
check_answer_label: "Person 4’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 4’s working situation?"
+ question_text: "Which of these best describes person 4’s working situation?"
details_known_5:
page_header: ""
@@ -294,9 +294,9 @@ en:
relat5:
page_header: ""
- check_answer_label: "Person 5’s relationship to Buyer 1"
+ check_answer_label: "Person 5’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 5’s relationship to Buyer 1?"
+ question_text: "What is person 5’s relationship to buyer 1?"
age5:
page_header: ""
@@ -308,18 +308,18 @@ en:
check_answer_label: "Person 5’s age"
hint_text: ""
question_text: "Age"
-
+
sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity"
hint_text: ""
- question_text: "Which of these best describes Person 5’s gender identity?"
+ question_text: "Which of these best describes person 5’s gender identity?"
ecstat5:
page_header: ""
check_answer_label: "Person 5’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 5’s working situation?"
+ question_text: "Which of these best describes person 5’s working situation?"
details_known_6:
page_header: ""
@@ -329,9 +329,9 @@ en:
relat6:
page_header: ""
- check_answer_label: "Person 6’s relationship to Buyer 1"
+ check_answer_label: "Person 6’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 6’s relationship to Buyer 1?"
+ question_text: "What is person 6’s relationship to buyer 1?"
age6:
page_header: ""
@@ -343,15 +343,15 @@ en:
check_answer_label: "Person 6’s age"
hint_text: ""
question_text: "Age"
-
+
sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity"
hint_text: ""
- question_text: "Which of these best describes Person 6’s gender identity?"
+ question_text: "Which of these best describes person 6’s gender identity?"
ecstat6:
page_header: ""
check_answer_label: "Person 6’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 6’s working situation?"
+ question_text: "Which of these best describes person 6’s working situation?"
diff --git a/config/locales/forms/2023/sales/income_benefits_and_savings.en.yml b/config/locales/forms/2023/sales/income_benefits_and_savings.en.yml
index 49262d7a1..689c611cb 100644
--- a/config/locales/forms/2023/sales/income_benefits_and_savings.en.yml
+++ b/config/locales/forms/2023/sales/income_benefits_and_savings.en.yml
@@ -13,7 +13,7 @@ en:
check_answer_label: "Buyer 1’s gross annual income"
hint_text: "Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments."
question_text: "Buyer 1’s gross annual income"
-
+
inc1mort:
page_header: ""
check_answer_label: "Buyer 1’s income used for mortgage application"
@@ -33,7 +33,7 @@ en:
inc2mort:
page_header: ""
- check_answer_label: "Buyer 2’s income used for mortgage application"
+ check_answer_label: "Buyer 2’s income used for mortgage application"
hint_text: ""
question_text: "Was buyer 2’s income used for a mortgage application?"
@@ -48,14 +48,14 @@ en:
check_answer_label: "Housing-related benefits buyer received before buying this property"
hint_text: ""
question_text: "Was the buyer receiving any of these housing-related benefits immediately before buying this property?"
-
+
savings:
joint_purchase:
page_header: ""
savingsnk:
- check_answer_label: "Buyers’ total savings known?"
+ check_answer_label: "Buyers’ total savings known?"
hint_text: ""
- question_text: "Do you know how much the 'buyers' had in savings before they paid any deposit for the property?"
+ question_text: "Do you know how much the buyers had in savings before they paid any deposit for the property?"
savings:
check_answer_label: "Buyers’ total savings before any deposit paid"
hint_text: "Include any savings, investments, ISAs, premium bonds, shares, or money held in a bank or building society account."
@@ -87,4 +87,4 @@ en:
page_header: ""
check_answer_label: "Previous property shared ownership?"
hint_text: "For any buyer"
- question_text: "Was the previous property under shared ownership?"
\ No newline at end of file
+ question_text: "Was the previous property under shared ownership?"
diff --git a/config/locales/forms/2023/sales/sale_information.en.yml b/config/locales/forms/2023/sales/sale_information.en.yml
index 318d7c7db..24f767026 100644
--- a/config/locales/forms/2023/sales/sale_information.en.yml
+++ b/config/locales/forms/2023/sales/sale_information.en.yml
@@ -28,7 +28,7 @@ en:
staircasing:
page_header: ""
check_answer_label: "Staircasing transaction"
- hint_text: "A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property"
+ hint_text: "A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property."
question_text: "Is this a staircasing transaction?"
about_staircasing:
page_header: "About the staircasing transaction"
@@ -69,7 +69,7 @@ en:
check_answer_label: "Household rehoused under a local authority nominations agreement?"
hint_text: "A local authority nominations agreement is a written agreement between a local authority and private registered provider (PRP) that some or all of its sales vacancies are offered to local authorities for rehousing"
question_text: "Was the household rehoused under a 'local authority nominations agreement'?"
-
+
soctenant:
joint_purchase:
page_header: ""
@@ -81,11 +81,11 @@ en:
check_answer_label: "Buyer was a registered provider, housing association or local authority tenant immediately before this sale?"
hint_text: ""
question_text: "Was the buyer a private registered provider, housing association or local authority tenant immediately before this sale?"
-
+
frombeds:
- page_header: "About the buyers’ previous property"
+ page_header: ""
check_answer_label: "Number of bedrooms in previous property"
- hint_text: "For bedsits enter 1"
+ hint_text: "A bedsit has 1 bedroom."
question_text: "How many bedrooms did the property have?"
fromprop:
@@ -113,13 +113,13 @@ en:
question_text: "What was the initial percentage equity stake purchased?"
mortgageused:
- page_header: "Mortgage Amount"
+ page_header: ""
check_answer_label: "Mortgage used"
hint_text: ""
question_text: "Was a mortgage used for the purchase of this property?"
mortgage:
- page_header: "Mortgage Amount"
+ page_header: ""
check_answer_label: "Mortgage amount"
hint_text: "Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments. Numeric in pounds. Rounded to the nearest pound."
question_text: "What is the mortgage amount?"
@@ -135,7 +135,7 @@ en:
check_answer_label: "Other Mortgage Lender"
hint_text: ""
question_text: "What is the other mortgage lender?"
-
+
mortlen:
page_header: ""
check_answer_label: "Length of mortgage"
@@ -147,7 +147,7 @@ en:
check_answer_label: "Any other borrowing?"
hint_text: ""
question_text: "Does this include any extra borrowing?"
-
+
deposit:
page_header: "About the deposit"
check_answer_label: "Deposit amount"
@@ -165,7 +165,7 @@ en:
check_answer_label: "Monthly rent"
hint_text: "Amount paid before any charges"
question_text: "What is the basic monthly rent?"
-
+
leaseholdcharges:
page_header: ""
has_mscharge:
@@ -199,10 +199,10 @@ en:
check_answer_label: "Percentage discount"
hint_text: "For Right to Buy (RTB), Preserved Right to Buy (PRTB), and Voluntary Right to Buy (VRTB)If discount capped, enter capped %If the property is being sold to an existing tenant under the RTB, PRTB, or VRTB schemes, enter the % discount from the full market value that is being given."
question_text: "What was the percentage discount?"
-
+
grant:
page_header: "About the price of the property"
check_answer_label: "Amount of any loan, grant or subsidy"
hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy"
question_text: "What was the amount of any loan, grant, discount or subsidy given?"
-
\ No newline at end of file
+
diff --git a/config/locales/forms/2024/lettings/household_characteristics.en.yml b/config/locales/forms/2024/lettings/household_characteristics.en.yml
index 04a311f06..0537a82bd 100644
--- a/config/locales/forms/2024/lettings/household_characteristics.en.yml
+++ b/config/locales/forms/2024/lettings/household_characteristics.en.yml
@@ -13,19 +13,19 @@ en:
page_header: ""
age1_known:
check_answer_label: ""
- hint_text: "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same amount of paid work, the lead tenant is whoever is the oldest."
+ hint_text: "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
question_text: "Do you know the lead tenant’s age?"
age1:
check_answer_label: "Lead tenant’s age"
hint_text: ""
question_text: "Age"
-
+
sex1:
page_header: ""
check_answer_label: "Lead tenant’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
question_text: "Which of these best describes the lead tenant’s gender identity?"
-
+
ethnic_group:
page_header: ""
check_answer_label: "Lead tenant’s ethnic group"
@@ -319,4 +319,4 @@ en:
page_header: ""
check_answer_label: "Person 8’s working situation"
hint_text: ""
- question_text: "Which of these best describes person 8’s working situation?"
\ No newline at end of file
+ question_text: "Which of these best describes person 8’s working situation?"
diff --git a/config/locales/forms/2024/lettings/income_and_benefits.en.yml b/config/locales/forms/2024/lettings/income_and_benefits.en.yml
index bb3cc320e..8e364f611 100644
--- a/config/locales/forms/2024/lettings/income_and_benefits.en.yml
+++ b/config/locales/forms/2024/lettings/income_and_benefits.en.yml
@@ -103,6 +103,6 @@ en:
hint_text: "You only need to give an approximate figure."
question_text: "Can you estimate the outstanding amount?"
tshortfall:
- check_answer_label: "Estimated outstanding amountt"
+ check_answer_label: "Estimated outstanding amount"
hint_text: "Also known as the ‘outstanding amount’."
question_text: "Estimated outstanding amount"
diff --git a/config/locales/forms/2024/sales/household_characteristics.en.yml b/config/locales/forms/2024/sales/household_characteristics.en.yml
index 22f9427e8..5b06639fe 100644
--- a/config/locales/forms/2024/sales/household_characteristics.en.yml
+++ b/config/locales/forms/2024/sales/household_characteristics.en.yml
@@ -13,7 +13,7 @@ en:
check_answer_label: "Buyer 1’s age"
hint_text: ""
question_text: "Age"
-
+
sex1:
page_header: ""
check_answer_label: "Buyer 1’s gender identity"
@@ -25,7 +25,7 @@ en:
check_answer_label: "Buyer 1’s ethnic group"
hint_text: ""
question_text: "What is buyer 1’s ethnic group?"
-
+
ethnic:
ethnic_background_black:
page_header: ""
@@ -85,9 +85,9 @@ en:
question_text: "What is buyer 2's relationship to buyer 1?"
person:
page_header: ""
- check_answer_label: "Person 2’s relationship to Buyer 1"
+ check_answer_label: "Person 2’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 2’s relationship to Buyer 1?"
+ question_text: "What is person 2’s relationship to buyer 1?"
age2:
buyer:
@@ -121,14 +121,14 @@ en:
page_header: ""
check_answer_label: "Person 2’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 2’s gender identity?"
+ question_text: "Which of these best describes person 2’s gender identity?"
ethnic_group2:
page_header: ""
check_answer_label: "Buyer 2’s ethnic group"
hint_text: ""
question_text: "What is buyer 2’s ethnic group?"
-
+
ethnicbuy2:
ethnic_background_black:
page_header: ""
@@ -167,7 +167,7 @@ en:
check_answer_label: "Buyer 2’s nationality"
hint_text: ""
question_text: "Enter a nationality"
-
+
ecstat2:
buyer:
page_header: ""
@@ -178,14 +178,14 @@ en:
page_header: ""
check_answer_label: "Person 2’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 2’s working situation?"
+ question_text: "Which of these best describes person 2’s working situation?"
buy2livein:
page_header: ""
check_answer_label: "Will buyer 2 live in the property?"
hint_text: ""
question_text: "Will buyer 2 live in the property?"
-
+
hholdcount:
joint_purchase:
page_header: ""
@@ -212,9 +212,9 @@ en:
relat3:
page_header: ""
- check_answer_label: "Person 3’s relationship to Buyer 1"
+ check_answer_label: "Person 3’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 3’s relationship to Buyer 1?"
+ question_text: "What is person 3’s relationship to buyer 1?"
age3:
page_header: ""
@@ -226,18 +226,18 @@ en:
check_answer_label: "Person 3’s age"
hint_text: ""
question_text: "Age"
-
+
sex3:
page_header: ""
check_answer_label: "Person 3’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 3’s gender identity?"
+ question_text: "Which of these best describes person 3’s gender identity?"
ecstat3:
page_header: ""
check_answer_label: "Person 3’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 3’s working situation?"
+ question_text: "Which of these best describes person 3’s working situation?"
details_known_4:
page_header: ""
@@ -247,9 +247,9 @@ en:
relat4:
page_header: ""
- check_answer_label: "Person 4’s relationship to Buyer 1"
+ check_answer_label: "Person 4’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 4’s relationship to Buyer 1?"
+ question_text: "What is person 4’s relationship to buyer 1?"
age4:
page_header: ""
@@ -261,18 +261,18 @@ en:
check_answer_label: "Person 4’s age"
hint_text: ""
question_text: "Age"
-
+
sex4:
page_header: ""
check_answer_label: "Person 4’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 4’s gender identity?"
+ question_text: "Which of these best describes person 4’s gender identity?"
ecstat4:
page_header: ""
check_answer_label: "Person 4’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 4’s working situation?"
+ question_text: "Which of these best describes person 4’s working situation?"
details_known_5:
page_header: ""
@@ -282,9 +282,9 @@ en:
relat5:
page_header: ""
- check_answer_label: "Person 5’s relationship to Buyer 1"
+ check_answer_label: "Person 5’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 5’s relationship to Buyer 1?"
+ question_text: "What is person 5’s relationship to buyer 1?"
age5:
page_header: ""
@@ -296,18 +296,18 @@ en:
check_answer_label: "Person 5’s age"
hint_text: ""
question_text: "Age"
-
+
sex5:
page_header: ""
check_answer_label: "Person 5’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 5’s gender identity?"
+ question_text: "Which of these best describes person 5’s gender identity?"
ecstat5:
page_header: ""
check_answer_label: "Person 5’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 5’s working situation?"
+ question_text: "Which of these best describes person 5’s working situation?"
details_known_6:
page_header: ""
@@ -317,9 +317,9 @@ en:
relat6:
page_header: ""
- check_answer_label: "Person 6’s relationship to Buyer 1"
+ check_answer_label: "Person 6’s relationship to buyer 1"
hint_text: ""
- question_text: "What is Person 6’s relationship to Buyer 1?"
+ question_text: "What is person 6’s relationship to buyer 1?"
age6:
page_header: ""
@@ -331,15 +331,15 @@ en:
check_answer_label: "Person 6’s age"
hint_text: ""
question_text: "Age"
-
+
sex6:
page_header: ""
check_answer_label: "Person 6’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 6’s gender identity?"
+ question_text: "Which of these best describes person 6’s gender identity?"
ecstat6:
page_header: ""
check_answer_label: "Person 6’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 6’s working situation?"
+ question_text: "Which of these best describes person 6’s working situation?"
diff --git a/config/locales/forms/2024/sales/income_benefits_and_savings.en.yml b/config/locales/forms/2024/sales/income_benefits_and_savings.en.yml
index 0654c9e1f..8d5ec0772 100644
--- a/config/locales/forms/2024/sales/income_benefits_and_savings.en.yml
+++ b/config/locales/forms/2024/sales/income_benefits_and_savings.en.yml
@@ -13,7 +13,7 @@ en:
check_answer_label: "Buyer 1’s gross annual income"
hint_text: "Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments."
question_text: "Buyer 1’s gross annual income"
-
+
inc1mort:
page_header: ""
check_answer_label: "Buyer 1’s income used for mortgage application"
@@ -48,14 +48,14 @@ en:
check_answer_label: "Housing-related benefits buyer received before buying this property"
hint_text: ""
question_text: "Was the buyer receiving any of these housing-related benefits immediately before buying this property?"
-
+
savings:
joint_purchase:
page_header: ""
savingsnk:
check_answer_label: "Buyers’ total savings known?"
hint_text: ""
- question_text: "Do you know how much the 'buyers' had in savings before they paid any deposit for the property?"
+ question_text: "Do you know how much the buyers had in savings before they paid any deposit for the property?"
savings:
check_answer_label: "Buyers’ total savings before any deposit paid"
hint_text: "Include any savings, investments, ISAs, premium bonds, shares, or money held in a bank or building society account."
@@ -87,4 +87,4 @@ en:
page_header: ""
check_answer_label: "Previous property shared ownership?"
hint_text: "For any buyer"
- question_text: "Was the previous property under shared ownership?"
\ No newline at end of file
+ question_text: "Was the previous property under shared ownership?"
diff --git a/config/locales/forms/2024/sales/sale_information.en.yml b/config/locales/forms/2024/sales/sale_information.en.yml
index d0031416e..92acca9cd 100644
--- a/config/locales/forms/2024/sales/sale_information.en.yml
+++ b/config/locales/forms/2024/sales/sale_information.en.yml
@@ -28,7 +28,7 @@ en:
staircasing:
page_header: ""
check_answer_label: "Staircasing transaction"
- hint_text: "A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property"
+ hint_text: "A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property."
question_text: "Is this a staircasing transaction?"
about_staircasing:
page_header: "About the staircasing transaction"
@@ -73,7 +73,7 @@ en:
check_answer_label: "Household rehoused under a local authority nominations agreement?"
hint_text: "A local authority nominations agreement is a written agreement between a local authority and private registered provider (PRP) that some or all of its sales vacancies are offered to local authorities for rehousing"
question_text: "Was the household rehoused under a 'local authority nominations agreement'?"
-
+
soctenant:
joint_purchase:
page_header: ""
@@ -85,11 +85,11 @@ en:
check_answer_label: "Buyer was a registered provider, housing association or local authority tenant immediately before this sale?"
hint_text: ""
question_text: "Was the buyer a private registered provider, housing association or local authority tenant immediately before this sale?"
-
+
frombeds:
- page_header: "About the buyers’ previous property"
+ page_header: ""
check_answer_label: "Number of bedrooms in previous property"
- hint_text: "For bedsits enter 1"
+ hint_text: "A bedsit has 1 bedroom."
question_text: "How many bedrooms did the property have?"
fromprop:
@@ -117,13 +117,13 @@ en:
question_text: "What was the initial percentage equity stake purchased?"
mortgageused:
- page_header: "Mortgage Amount"
+ page_header: ""
check_answer_label: "Mortgage used"
hint_text: ""
question_text: "Was a mortgage used for the purchase of this property?"
mortgage:
- page_header: "Mortgage Amount"
+ page_header: ""
check_answer_label: "Mortgage amount"
hint_text: "Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments. Numeric in pounds. Rounded to the nearest pound."
question_text: "What is the mortgage amount?"
@@ -139,7 +139,7 @@ en:
check_answer_label: "Other Mortgage Lender"
hint_text: ""
question_text: "What is the other mortgage lender?"
-
+
mortlen:
page_header: ""
check_answer_label: "Length of mortgage"
@@ -151,7 +151,7 @@ en:
check_answer_label: "Any other borrowing?"
hint_text: ""
question_text: "Does this include any extra borrowing?"
-
+
deposit:
page_header: "About the deposit"
check_answer_label: "Deposit amount"
@@ -169,7 +169,7 @@ en:
check_answer_label: "Monthly rent"
hint_text: "Amount paid before any charges"
question_text: "What is the basic monthly rent?"
-
+
leaseholdcharges:
page_header: ""
has_mscharge:
@@ -198,10 +198,10 @@ en:
check_answer_label: "Percentage discount"
hint_text: "For Right to Buy (RTB), Preserved Right to Buy (PRTB), and Voluntary Right to Buy (VRTB)If discount capped, enter capped %If the property is being sold to an existing tenant under the RTB, PRTB, or VRTB schemes, enter the % discount from the full market value that is being given."
question_text: "What was the percentage discount?"
-
+
grant:
page_header: "About the price of the property"
check_answer_label: "Amount of any loan, grant or subsidy"
hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy"
question_text: "What was the amount of any loan, grant, discount or subsidy given?"
-
\ No newline at end of file
+
diff --git a/config/locales/forms/2025/lettings/household_characteristics.en.yml b/config/locales/forms/2025/lettings/household_characteristics.en.yml
index 1aef9297a..b6d7ad7e9 100644
--- a/config/locales/forms/2025/lettings/household_characteristics.en.yml
+++ b/config/locales/forms/2025/lettings/household_characteristics.en.yml
@@ -13,7 +13,7 @@ en:
page_header: ""
age1_known:
check_answer_label: ""
- hint_text: "The ’lead’ or ’main’ tenant is the person in the household who does the most paid work. If several people do the same amount of paid work, the lead tenant is whoever is the oldest."
+ hint_text: "The lead tenant is the person in the household who does the most paid work. If several people do the same paid work, the lead tenant is whoever is the oldest."
question_text: "Do you know the lead tenant’s age?"
age1:
check_answer_label: "Lead tenant’s age"
diff --git a/config/locales/forms/2025/lettings/income_and_benefits.en.yml b/config/locales/forms/2025/lettings/income_and_benefits.en.yml
index 55e193ff7..5b8ed26c0 100644
--- a/config/locales/forms/2025/lettings/income_and_benefits.en.yml
+++ b/config/locales/forms/2025/lettings/income_and_benefits.en.yml
@@ -103,6 +103,6 @@ en:
hint_text: "You only need to give an approximate figure."
question_text: "Can you estimate the outstanding amount?"
tshortfall:
- check_answer_label: "Estimated outstanding amountt"
+ check_answer_label: "Estimated outstanding amount"
hint_text: "Also known as the ‘outstanding amount’."
question_text: "Estimated outstanding amount"
diff --git a/config/locales/forms/2025/sales/household_characteristics.en.yml b/config/locales/forms/2025/sales/household_characteristics.en.yml
index 3f9f503be..a217c578c 100644
--- a/config/locales/forms/2025/sales/household_characteristics.en.yml
+++ b/config/locales/forms/2025/sales/household_characteristics.en.yml
@@ -121,7 +121,7 @@ en:
page_header: ""
check_answer_label: "Person 2’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 2’s gender identity?"
+ question_text: "Which of these best describes person 2’s gender identity?"
ethnic_group2:
page_header: ""
@@ -178,7 +178,7 @@ en:
page_header: ""
check_answer_label: "Person 2’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 2’s working situation?"
+ question_text: "Which of these best describes person 2’s working situation?"
buy2livein:
page_header: ""
@@ -231,13 +231,13 @@ en:
page_header: ""
check_answer_label: "Person 3’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 3’s gender identity?"
+ question_text: "Which of these best describes person 3’s gender identity?"
ecstat3:
page_header: ""
check_answer_label: "Person 3’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 3’s working situation?"
+ question_text: "Which of these best describes person 3’s working situation?"
details_known_4:
page_header: ""
@@ -266,13 +266,13 @@ en:
page_header: ""
check_answer_label: "Person 4’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 4’s gender identity?"
+ question_text: "Which of these best describes person 4’s gender identity?"
ecstat4:
page_header: ""
check_answer_label: "Person 4’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 4’s working situation?"
+ question_text: "Which of these best describes person 4’s working situation?"
details_known_5:
page_header: ""
@@ -301,13 +301,13 @@ en:
page_header: ""
check_answer_label: "Person 5’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 5’s gender identity?"
+ question_text: "Which of these best describes person 5’s gender identity?"
ecstat5:
page_header: ""
check_answer_label: "Person 5’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 5’s working situation?"
+ question_text: "Which of these best describes person 5’s working situation?"
details_known_6:
page_header: ""
@@ -336,10 +336,10 @@ en:
page_header: ""
check_answer_label: "Person 6’s gender identity"
hint_text: "This should be however they personally choose to identify from the options below. This may or may not be the same as their biological sex or the sex they were assigned at birth."
- question_text: "Which of these best describes Person 6’s gender identity?"
+ question_text: "Which of these best describes person 6’s gender identity?"
ecstat6:
page_header: ""
check_answer_label: "Person 6’s working situation"
hint_text: ""
- question_text: "Which of these best describes Person 6’s working situation?"
+ question_text: "Which of these best describes person 6’s working situation?"
diff --git a/config/locales/forms/2025/sales/income_benefits_and_savings.en.yml b/config/locales/forms/2025/sales/income_benefits_and_savings.en.yml
index 20beb0b85..b38c203f7 100644
--- a/config/locales/forms/2025/sales/income_benefits_and_savings.en.yml
+++ b/config/locales/forms/2025/sales/income_benefits_and_savings.en.yml
@@ -55,7 +55,7 @@ en:
savingsnk:
check_answer_label: "Buyers’ total savings known?"
hint_text: ""
- question_text: "Do you know how much the 'buyers' had in savings before they paid any deposit for the property?"
+ question_text: "Do you know how much the buyers had in savings before they paid any deposit for the property?"
savings:
check_answer_label: "Buyers’ total savings before any deposit paid"
hint_text: "Include any savings, investments, ISAs, premium bonds, shares, or money held in a bank or building society account."
diff --git a/config/locales/forms/2025/sales/sale_information.en.yml b/config/locales/forms/2025/sales/sale_information.en.yml
index 9a273d1c3..278c8a235 100644
--- a/config/locales/forms/2025/sales/sale_information.en.yml
+++ b/config/locales/forms/2025/sales/sale_information.en.yml
@@ -26,24 +26,47 @@ en:
question_text: "Did the buyer live in the property before purchasing it?"
about_staircasing:
- page_header: "About the staircasing transaction"
- stairbought:
- check_answer_label: "Percentage bought in this staircasing transaction"
+ page_header: "About the staircasing transaction"
+ stairbought:
+ check_answer_label: "Percentage bought in this staircasing transaction"
+ hint_text: ""
+ question_text: "What percentage of the property has been bought in this staircasing transaction?"
+ stairowned:
+ joint_purchase:
+ check_answer_label: "Percentage the buyers now own in total"
hint_text: ""
- question_text: "What percentage of the property has been bought in this staircasing transaction?"
- stairowned:
- joint_purchase:
- check_answer_label: "Percentage the buyers now own in total"
- hint_text: ""
- question_text: "What percentage of the property do the buyers now own in total?"
- not_joint_purchase:
- check_answer_label: "Percentage the buyer now owns in total"
- hint_text: ""
- question_text: "What percentage of the property does the buyer now own in total?"
- staircasesale:
- check_answer_label: "Part of a back-to-back staircasing transaction"
+ question_text: "What percentage of the property do the buyers now own in total?"
+ not_joint_purchase:
+ check_answer_label: "Percentage the buyer now owns in total"
hint_text: ""
- question_text: "Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?"
+ question_text: "What percentage of the property does the buyer now own in total?"
+
+ staircasesale:
+ page_header: ""
+ check_answer_label: "Part of a back-to-back staircasing transaction?"
+ hint_text: ""
+ question_text: "Is this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?"
+
+ firststair:
+ page_header: ""
+ check_answer_label: "First time staircasing?"
+ hint_text: ""
+ question_text: "Is this the first time the shared owner has engaged in staircasing in the home?"
+
+ stairprevious:
+ page_header: "About previous staircasing transactions"
+ numstair:
+ check_answer_label: "Number of staircasing transactions"
+ hint_text: ""
+ question_text: "Including this time, how many times has the shared owner engaged in staircasing in the home?"
+ initialpurchase:
+ check_answer_label: "Initial staircasing transaction"
+ hint_text: ""
+ question_text: "What was the date of the initial purchase of a share in the property?"
+ lasttransaction:
+ check_answer_label: "Last staircasing transaction"
+ hint_text: ""
+ question_text: "What was the date of the last staircasing transaction?"
resale:
page_header: ""
@@ -82,9 +105,9 @@ en:
question_text: "Was the buyer a private registered provider, housing association or local authority tenant immediately before this sale?"
frombeds:
- page_header: "About the buyers’ previous property"
+ page_header: ""
check_answer_label: "Number of bedrooms in previous property"
- hint_text: "For bedsits enter 1"
+ hint_text: "A bedsit has 1 bedroom."
question_text: "How many bedrooms did the property have?"
fromprop:
@@ -101,24 +124,34 @@ en:
value:
page_header: "About the price of the property"
- check_answer_label: "Full purchase price"
- hint_text: "Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser)"
- question_text: "What was the full purchase price?"
+ value_shared_ownership:
+ check_answer_label: "Full purchase price"
+ hint_text: "Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser)."
+ question_text: "What was the full purchase price?"
+ value_shared_ownership_staircase:
+ check_answer_label: "Full purchase price"
+ hint_text: "Enter the full purchase price paid for the equity bought in this staircasing transaction (this is equal to the value of the share bought by the purchaser)."
+ question_text: "What was the full purchase price for this staircasing transaction?"
equity:
page_header: "About the price of the property"
- check_answer_label: "Initial percentage equity share"
- hint_text: "Enter the amount of initial equity share held by the purchaser (for example, 25% or 50%)"
- question_text: "What was the initial percentage share purchased?"
+ initial_equity:
+ check_answer_label: "Initial percentage equity share"
+ hint_text: "Enter the amount of initial equity share held by the purchaser (for example, 25% or 50%)"
+ question_text: "What was the initial percentage share purchased?"
+ staircase_equity:
+ check_answer_label: "Initial percentage equity share"
+ hint_text: "Enter the amount of initial equity share held by the purchaser (for example, 25% or 50%)"
+ question_text: "What was the percentage shared purchased in the initial transaction?"
mortgageused:
- page_header: "Mortgage Amount"
- check_answer_label: "Mortgage used"
+ page_header: ""
+ check_answer_label: "Mortgage used?"
hint_text: ""
question_text: "Was a mortgage used for the purchase of this property?"
mortgage:
- page_header: "Mortgage Amount"
+ page_header: ""
check_answer_label: "Mortgage amount"
hint_text: "Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments. Numeric in pounds. Rounded to the nearest pound."
question_text: "What is the mortgage amount?"
@@ -165,6 +198,17 @@ en:
hint_text: "Amount paid before any charges"
question_text: "What is the basic monthly rent?"
+ mrent_staircasing:
+ page_header: "Monthly rent"
+ prestaircasing:
+ check_answer_label: "Monthly rent prior to staircasing"
+ hint_text: "Amount paid before any charges"
+ question_text: "What was the basic monthly rent prior to staircasing?"
+ poststaircasing:
+ check_answer_label: "Monthly rent after staircasing"
+ hint_text: "Amount paid before any charges"
+ question_text: "What is the basic monthly rent after staircasing?"
+
leaseholdcharges:
page_header: ""
has_mscharge:
@@ -199,7 +243,7 @@ en:
check_answer_label: "Amount of any loan, grant or subsidy"
hint_text: "For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB), Voluntary Right to Buy (VRTB) and Rent to Buy"
question_text: "What was the amount of any loan, grant, discount or subsidy given?"
-
+
management_fee:
page_header: ""
has_management_fee:
diff --git a/config/locales/forms/2025/sales/setup.en.yml b/config/locales/forms/2025/sales/setup.en.yml
index 6f7c5da98..c4cc9a42c 100644
--- a/config/locales/forms/2025/sales/setup.en.yml
+++ b/config/locales/forms/2025/sales/setup.en.yml
@@ -42,7 +42,7 @@ en:
staircasing:
page_header: ""
check_answer_label: "Staircasing transaction"
- hint_text: "A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property"
+ hint_text: "A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property."
question_text: "Is this a staircasing transaction?"
type:
diff --git a/config/locales/test.en.yml b/config/locales/test.en.yml
new file mode 100644
index 000000000..ca1a93282
--- /dev/null
+++ b/config/locales/test.en.yml
@@ -0,0 +1,17 @@
+en:
+ forms:
+ 2021:
+ lettings:
+ guidance:
+ what_counts_as_income:
+ title: "What counts as income?"
+ content: "What counts as income?"
+ finding_scheme:
+ title: "Can’t find your scheme?"
+ content: "
Schemes are attached to the organisation that owns the property. Check you have correctly answered question 1 \"Which organisation owns this property?\"
+
If your organisation’s schemes were migrated from old CORE, they may have new names and codes. Search by postcode to find your scheme.
"
+ scheme_changes_link_text: "Read more about how schemes have changed"
+ view_schemes_link_text: "View your organisation’s schemes"
+ soft_validations:
+ net_income:
+ hint_text: "hint text"
diff --git a/config/locales/validations/lettings/2024/bulk_upload.en.yml b/config/locales/validations/lettings/2024/bulk_upload.en.yml
index 76985ee32..a16090543 100644
--- a/config/locales/validations/lettings/2024/bulk_upload.en.yml
+++ b/config/locales/validations/lettings/2024/bulk_upload.en.yml
@@ -50,6 +50,8 @@ en:
invalid: "Age of person %{person_num} must be a number or the letter R"
address:
not_found: "We could not find this address. Check the address data in your CSV file is correct and complete, or select the correct address using the CORE site."
+ not_determined: "There are multiple matches for this address. Either select the correct address manually or correct the UPRN in the CSV file."
+ not_answered: "Enter either the UPRN or the full address."
nationality:
invalid: "Select a valid nationality."
charges:
diff --git a/config/locales/validations/sales/2024/bulk_upload.en.yml b/config/locales/validations/sales/2024/bulk_upload.en.yml
index fc68662d1..2621386c1 100644
--- a/config/locales/validations/sales/2024/bulk_upload.en.yml
+++ b/config/locales/validations/sales/2024/bulk_upload.en.yml
@@ -40,5 +40,7 @@ en:
buyer_cannot_be_over_16_and_child: "Buyer 2's age cannot be 16 or over if their working situation is child under 16."
address:
not_found: "We could not find this address. Check the address data in your CSV file is correct and complete, or select the correct address using the CORE site."
+ not_determined: "There are multiple matches for this address. Either select the correct address manually or correct the UPRN in the CSV file."
+ not_answered: "Enter either the UPRN or the full address."
nationality:
invalid: "Select a valid nationality."
diff --git a/config/locales/validations/sales/sale_information.en.yml b/config/locales/validations/sales/sale_information.en.yml
index 8fb7d02d4..ea17953fb 100644
--- a/config/locales/validations/sales/sale_information.en.yml
+++ b/config/locales/validations/sales/sale_information.en.yml
@@ -19,6 +19,8 @@ en:
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."
+ initialpurchase:
+ must_be_after_1980: "The initial purchase date must be after January 1, 1980."
fromprop:
previous_property_type_bedsit: "A bedsit cannot have more than 1 bedroom."
frombeds:
@@ -125,3 +127,7 @@ en:
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."
+ numstair:
+ must_be_greater_than_one: "The number of staircasing transactions must be greater than 1 when this is not the first staircasing transaction."
+ firststair:
+ cannot_be_no: "The answer to 'Is this the first staircasing transaction?' cannot be 'no' if the number of staircasing transactions is 1."
diff --git a/config/puma.rb b/config/puma.rb
index d9b3e836c..5d0898ea5 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -1,9 +1,25 @@
-# Puma can serve each request in a thread from an internal thread pool.
-# The `threads` method setting takes two numbers: a minimum and maximum.
-# Any libraries that use thread pools should be configured to match
-# the maximum value specified for Puma. Default is set to 5 threads for minimum
-# and maximum; this matches the default thread size of Active Record.
+# This configuration file will be evaluated by Puma. The top-level methods that
+# are invoked here are part of Puma's configuration DSL. For more information
+# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
+
+# Puma starts a configurable number of processes (workers) and each process
+# serves each request in a thread from an internal thread pool.
+#
+# The ideal number of threads per worker depends both on how much time the
+# application spends waiting for IO operations and on how much you wish to
+# to prioritize throughput over latency.
+#
+# As a rule of thumb, increasing the number of threads will increase how much
+# traffic a given process can handle (throughput), but due to CRuby's
+# Global VM Lock (GVL) it has diminishing returns and will degrade the
+# response time (latency) of the application.
#
+# The default is set to 3 threads as it's deemed a decent compromise between
+# throughput and latency for the average Rails application.
+#
+# Any libraries that use a connection pool or another resource pool should
+# be configured to provide at least as many connections as the number of
+# threads. This includes Active Record's `pool` parameter in `database.yml`.
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
@@ -14,30 +30,16 @@ threads min_threads_count, max_threads_count
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
-#
+#
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
-# Specifies the `pidfile` that Puma will use.
+# Specify the PID file. Defaults to tmp/pids/server.pid in development.
+# In other environments, only set the PID file if requested.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
-# Specifies the number of `workers` to boot in clustered mode.
-# Workers are forked web server processes. If using threads and workers together
-# the concurrency of the application would be max `threads` * `workers`.
-# Workers do not work on JRuby or Windows (both of which do not support
-# processes).
-#
-# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
-
-# Use the `preload_app!` method when specifying a `workers` number.
-# This directive tells Puma to first boot the application and load code
-# before forking the application. This takes advantage of Copy On Write
-# process behavior so workers use less memory.
-#
-# preload_app!
-
-# Allow puma to be restarted by `rails restart` command.
+# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart
diff --git a/db/migrate/20241114173226_add_fields_to_sales_log.rb b/db/migrate/20241114173226_add_fields_to_sales_log.rb
new file mode 100644
index 000000000..2f7dbbd2b
--- /dev/null
+++ b/db/migrate/20241114173226_add_fields_to_sales_log.rb
@@ -0,0 +1,11 @@
+class AddFieldsToSalesLog < ActiveRecord::Migration[7.0]
+ def change
+ change_table :sales_logs, bulk: true do |t|
+ t.column :firststair, :integer
+ t.column :numstair, :integer
+ t.column :mrentprestaircasing, :decimal, precision: 10, scale: 2
+ t.column :lasttransaction, :datetime
+ t.column :initialpurchase, :datetime
+ end
+ end
+end
diff --git a/db/migrate/20241125153349_add_unique_index_to_org_name.rb b/db/migrate/20241125153349_add_unique_index_to_org_name.rb
new file mode 100644
index 000000000..a7a124183
--- /dev/null
+++ b/db/migrate/20241125153349_add_unique_index_to_org_name.rb
@@ -0,0 +1,5 @@
+class AddUniqueIndexToOrgName < ActiveRecord::Migration[7.0]
+ def change
+ add_index :organisations, :name, unique: true
+ end
+end
diff --git a/db/migrate/20241204100518_add_year_to_export.rb b/db/migrate/20241204100518_add_year_to_export.rb
new file mode 100644
index 000000000..784754888
--- /dev/null
+++ b/db/migrate/20241204100518_add_year_to_export.rb
@@ -0,0 +1,5 @@
+class AddYearToExport < ActiveRecord::Migration[7.0]
+ def change
+ add_column :exports, :year, :integer
+ end
+end
diff --git a/db/migrate/20241206142942_add_service_name_to_active_storage_blobs.active_storage.rb b/db/migrate/20241206142942_add_service_name_to_active_storage_blobs.active_storage.rb
new file mode 100644
index 000000000..0a13344fc
--- /dev/null
+++ b/db/migrate/20241206142942_add_service_name_to_active_storage_blobs.active_storage.rb
@@ -0,0 +1,22 @@
+# This migration comes from active_storage (originally 20190112182829)
+class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
+ def up
+ return unless table_exists?(:active_storage_blobs)
+
+ unless column_exists?(:active_storage_blobs, :service_name)
+ add_column :active_storage_blobs, :service_name, :string
+
+ if configured_service = ActiveStorage::Blob.service.name # rubocop:disable Lint/AssignmentInCondition
+ ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
+ end
+
+ change_column :active_storage_blobs, :service_name, :string, null: false
+ end
+ end
+
+ def down
+ return unless table_exists?(:active_storage_blobs)
+
+ remove_column :active_storage_blobs, :service_name
+ end
+end
diff --git a/db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb b/db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb
new file mode 100644
index 000000000..78b8564cf
--- /dev/null
+++ b/db/migrate/20241206142943_create_active_storage_variant_records.active_storage.rb
@@ -0,0 +1,28 @@
+# This migration comes from active_storage (originally 20191206030411)
+class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
+ def change
+ return unless table_exists?(:active_storage_blobs)
+
+ # Use Active Record's configured type for primary key
+ create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t| # rubocop:disable Rails/CreateTableWithTimestamps
+ t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
+ t.string :variation_digest, null: false
+
+ t.index %i[blob_id variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+ end
+
+private
+
+ def primary_key_type
+ config = Rails.configuration.generators
+ config.options[config.orm][:primary_key_type] || :primary_key
+ end
+
+ def blobs_primary_key_type
+ pkey_name = connection.primary_key(:active_storage_blobs)
+ pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
+ pkey_column.bigint? ? :bigint : pkey_column.type
+ end
+end
diff --git a/db/migrate/20241206142944_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb b/db/migrate/20241206142944_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
new file mode 100644
index 000000000..93c8b85ad
--- /dev/null
+++ b/db/migrate/20241206142944_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
@@ -0,0 +1,8 @@
+# This migration comes from active_storage (originally 20211119233751)
+class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
+ def change
+ return unless table_exists?(:active_storage_blobs)
+
+ change_column_null(:active_storage_blobs, :checksum, true)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c53872020..bd7672c24 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2024_11_22_154743) do
+ActiveRecord::Schema[7.2].define(version: 2024_12_06_142944) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -113,6 +113,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_11_22_154743) do
t.integer "increment_number", default: 1, null: false
t.boolean "empty_export", default: false, null: false
t.string "collection"
+ t.integer "year"
end
create_table "la_rent_ranges", force: :cascade do |t|
@@ -545,6 +546,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_11_22_154743) do
t.datetime "discarded_at"
t.datetime "schemes_deduplicated_at"
t.index ["absorbing_organisation_id"], name: "index_organisations_on_absorbing_organisation_id"
+ t.index ["name"], name: "index_organisations_on_name", unique: true
t.index ["old_visible_id"], name: "index_organisations_on_old_visible_id", unique: true
end
@@ -761,6 +763,11 @@ ActiveRecord::Schema[7.0].define(version: 2024_11_22_154743) do
t.bigint "created_by_id"
t.integer "has_management_fee"
t.decimal "management_fee", precision: 10, scale: 2
+ t.integer "firststair"
+ t.integer "numstair"
+ t.decimal "mrentprestaircasing", precision: 10, scale: 2
+ t.datetime "lasttransaction"
+ t.datetime "initialpurchase"
t.index ["assigned_to_id"], name: "index_sales_logs_on_assigned_to_id"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
diff --git a/db/seeds.rb b/db/seeds.rb
index 2f018be91..f3dcb688d 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -12,13 +12,13 @@ def find_or_create_user(organisation, email, name, role)
end
end
-unless Rails.env.test?
- if LocalAuthority.count.zero?
- la_path = "config/local_authorities_data/initial_local_authorities.csv"
- service = Imports::LocalAuthoritiesService.new(path: la_path)
- service.call
- end
+if LocalAuthority.count.zero?
+ la_path = "config/local_authorities_data/initial_local_authorities.csv"
+ service = Imports::LocalAuthoritiesService.new(path: la_path)
+ service.call
+end
+unless Rails.env.test?
if LaRentRange.count.zero?
Dir.glob("config/rent_range_data/*.csv").each do |path|
start_year = File.basename(path, ".csv")
diff --git a/lib/tasks/correct_rent_type_value.rake b/lib/tasks/correct_rent_type_value.rake
index 1d43383c7..e3b166d04 100644
--- a/lib/tasks/correct_rent_type_value.rake
+++ b/lib/tasks/correct_rent_type_value.rake
@@ -9,7 +9,6 @@ task correct_rent_type_value: :environment do
new_rent_type_value = BulkUpload::Lettings::Year2024::RowParser::RENT_TYPE_BU_MAPPING[rent_type_at_upload]
log.rent_type = new_rent_type_value
- log.skip_update_status = true if log.status == "pending"
if log.save
Rails.logger.info("Log #{log.id} rent_type updated from #{rent_type_at_upload} to #{log.rent_type}")
else
diff --git a/lib/tasks/data_export.rake b/lib/tasks/data_export.rake
index 7a9a90bc8..43ef06374 100644
--- a/lib/tasks/data_export.rake
+++ b/lib/tasks/data_export.rake
@@ -7,13 +7,13 @@ namespace :core do
end
desc "Export all data XMLs for import into Central Data System (CDS)"
- task :full_data_export_xml, %i[collection] => :environment do |_task, args|
+ task :full_data_export_xml, %i[collection year] => :environment do |_task, args|
collection = args[:collection].presence
- collection = collection.to_i if collection.present? && collection.scan(/\D/).empty?
+ year = args[:year]&.to_i
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["EXPORT_BUCKET"])
export_service = Exports::ExportService.new(storage_service)
- export_service.export_xml(full_update: true, collection:)
+ export_service.export_xml(full_update: true, collection:, year:)
end
end
diff --git a/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation.rake b/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation.rake
index e827e1c10..b1cbd56f0 100644
--- a/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation.rake
+++ b/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation.rake
@@ -3,7 +3,6 @@ task recalculate_status_over_retirement: :environment do
validation_trigger_condition = "(ecstat1 != 5 AND age1 > 66) OR (ecstat2 != 5 AND age2 > 66) OR (ecstat3 != 5 AND age3 > 66) OR (ecstat4 != 5 AND age4 > 66) OR (ecstat5 != 5 AND age5 > 66) OR (ecstat6 != 5 AND age6 > 66)"
SalesLog.filter_by_year(2024).where(status: "pending", status_cache: "completed").where(validation_trigger_condition).find_each do |log|
log.status_cache = log.calculate_status
- log.skip_update_status = true
unless log.save
Rails.logger.info "Could not save changes to pending sales log #{log.id}"
diff --git a/lib/tasks/set_export_collection_years.rake b/lib/tasks/set_export_collection_years.rake
new file mode 100644
index 000000000..badaab0eb
--- /dev/null
+++ b/lib/tasks/set_export_collection_years.rake
@@ -0,0 +1,8 @@
+desc "Set export collection years for lettings exports"
+task set_export_collection_years: :environment do
+ Export.where(collection: %w[2022 2023 2024 2025]).find_each do |export|
+ export.year = export.collection.to_i
+ export.collection = "lettings"
+ export.save!
+ end
+end
diff --git a/package.json b/package.json
index ef37dc8aa..c5734b348 100644
--- a/package.json
+++ b/package.json
@@ -20,11 +20,11 @@
"css-loader": "^6.7.1",
"custom-event-polyfill": "^1.0.7",
"file-loader": "^6.2.0",
- "govuk-frontend": "5.1.0",
+ "govuk-frontend": "5.7.1",
"html5shiv": "^3.7.3",
"intersection-observer": "^0.12.0",
"mini-css-extract-plugin": "^2.6.0",
- "rails_admin": "3.1.3",
+ "rails_admin": "3.3.0",
"regenerator-runtime": "^0.13.9",
"sass": "^1.49.9",
"sass-loader": "^12.6.0",
diff --git a/spec/components/primary_navigation_component_spec.rb b/spec/components/primary_navigation_component_spec.rb
index f5f41bff0..aeefa6130 100644
--- a/spec/components/primary_navigation_component_spec.rb
+++ b/spec/components/primary_navigation_component_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe PrimaryNavigationComponent, type: :component do
it "then that item appears as selected" do
result = render_inline(described_class.new(items:))
- expect(result.css('.app-primary-navigation__link[aria-current="page"]').text).to include("Organisations")
+ expect(result.css('.govuk-service-navigation__link[aria-current="page"]').text).to include("Organisations")
end
end
diff --git a/spec/components/search_result_caption_component_spec.rb b/spec/components/search_result_caption_component_spec.rb
index 25cbc1bdd..05ac09630 100644
--- a/spec/components/search_result_caption_component_spec.rb
+++ b/spec/components/search_result_caption_component_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
let(:count) { 2 }
let(:item_label) { "user" }
let(:total_count) { 3 }
- let(:item) { "schemes" }
+ let(:item) { "scheme" }
let(:filters_count) { 1 }
let(:result) { render_inline(described_class.new(searched:, count:, item_label:, total_count:, item:, filters_count:)) }
@@ -21,6 +21,14 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
it "renders table caption including the search results and total" do
expect(result.to_html).to eq("\n 2 users matching search \n\n")
end
+
+ context "with 1 result" do
+ let(:count) { 1 }
+
+ it "renders table caption including the search results and total" do
+ expect(result.to_html).to eq("\n 1 user matching search \n\n")
+ end
+ end
end
context "when filter results are found" do
@@ -29,6 +37,14 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
it "renders table caption including the search results and total" do
expect(result.to_html).to eq("\n 2 users matching filters \n\n")
end
+
+ context "with 1 result" do
+ let(:count) { 1 }
+
+ it "renders table caption including the search results and total" do
+ expect(result.to_html).to eq("\n 1 user matching filters \n\n")
+ end
+ end
end
context "when no search/filter is applied" do
@@ -38,6 +54,14 @@ RSpec.describe SearchResultCaptionComponent, type: :component do
it "renders table caption with total count only" do
expect(result.to_html).to eq("\n \n 2 total schemes\n \n\n")
end
+
+ context "with 1 result" do
+ let(:count) { 1 }
+
+ it "renders table caption with total count only" do
+ expect(result.to_html).to eq("\n \n 1 total scheme\n \n\n")
+ end
+ end
end
context "when nothing is found" do
diff --git a/spec/controllers/errors_controller_spec.rb b/spec/controllers/errors_controller_spec.rb
index d8ae16165..30361ea42 100644
--- a/spec/controllers/errors_controller_spec.rb
+++ b/spec/controllers/errors_controller_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe ErrorsController, type: :controller do
describe "GET #unprocessable_entity" do
it "returns unprocessable_entity" do
get :unprocessable_entity
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
end
end
end
diff --git a/spec/db/seeds_spec.rb b/spec/db/seeds_spec.rb
index 316f04ba6..6ae07ddb0 100644
--- a/spec/db/seeds_spec.rb
+++ b/spec/db/seeds_spec.rb
@@ -21,7 +21,8 @@ RSpec.describe "seeding process", type: task do
allow(Rails.env).to receive(:review?).and_return(true)
end
- it "sets up correct data" do
+ # Doing this in one test should save ~2 minutes
+ it "sets up correct data idempotently" do
expect {
Rails.application.load_seed
}.to change(User, :count)
@@ -30,10 +31,6 @@ RSpec.describe "seeding process", type: task do
.and change(Scheme, :count)
.and change(Location, :count)
.and change(LaRentRange, :count)
- end
-
- it "is idempotent" do
- Rails.application.load_seed
expect {
Rails.application.load_seed
diff --git a/spec/factories/organisation.rb b/spec/factories/organisation.rb
index ecab5bf81..f3184277e 100644
--- a/spec/factories/organisation.rb
+++ b/spec/factories/organisation.rb
@@ -1,6 +1,6 @@
FactoryBot.define do
factory :organisation do
- name { Faker::Company.name }
+ sequence(:name) { |n| "#{Faker::Company.name} #{n}" }
address_line1 { Faker::Address.street_address }
address_line2 { Faker::Address.city }
provider_type { "LA" }
diff --git a/spec/features/form/page_routing_spec.rb b/spec/features/form/page_routing_spec.rb
index 42a2c25fb..118b52543 100644
--- a/spec/features/form/page_routing_spec.rb
+++ b/spec/features/form/page_routing_spec.rb
@@ -92,7 +92,7 @@ RSpec.describe "Form Page Routing" do
expect(find("#lettings-log-postcode-full-field-error").value).to eq("FAKE_POSTCODE")
end
- it "does not reset the displayed date" do
+ it "does not reset the displayed date if it's an invalid date" do
lettings_log.update!(startdate: "2021/10/13")
visit("/lettings-logs/#{id}/tenancy-start-date")
fill_in("lettings_log[startdate(1i)]", with: "202")
@@ -106,6 +106,20 @@ RSpec.describe "Form Page Routing" do
expect(find_field("lettings_log[startdate(1i)]").value).to eq("2021")
end
+ it "displays the entered date if it's in a valid format" do
+ lettings_log.update!(startdate: "2021/10/13")
+ visit("/lettings-logs/#{id}/tenancy-start-date")
+ fill_in("lettings_log[startdate(1i)]", with: "202")
+ fill_in("lettings_log[startdate(2i)]", with: "12")
+ fill_in("lettings_log[startdate(3i)]", with: "1")
+ click_button("Save and continue")
+
+ expect(page).to have_current_path("/lettings-logs/#{id}/tenancy-start-date")
+ expect(find_field("lettings_log[startdate(3i)]").value).to eq("1")
+ expect(find_field("lettings_log[startdate(2i)]").value).to eq("12")
+ expect(find_field("lettings_log[startdate(1i)]").value).to eq("202")
+ end
+
it "does not reset the displayed date if it's empty" do
lettings_log.update!(startdate: nil)
visit("/lettings-logs/#{id}/tenancy-start-date")
diff --git a/spec/features/lettings_log_spec.rb b/spec/features/lettings_log_spec.rb
index efb7e7665..b10dfc6e5 100644
--- a/spec/features/lettings_log_spec.rb
+++ b/spec/features/lettings_log_spec.rb
@@ -729,5 +729,353 @@ RSpec.describe "Lettings Log Features" do
expect(duplicate_log.duplicate_set_id).to be_nil
end
end
+
+ context "when filling out address fields" do
+ let(:lettings_log) { create(:lettings_log, :setup_completed, assigned_to: user) }
+
+ before do
+ body = {
+ results: [
+ {
+ DPA: {
+ "POSTCODE": "AA1 1AA",
+ "POST_TOWN": "Bristol",
+ "ORGANISATION_NAME": "Some place",
+ },
+ },
+ ],
+ }.to_json
+
+ WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=111")
+ .to_return(status: 200, body:, headers: {})
+
+ body = { results: [{ DPA: { UPRN: "111" } }] }.to_json
+ WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AA&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
+ .to_return(status: 200, body:, headers: {})
+
+ WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA11AA")
+ .to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 1AA\",\"admin_district\":\"Westminster\",\"codes\":{\"admin_district\":\"E09000033\"}}}", headers: {})
+
+ WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA12AA")
+ .to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 2AA\",\"admin_district\":\"Wigan\",\"codes\":{\"admin_district\":\"E08000010\"}}}", headers: {})
+
+ body = { results: [] }.to_json
+ WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AB&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
+ .to_return(status: 200, body:, headers: {})
+
+ visit("/lettings-logs/#{lettings_log.id}/uprn")
+ end
+
+ context "and uprn is known and answered" do
+ before do
+ choose "Yes"
+ fill_in("lettings_log[uprn]", with: "111")
+ click_button("Save and continue")
+ end
+
+ context "and uprn is confirmed" do
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(1) # yes
+ expect(lettings_log.uprn).to eq("111")
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 1AA")
+ expect(lettings_log.address_line1).to eq("Some Place")
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq("Bristol")
+ expect(lettings_log.address_line1_input).to eq(nil)
+ expect(lettings_log.postcode_full_input).to eq(nil)
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E09000033")
+
+ choose "Yes"
+ click_button("Save and continue")
+
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(1) # yes
+ expect(lettings_log.uprn).to eq("111")
+ expect(lettings_log.uprn_confirmed).to eq(1) # yes
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 1AA")
+ expect(lettings_log.address_line1).to eq("Some Place")
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq("Bristol")
+ expect(lettings_log.address_line1_input).to eq(nil)
+ expect(lettings_log.postcode_full_input).to eq(nil)
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E09000033")
+ end
+
+ context "and changes to uprn not known" do
+ it "sets correct address fields" do
+ visit("/lettings-logs/#{lettings_log.id}/uprn")
+
+ choose "No"
+ click_button("Save and continue")
+
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(nil)
+ expect(lettings_log.postcode_full).to eq(nil)
+ expect(lettings_log.address_line1).to eq(nil)
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq(nil)
+ expect(lettings_log.address_line1_input).to eq(nil)
+ expect(lettings_log.postcode_full_input).to eq(nil)
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq(nil)
+ end
+ end
+ end
+
+ context "and uprn is not confirmed" do
+ before do
+ choose "No, I want to search for the address instead"
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(nil)
+ expect(lettings_log.postcode_full).to eq(nil)
+ expect(lettings_log.address_line1).to eq(nil)
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq(nil)
+ expect(lettings_log.address_line1_input).to eq(nil)
+ expect(lettings_log.postcode_full_input).to eq(nil)
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq(nil)
+ end
+ end
+ end
+
+ context "and uprn is not known" do
+ before do
+ choose "No"
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(nil)
+ expect(lettings_log.postcode_full).to eq(nil)
+ expect(lettings_log.address_line1).to eq(nil)
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq(nil)
+ expect(lettings_log.address_line1_input).to eq(nil)
+ expect(lettings_log.postcode_full_input).to eq(nil)
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq(nil)
+ end
+
+ context "and the address is not found" do
+ it "sets correct address fields" do
+ fill_in("lettings_log[address_line1_input]", with: "Address line 1")
+ fill_in("lettings_log[postcode_full_input]", with: "AA1 1AB")
+ click_button("Search")
+
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(nil)
+ expect(lettings_log.postcode_full).to eq(nil)
+ expect(lettings_log.address_line1).to eq(nil)
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq(nil)
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AB")
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq(nil)
+
+ click_button("Confirm and continue")
+
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(nil)
+ expect(lettings_log.postcode_full).to eq(nil)
+ expect(lettings_log.address_line1).to eq(nil)
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq(nil)
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AB")
+ expect(lettings_log.address_search_value_check).to eq(0)
+ expect(lettings_log.la).to eq(nil)
+ end
+ end
+
+ context "and address is found, re-searched and not found" do
+ before do
+ fill_in("lettings_log[address_line1_input]", with: "Address line 1")
+ fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ visit("/lettings-logs/#{lettings_log.id}/address-matcher")
+
+ fill_in("lettings_log[address_line1_input]", with: "Address line 1")
+ fill_in("lettings_log[postcode_full_input]", with: "AA1 1AB")
+ click_button("Search")
+ end
+
+ it "routes to the correct page" do
+ expect(page).to have_current_path("/lettings-logs/#{lettings_log.id}/no-address-found")
+ end
+ end
+
+ context "and the user selects 'address_not_listed'" do
+ before do
+ fill_in("lettings_log[address_line1_input]", with: "Address line 1")
+ fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ choose "The address is not listed, I want to enter the address manually"
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 1AA")
+ expect(lettings_log.address_line1).to eq("Address line 1")
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq(nil)
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E09000033")
+ end
+
+ context "and the user enters a new address manually" do
+ context "without changing a valid postcode" do
+ before do
+ fill_in("lettings_log[town_or_city]", with: "Town")
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 1AA")
+ expect(lettings_log.address_line1).to eq("Address line 1")
+ expect(lettings_log.address_line2).to eq("")
+ expect(lettings_log.town_or_city).to eq("Town")
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E09000033")
+ end
+ end
+
+ context "with changing the postcode" do
+ before do
+ fill_in("lettings_log[town_or_city]", with: "Town")
+ fill_in("lettings_log[postcode_full]", with: "AA12AA")
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(0) # no
+ expect(lettings_log.uprn).to eq(nil)
+ expect(lettings_log.uprn_confirmed).to eq(nil)
+ expect(lettings_log.uprn_selection).to eq("uprn_not_listed")
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 2AA")
+ expect(lettings_log.address_line1).to eq("Address line 1")
+ expect(lettings_log.address_line2).to eq("")
+ expect(lettings_log.town_or_city).to eq("Town")
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E08000010")
+ end
+ end
+ end
+ end
+
+ context "and the user selects 'address_not_listed' and then changes their mind and selects an address" do
+ before do
+ fill_in("lettings_log[address_line1_input]", with: "Address line 1")
+ fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ choose "The address is not listed, I want to enter the address manually"
+ click_button("Save and continue")
+
+ visit("/lettings-logs/#{lettings_log.id}/uprn-selection")
+ choose("lettings-log-uprn-selection-111-field", allow_label_click: true)
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(1)
+ expect(lettings_log.uprn).to eq("111")
+ expect(lettings_log.uprn_confirmed).to eq(1)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 1AA")
+ expect(lettings_log.address_line1).to eq("Some Place")
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq("Bristol")
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E09000033")
+ end
+ end
+
+ context "and possible addresses found and selected" do
+ before do
+ fill_in("lettings_log[address_line1_input]", with: "Address line 1")
+ fill_in("lettings_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ choose("lettings-log-uprn-selection-111-field", allow_label_click: true)
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ lettings_log.reload
+ expect(lettings_log.uprn_known).to eq(1)
+ expect(lettings_log.uprn).to eq("111")
+ expect(lettings_log.uprn_confirmed).to eq(1)
+ expect(lettings_log.uprn_selection).to eq(nil)
+ expect(lettings_log.postcode_known).to eq(1)
+ expect(lettings_log.postcode_full).to eq("AA1 1AA")
+ expect(lettings_log.address_line1).to eq("Some Place")
+ expect(lettings_log.address_line2).to eq(nil)
+ expect(lettings_log.town_or_city).to eq("Bristol")
+ expect(lettings_log.address_line1_input).to eq("Address line 1")
+ expect(lettings_log.postcode_full_input).to eq("AA1 1AA")
+ expect(lettings_log.address_search_value_check).to eq(nil)
+ expect(lettings_log.la).to eq("E09000033")
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/features/organisation_spec.rb b/spec/features/organisation_spec.rb
index 6f90428be..15bfecf39 100644
--- a/spec/features/organisation_spec.rb
+++ b/spec/features/organisation_spec.rb
@@ -206,14 +206,14 @@ RSpec.describe "User Features" do
it "can filter lettings logs by year" do
check("years-2022-field")
click_button("Apply filters")
- expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2022&status[]=&needstypes[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
+ expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?%5Byears%5D[]=&years[]=2022&%5Bstatus%5D[]=&%5Bneedstypes%5D[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).not_to have_link first_log.id.to_s, href: "/lettings-logs/#{first_log.id}"
end
it "can filter lettings logs by needstype" do
check("needstypes-1-field")
click_button("Apply filters")
- expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&status[]=&needstypes[]=&needstypes[]=1&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
+ expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?%5Byears%5D[]=&%5Bstatus%5D[]=&%5Bneedstypes%5D[]=&needstypes[]=1&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
other_general_needs_logs.each do |general_needs_log|
expect(page).to have_link general_needs_log.id.to_s, href: "/lettings-logs/#{general_needs_log.id}"
end
@@ -256,7 +256,7 @@ RSpec.describe "User Features" do
end
check("years-2022-field")
click_button("Apply filters")
- expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?years[]=&years[]=2022&status[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
+ expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?%5Byears%5D[]=&years[]=2022&%5Bstatus%5D[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).not_to have_link first_log.id.to_s, href: "/sales-logs/#{first_log.id}"
end
end
diff --git a/spec/features/sales_log_spec.rb b/spec/features/sales_log_spec.rb
index 879f2b5c8..d418bcb37 100644
--- a/spec/features/sales_log_spec.rb
+++ b/spec/features/sales_log_spec.rb
@@ -310,6 +310,354 @@ RSpec.describe "Sales Log Features" do
expect(page).to have_current_path("/sales-logs/bulk-uploads")
end
end
+
+ context "when filling out address fields" do
+ let(:sales_log) { create(:sales_log, :shared_ownership_setup_complete, assigned_to: user) }
+
+ before do
+ body = {
+ results: [
+ {
+ DPA: {
+ "POSTCODE": "AA1 1AA",
+ "POST_TOWN": "Bristol",
+ "ORGANISATION_NAME": "Some place",
+ },
+ },
+ ],
+ }.to_json
+
+ WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?dataset=DPA,LPI&key=OS_DATA_KEY&uprn=111")
+ .to_return(status: 200, body:, headers: {})
+
+ body = { results: [{ DPA: { UPRN: "111" } }] }.to_json
+ WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AA&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
+ .to_return(status: 200, body:, headers: {})
+
+ WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA11AA")
+ .to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 1AA\",\"admin_district\":\"Westminster\",\"codes\":{\"admin_district\":\"E09000033\"}}}", headers: {})
+
+ WebMock.stub_request(:get, "https://api.postcodes.io/postcodes/AA12AA")
+ .to_return(status: 200, body: "{\"status\":200,\"result\":{\"postcode\":\"AA1 2AA\",\"admin_district\":\"Wigan\",\"codes\":{\"admin_district\":\"E08000010\"}}}", headers: {})
+
+ body = { results: [] }.to_json
+ WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/find?query=Address+line+1%2C+AA1+1AB&key=OS_DATA_KEY&maxresults=10&minmatch=0.4")
+ .to_return(status: 200, body:, headers: {})
+
+ visit("/sales-logs/#{sales_log.id}/uprn")
+ end
+
+ context "and uprn is known and answered" do
+ before do
+ choose "Yes"
+ fill_in("sales_log[uprn]", with: "111")
+ click_button("Save and continue")
+ end
+
+ context "and uprn is confirmed" do
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(1) # yes
+ expect(sales_log.uprn).to eq("111")
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 1AA")
+ expect(sales_log.address_line1).to eq("Some Place")
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq("Bristol")
+ expect(sales_log.address_line1_input).to eq(nil)
+ expect(sales_log.postcode_full_input).to eq(nil)
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E09000033")
+
+ choose "Yes"
+ click_button("Save and continue")
+
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(1) # yes
+ expect(sales_log.uprn).to eq("111")
+ expect(sales_log.uprn_confirmed).to eq(1) # yes
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 1AA")
+ expect(sales_log.address_line1).to eq("Some Place")
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq("Bristol")
+ expect(sales_log.address_line1_input).to eq(nil)
+ expect(sales_log.postcode_full_input).to eq(nil)
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E09000033")
+ end
+
+ context "and changes to uprn not known" do
+ it "sets correct address fields" do
+ visit("/sales-logs/#{sales_log.id}/uprn")
+
+ choose "No"
+ click_button("Save and continue")
+
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(nil)
+ expect(sales_log.postcode_full).to eq(nil)
+ expect(sales_log.address_line1).to eq(nil)
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq(nil)
+ expect(sales_log.address_line1_input).to eq(nil)
+ expect(sales_log.postcode_full_input).to eq(nil)
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq(nil)
+ end
+ end
+ end
+
+ context "and uprn is not confirmed" do
+ before do
+ choose "No, I want to search for the address instead"
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(nil)
+ expect(sales_log.postcode_full).to eq(nil)
+ expect(sales_log.address_line1).to eq(nil)
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq(nil)
+ expect(sales_log.address_line1_input).to eq(nil)
+ expect(sales_log.postcode_full_input).to eq(nil)
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq(nil)
+ end
+ end
+ end
+
+ context "and uprn is not known" do
+ before do
+ choose "No"
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(nil)
+ expect(sales_log.postcode_full).to eq(nil)
+ expect(sales_log.address_line1).to eq(nil)
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq(nil)
+ expect(sales_log.address_line1_input).to eq(nil)
+ expect(sales_log.postcode_full_input).to eq(nil)
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq(nil)
+ end
+
+ context "and the address is not found" do
+ it "sets correct address fields" do
+ fill_in("sales_log[address_line1_input]", with: "Address line 1")
+ fill_in("sales_log[postcode_full_input]", with: "AA1 1AB")
+ click_button("Search")
+
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(nil)
+ expect(sales_log.postcode_full).to eq(nil)
+ expect(sales_log.address_line1).to eq(nil)
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq(nil)
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AB")
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq(nil)
+
+ click_button("Confirm and continue")
+
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(nil)
+ expect(sales_log.postcode_full).to eq(nil)
+ expect(sales_log.address_line1).to eq(nil)
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq(nil)
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AB")
+ expect(sales_log.address_search_value_check).to eq(0)
+ expect(sales_log.la).to eq(nil)
+ end
+ end
+
+ context "and address is found, re-searched and not found" do
+ before do
+ fill_in("sales_log[address_line1_input]", with: "Address line 1")
+ fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ visit("/sales-logs/#{sales_log.id}/address-matcher")
+
+ fill_in("sales_log[address_line1_input]", with: "Address line 1")
+ fill_in("sales_log[postcode_full_input]", with: "AA1 1AB")
+ click_button("Search")
+ end
+
+ it "routes to the correct page" do
+ expect(page).to have_current_path("/sales-logs/#{sales_log.id}/no-address-found")
+ end
+ end
+
+ context "and the user selects 'address_not_listed'" do
+ before do
+ fill_in("sales_log[address_line1_input]", with: "Address line 1")
+ fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ choose "The address is not listed, I want to enter the address manually"
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq("uprn_not_listed")
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 1AA")
+ expect(sales_log.address_line1).to eq("Address line 1")
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq(nil)
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AA")
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E09000033")
+ end
+
+ context "and the user enters a new address manually" do
+ context "without changing a valid postcode" do
+ before do
+ fill_in("sales_log[town_or_city]", with: "Town")
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq("uprn_not_listed")
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 1AA")
+ expect(sales_log.address_line1).to eq("Address line 1")
+ expect(sales_log.address_line2).to eq("")
+ expect(sales_log.town_or_city).to eq("Town")
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AA")
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E09000033")
+ end
+ end
+
+ context "with changing the postcode" do
+ before do
+ fill_in("sales_log[town_or_city]", with: "Town")
+ fill_in("sales_log[postcode_full]", with: "AA12AA")
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(0) # no
+ expect(sales_log.uprn).to eq(nil)
+ expect(sales_log.uprn_confirmed).to eq(nil)
+ expect(sales_log.uprn_selection).to eq("uprn_not_listed")
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 2AA")
+ expect(sales_log.address_line1).to eq("Address line 1")
+ expect(sales_log.address_line2).to eq("")
+ expect(sales_log.town_or_city).to eq("Town")
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AA")
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E08000010")
+ end
+ end
+ end
+ end
+
+ context "and the user selects 'address_not_listed' and then changes their mind and selects an address" do
+ before do
+ fill_in("sales_log[address_line1_input]", with: "Address line 1")
+ fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ choose "The address is not listed, I want to enter the address manually"
+ click_button("Save and continue")
+
+ visit("/sales-logs/#{sales_log.id}/uprn-selection")
+ choose("sales-log-uprn-selection-111-field", allow_label_click: true)
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(1)
+ expect(sales_log.uprn).to eq("111")
+ expect(sales_log.uprn_confirmed).to eq(1)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 1AA")
+ expect(sales_log.address_line1).to eq("Some Place")
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq("Bristol")
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AA")
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E09000033")
+ end
+ end
+
+ context "and possible addresses found and selected" do
+ before do
+ fill_in("sales_log[address_line1_input]", with: "Address line 1")
+ fill_in("sales_log[postcode_full_input]", with: "AA1 1AA")
+ click_button("Search")
+ choose("sales-log-uprn-selection-111-field", allow_label_click: true)
+ click_button("Save and continue")
+ end
+
+ it "sets correct address fields" do
+ sales_log.reload
+ expect(sales_log.uprn_known).to eq(1)
+ expect(sales_log.uprn).to eq("111")
+ expect(sales_log.uprn_confirmed).to eq(1)
+ expect(sales_log.uprn_selection).to eq(nil)
+ expect(sales_log.pcodenk).to eq(0)
+ expect(sales_log.postcode_full).to eq("AA1 1AA")
+ expect(sales_log.address_line1).to eq("Some Place")
+ expect(sales_log.address_line2).to eq(nil)
+ expect(sales_log.town_or_city).to eq("Bristol")
+ expect(sales_log.address_line1_input).to eq("Address line 1")
+ expect(sales_log.postcode_full_input).to eq("AA1 1AA")
+ expect(sales_log.address_search_value_check).to eq(nil)
+ expect(sales_log.la).to eq("E09000033")
+ end
+ end
+ end
+ end
end
context "when a log becomes a duplicate" do
diff --git a/spec/features/schemes_spec.rb b/spec/features/schemes_spec.rb
index dfb56665b..84572f35b 100644
--- a/spec/features/schemes_spec.rb
+++ b/spec/features/schemes_spec.rb
@@ -87,7 +87,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays the filters component with a correct count and clear button" do
expect(page).to have_content("2 filters applied")
- expect(page).to have_link("Clear", href: /clear-filters\?filter_type=schemes/)
+ expect(page).to have_link("Clear", href: /clear-filters\?.*filter_type=schemes/)
end
context "when clearing the filters" do
@@ -326,7 +326,7 @@ RSpec.describe "Schemes scheme Features" do
it "displays the filters component with a correct count and clear button" do
expect(page).to have_content("2 filters applied")
- expect(page).to have_link("Clear", href: /\/clear-filters\?filter_type=scheme_locations/)
+ expect(page).to have_link("Clear", href: /\/clear-filters\?.*filter_type=scheme_locations/)
end
context "when clearing the filters" do
diff --git a/spec/features/user_spec.rb b/spec/features/user_spec.rb
index bc562824c..de3f2cbc5 100644
--- a/spec/features/user_spec.rb
+++ b/spec/features/user_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe "User Features" do
context "when the user has forgotten their password" do
it "is redirected to the reset password page when they click the reset password link" do
visit("/lettings-logs")
- click_link("reset your password")
+ click_link("Forgot password")
expect(page).to have_current_path("/account/password/new")
end
@@ -269,7 +269,7 @@ RSpec.describe "User Features" do
it "displays the filters component with a correct count and clear button" do
expect(page).to have_content("2 filters applied")
- expect(page).to have_link("Clear", href: /clear-filters\?filter_type=users/)
+ expect(page).to have_link("Clear", href: /clear-filters\?.*filter_type=users/)
end
context "when clearing the filters" do
@@ -678,7 +678,7 @@ RSpec.describe "User Features" do
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Check your email")
- expect(page).to have_http_status(:unprocessable_entity)
+ expect(page).to have_http_status(:unprocessable_content)
expect(page).to have_title("Error")
expect(page).to have_selector(".govuk-error-summary__title")
end
@@ -691,7 +691,7 @@ RSpec.describe "User Features" do
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Check your email")
- expect(page).to have_http_status(:unprocessable_entity)
+ expect(page).to have_http_status(:unprocessable_content)
expect(page).to have_title("Error")
expect(page).to have_selector(".govuk-error-summary__title")
end
@@ -744,7 +744,7 @@ RSpec.describe "User Features" do
it "is redirected to the reset password page when they click the reset password link" do
visit("/account/sign-in")
- click_link("reset your password")
+ click_link("Forgot password")
expect(page).to have_current_path("/account/password/new")
end
diff --git a/spec/fixtures/exports/general_needs_log.xml b/spec/fixtures/exports/general_needs_log.xml
index bacc7e9f0..0341dd2d4 100644
--- a/spec/fixtures/exports/general_needs_log.xml
+++ b/spec/fixtures/exports/general_needs_log.xml
@@ -147,10 +147,10 @@
{id}{owning_org_id}
- MHCLG
+ {owning_org_name}1234{managing_org_id}
- MHCLG
+ {managing_org_name}12342022-05-01T00:00:00+01:002022-05-01T00:00:00+01:00
diff --git a/spec/fixtures/exports/general_needs_log_23_24.xml b/spec/fixtures/exports/general_needs_log_23_24.xml
index 9635cd0e4..ef0c4066c 100644
--- a/spec/fixtures/exports/general_needs_log_23_24.xml
+++ b/spec/fixtures/exports/general_needs_log_23_24.xml
@@ -148,10 +148,10 @@
{id}{owning_org_id}
- MHCLG
+ {owning_org_name}1234{managing_org_id}
- MHCLG
+ {managing_org_name}12342023-04-03T00:00:00+01:002023-04-03T00:00:00+01:00
diff --git a/spec/fixtures/exports/general_needs_log_24_25.xml b/spec/fixtures/exports/general_needs_log_24_25.xml
index a665a284e..00d8bb1a5 100644
--- a/spec/fixtures/exports/general_needs_log_24_25.xml
+++ b/spec/fixtures/exports/general_needs_log_24_25.xml
@@ -161,10 +161,10 @@
la as entered{id}{owning_org_id}
- MHCLG
+ {owning_org_name}1234{managing_org_id}
- MHCLG
+ {managing_org_name}12342024-04-03T00:00:00+01:002024-04-03T00:00:00+01:00
diff --git a/spec/fixtures/exports/organisation.xml b/spec/fixtures/exports/organisation.xml
index 8d87da16c..70c699915 100644
--- a/spec/fixtures/exports/organisation.xml
+++ b/spec/fixtures/exports/organisation.xml
@@ -2,7 +2,7 @@
diff --git a/spec/helpers/tab_nav_helper_spec.rb b/spec/helpers/tab_nav_helper_spec.rb
index 89f867775..bd29f4c46 100644
--- a/spec/helpers/tab_nav_helper_spec.rb
+++ b/spec/helpers/tab_nav_helper_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe TabNavHelper do
describe "#user_cell" do
it "returns user link and email separated by a newline character" do
expected_html = "#{current_user.name}\nUser #{current_user.email}"
- expect(user_cell(current_user)).to match(expected_html)
+ expect(CGI.unescapeHTML(user_cell(current_user))).to match(expected_html)
end
end
diff --git a/spec/lib/tasks/correct_rent_type_value_spec.rb b/spec/lib/tasks/correct_rent_type_value_spec.rb
index adccda405..d4e015b5d 100644
--- a/spec/lib/tasks/correct_rent_type_value_spec.rb
+++ b/spec/lib/tasks/correct_rent_type_value_spec.rb
@@ -35,7 +35,6 @@ RSpec.describe "correct_rent_type_value" do
it "updates the rent_type value on a pending log where it was set to 1 on create" do
log = build(:lettings_log, :completed, rent_type: 1, bulk_upload:, status: "pending")
- log.skip_update_status = true
log.save!
initial_updated_at = log.updated_at
expect(log.status).to eq("pending")
@@ -166,7 +165,6 @@ RSpec.describe "correct_rent_type_value" do
it "updates the rent_type value on a pending log where it was set to 2 on create" do
log = build(:lettings_log, :completed, rent_type: 2, bulk_upload:, status: "pending")
- log.skip_update_status = true
log.save!
initial_updated_at = log.updated_at
expect(log.status).to eq("pending")
diff --git a/spec/lib/tasks/data_export_spec.rb b/spec/lib/tasks/data_export_spec.rb
index 8b5dd5fbe..2f6127512 100644
--- a/spec/lib/tasks/data_export_spec.rb
+++ b/spec/lib/tasks/data_export_spec.rb
@@ -30,7 +30,7 @@ describe "rake core:data_export", type: task do
context "with all available years" do
it "calls the export service" do
- expect(export_service).to receive(:export_xml).with(full_update: true, collection: nil)
+ expect(export_service).to receive(:export_xml).with(full_update: true, collection: nil, year: nil)
task.invoke
end
@@ -38,9 +38,9 @@ describe "rake core:data_export", type: task do
context "with a specific collection" do
it "calls the export service" do
- expect(export_service).to receive(:export_xml).with(full_update: true, collection: 2022)
+ expect(export_service).to receive(:export_xml).with(full_update: true, collection: "lettings", year: 2022)
- task.invoke("2022")
+ task.invoke("lettings", "2022")
end
end
end
diff --git a/spec/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation_spec.rb b/spec/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation_spec.rb
index b76380f90..cd5e0255c 100644
--- a/spec/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation_spec.rb
+++ b/spec/lib/tasks/recalculate_status_after_sales_over_retirement_age_validation_spec.rb
@@ -17,7 +17,6 @@ RSpec.describe "recalculate_status_after_sales_over_retirement_age_validation" d
before do
log.status = "completed"
- log.skip_update_status = true
log.save!
end
@@ -34,7 +33,6 @@ RSpec.describe "recalculate_status_after_sales_over_retirement_age_validation" d
before do
log.status = "pending"
log.status_cache = "completed"
- log.skip_update_status = true
log.save!
end
diff --git a/spec/lib/tasks/set_export_collection_years_spec.rb b/spec/lib/tasks/set_export_collection_years_spec.rb
new file mode 100644
index 000000000..76ff779b0
--- /dev/null
+++ b/spec/lib/tasks/set_export_collection_years_spec.rb
@@ -0,0 +1,41 @@
+require "rails_helper"
+require "rake"
+
+RSpec.describe "set_export_collection_years" do
+ describe ":set_export_collection_years", type: :task do
+ subject(:task) { Rake::Task["set_export_collection_years"] }
+
+ before do
+ Rake.application.rake_require("tasks/set_export_collection_years")
+ Rake::Task.define_task(:environment)
+ task.reenable
+ end
+
+ context "when the rake task is run" do
+ let!(:lettings_export_2023) { Export.create(collection: "2023", year: nil, started_at: Time.zone.now) }
+ let!(:lettings_export_2024) { Export.create(collection: "2024", year: nil, started_at: Time.zone.now) }
+ let!(:updated_lettings_export) { Export.create(collection: "lettings", year: 2023, started_at: Time.zone.now) }
+ let!(:organisations_export) { Export.create(collection: "organisations", year: nil, started_at: Time.zone.now) }
+ let!(:users_export) { Export.create(collection: "users", year: nil, started_at: Time.zone.now) }
+
+ it "correctly updates collection years" do
+ task.invoke
+
+ expect(lettings_export_2023.reload.collection).to eq("lettings")
+ expect(lettings_export_2023.year).to eq(2023)
+
+ expect(lettings_export_2024.reload.collection).to eq("lettings")
+ expect(lettings_export_2024.year).to eq(2024)
+
+ expect(updated_lettings_export.reload.collection).to eq("lettings")
+ expect(updated_lettings_export.year).to eq(2023)
+
+ expect(organisations_export.reload.collection).to eq("organisations")
+ expect(organisations_export.year).to eq(nil)
+
+ expect(users_export.reload.collection).to eq("users")
+ expect(users_export.year).to eq(nil)
+ end
+ end
+ end
+end
diff --git a/spec/mailers/bulk_upload_mailer_spec.rb b/spec/mailers/bulk_upload_mailer_spec.rb
index c3225c937..910bca4a9 100644
--- a/spec/mailers/bulk_upload_mailer_spec.rb
+++ b/spec/mailers/bulk_upload_mailer_spec.rb
@@ -121,4 +121,29 @@ RSpec.describe BulkUploadMailer do
mailer.send_check_soft_validations_mail(bulk_upload:)
end
end
+
+ describe "#send_correct_duplicates_and_upload_again_mail" do
+ context "when 2 columns with errors" do
+ before do
+ create(:bulk_upload_error, bulk_upload:, col: "A")
+ create(:bulk_upload_error, bulk_upload:, col: "B")
+ end
+
+ it "sends correctly formed email" do
+ expect(notify_client).to receive(:send_email).with(
+ email_address: user.email,
+ template_id: described_class::FAILED_CSV_DUPLICATE_ERRORS_TEMPLATE_ID,
+ personalisation: {
+ filename: bulk_upload.filename,
+ upload_timestamp: bulk_upload.created_at.to_fs(:govuk_date_and_time),
+ year_combo: bulk_upload.year_combo,
+ lettings_or_sales: bulk_upload.log_type,
+ summary_report_link: "http://localhost:3000/lettings-logs/bulk-upload-results/#{bulk_upload.id}",
+ },
+ )
+
+ mailer.send_correct_duplicates_and_upload_again_mail(bulk_upload:)
+ end
+ end
+ end
end
diff --git a/spec/mailers/devise_notify_mailer_spec.rb b/spec/mailers/devise_notify_mailer_spec.rb
index 4ed209b24..7a7123be0 100644
--- a/spec/mailers/devise_notify_mailer_spec.rb
+++ b/spec/mailers/devise_notify_mailer_spec.rb
@@ -36,8 +36,11 @@ RSpec.describe DeviseNotifyMailer do
end
context "when the email domain is in the allowlist" do
- let(:domain) { Rails.application.credentials[:email_allowlist].first }
- let(:email) { "test@#{domain}" }
+ before do
+ allow(Rails.application.credentials).to receive(:[]).with(:email_allowlist).and_return(["example.com"])
+ end
+
+ let(:email) { "test@example.com" }
it "does send emails" do
expect(notify_client).to receive(:send_email).once
@@ -48,10 +51,10 @@ RSpec.describe DeviseNotifyMailer do
context "when notify mailer raises BadRequestError" do
before do
allow(notify_client).to receive(:send_email).and_raise(bad_request_error)
+ allow(Rails.application.credentials).to receive(:[]).with(:email_allowlist).and_return(["example.com"])
end
- let(:domain) { Rails.application.credentials[:email_allowlist].first }
- let(:email) { "test@#{domain}" }
+ let(:email) { "test@example.com" }
it "does not raise an error" do
expect {
diff --git a/spec/models/form/lettings/questions/managing_organisation_spec.rb b/spec/models/form/lettings/questions/managing_organisation_spec.rb
index 776a873a6..00485e80f 100644
--- a/spec/models/form/lettings/questions/managing_organisation_spec.rb
+++ b/spec/models/form/lettings/questions/managing_organisation_spec.rb
@@ -185,7 +185,7 @@ RSpec.describe Form::Lettings::Questions::ManagingOrganisation, type: :model do
context "when organisation has merged" do
let(:absorbing_org) { create(:organisation, name: "Absorbing org", holds_own_stock: true) }
let!(:merged_org) { create(:organisation, name: "Merged org", holds_own_stock: false) }
- let!(:merged_deleted_org) { create(:organisation, name: "Merged org", holds_own_stock: false, discarded_at: Time.zone.yesterday) }
+ let!(:merged_deleted_org) { create(:organisation, name: "Merged org 2", holds_own_stock: false, discarded_at: Time.zone.yesterday) }
let(:user) { create(:user, :data_coordinator, organisation: absorbing_org) }
let(:log) do
diff --git a/spec/models/form/sales/pages/about_staircase_spec.rb b/spec/models/form/sales/pages/about_staircase_spec.rb
index 181c3f695..7d8254d4b 100644
--- a/spec/models/form/sales/pages/about_staircase_spec.rb
+++ b/spec/models/form/sales/pages/about_staircase_spec.rb
@@ -15,6 +15,10 @@ RSpec.describe Form::Sales::Pages::AboutStaircase, type: :model do
describe "questions" do
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date:)) }
+ before do
+ allow(subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
+
context "when 2022" do
let(:start_date) { Time.utc(2022, 2, 8) }
diff --git a/spec/models/form/sales/pages/equity_spec.rb b/spec/models/form/sales/pages/equity_spec.rb
index a44085101..e27538263 100644
--- a/spec/models/form/sales/pages/equity_spec.rb
+++ b/spec/models/form/sales/pages/equity_spec.rb
@@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Pages::Equity, type: :model do
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) }
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
+
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
@@ -15,10 +19,6 @@ RSpec.describe Form::Sales::Pages::Equity, type: :model do
expect(page.questions.map(&:id)).to eq(%w[equity])
end
- it "has the correct id" do
- expect(page.id).to eq("equity")
- end
-
it "has the correct description" do
expect(page.description).to be_nil
end
diff --git a/spec/models/form/sales/pages/monthly_rent_staircasing_owned_spec.rb b/spec/models/form/sales/pages/monthly_rent_staircasing_owned_spec.rb
new file mode 100644
index 000000000..21f0e0ee6
--- /dev/null
+++ b/spec/models/form/sales/pages/monthly_rent_staircasing_owned_spec.rb
@@ -0,0 +1,31 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Pages::MonthlyRentStaircasingOwned, type: :model do
+ subject(:page) { described_class.new(page_id, page_definition, subsection) }
+
+ let(:page_id) { nil }
+ let(:page_definition) { nil }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) }
+
+ it "has correct subsection" do
+ expect(page.subsection).to eq(subsection)
+ end
+
+ it "has correct questions" do
+ expect(page.questions.map(&:id)).to eq(%w[mrentprestaircasing])
+ end
+
+ it "has the correct id" do
+ expect(page.id).to eq("monthly_rent_staircasing_owned")
+ end
+
+ it "has the correct description" do
+ expect(page.description).to be_nil
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq([
+ { "stairowned_100?" => true },
+ ])
+ end
+end
diff --git a/spec/models/form/sales/pages/monthly_rent_staircasing_spec.rb b/spec/models/form/sales/pages/monthly_rent_staircasing_spec.rb
new file mode 100644
index 000000000..347e105fd
--- /dev/null
+++ b/spec/models/form/sales/pages/monthly_rent_staircasing_spec.rb
@@ -0,0 +1,31 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Pages::MonthlyRentStaircasing, type: :model do
+ subject(:page) { described_class.new(page_id, page_definition, subsection) }
+
+ let(:page_id) { nil }
+ let(:page_definition) { nil }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) }
+
+ it "has correct subsection" do
+ expect(page.subsection).to eq(subsection)
+ end
+
+ it "has correct questions" do
+ expect(page.questions.map(&:id)).to eq(%w[mrentprestaircasing mrent])
+ end
+
+ it "has the correct id" do
+ expect(page.id).to eq("monthly_rent_staircasing")
+ end
+
+ it "has the correct description" do
+ expect(page.description).to be_nil
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq([
+ { "stairowned_100?" => false },
+ ])
+ end
+end
diff --git a/spec/models/form/sales/pages/staircase_first_time_spec.rb b/spec/models/form/sales/pages/staircase_first_time_spec.rb
new file mode 100644
index 000000000..9c7d713af
--- /dev/null
+++ b/spec/models/form/sales/pages/staircase_first_time_spec.rb
@@ -0,0 +1,31 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Pages::StaircaseFirstTime, type: :model do
+ subject(:page) { described_class.new(page_id, page_definition, subsection) }
+
+ let(:page_id) { nil }
+ let(:page_definition) { nil }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) }
+
+ it "has correct subsection" do
+ expect(page.subsection).to eq(subsection)
+ end
+
+ it "has correct questions" do
+ expect(page.questions.map(&:id)).to eq(%w[firststair])
+ end
+
+ it "has the correct id" do
+ expect(page.id).to eq("staircase_first_time")
+ end
+
+ it "has the correct description" do
+ expect(page.description).to be_nil
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq([
+ { "staircase" => 1 },
+ ])
+ end
+end
diff --git a/spec/models/form/sales/pages/staircase_initial_date_spec.rb b/spec/models/form/sales/pages/staircase_initial_date_spec.rb
new file mode 100644
index 000000000..de5806500
--- /dev/null
+++ b/spec/models/form/sales/pages/staircase_initial_date_spec.rb
@@ -0,0 +1,31 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Pages::StaircaseInitialDate, type: :model do
+ subject(:page) { described_class.new(page_id, page_definition, subsection) }
+
+ let(:page_id) { nil }
+ let(:page_definition) { nil }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) }
+
+ it "has correct subsection" do
+ expect(page.subsection).to eq(subsection)
+ end
+
+ it "has correct questions" do
+ expect(page.questions.map(&:id)).to eq(%w[initialpurchase])
+ end
+
+ it "has the correct id" do
+ expect(page.id).to eq("staircase_initial_date")
+ end
+
+ it "has the correct description" do
+ expect(page.description).to be_nil
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq([
+ { "is_firststair?" => true },
+ ])
+ end
+end
diff --git a/spec/models/form/sales/pages/staircase_previous_spec.rb b/spec/models/form/sales/pages/staircase_previous_spec.rb
new file mode 100644
index 000000000..336cf0454
--- /dev/null
+++ b/spec/models/form/sales/pages/staircase_previous_spec.rb
@@ -0,0 +1,31 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Pages::StaircasePrevious, type: :model do
+ subject(:page) { described_class.new(page_id, page_definition, subsection) }
+
+ let(:page_id) { nil }
+ let(:page_definition) { nil }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) }
+
+ it "has correct subsection" do
+ expect(page.subsection).to eq(subsection)
+ end
+
+ it "has correct questions" do
+ expect(page.questions.map(&:id)).to eq(%w[numstair lasttransaction initialpurchase])
+ end
+
+ it "has the correct id" do
+ expect(page.id).to eq("staircase_previous")
+ end
+
+ it "has the correct description" do
+ expect(page.description).to be_nil
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq([
+ { "is_firststair?" => false },
+ ])
+ end
+end
diff --git a/spec/models/form/sales/pages/staircase_sale_spec.rb b/spec/models/form/sales/pages/staircase_sale_spec.rb
new file mode 100644
index 000000000..17a140797
--- /dev/null
+++ b/spec/models/form/sales/pages/staircase_sale_spec.rb
@@ -0,0 +1,38 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Pages::StaircaseSale, type: :model do
+ subject(:page) { described_class.new(page_id, page_definition, subsection) }
+
+ let(:page_id) { nil }
+ let(:page_definition) { nil }
+ let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1))) }
+
+ before do
+ allow(subsection.form).to receive(:start_year_2025_or_later?).and_return(true)
+ end
+
+ it "has correct subsection" do
+ expect(page.subsection).to eq(subsection)
+ end
+
+ it "has correct questions" do
+ expect(page.questions.map(&:id)).to eq(%w[staircasesale])
+ end
+
+ it "has the correct id" do
+ expect(page.id).to eq("staircase_sale")
+ end
+
+ it "has the correct description" do
+ expect(page.description).to be_nil
+ end
+
+ it "has correct depends_on" do
+ expect(page.depends_on).to eq([
+ {
+ "staircase" => 1,
+ "stairowned" => 100,
+ },
+ ])
+ end
+end
diff --git a/spec/models/form/sales/pages/value_shared_ownership_spec.rb b/spec/models/form/sales/pages/value_shared_ownership_spec.rb
index f8232894b..82e6c1055 100644
--- a/spec/models/form/sales/pages/value_shared_ownership_spec.rb
+++ b/spec/models/form/sales/pages/value_shared_ownership_spec.rb
@@ -3,10 +3,14 @@ require "rails_helper"
RSpec.describe Form::Sales::Pages::ValueSharedOwnership, type: :model do
subject(:page) { described_class.new(page_id, page_definition, subsection) }
- let(:page_id) { nil }
+ let(:page_id) { "value_shared_ownership" }
let(:page_definition) { nil }
let(:subsection) { instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1))) }
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
+
it "has correct subsection" do
expect(page.subsection).to eq(subsection)
end
diff --git a/spec/models/form/sales/questions/buyer2_working_situation_spec.rb b/spec/models/form/sales/questions/buyer2_working_situation_spec.rb
index 7b825185c..c8ee349b6 100644
--- a/spec/models/form/sales/questions/buyer2_working_situation_spec.rb
+++ b/spec/models/form/sales/questions/buyer2_working_situation_spec.rb
@@ -36,6 +36,22 @@ RSpec.describe Form::Sales::Questions::Buyer2WorkingSituation, type: :model do
"0" => { "value" => "Other" },
"10" => { "value" => "Buyer prefers not to say" },
"7" => { "value" => "Full-time student" },
+ "9" => { "value" => "Child under 16" },
+ })
+ end
+
+ it "has the correct displayed_answer_options" do
+ expect(question.displayed_answer_options(nil)).to eq({
+ "1" => { "value" => "Full-time - 30 hours or more" },
+ "2" => { "value" => "Part-time - Less than 30 hours" },
+ "3" => { "value" => "In government training into work" },
+ "4" => { "value" => "Jobseeker" },
+ "6" => { "value" => "Not seeking work" },
+ "8" => { "value" => "Unable to work due to long term sick or disability" },
+ "5" => { "value" => "Retired" },
+ "0" => { "value" => "Other" },
+ "10" => { "value" => "Buyer prefers not to say" },
+ "7" => { "value" => "Full-time student" },
})
end
@@ -43,7 +59,11 @@ RSpec.describe Form::Sales::Questions::Buyer2WorkingSituation, type: :model do
let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1), start_year_2025_or_later?: false) }
it "uses the old ordering for answer options" do
- expect(question.answer_options.keys).to eq(%w[1 2 3 4 6 8 5 0 10 7])
+ expect(question.answer_options.keys).to eq(%w[1 2 3 4 6 8 5 0 10 7 9])
+ end
+
+ it "uses the old ordering for displayed answer options" do
+ expect(question.displayed_answer_options(nil).keys).to eq(%w[1 2 3 4 6 8 5 0 10 7])
end
end
@@ -51,7 +71,11 @@ RSpec.describe Form::Sales::Questions::Buyer2WorkingSituation, type: :model do
let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 1), start_year_2025_or_later?: true) }
it "uses the new ordering for answer options" do
- expect(question.answer_options.keys).to eq(%w[1 2 3 4 5 6 7 8 0 10])
+ expect(question.answer_options.keys).to eq(%w[1 2 3 4 5 6 7 8 9 0 10])
+ end
+
+ it "uses the new ordering for displayed answer options" do
+ expect(question.displayed_answer_options(nil).keys).to eq(%w[1 2 3 4 5 6 7 8 0 10])
end
end
diff --git a/spec/models/form/sales/questions/equity_spec.rb b/spec/models/form/sales/questions/equity_spec.rb
index f58c042f5..3e6b9d85a 100644
--- a/spec/models/form/sales/questions/equity_spec.rb
+++ b/spec/models/form/sales/questions/equity_spec.rb
@@ -5,7 +5,11 @@ RSpec.describe Form::Sales::Questions::Equity, type: :model do
let(:question_id) { nil }
let(:question_definition) { nil }
- let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+ let(:page) { instance_double(Form::Page, id: "initial_equity", subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
it "has correct page" do
expect(question.page).to eq(page)
diff --git a/spec/models/form/sales/questions/monthly_rent_after_staircasing_spec.rb b/spec/models/form/sales/questions/monthly_rent_after_staircasing_spec.rb
new file mode 100644
index 000000000..4ceb3df00
--- /dev/null
+++ b/spec/models/form/sales/questions/monthly_rent_after_staircasing_spec.rb
@@ -0,0 +1,37 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Questions::MonthlyRentAfterStaircasing, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("mrent")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("numeric")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?(nil)).to be false
+ end
+
+ it "has correct width" do
+ expect(question.width).to eq(5)
+ end
+
+ it "has correct prefix" do
+ expect(question.prefix).to eq("£")
+ end
+
+ it "has correct min" do
+ expect(question.min).to eq(0)
+ end
+end
diff --git a/spec/models/form/sales/questions/monthly_rent_before_staircasing_spec.rb b/spec/models/form/sales/questions/monthly_rent_before_staircasing_spec.rb
new file mode 100644
index 000000000..8d7d864a8
--- /dev/null
+++ b/spec/models/form/sales/questions/monthly_rent_before_staircasing_spec.rb
@@ -0,0 +1,37 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Questions::MonthlyRentBeforeStaircasing, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("mrentprestaircasing")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("numeric")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?(nil)).to be false
+ end
+
+ it "has correct width" do
+ expect(question.width).to eq(5)
+ end
+
+ it "has correct prefix" do
+ expect(question.prefix).to eq("£")
+ end
+
+ it "has correct min" do
+ expect(question.min).to eq(0)
+ end
+end
diff --git a/spec/models/form/sales/questions/mortgageused_spec.rb b/spec/models/form/sales/questions/mortgageused_spec.rb
index e85238a4d..971eb2909 100644
--- a/spec/models/form/sales/questions/mortgageused_spec.rb
+++ b/spec/models/form/sales/questions/mortgageused_spec.rb
@@ -3,28 +3,39 @@ require "rails_helper"
RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do
subject(:question) { described_class.new(question_id, question_definition, page, ownershipsch:) }
- let(:ownershipsch) { 1 }
let(:question_id) { nil }
let(:question_definition) { nil }
- let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) }
- let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:)) }
let(:stairowned) { nil }
let(:staircase) { nil }
let(:saledate) { Time.zone.today }
let(:log) { build(:sales_log, :in_progress, ownershipsch:, stairowned:, staircase:) }
- it "has the correct answer_options" do
- expect(question.answer_options).to eq({
- "1" => { "value" => "Yes" },
- "2" => { "value" => "No" },
- "3" => { "value" => "Don’t know" },
- })
- end
+ context "when the form start year is 2024" do
+ let(:form) { instance_double(Form, start_date: Time.zone.local(2024, 4, 1)) }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:)) }
+ let(:saledate) { Time.zone.local(2024, 5, 1) }
+ let(:ownershipsch) { 1 }
+
+ before do
+ allow(form).to receive(:start_year_2024_or_later?).and_return true
+ allow(form).to receive(:start_year_2025_or_later?).and_return false
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "1" => { "value" => "Yes" },
+ "2" => { "value" => "No" },
+ "3" => { "value" => "Don’t know" },
+ })
+ end
- describe "the displayed answer options" do
context "when it is a discounted ownership sale" do
let(:ownershipsch) { 2 }
+ it "shows the correct question number" do
+ expect(question.question_number).to eq 104
+ end
+
it "does not show the don't know option" do
expect_the_question_not_to_show_dont_know
end
@@ -34,20 +45,14 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do
let(:ownershipsch) { 3 }
context "and the saledate is before 24/25" do
- before do
- allow(form).to receive(:start_year_2024_or_later?).and_return false
- end
+ let(:saledate) { Time.zone.local(2023, 5, 1) }\
- it "does not show the don't know option" do
- expect_the_question_not_to_show_dont_know
+ it "does show the don't know option" do
+ expect_the_question_to_show_dont_know
end
end
- context "and the saledate is 24/25 or after" do
- before do
- allow(form).to receive(:start_year_2024_or_later?).and_return true
- end
-
+ context "and the saledate is 24/25" do
it "shows the don't know option" do
expect_the_question_to_show_dont_know
end
@@ -87,6 +92,57 @@ RSpec.describe Form::Sales::Questions::Mortgageused, type: :model do
end
end
+ context "when the form start year is 2025" do
+ let(:form) { instance_double(Form, start_date: Time.zone.local(2025, 4, 1)) }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form:)) }
+ let(:saledate) { Time.zone.local(2025, 5, 1) }
+
+ before do
+ allow(form).to receive(:start_year_2024_or_later?).and_return true
+ allow(form).to receive(:start_year_2025_or_later?).and_return true
+ end
+
+ context "when it is a discounted ownership sale" do
+ let(:ownershipsch) { 2 }
+
+ it "shows the correct question number" do
+ expect(question.question_number).to eq 104
+ end
+
+ it "does not show the don't know option" do
+ expect_the_question_not_to_show_dont_know
+ end
+ end
+
+ context "when it is a shared ownership scheme" do
+ let(:ownershipsch) { 1 }
+
+ context "and it is a staircasing transaction" do
+ let(:staircase) { 1 }
+
+ it "does show the don't know option" do
+ expect_the_question_to_show_dont_know
+ end
+
+ context "and stairowned is 100" do
+ let(:stairowned) { 100 }
+
+ it "shows the don't know option" do
+ expect_the_question_to_show_dont_know
+ end
+ end
+ end
+
+ context "and it is not a staircasing transaction" do
+ let(:staircase) { 2 }
+
+ it "does not show the don't know option" do
+ expect_the_question_not_to_show_dont_know
+ end
+ end
+ end
+ end
+
private
def expect_the_question_not_to_show_dont_know
diff --git a/spec/models/form/sales/questions/staircase_count_spec.rb b/spec/models/form/sales/questions/staircase_count_spec.rb
new file mode 100644
index 000000000..06f39b3d0
--- /dev/null
+++ b/spec/models/form/sales/questions/staircase_count_spec.rb
@@ -0,0 +1,33 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Questions::StaircaseCount, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))) }
+
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(true)
+ end
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("numstair")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("numeric")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?(nil)).to be false
+ end
+
+ it "has correct conditional for" do
+ expect(question.conditional_for).to eq(nil)
+ end
+end
diff --git a/spec/models/form/sales/questions/staircase_first_time_spec.rb b/spec/models/form/sales/questions/staircase_first_time_spec.rb
new file mode 100644
index 000000000..59d0281a2
--- /dev/null
+++ b/spec/models/form/sales/questions/staircase_first_time_spec.rb
@@ -0,0 +1,40 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Questions::StaircaseFirstTime, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))) }
+
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(true)
+ end
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("firststair")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("radio")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?(nil)).to be false
+ end
+
+ it "has the correct answer_options" do
+ expect(question.answer_options).to eq({
+ "1" => { "value" => "Yes" },
+ "2" => { "value" => "No" },
+ })
+ end
+
+ it "has correct conditional for" do
+ expect(question.conditional_for).to eq(nil)
+ end
+end
diff --git a/spec/models/form/sales/questions/staircase_initial_date_spec.rb b/spec/models/form/sales/questions/staircase_initial_date_spec.rb
new file mode 100644
index 000000000..3c244e512
--- /dev/null
+++ b/spec/models/form/sales/questions/staircase_initial_date_spec.rb
@@ -0,0 +1,33 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Questions::StaircaseInitialDate, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))) }
+
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(true)
+ end
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("initialpurchase")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("date")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?(nil)).to be false
+ end
+
+ it "has correct conditional for" do
+ expect(question.conditional_for).to eq(nil)
+ end
+end
diff --git a/spec/models/form/sales/questions/staircase_last_date_spec.rb b/spec/models/form/sales/questions/staircase_last_date_spec.rb
new file mode 100644
index 000000000..2efa5ccc7
--- /dev/null
+++ b/spec/models/form/sales/questions/staircase_last_date_spec.rb
@@ -0,0 +1,33 @@
+require "rails_helper"
+
+RSpec.describe Form::Sales::Questions::StaircaseLastDate, type: :model do
+ subject(:question) { described_class.new(question_id, question_definition, page) }
+
+ let(:question_id) { nil }
+ let(:question_definition) { nil }
+ let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2025, 4, 1)))) }
+
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(true)
+ end
+
+ it "has correct page" do
+ expect(question.page).to eq(page)
+ end
+
+ it "has the correct id" do
+ expect(question.id).to eq("lasttransaction")
+ end
+
+ it "has the correct type" do
+ expect(question.type).to eq("date")
+ end
+
+ it "is not marked as derived" do
+ expect(question.derived?(nil)).to be false
+ end
+
+ it "has correct conditional for" do
+ expect(question.conditional_for).to eq(nil)
+ end
+end
diff --git a/spec/models/form/sales/questions/staircase_sale_spec.rb b/spec/models/form/sales/questions/staircase_sale_spec.rb
index d330aecb3..8d3d1e5da 100644
--- a/spec/models/form/sales/questions/staircase_sale_spec.rb
+++ b/spec/models/form/sales/questions/staircase_sale_spec.rb
@@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Questions::StaircaseSale, type: :model do
let(:question_definition) { nil }
let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
+
it "has correct page" do
expect(question.page).to eq(page)
end
diff --git a/spec/models/form/sales/questions/value_spec.rb b/spec/models/form/sales/questions/value_spec.rb
index 771607c16..10f281e63 100644
--- a/spec/models/form/sales/questions/value_spec.rb
+++ b/spec/models/form/sales/questions/value_spec.rb
@@ -5,7 +5,11 @@ RSpec.describe Form::Sales::Questions::Value, type: :model do
let(:question_id) { nil }
let(:question_definition) { nil }
- let(:page) { instance_double(Form::Page, subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+ let(:page) { instance_double(Form::Page, id: "value_shared_ownership", subsection: instance_double(Form::Subsection, form: instance_double(Form, start_date: Time.zone.local(2023, 4, 1)))) }
+
+ before do
+ allow(page.subsection.form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
it "has correct page" do
expect(question.page).to eq(page)
diff --git a/spec/models/form/sales/sections/sale_information_spec.rb b/spec/models/form/sales/sections/sale_information_spec.rb
index 8167c92a3..191bca4bb 100644
--- a/spec/models/form/sales/sections/sale_information_spec.rb
+++ b/spec/models/form/sales/sections/sale_information_spec.rb
@@ -7,6 +7,10 @@ RSpec.describe Form::Sales::Sections::SaleInformation, type: :model do
let(:section_definition) { nil }
let(:form) { instance_double(Form, start_year_2025_or_later?: false) }
+ before do
+ allow(form).to receive(:start_year_2025_or_later?).and_return(false)
+ end
+
it "has correct form" do
expect(sale_information.form).to eq(form)
end
@@ -22,11 +26,16 @@ RSpec.describe Form::Sales::Sections::SaleInformation, type: :model do
end
context "when form is 2025 or later" do
- let(:form) { instance_double(Form, start_year_2025_or_later?: true) }
+ let(:form) { instance_double(Form) }
+
+ before do
+ allow(form).to receive(:start_year_2025_or_later?).and_return(true)
+ end
it "has correct subsections" do
expect(sale_information.subsections.map(&:id)).to eq(%w[
shared_ownership_initial_purchase
+ shared_ownership_staircasing_transaction
discounted_ownership_scheme
outright_sale
])
diff --git a/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb b/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb
index 3b2d72b01..3e473a162 100644
--- a/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb
+++ b/spec/models/form/sales/subsections/shared_ownership_initial_purchase_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe Form::Sales::Subsections::SharedOwnershipInitialPurchase, type: :
shared_ownership_previous_tenure
value_shared_ownership
about_price_shared_ownership_value_check
- equity
+ initial_equity
shared_ownership_equity_value_check
mortgage_used_shared_ownership
mortgage_used_mortgage_value_check
diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb
index 11e663469..17cad1c5e 100644
--- a/spec/models/lettings_log_spec.rb
+++ b/spec/models/lettings_log_spec.rb
@@ -5,8 +5,8 @@ require "shared/shared_log_examples"
# rubocop:disable RSpec/MessageChain
RSpec.describe LettingsLog do
let(:different_managing_organisation) { create(:organisation) }
- let(:assigned_to_user) { create(:user) }
- let(:owning_organisation) { assigned_to_user.organisation }
+ let(:owning_organisation) { create(:organisation, rent_periods: [2]) }
+ let(:assigned_to_user) { create(:user, organisation: owning_organisation) }
let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") }
around do |example|
@@ -220,6 +220,31 @@ RSpec.describe LettingsLog do
expect(completed_lettings_log.completed?).to be(true)
end
end
+
+ it "sets the correct status for a completed lettings log" do
+ complete_lettings_log = build(:lettings_log, :completed, assigned_to: assigned_to_user)
+ complete_lettings_log.save!
+ expect(complete_lettings_log.reload.status).to eq "completed"
+ end
+
+ it "returns the correct status for an in progress lettings log" do
+ in_progress_lettings_log = build(:lettings_log, :in_progress, assigned_to: assigned_to_user)
+ in_progress_lettings_log.save!
+ expect(in_progress_lettings_log.reload.status).to eq "in_progress"
+ end
+
+ it "recalculates the status if it's currently set incorrectly" do
+ complete_lettings_log = build(:lettings_log, :completed, assigned_to: assigned_to_user, status: "in_progress")
+ complete_lettings_log.save!
+ expect(complete_lettings_log.reload.status).to eq "completed"
+ end
+
+ it "recalculates status_cache if the log is pending" do
+ complete_lettings_log = build(:lettings_log, :completed, assigned_to: assigned_to_user, status_cache: "in_progress", status: "pending")
+ complete_lettings_log.save!
+ expect(complete_lettings_log.reload.status_cache).to eq "completed"
+ expect(complete_lettings_log.status).to eq "pending"
+ end
end
describe "weekly_net_income" do
diff --git a/spec/models/location_spec.rb b/spec/models/location_spec.rb
index 18581fb6e..c220914b7 100644
--- a/spec/models/location_spec.rb
+++ b/spec/models/location_spec.rb
@@ -740,10 +740,38 @@ RSpec.describe Location, type: :model do
describe "#units" do
let(:location) { FactoryBot.build(:location) }
- it "does add an error when the number of units is invalid" do
- location.units = nil
- location.valid?(:units)
- expect(location.errors.count).to eq(1)
+ context "when the number of units is invalid" do
+ it "adds an error when units is nil" do
+ location.units = nil
+ location.valid?(:units)
+ expect(location.errors.count).to eq(2)
+ end
+
+ it "adds an error when units is 0" do
+ location.units = 0
+ location.valid?(:units)
+ expect(location.errors.count).to eq(1)
+ end
+ end
+
+ context "when the number of units is valid" do
+ it "does not add an error when units is 1" do
+ location.units = 1
+ location.valid?(:units)
+ expect(location.errors.count).to eq(0)
+ end
+
+ it "does not add an error when units is 10" do
+ location.units = 10
+ location.valid?(:units)
+ expect(location.errors.count).to eq(0)
+ end
+
+ it "does not add an error when units is 200" do
+ location.units = 200
+ location.valid?(:units)
+ expect(location.errors.count).to eq(0)
+ end
end
end
diff --git a/spec/models/log_spec.rb b/spec/models/log_spec.rb
index 76fadad6b..c5f133730 100644
--- a/spec/models/log_spec.rb
+++ b/spec/models/log_spec.rb
@@ -29,6 +29,17 @@ RSpec.describe Log, type: :model do
in_progress_lettings_log = build(:lettings_log, :in_progress, assigned_to: user)
expect(in_progress_lettings_log.calculate_status).to eq "in_progress"
end
+
+ it "recalculates the status if it's currently set incorrectly" do
+ complete_lettings_log = build(:lettings_log, :completed, assigned_to: user, status: "in_progress")
+ expect(complete_lettings_log.calculate_status).to eq "completed"
+ end
+
+ it "recalculates status_cache if the log is pending" do
+ complete_lettings_log = build(:lettings_log, :completed, assigned_to: user, status_cache: "in_progress", status: "pending")
+ expect(complete_lettings_log.calculate_status).to eq "completed"
+ expect(complete_lettings_log.calculate_status).to eq "completed"
+ end
end
describe "#blank_invalid_non_setup_fields!" do
diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb
index 9b01845ae..b117feef7 100644
--- a/spec/models/organisation_spec.rb
+++ b/spec/models/organisation_spec.rb
@@ -19,6 +19,12 @@ RSpec.describe Organisation, type: :model do
.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Provider type #{I18n.t('validations.organisation.provider_type_missing')}")
end
+ it "validates uniqueness of name" do
+ org = build(:organisation, name: organisation.name.downcase)
+ org.valid?
+ expect(org.errors[:name]).to include(I18n.t("validations.organisation.name_not_unique"))
+ end
+
context "with parent/child associations", :aggregate_failures do
let!(:child_organisation) { create(:organisation, name: "MHCLG Child") }
let!(:grandchild_organisation) { create(:organisation, name: "MHCLG Grandchild") }
diff --git a/spec/models/scheme_spec.rb b/spec/models/scheme_spec.rb
index 65174388d..21b60cd52 100644
--- a/spec/models/scheme_spec.rb
+++ b/spec/models/scheme_spec.rb
@@ -390,6 +390,13 @@ RSpec.describe Scheme, type: :model do
scheme.startdate = Time.zone.today + 2.weeks
expect(scheme.status).to eq(:activating_soon)
end
+
+ it "returns deactivated if scheme is deactivated and incomplete" do
+ scheme.update!(support_type: nil, confirmed: nil)
+ FactoryBot.create(:scheme_deactivation_period, deactivation_date: Time.zone.yesterday, scheme:)
+ scheme.reload
+ expect(scheme.status).to eq(:deactivated)
+ end
end
context "when there have been previous deactivations" do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 51cfc00bd..cd79977cc 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -300,6 +300,7 @@ RSpec.describe User, type: :model do
context "when the user is in staging environment" do
before do
allow(Rails.env).to receive(:staging?).and_return(true)
+ allow(Rails.application.credentials).to receive(:[]).with(:staging_role_update_email_allowlist).and_return(["not_one_of_the_examples.com"])
end
context "and the user is not in the staging role update email allowlist" do
@@ -725,15 +726,8 @@ RSpec.describe User, type: :model do
context "and the user has pending logs assigned to them" do
let(:lettings_bu) { create(:bulk_upload, :lettings) }
let(:sales_bu) { create(:bulk_upload, :sales) }
- let!(:pending_lettings_log) { build(:lettings_log, status: "pending", assigned_to: user, bulk_upload: lettings_bu) }
- let!(:pending_sales_log) { build(:sales_log, status: "pending", assigned_to: user, bulk_upload: sales_bu) }
-
- before do
- pending_lettings_log.skip_update_status = true
- pending_lettings_log.save!
- pending_sales_log.skip_update_status = true
- pending_sales_log.save!
- end
+ let!(:pending_lettings_log) { create(:lettings_log, status: "pending", assigned_to: user, bulk_upload: lettings_bu) }
+ let!(:pending_sales_log) { create(:sales_log, status: "pending", assigned_to: user, bulk_upload: sales_bu) }
it "sets choice for fixing the logs to cancelled-by-moved-user" do
user.reassign_logs_and_update_organisation(new_organisation, "reassign_all")
diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb
index 5cc0cdf07..fabe3c8c5 100644
--- a/spec/models/validations/sales/sale_information_validations_spec.rb
+++ b/spec/models/validations/sales/sale_information_validations_spec.rb
@@ -1407,4 +1407,44 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
end
end
+
+ describe "#validate_number_of_staircase_transactions" do
+ let(:record) { build(:sales_log, numstair:, firststair:) }
+
+ before do
+ sale_information_validator.validate_number_of_staircase_transactions(record)
+ end
+
+ context "when it is not the first staircasing transaction" do
+ context "and the number of staircasing transactions is between 2 and 10" do
+ let(:numstair) { 6 }
+ let(:firststair) { 2 }
+
+ it "does not add an error" do
+ expect(record.errors).to be_empty
+ end
+ end
+
+ context "and the number of staircasing transactions is less than 2" do
+ let(:numstair) { 1 }
+ let(:firststair) { 2 }
+
+ it "adds an error" do
+ expect(record.errors[:numstair]).to include(I18n.t("validations.sales.sale_information.numstair.must_be_greater_than_one"))
+ expect(record.errors[:firststair]).to include(I18n.t("validations.sales.sale_information.firststair.cannot_be_no"))
+ end
+ end
+ end
+
+ context "when it is the first staircasing transaction" do
+ context "and numstair is also 1" do
+ let(:numstair) { 1 }
+ let(:firststair) { 1 }
+
+ it "does not add an error" do
+ expect(record.errors).to be_empty
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/validations/setup_validations_spec.rb b/spec/models/validations/setup_validations_spec.rb
index 5b4f03365..1104cc23d 100644
--- a/spec/models/validations/setup_validations_spec.rb
+++ b/spec/models/validations/setup_validations_spec.rb
@@ -703,7 +703,7 @@ RSpec.describe Validations::SetupValidations do
.to include("This location is incomplete. Select another location or update this one.")
end
- it "produces no error when location is completes" do
+ it "produces no error when location is complete" do
location.update!(units: 1)
location.reload
record.location = location
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 5f7529554..c663087d5 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -51,7 +51,7 @@ rescue ActiveRecord::PendingMigrationError => e
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
+ config.fixture_paths = ["#{::Rails.root}/spec/fixtures"]
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
diff --git a/spec/requests/auth/passwords_controller_spec.rb b/spec/requests/auth/passwords_controller_spec.rb
index 7f9bdfa30..2685e19ab 100644
--- a/spec/requests/auth/passwords_controller_spec.rb
+++ b/spec/requests/auth/passwords_controller_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe Auth::PasswordsController, type: :request do
put "/account/password", params: update_password_params
# Devise redirects once after re-sign in with new password and then root redirects as well.
follow_redirect!
- expect(page).to have_css("div", class: "govuk-notification-banner__heading", text: message)
+ expect(page).to have_css("p", class: "govuk-notification-banner__heading", text: message)
end
end
end
@@ -107,7 +107,7 @@ RSpec.describe Auth::PasswordsController, type: :request do
it "shows an error on the same page" do
put "/account/password", headers: headers, params: params
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_css("h1", text: "Reset your password")
expect(page).to have_content("passwords you entered do not match")
end
diff --git a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb
index 18b208e74..c9a22768d 100644
--- a/spec/requests/bulk_upload_lettings_logs_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_logs_controller_spec.rb
@@ -73,5 +73,116 @@ RSpec.describe BulkUploadLettingsLogsController, type: :request do
expect(response.body).to include("How to upload logs in bulk")
end
end
+
+ context "when no year is specified" do
+ it "shows guidance page with links defaulting to the current year" do
+ get "/lettings-logs/bulk-upload-logs/guidance"
+
+ expect(response.body).to include("Download the lettings bulk upload template (#{current_collection_start_year} to #{current_collection_start_year + 1})")
+ end
+ end
+
+ context "when an invalid year is specified" do
+ it "shows not found" do
+ get "/lettings-logs/bulk-upload-logs/guidance?form%5Byear%5D=10000"
+
+ expect(response).to be_not_found
+ end
+ end
+ end
+
+ describe "GET /lettings-logs/bulk-upload-logs/year" do
+ it "does not require a year to be specified" do
+ get "/lettings-logs/bulk-upload-logs/year"
+
+ expect(response).to be_ok
+ end
+ end
+
+ pages_requiring_year_specification = %w[prepare-your-file upload-your-file checking-file]
+ pages_requiring_year_specification.each do |page_id|
+ describe "GET /lettings-logs/bulk-upload-logs/#{page_id}" do
+ context "when no year is provided" do
+ it "returns not found" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting the previous year in a crossover period" do
+ before do
+ allow(FormHandler.instance).to receive(:lettings_in_crossover_period?).and_return(true)
+ end
+
+ it "succeeds" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year - 1}"
+
+ expect(response).to be_ok
+ end
+ end
+
+ context "when requesting the previous year outside a crossover period" do
+ before do
+ allow(FormHandler.instance).to receive(:lettings_in_crossover_period?).and_return(false)
+ end
+
+ it "returns not found" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year - 1}"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting the current year" do
+ it "succeeds" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year}"
+
+ expect(response).to be_ok
+ end
+ end
+
+ if page_id != "prepare-your-file"
+ context "when requesting the next year with future form use toggled on" do
+ before do
+ allow(FeatureToggle).to receive(:allow_future_form_use?).and_return(true)
+ end
+
+ it "succeeds" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year + 1}"
+
+ expect(response).to be_ok
+ end
+ end
+ end
+
+ context "when requesting the next year with future form use toggled off" do
+ before do
+ allow(FeatureToggle).to receive(:allow_future_form_use?).and_return(false)
+ end
+
+ it "returns not found" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year + 1}"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting a far future year" do
+ it "returns not found" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=9990"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting a nonsense value for year" do
+ it "returns not found" do
+ get "/lettings-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=thisisnotayear"
+
+ expect(response).to be_not_found
+ end
+ end
+ end
end
end
diff --git a/spec/requests/bulk_upload_lettings_results_controller_spec.rb b/spec/requests/bulk_upload_lettings_results_controller_spec.rb
index afbd84df9..3c84487ac 100644
--- a/spec/requests/bulk_upload_lettings_results_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_results_controller_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("Some logs in this upload are assigned to #{user.name}, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(CGI.unescapeHTML(response.body)).to include("Some logs in this upload are assigned to #{user.name}, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
@@ -79,7 +79,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
end
@@ -138,7 +138,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("Some logs in this upload are assigned to #{other_user.name}, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("Some logs in this upload are assigned to #{other_user.name}, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
@@ -149,7 +149,7 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
end
diff --git a/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb b/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
index 315235e0e..2a2668d60 100644
--- a/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_soft_validations_check_controller_spec.rb
@@ -98,7 +98,7 @@ RSpec.describe BulkUploadLettingsSoftValidationsCheckController, type: :request
expect(response).to be_successful
- expect(response.body).to include("You have chosen to upload all logs from this bulk upload.")
+ expect(response.body).to include("Are you sure you want to upload all logs from this bulk upload?")
expect(response.body).to include("You will upload 2 logs. There are unexpected answers in 2 logs, and 2 unexpected answers in total. These unexpected answers will be marked as correct.")
expect(response.body).not_to include("You’ve successfully uploaded")
end
diff --git a/spec/requests/bulk_upload_sales_logs_controller_spec.rb b/spec/requests/bulk_upload_sales_logs_controller_spec.rb
index 7cd6d4be8..4c20482be 100644
--- a/spec/requests/bulk_upload_sales_logs_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_logs_controller_spec.rb
@@ -73,5 +73,116 @@ RSpec.describe BulkUploadSalesLogsController, type: :request do
expect(response.body).to include("How to upload logs in bulk")
end
end
+
+ context "when no year is specified" do
+ it "shows guidance page with links defaulting to the current year" do
+ get "/sales-logs/bulk-upload-logs/guidance"
+
+ expect(response.body).to include("Download the sales bulk upload template (#{current_collection_start_year} to #{current_collection_start_year + 1})")
+ end
+ end
+
+ context "when an invalid year is specified" do
+ it "shows not found" do
+ get "/sales-logs/bulk-upload-logs/guidance?form%5Byear%5D=10000"
+
+ expect(response).to be_not_found
+ end
+ end
+ end
+
+ describe "GET /sales-logs/bulk-upload-logs/year" do
+ it "does not require a year to be specified" do
+ get "/sales-logs/bulk-upload-logs/year"
+
+ expect(response).to be_ok
+ end
+ end
+
+ pages_requiring_year_specification = %w[prepare-your-file upload-your-file checking-file]
+ pages_requiring_year_specification.each do |page_id|
+ describe "GET /sales-logs/bulk-upload-logs/#{page_id}" do
+ context "when no year is provided" do
+ it "returns not found" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting the previous year in a crossover period" do
+ before do
+ allow(FormHandler.instance).to receive(:sales_in_crossover_period?).and_return(true)
+ end
+
+ it "succeeds" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year - 1}"
+
+ expect(response).to be_ok
+ end
+ end
+
+ context "when requesting the previous year outside a crossover period" do
+ before do
+ allow(FormHandler.instance).to receive(:sales_in_crossover_period?).and_return(false)
+ end
+
+ it "returns not found" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year - 1}"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting the current year" do
+ it "succeeds" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year}"
+
+ expect(response).to be_ok
+ end
+ end
+
+ if page_id != "prepare-your-file"
+ context "when requesting the next year with future form use toggled on" do
+ before do
+ allow(FeatureToggle).to receive(:allow_future_form_use?).and_return(true)
+ end
+
+ it "succeeds" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year + 1}"
+
+ expect(response).to be_ok
+ end
+ end
+ end
+
+ context "when requesting the next year with future form use toggled off" do
+ before do
+ allow(FeatureToggle).to receive(:allow_future_form_use?).and_return(false)
+ end
+
+ it "returns not found" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=#{current_collection_start_year + 1}"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting a far future year" do
+ it "returns not found" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=9990"
+
+ expect(response).to be_not_found
+ end
+ end
+
+ context "when requesting a nonsense value for year" do
+ it "returns not found" do
+ get "/sales-logs/bulk-upload-logs/#{page_id}?form%5Byear%5D=thisisnotayear"
+
+ expect(response).to be_not_found
+ end
+ end
+ end
end
end
diff --git a/spec/requests/bulk_upload_sales_results_controller_spec.rb b/spec/requests/bulk_upload_sales_results_controller_spec.rb
index b7bb7a10c..e58303d68 100644
--- a/spec/requests/bulk_upload_sales_results_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_results_controller_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe BulkUploadSalesResultsController, type: :request do
get "/sales-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("Some logs in this upload are assigned to #{user.name}, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("Some logs in this upload are assigned to #{user.name}, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
@@ -41,7 +41,7 @@ RSpec.describe BulkUploadSalesResultsController, type: :request do
get "/sales-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
end
@@ -113,7 +113,7 @@ RSpec.describe BulkUploadSalesResultsController, type: :request do
get "/sales-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("Some logs in this upload are assigned to #{other_user.name}, who has moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("Some logs in this upload are assigned to #{other_user.name}, who has moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
@@ -124,7 +124,7 @@ RSpec.describe BulkUploadSalesResultsController, type: :request do
get "/sales-logs/bulk-upload-results/#{bulk_upload.id}/summary"
expect(response.body).to include("This error report is out of date.")
- expect(response.body).to include("You moved to a different organisation since this file was uploaded. Reupload the file to get an accurate error report.")
+ expect(response.body).to include("You moved to a different organisation since this file was uploaded. Upload the file again to get an accurate error report.")
end
end
end
diff --git a/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb b/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb
index 90e44a4f7..a3a826ad3 100644
--- a/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_soft_validations_check_controller_spec.rb
@@ -98,7 +98,7 @@ RSpec.describe BulkUploadSalesSoftValidationsCheckController, type: :request do
expect(response).to be_successful
- expect(response.body).to include("You have chosen to upload all logs from this bulk upload.")
+ expect(response.body).to include("Are you sure you want to upload all logs from this bulk upload?")
expect(response.body).to include("You will upload 2 logs. There are unexpected answers in 2 logs, and 2 unexpected answers in total. These unexpected answers will be marked as correct.")
expect(response.body).not_to include("You’ve successfully uploaded")
end
diff --git a/spec/requests/check_errors_controller_spec.rb b/spec/requests/check_errors_controller_spec.rb
index 29130f547..9c4d3f8c1 100644
--- a/spec/requests/check_errors_controller_spec.rb
+++ b/spec/requests/check_errors_controller_spec.rb
@@ -84,7 +84,7 @@ RSpec.describe CheckErrorsController, type: :request do
end
it "displays correct clear and change links" do
- expect(page.all(:button, value: "Clear").count).to eq(2)
+ expect(page.all(:button, value: "Clear").count).to eq(1)
expect(page).to have_link("Change", count: 1)
expect(page).to have_button("Clear all")
end
diff --git a/spec/requests/delete_logs_controller_spec.rb b/spec/requests/delete_logs_controller_spec.rb
index cde0848f6..b055e9dd1 100644
--- a/spec/requests/delete_logs_controller_spec.rb
+++ b/spec/requests/delete_logs_controller_spec.rb
@@ -61,8 +61,9 @@ RSpec.describe "DeleteLogs", type: :request do
allow(FilterManager).to receive(:filter_logs).and_return LettingsLog.all
end
- it "throws an error if selected ids are not provided" do
- expect { post delete_logs_lettings_logs_path }.to raise_error ActionController::ParameterMissing
+ it "returns bad request if selected ids are not provided" do
+ post delete_logs_lettings_logs_path
+ expect(response).to have_http_status(:bad_request)
end
it "calls the filter service with the filters in the session and the search term from the query params" do
@@ -120,7 +121,8 @@ RSpec.describe "DeleteLogs", type: :request do
end
it "requires delete logs form data to be provided" do
- expect { post delete_logs_confirmation_lettings_logs_path }.to raise_error(ActionController::ParameterMissing)
+ post delete_logs_confirmation_lettings_logs_path
+ expect(response).to have_http_status(:bad_request)
end
it "shows the correct title" do
@@ -346,8 +348,9 @@ RSpec.describe "DeleteLogs", type: :request do
allow(FilterManager).to receive(:filter_logs).and_return SalesLog.all
end
- it "throws an error if selected ids are not provided" do
- expect { post delete_logs_sales_logs_path }.to raise_error ActionController::ParameterMissing
+ it "returns bad request if selected ids are not provided" do
+ post delete_logs_sales_logs_path
+ expect(response).to have_http_status(:bad_request)
end
it "calls the filter service with the filters in the session and the search term from the query params" do
@@ -405,7 +408,8 @@ RSpec.describe "DeleteLogs", type: :request do
end
it "requires delete logs form data to be provided" do
- expect { post delete_logs_confirmation_sales_logs_path }.to raise_error(ActionController::ParameterMissing)
+ post delete_logs_confirmation_sales_logs_path
+ expect(response).to have_http_status(:bad_request)
end
it "shows the correct title" do
@@ -635,8 +639,9 @@ RSpec.describe "DeleteLogs", type: :request do
allow(FilterManager).to receive(:filter_logs).and_return LettingsLog.all
end
- it "throws an error if selected ids are not provided" do
- expect { post delete_lettings_logs_organisation_path(id: organisation) }.to raise_error ActionController::ParameterMissing
+ it "returns bad request if selected ids are not provided" do
+ post delete_lettings_logs_organisation_path(id: organisation)
+ expect(response).to have_http_status(:bad_request)
end
it "calls the filter service with the filters in the session and the search term from the query params" do
@@ -694,7 +699,8 @@ RSpec.describe "DeleteLogs", type: :request do
end
it "requires delete logs form data to be provided" do
- expect { post delete_lettings_logs_confirmation_organisation_path(id: organisation) }.to raise_error(ActionController::ParameterMissing)
+ post delete_lettings_logs_confirmation_organisation_path(id: organisation)
+ expect(response).to have_http_status(:bad_request)
end
it "shows the correct title" do
@@ -858,8 +864,9 @@ RSpec.describe "DeleteLogs", type: :request do
allow(FilterManager).to receive(:filter_logs).and_return SalesLog.all
end
- it "throws an error if selected ids are not provided" do
- expect { post delete_sales_logs_organisation_path(id: organisation) }.to raise_error ActionController::ParameterMissing
+ it "returns bad request if selected ids are not provided" do
+ post delete_sales_logs_organisation_path(id: organisation)
+ expect(response).to have_http_status(:bad_request)
end
it "calls the filter service with the filters in the session and the search term from the query params" do
@@ -917,7 +924,8 @@ RSpec.describe "DeleteLogs", type: :request do
end
it "requires delete logs form data to be provided" do
- expect { post delete_sales_logs_confirmation_organisation_path(id: organisation) }.to raise_error(ActionController::ParameterMissing)
+ post delete_sales_logs_confirmation_organisation_path
+ expect(response).to have_http_status(:bad_request)
end
it "shows the correct title" do
diff --git a/spec/requests/form_controller_spec.rb b/spec/requests/form_controller_spec.rb
index 4f6ac9b91..2d0337a6e 100644
--- a/spec/requests/form_controller_spec.rb
+++ b/spec/requests/form_controller_spec.rb
@@ -512,7 +512,6 @@ RSpec.describe FormController, type: :request do
owning_organisation: organisation,
assigned_to: user,
status: "pending",
- skip_update_status: true,
)
end
@@ -582,8 +581,9 @@ RSpec.describe FormController, type: :request do
allow(Rails.logger).to receive(:info)
end
- it "re-renders the same page with errors if validation fails" do
+ it "redirects to the same page with errors if validation fails" do
post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params
+ follow_redirect!
expect(page).to have_content("There is a problem")
expect(page).to have_content("Error: What is the tenant’s age?")
end
@@ -591,6 +591,8 @@ RSpec.describe FormController, type: :request do
it "resets errors when fixed" do
post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params
post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: valid_params
+ follow_redirect!
+ expect(page).not_to have_content("There is a problem")
get "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}"
expect(page).not_to have_content("There is a problem")
end
@@ -616,6 +618,7 @@ RSpec.describe FormController, type: :request do
it "validates the date correctly" do
post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params
+ follow_redirect!
expect(page).to have_content("There is a problem")
end
end
@@ -693,6 +696,7 @@ RSpec.describe FormController, type: :request do
it "validates the date correctly" do
post "/lettings-logs/#{lettings_log.id}/#{page_id.dasherize}", params: params
+ follow_redirect!
expect(page).to have_content("There is a problem")
end
end
@@ -713,10 +717,31 @@ RSpec.describe FormController, type: :request do
it "validates the date correctly" do
post "/sales-logs/#{sales_log.id}/#{page_id.dasherize}", params: params
+ follow_redirect!
expect(page).to have_content("There is a problem")
end
end
end
+
+ context "with long error messages" do
+ let(:sales_log) { create(:sales_log, :completed, assigned_to: user) }
+ let(:page_id) { "purchase_price" }
+ let(:params) do
+ {
+ id: sales_log.id,
+ sales_log: {
+ page: page_id,
+ "value" => 1,
+ },
+ }
+ end
+
+ it "can deal with long error messages" do
+ post "/sales-logs/#{sales_log.id}/#{page_id.dasherize}", params: params
+ follow_redirect!
+ expect(page).to have_content("There is a problem")
+ end
+ end
end
context "with invalid multiple question answers" do
@@ -776,8 +801,9 @@ RSpec.describe FormController, type: :request do
Timecop.unfreeze
end
- it "re-renders the same page with errors if validation fails" do
+ it "redirects the same page with errors if validation fails" do
post "/lettings-logs/#{lettings_log.id}/managing-organisation", params: params
+ follow_redirect!
expect(page).to have_content("There is a problem")
expect(page).to have_content("Error: Which organisation manages this letting?")
end
@@ -1161,7 +1187,6 @@ RSpec.describe FormController, type: :request do
it "displays a success banner" do
follow_redirect!
- follow_redirect!
expect(response.body).to include("You have successfully updated Q31: lead tenant’s age")
end
@@ -1184,7 +1209,6 @@ RSpec.describe FormController, type: :request do
end
it "displays a success banner without crashing" do
- follow_redirect!
follow_redirect!
expect(response.body).to include("You have successfully updated")
end
diff --git a/spec/requests/lettings_logs_controller_spec.rb b/spec/requests/lettings_logs_controller_spec.rb
index 3d2c27400..05d83a162 100644
--- a/spec/requests/lettings_logs_controller_spec.rb
+++ b/spec/requests/lettings_logs_controller_spec.rb
@@ -81,7 +81,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "validates lettings log parameters" do
json_response = JSON.parse(response.body)
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(json_response["errors"]).to match_array([["offered", [I18n.t("validations.shared.numeric.within_range", field: "Times previously offered since becoming available", min: 0, max: 20)]], ["age1", [I18n.t("validations.shared.numeric.within_range", field: "Lead tenant’s age", min: 16, max: 120)]]])
end
end
@@ -244,7 +244,6 @@ RSpec.describe LettingsLogsController, type: :request do
assigned_to: user,
tenancycode: "LC999",
status: "pending",
- skip_update_status: true,
)
end
@@ -610,7 +609,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "displays filter" do
get "/lettings-logs?bulk_upload_id[]=#{bulk_upload.id}"
- expect(page).to have_content("With logs from bulk upload")
+ expect(page).to have_content("Only logs from this bulk upload")
end
it "hides collection year filter" do
@@ -696,7 +695,7 @@ RSpec.describe LettingsLogsController, type: :request do
context "without bulk_upload_id" do
it "does not display filter" do
get "/lettings-logs"
- expect(page).not_to have_content("With logs from bulk upload")
+ expect(page).not_to have_content("Only logs from this bulk upload")
end
it "displays button to create a new log" do
@@ -759,7 +758,7 @@ RSpec.describe LettingsLogsController, type: :request do
it "has search results in the title" do
get "/lettings-logs?search=#{log_to_search.id}", headers:, params: {}
- expect(page).to have_title("Lettings logs (1 logs matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
+ expect(page).to have_title("Lettings logs (1 log matching ‘#{log_to_search.id}’) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
it "shows lettings logs matching the id" do
@@ -895,7 +894,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
it "shows the total log count" do
- expect(CGI.unescape_html(response.body)).to match("1 total logs")
+ expect(CGI.unescape_html(response.body)).to match("1 total log")
end
it "does not show the pagination links" do
@@ -1107,7 +1106,6 @@ RSpec.describe LettingsLogsController, type: :request do
:in_progress,
assigned_to: user,
status: "pending",
- skip_update_status: true,
)
end
@@ -1161,11 +1159,10 @@ RSpec.describe LettingsLogsController, type: :request do
context "with bulk_upload_id filter" do
let(:bulk_upload) { create(:bulk_upload, :lettings, user:) }
- let(:lettings_log) { create(:lettings_log, :completed, age1: nil, bulk_upload:, assigned_to: user, creation_method: "bulk upload") }
+ let(:lettings_log) { create(:lettings_log, :completed, bulk_upload:, assigned_to: user, creation_method: "bulk upload") }
before do
lettings_log.status = "completed"
- lettings_log.skip_update_status = true
lettings_log.save!(validate: false)
end
@@ -1483,7 +1480,7 @@ RSpec.describe LettingsLogsController, type: :request do
end
context "when viewing a collection of logs affected by deactivated location" do
- let!(:affected_lettings_logs) { FactoryBot.create_list(:lettings_log, 3, unresolved: true, assigned_to: user) }
+ let!(:affected_lettings_logs) { FactoryBot.create_list(:lettings_log, 3, unresolved: true, assigned_to: user, tenancycode: "affected tenancycode", propcode: "affected propcode") }
let!(:other_user_affected_lettings_log) { FactoryBot.create(:lettings_log, unresolved: true) }
let!(:non_affected_lettings_logs) { FactoryBot.create_list(:lettings_log, 4, assigned_to: user) }
let(:other_user) { FactoryBot.create(:user, organisation: user.organisation) }
@@ -1615,7 +1612,7 @@ RSpec.describe LettingsLogsController, type: :request do
let(:params) { { age1: 200 } }
it "returns 422" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
end
it "returns an error message" do
diff --git a/spec/requests/locations_controller_spec.rb b/spec/requests/locations_controller_spec.rb
index 6de8e7d33..9afdc94f2 100644
--- a/spec/requests/locations_controller_spec.rb
+++ b/spec/requests/locations_controller_spec.rb
@@ -1192,7 +1192,7 @@ RSpec.describe LocationsController, type: :request do
end
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.startdate_invalid"))
end
end
@@ -1266,7 +1266,7 @@ RSpec.describe LocationsController, type: :request do
end
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.startdate_invalid"))
end
end
@@ -1702,7 +1702,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { "deactivation_date": "" } } }
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.not_selected"))
end
end
@@ -1711,7 +1711,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "44", "deactivation_date(1i)": "2022" } } }
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -1720,7 +1720,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "10", "deactivation_date(2i)": "4", "deactivation_date(1i)": "2020" } } }
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.out_of_range", date: "1 April 2022"))
end
end
@@ -1729,7 +1729,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "", "deactivation_date(2i)": "2", "deactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -1738,7 +1738,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "2", "deactivation_date(2i)": "", "deactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -1747,7 +1747,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { deactivation_date_type: "other", "deactivation_date(3i)": "2", "deactivation_date(2i)": "2", "deactivation_date(1i)": "" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -1758,7 +1758,7 @@ RSpec.describe LocationsController, type: :request do
let(:add_deactivations) { create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 10, 12), location:) }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.deactivation.during_deactivated_period"))
end
end
@@ -2110,7 +2110,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { "reactivation_date": "" } } }
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.not_selected"))
end
end
@@ -2119,7 +2119,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "10", "reactivation_date(2i)": "44", "reactivation_date(1i)": "2022" } } }
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -2128,7 +2128,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "10", "reactivation_date(2i)": "4", "reactivation_date(1i)": "2020" } } }
it "displays the new page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.out_of_range", date: "1 April 2022"))
end
end
@@ -2137,7 +2137,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "", "reactivation_date(2i)": "2", "reactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -2146,7 +2146,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "2", "reactivation_date(2i)": "", "reactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -2155,7 +2155,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "2", "reactivation_date(2i)": "2", "reactivation_date(1i)": "" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.toggle_date.invalid"))
end
end
@@ -2165,7 +2165,7 @@ RSpec.describe LocationsController, type: :request do
let(:params) { { location_deactivation_period: { reactivation_date_type: "other", "reactivation_date(3i)": "8", "reactivation_date(2i)": "9", "reactivation_date(1i)": "2022" } } }
it "displays page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.location.reactivation.before_deactivation", date: "10 October 2022"))
end
end
diff --git a/spec/requests/merge_requests_controller_spec.rb b/spec/requests/merge_requests_controller_spec.rb
index 074d2186c..4c07dbfb7 100644
--- a/spec/requests/merge_requests_controller_spec.rb
+++ b/spec/requests/merge_requests_controller_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe MergeRequestsController, type: :request do
end
it "displays the page with an error message" do
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("Another merge request records #{another_organisation.name} as merging into #{other_merge_request.absorbing_organisation&.name} on 4 May 2022. Select another organisation or remove this organisation from the other merge request.")
end
end
@@ -113,7 +113,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "does not update the merge request" do
merge_request.reload
expect(merge_request.merging_organisations.count).to eq(0)
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.merge_request.organisation_part_of_another_merge"))
end
end
@@ -159,7 +159,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "does not update the merge request" do
merge_request.reload
expect(merge_request.merging_organisations.count).to eq(0)
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.merge_request.organisation_not_selected"))
end
end
@@ -174,7 +174,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "does not update the merge request" do
merge_request.reload
expect(merge_request.merging_organisations.count).to eq(0)
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content(I18n.t("validations.merge_request.organisation_not_selected"))
end
end
@@ -363,7 +363,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "renders the error" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("Enter a merge date")
end
@@ -385,7 +385,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "displays the page with an error message" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("Enter a valid merge date")
end
end
@@ -426,7 +426,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "displays the page with an error message" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("The merge date must not be later than a year from today’s date.")
end
end
@@ -467,7 +467,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "renders the error" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("You must answer absorbing organisation already active?")
end
@@ -490,7 +490,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "renders the error" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("You must answer was this merge reported by a helpdesk ticket?")
end
@@ -511,7 +511,7 @@ RSpec.describe MergeRequestsController, type: :request do
it "renders the error" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
expect(page).to have_content("You must answer the ticket number")
end
diff --git a/spec/requests/notifications_controller_spec.rb b/spec/requests/notifications_controller_spec.rb
index 0982057ab..367c64550 100644
--- a/spec/requests/notifications_controller_spec.rb
+++ b/spec/requests/notifications_controller_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe NotificationsController, type: :request do
it "gives an error response" do
request
- expect(response).to have_http_status(:unprocessable_entity)
+ expect(response).to have_http_status(:unprocessable_content)
end
end
diff --git a/spec/requests/organisation_relationships_controller_spec.rb b/spec/requests/organisation_relationships_controller_spec.rb
index feb687e0c..1b1b3169e 100644
--- a/spec/requests/organisation_relationships_controller_spec.rb
+++ b/spec/requests/organisation_relationships_controller_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe OrganisationRelationshipsController, type: :request do
end
it "shows the tab navigation" do
- expected_html = "