From 99ea75f9e8e796e9596a99b26825fc25fb6a82d4 Mon Sep 17 00:00:00 2001
From: Samuel Young
Date: Wed, 29 Apr 2026 15:51:29 +0100
Subject: [PATCH 1/7] Revert "CLDC-4300: Update sale date staircasing date
validation (#3307)" (#3339)
This reverts commit 1e1773605c29e681981d9a8609b058306f5e5ce6.
---
.../sales/sale_information_validations.rb | 6 +-
...valid_initialpurchase_lasttransaction.rake | 13 ----
.../sale_information_validations_spec.rb | 69 ++++---------------
3 files changed, 15 insertions(+), 73 deletions(-)
delete mode 100644 lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake
diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb
index 7cd24183c..32bf2a716 100644
--- a/app/models/validations/sales/sale_information_validations.rb
+++ b/app/models/validations/sales/sale_information_validations.rb
@@ -42,7 +42,7 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :initialpurchase, I18n.t("validations.sales.sale_information.initialpurchase.must_be_after_1980")
end
- if record.saledate.present? && ((record.initialpurchase > record.saledate) || (record.initialpurchase == record.saledate && record.form.start_year_2026_or_later?))
+ if record.saledate.present? && record.initialpurchase > record.saledate
record.errors.add :initialpurchase, I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_saledate")
record.errors.add :saledate, :skip_bu_error, message: I18n.t("validations.sales.sale_information.saledate.must_be_after_initial_purchase_date")
end
@@ -55,11 +55,11 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :lasttransaction, I18n.t("validations.sales.sale_information.lasttransaction.must_be_after_1980")
end
- if record.saledate.present? && ((record.lasttransaction > record.saledate) || (record.lasttransaction == record.saledate && record.form.start_year_2026_or_later?))
+ if record.saledate.present? && record.lasttransaction > record.saledate
record.errors.add :lasttransaction, I18n.t("validations.sales.sale_information.lasttransaction.must_be_before_saledate")
record.errors.add :saledate, :skip_bu_error, message: I18n.t("validations.sales.sale_information.saledate.must_be_after_last_transaction_date")
end
- if record.initialpurchase.present? && ((record.lasttransaction < record.initialpurchase) || (record.lasttransaction == record.initialpurchase && record.form.start_year_2026_or_later?))
+ if record.initialpurchase.present? && record.lasttransaction < record.initialpurchase
record.errors.add :initialpurchase, I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_last_transaction")
record.errors.add :lasttransaction, I18n.t("validations.sales.sale_information.lasttransaction.must_be_after_initial_purchase")
end
diff --git a/lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake b/lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake
deleted file mode 100644
index 44475a46f..000000000
--- a/lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake
+++ /dev/null
@@ -1,13 +0,0 @@
-desc "We tightened the validation in 2026 between initial purchase date, last transaction date and sale date so that no two can be equal and initial purchase date < last transaction date < sale date. To avoid invalid logs we clear lasttransaction if it equals saledate and if initialpurchase = lasttransaction we clear both"
-task fix_sales_logs_with_invalid_initialpurchase_lasttransaction: :environment do
- lasttransaction_equal_saledate_logs = SalesLog.filter_by_year_or_later(2026).where("lasttransaction = saledate")
- initial_purchase_equal_lasttransaction_logs = SalesLog.filter_by_year_or_later(2026).where("initialpurchase = lasttransaction")
-
- puts "Updating #{lasttransaction_equal_saledate_logs.count} logs where lasttransaction = saledate, #{lasttransaction_equal_saledate_logs.map(&:id)}"
- lasttransaction_equal_saledate_logs.update!(lasttransaction: nil)
-
- puts "Updating #{initial_purchase_equal_lasttransaction_logs.count} logs where initialpurchase = lasttransaction, #{initial_purchase_equal_lasttransaction_logs.map(&:id)}"
- initial_purchase_equal_lasttransaction_logs.update!(initialpurchase: nil, lasttransaction: nil)
-
- puts "Done"
-end
diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb
index 04f71b198..8d4eb4cea 100644
--- a/spec/models/validations/sales/sale_information_validations_spec.rb
+++ b/spec/models/validations/sales/sale_information_validations_spec.rb
@@ -252,27 +252,12 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when initial purchase date == saledate" do
- let(:record) { build(:sales_log, initialpurchase: collection_start_date_for_year(start_year), saledate: collection_start_date_for_year(start_year)) }
+ let(:record) { build(:sales_log, initialpurchase: current_collection_start_date, saledate: current_collection_start_date) }
- context "and 2025", metadata: { year: 25 } do
- let(:start_year) { 2025 }
-
- it "does not add an error" do
- sale_information_validator.validate_staircasing_initial_purchase_date(record)
-
- expect(record.errors[:lasttransaction]).not_to be_present
- end
- end
-
- context "and 2026", metadata: { year: 26 } do
- let(:start_year) { 2026 }
-
- it "adds error" do
- sale_information_validator.validate_staircasing_initial_purchase_date(record)
+ it "does not add an error" do
+ sale_information_validator.validate_staircasing_initial_purchase_date(record)
- expect(record.errors[:initialpurchase]).to eq([I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_saledate")])
- expect(record.errors[:saledate]).to eq([I18n.t("validations.sales.sale_information.saledate.must_be_after_initial_purchase_date")])
- end
+ expect(record.errors[:initialpurchase]).not_to be_present
end
end
end
@@ -330,27 +315,12 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when last transaction date == saledate" do
- let(:record) { build(:sales_log, lasttransaction: collection_start_date_for_year(start_year), saledate: collection_start_date_for_year(start_year)) }
-
- context "and 2025", metadata: { year: 25 } do
- let(:start_year) { 2025 }
-
- it "does not add an error" do
- sale_information_validator.validate_staircasing_last_transaction_date(record)
-
- expect(record.errors[:lasttransaction]).not_to be_present
- end
- end
+ let(:record) { build(:sales_log, lasttransaction: current_collection_start_date, saledate: current_collection_start_date) }
- context "and 2026", metadata: { year: 26 } do
- let(:start_year) { 2026 }
-
- it "adds error" do
- sale_information_validator.validate_staircasing_last_transaction_date(record)
+ it "does not add an error" do
+ sale_information_validator.validate_staircasing_last_transaction_date(record)
- expect(record.errors[:lasttransaction]).to eq([I18n.t("validations.sales.sale_information.lasttransaction.must_be_before_saledate")])
- expect(record.errors[:saledate]).to eq([I18n.t("validations.sales.sale_information.saledate.must_be_after_last_transaction_date")])
- end
+ expect(record.errors[:lasttransaction]).not_to be_present
end
end
@@ -376,27 +346,12 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when last transaction date == initial purchase date" do
- let(:record) { build(:sales_log, lasttransaction: collection_start_date_for_year(start_year), initialpurchase: collection_start_date_for_year(start_year), saledate: collection_start_date_for_year(start_year) + 1.day) }
-
- context "and 2025", metadata: { year: 25 } do
- let(:start_year) { 2025 }
-
- it "does not add an error" do
- sale_information_validator.validate_staircasing_last_transaction_date(record)
+ let(:record) { build(:sales_log, lasttransaction: current_collection_start_date, initialpurchase: current_collection_start_date) }
- expect(record.errors[:lasttransaction]).not_to be_present
- end
- end
-
- context "and 2026", metadata: { year: 26 } do
- let(:start_year) { 2026 }
-
- it "adds error" do
- sale_information_validator.validate_staircasing_last_transaction_date(record)
+ it "does not add an error" do
+ sale_information_validator.validate_staircasing_last_transaction_date(record)
- expect(record.errors[:lasttransaction]).to eq([I18n.t("validations.sales.sale_information.lasttransaction.must_be_after_initial_purchase")])
- expect(record.errors[:initialpurchase]).to eq([I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_last_transaction")])
- end
+ expect(record.errors[:lasttransaction]).not_to be_present
end
end
end
From e079455f65372dcdef152c74d59a29c853acdf30 Mon Sep 17 00:00:00 2001
From: Samuel Young
Date: Wed, 29 Apr 2026 16:16:43 +0100
Subject: [PATCH 2/7] CLDC-4332: Check that a support user can only be in
certain orgs (#3305)
* CLDC-4332: Check that a support user can only be in certain orgs
* CLDC-4332: Add tests
* CLDC-4332: Lint
* CLDC-4332: Only show the check for wrong org on edit
* CLDC-4332: Add the error to role if role is wrong
* fixup! CLDC-4332: Only show the check for wrong org on edit
lint
* CLDC-4332: TEMP: Add MHCLG filter for review apps
* Revert "CLDC-4332: TEMP: Add MHCLG filter for review apps"
This reverts commit b65c630edea1f06403b4f837ec79d2541b7fb46b.
---
app/controllers/users_controller.rb | 10 ++++--
app/models/user.rb | 17 +++++++++++
app/services/feature_toggle.rb | 6 ++++
config/locales/en.yml | 4 +++
spec/models/user_spec.rb | 47 +++++++++++++++++++++++++++++
5 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 57036cabe..677925679 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -221,8 +221,14 @@ private
@user.errors.add :phone
end
- if user_params.key?(:organisation_id) && user_params[:organisation_id].blank?
- @user.errors.add :organisation_id, :blank
+ if user_params.key?(:organisation_id)
+ if user_params[:organisation_id].blank?
+ @user.errors.add :organisation_id, :blank
+ elsif !@user.role_is_allowed_to_be_in_organisation?(override_organisation_id: user_params[:organisation_id].to_i) && @user.id.present?
+ # this will also be flagged by the validation in user.rb.
+ # for convenience we show the error early before they go through the change org flow (involves reassigning logs).
+ @user.errors.add :organisation_id, I18n.t("validations.user.support_user_in_wrong_organisation.change_organisation")
+ end
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 92fd37dc8..b2ab58c11 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -25,6 +25,7 @@ class User < ApplicationRecord
validates :organisation_id, presence: true
validate :organisation_not_merged
+ validate :support_user_is_in_correct_organisation
has_paper_trail ignore: %w[last_sign_in_at
current_sign_in_at
@@ -390,6 +391,12 @@ class User < ApplicationRecord
end
end
+ def role_is_allowed_to_be_in_organisation?(override_organisation_id: nil)
+ return true unless support? && FeatureToggle.support_organisation_allow_list.present?
+
+ FeatureToggle.support_organisation_allow_list.include?(override_organisation_id || organisation_id)
+ end
+
protected
# Checks whether a password is needed or not. For validations only.
@@ -407,6 +414,16 @@ private
end
end
+ def support_user_is_in_correct_organisation
+ return if role_is_allowed_to_be_in_organisation?
+
+ if role_changed?
+ errors.add :role, I18n.t("validations.user.support_user_in_wrong_organisation.change_role")
+ else
+ errors.add :organisation_id, I18n.t("validations.user.support_user_in_wrong_organisation.change_organisation")
+ end
+ end
+
def send_data_protection_confirmation_reminder
return unless persisted?
return unless is_dpo?
diff --git a/app/services/feature_toggle.rb b/app/services/feature_toggle.rb
index 2f301294c..7404309f2 100644
--- a/app/services/feature_toggle.rb
+++ b/app/services/feature_toggle.rb
@@ -34,4 +34,10 @@ class FeatureToggle
def self.sales_export_enabled?
Time.zone.now >= Time.zone.local(2025, 4, 1) || (Rails.env.review? || Rails.env.staging?)
end
+
+ # IDs of organisations a user must be in to be allowed the support role
+ # if nil this feature will be disabled
+ def self.support_organisation_allow_list
+ [1] if Rails.env.production?
+ end
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 26a834e5d..843d1da8b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -260,6 +260,10 @@ en:
blank: "Enter an email address."
role:
invalid: "Role must be data accessor, data provider or data coordinator."
+ user:
+ support_user_in_wrong_organisation:
+ change_role: "You cannot create a support account type for a user in this organisation. Support accounts should only be created for MHCLG and contractor staff as they are administrator level accounts with access to all organisations' data. Any support accounts for housing organisations would be a data protection breach."
+ change_organisation: "You cannot move a user with a support account to a non-MHCLG organisation. If you need to move the user, change their role type to data coordinator or data provider."
setup:
saledate:
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 495d5ba13..b461b60e5 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -540,6 +540,53 @@ RSpec.describe User, type: :model do
.to raise_error(ActiveRecord::RecordInvalid, error_message)
end
end
+
+ describe "#support_user_is_in_correct_organisation" do
+ let(:organisation) { create(:organisation) }
+
+ context "when the user is not a support user" do
+ let(:user) { build(:user, :data_coordinator, organisation:) }
+
+ it "is valid regardless of the allow list" do
+ allow(FeatureToggle).to receive(:support_organisation_allow_list).and_return([999])
+ expect(user).to be_valid
+ end
+ end
+
+ context "when the user is a support user" do
+ let(:user) { build(:user, :support, organisation:) }
+
+ context "and the allow list is nil" do
+ before do
+ allow(FeatureToggle).to receive(:support_organisation_allow_list).and_return(nil)
+ end
+
+ it "is valid" do
+ expect(user).to be_valid
+ end
+ end
+
+ context "and the organisation is in the allow list" do
+ before do
+ allow(FeatureToggle).to receive(:support_organisation_allow_list).and_return([organisation.id])
+ end
+
+ it "is valid" do
+ expect(user).to be_valid
+ end
+ end
+
+ context "and the organisation is not in the allow list" do
+ before do
+ allow(FeatureToggle).to receive(:support_organisation_allow_list).and_return([organisation.id + 1])
+ end
+
+ it "is not valid" do
+ expect(user).not_to be_valid
+ end
+ end
+ end
+ end
end
describe "delete" do
From 13b2fc61c1b047d7e5494b07f543a86826de1556 Mon Sep 17 00:00:00 2001
From: Samuel Young
Date: Thu, 30 Apr 2026 15:16:27 +0100
Subject: [PATCH 3/7] CLDC-3280: Remove error report upload again when viewing
before decision (#3303)
* CLDC-3280: Add back link to error summary page
* CLDC-3280: Hide upload button if navigating from a view url
* CLDC-3280: Update tests
* CLDC-3280: Make read only error report opt-in
this means we can be happy only the touched pages will have a change in behaviour
* CLDC-3280: Rename fix errors in tests to reupload file
Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com>
---------
Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com>
---
.../forms/bulk_upload_resume/confirm.rb | 6 ++--
.../forms/bulk_upload_resume/fix_choice.rb | 6 ++--
.../show.html.erb | 4 ++-
.../summary.html.erb | 8 ++++-
.../confirm.html.erb | 2 +-
.../fix_choice.html.erb | 2 +-
.../bulk_upload_sales_results/show.html.erb | 4 ++-
.../summary.html.erb | 8 ++++-
.../bulk_upload_sales_resume/confirm.html.erb | 2 +-
.../fix_choice.html.erb | 2 +-
...upload_lettings_results_controller_spec.rb | 36 +++++++++++++++++++
...lk_upload_sales_results_controller_spec.rb | 36 +++++++++++++++++++
12 files changed, 102 insertions(+), 14 deletions(-)
diff --git a/app/models/forms/bulk_upload_resume/confirm.rb b/app/models/forms/bulk_upload_resume/confirm.rb
index 96a1fe1bc..596018d50 100644
--- a/app/models/forms/bulk_upload_resume/confirm.rb
+++ b/app/models/forms/bulk_upload_resume/confirm.rb
@@ -20,11 +20,11 @@ module Forms
send("resume_bulk_upload_#{log_type}_result_path", bulk_upload)
end
- def error_report_path
+ def error_report_path(read_only: false)
if BulkUploadErrorSummaryTableComponent.new(bulk_upload:).errors?
- send("summary_bulk_upload_#{log_type}_result_path", bulk_upload)
+ send("summary_bulk_upload_#{log_type}_result_path", bulk_upload, hide_upload_button: read_only ? "true" : nil)
else
- send("bulk_upload_#{log_type}_result_path", bulk_upload)
+ send("bulk_upload_#{log_type}_result_path", bulk_upload, hide_upload_button: read_only ? "true" : nil)
end
end
diff --git a/app/models/forms/bulk_upload_resume/fix_choice.rb b/app/models/forms/bulk_upload_resume/fix_choice.rb
index c731dbf93..b14970643 100644
--- a/app/models/forms/bulk_upload_resume/fix_choice.rb
+++ b/app/models/forms/bulk_upload_resume/fix_choice.rb
@@ -34,11 +34,11 @@ module Forms
end
end
- def error_report_path
+ def error_report_path(read_only: false)
if BulkUploadErrorSummaryTableComponent.new(bulk_upload:).errors?
- send("summary_bulk_upload_#{log_type}_result_path", bulk_upload)
+ send("summary_bulk_upload_#{log_type}_result_path", bulk_upload, hide_upload_button: read_only ? "true" : nil)
else
- send("bulk_upload_#{log_type}_result_path", bulk_upload)
+ send("bulk_upload_#{log_type}_result_path", bulk_upload, hide_upload_button: read_only ? "true" : nil)
end
end
diff --git a/app/views/bulk_upload_lettings_results/show.html.erb b/app/views/bulk_upload_lettings_results/show.html.erb
index 5eedefe49..cf8c62eeb 100644
--- a/app/views/bulk_upload_lettings_results/show.html.erb
+++ b/app/views/bulk_upload_lettings_results/show.html.erb
@@ -33,4 +33,6 @@
-<%= govuk_button_link_to "Upload your file again", start_bulk_upload_lettings_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% if params[:hide_upload_button] != "true" %>
+ <%= govuk_button_link_to "Upload your file again", start_bulk_upload_lettings_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% end %>
diff --git a/app/views/bulk_upload_lettings_results/summary.html.erb b/app/views/bulk_upload_lettings_results/summary.html.erb
index ab60212e9..1a31eb0a9 100644
--- a/app/views/bulk_upload_lettings_results/summary.html.erb
+++ b/app/views/bulk_upload_lettings_results/summary.html.erb
@@ -1,3 +1,7 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link(href: :back) %>
+<% end %>
+
<%= render partial: "bulk_upload_shared/moved_user_banner" %>
@@ -34,4 +38,6 @@
<% end %>
-<%= govuk_button_link_to "Upload your file again", start_bulk_upload_lettings_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% if params[:hide_upload_button] != "true" %>
+ <%= govuk_button_link_to "Upload your file again", start_bulk_upload_lettings_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% end %>
diff --git a/app/views/bulk_upload_lettings_resume/confirm.html.erb b/app/views/bulk_upload_lettings_resume/confirm.html.erb
index bb15c7101..c72cab57b 100644
--- a/app/views/bulk_upload_lettings_resume/confirm.html.erb
+++ b/app/views/bulk_upload_lettings_resume/confirm.html.erb
@@ -9,7 +9,7 @@
<%= logs_and_errors_warning(@bulk_upload) %>
- <%= govuk_link_to "View the error report", @form.error_report_path %>
+ <%= govuk_link_to "View the error report", @form.error_report_path(read_only: true) %>
<% if unique_answers_to_be_cleared(@bulk_upload).present? %>
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 225fb07bf..3bb326d02 100644
--- a/app/views/bulk_upload_lettings_resume/fix_choice.html.erb
+++ b/app/views/bulk_upload_lettings_resume/fix_choice.html.erb
@@ -19,7 +19,7 @@
- <%= govuk_link_to "View the error report", @form.error_report_path %>
+ <%= govuk_link_to "View the error report", @form.error_report_path(read_only: true) %>
<%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %>
diff --git a/app/views/bulk_upload_sales_results/show.html.erb b/app/views/bulk_upload_sales_results/show.html.erb
index 2276285fe..68aaf7311 100644
--- a/app/views/bulk_upload_sales_results/show.html.erb
+++ b/app/views/bulk_upload_sales_results/show.html.erb
@@ -33,4 +33,6 @@
-<%= govuk_button_link_to "Upload your file again", start_bulk_upload_sales_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% if params[:hide_upload_button] != "true" %>
+ <%= govuk_button_link_to "Upload your file again", start_bulk_upload_sales_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% end %>
diff --git a/app/views/bulk_upload_sales_results/summary.html.erb b/app/views/bulk_upload_sales_results/summary.html.erb
index ed2ec14b3..fc9f39d82 100644
--- a/app/views/bulk_upload_sales_results/summary.html.erb
+++ b/app/views/bulk_upload_sales_results/summary.html.erb
@@ -1,3 +1,7 @@
+<% content_for :before_content do %>
+ <%= govuk_back_link(href: :back) %>
+<% end %>
+
<%= render partial: "bulk_upload_shared/moved_user_banner" %>
@@ -34,4 +38,6 @@
<% end %>
-<%= govuk_button_link_to "Upload your file again", start_bulk_upload_sales_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% if params[:hide_upload_button] != "true" %>
+ <%= govuk_button_link_to "Upload your file again", start_bulk_upload_sales_logs_path(organisation_id: @bulk_upload.organisation_id) %>
+<% end %>
diff --git a/app/views/bulk_upload_sales_resume/confirm.html.erb b/app/views/bulk_upload_sales_resume/confirm.html.erb
index b47619053..1259d8bf9 100644
--- a/app/views/bulk_upload_sales_resume/confirm.html.erb
+++ b/app/views/bulk_upload_sales_resume/confirm.html.erb
@@ -9,7 +9,7 @@
<%= logs_and_errors_warning(@bulk_upload) %>
- <%= govuk_link_to "View the error report", @form.error_report_path %>
+ <%= govuk_link_to "View the error report", @form.error_report_path(read_only: true) %>
<% if unique_answers_to_be_cleared(@bulk_upload).present? %>
diff --git a/app/views/bulk_upload_sales_resume/fix_choice.html.erb b/app/views/bulk_upload_sales_resume/fix_choice.html.erb
index b376ee62d..8a2887678 100644
--- a/app/views/bulk_upload_sales_resume/fix_choice.html.erb
+++ b/app/views/bulk_upload_sales_resume/fix_choice.html.erb
@@ -19,7 +19,7 @@
- <%= govuk_link_to "View the error report", @form.error_report_path %>
+ <%= govuk_link_to "View the error report", @form.error_report_path(read_only: true) %>
<%= govuk_details(summary_text: "How to choose between fixing errors on the CORE site or in the CSV") do %>
diff --git a/spec/requests/bulk_upload_lettings_results_controller_spec.rb b/spec/requests/bulk_upload_lettings_results_controller_spec.rb
index 3416b6da9..e3ec4b4c1 100644
--- a/spec/requests/bulk_upload_lettings_results_controller_spec.rb
+++ b/spec/requests/bulk_upload_lettings_results_controller_spec.rb
@@ -82,6 +82,24 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
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
+
+ context "and user has upload button shown" do
+ it "displays a link to reupload file" do
+ get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary"
+
+ expect(response.body).to include("Upload your file again")
+ expect(response.body).to include("/lettings-logs/bulk-upload-logs/start")
+ end
+ end
+
+ context "and user has upload button hidden" do
+ it "does not display a link to reupload file" do
+ get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary?hide_upload_button=true"
+
+ expect(response.body).not_to include("Upload your file again")
+ expect(response.body).not_to include("/lettings-logs/bulk-upload-logs/start")
+ end
+ end
end
end
@@ -152,5 +170,23 @@ RSpec.describe BulkUploadLettingsResultsController, type: :request do
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
+
+ context "and user has upload button shown" do
+ it "displays a link to reupload file" do
+ get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}"
+
+ expect(response.body).to include("Upload your file again")
+ expect(response.body).to include("/lettings-logs/bulk-upload-logs/start")
+ end
+ end
+
+ context "and user has upload button hidden" do
+ it "does not display a link to reupload file" do
+ get "/lettings-logs/bulk-upload-results/#{bulk_upload.id}?hide_upload_button=true"
+
+ expect(response.body).not_to include("Upload your file again")
+ expect(response.body).not_to include("/lettings-logs/bulk-upload-logs/start")
+ 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 2236475fa..c6d31b0f4 100644
--- a/spec/requests/bulk_upload_sales_results_controller_spec.rb
+++ b/spec/requests/bulk_upload_sales_results_controller_spec.rb
@@ -44,6 +44,24 @@ RSpec.describe BulkUploadSalesResultsController, type: :request do
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
+
+ context "and user has upload button shown" do
+ it "displays a link to reupload file" do
+ get "/sales-logs/bulk-upload-results/#{bulk_upload.id}/summary"
+
+ expect(response.body).to include("Upload your file again")
+ expect(response.body).to include("/sales-logs/bulk-upload-logs/start")
+ end
+ end
+
+ context "and user has upload button hidden" do
+ it "does not display a link to reupload file" do
+ get "/sales-logs/bulk-upload-results/#{bulk_upload.id}/summary?hide_upload_button=true"
+
+ expect(response.body).not_to include("Upload your file again")
+ expect(response.body).not_to include("/sales-logs/bulk-upload-logs/start")
+ end
+ end
end
end
@@ -127,5 +145,23 @@ RSpec.describe BulkUploadSalesResultsController, type: :request do
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
+
+ context "and user has upload button shown" do
+ it "displays a link to reupload file" do
+ get "/sales-logs/bulk-upload-results/#{bulk_upload.id}"
+
+ expect(response.body).to include("Upload your file again")
+ expect(response.body).to include("/sales-logs/bulk-upload-logs/start")
+ end
+ end
+
+ context "and user has upload button hidden" do
+ it "does not display a link to reupload file" do
+ get "/sales-logs/bulk-upload-results/#{bulk_upload.id}?hide_upload_button=true"
+
+ expect(response.body).not_to include("Upload your file again")
+ expect(response.body).not_to include("/sales-logs/bulk-upload-logs/start")
+ end
+ end
end
end
From 614fd111e5b11469fb3f9087bf6b0249b261b7f5 Mon Sep 17 00:00:00 2001
From: Samuel Young
Date: Thu, 30 Apr 2026 17:04:20 +0100
Subject: [PATCH 4/7] CLDC-4300: Update sale date staircasing date validation
(#3341)
* CLDC-4300: Stricten validation on initialpurchase
* CLDC-4300: Add a correction script
* CLDC-4300: Update tests
* CLDC-4300: Ensure lasttransaction cannot equal saledate or initialtransaction
* CLDC-4300: Update date correcting rake to also account for lasttransaction = initialpurchase
* CLDC-4300: Revert updates to copy
* CLDC-4300: Apply these changes from 2026 only
* CLDC-4300: Compare lasttransaction to saledate
due to the it <= lt <= sd relationship, there's no need to comparse it to sd. if it == sd then it == lt and lt == sd
* CLDC-4300: Fix script in cases where all 3 dates are equal
---
.../sales/sale_information_validations.rb | 6 +-
...valid_initialpurchase_lasttransaction.rake | 15 ++++
.../sale_information_validations_spec.rb | 69 +++++++++++++++----
3 files changed, 75 insertions(+), 15 deletions(-)
create mode 100644 lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake
diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb
index 32bf2a716..7cd24183c 100644
--- a/app/models/validations/sales/sale_information_validations.rb
+++ b/app/models/validations/sales/sale_information_validations.rb
@@ -42,7 +42,7 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :initialpurchase, I18n.t("validations.sales.sale_information.initialpurchase.must_be_after_1980")
end
- if record.saledate.present? && record.initialpurchase > record.saledate
+ if record.saledate.present? && ((record.initialpurchase > record.saledate) || (record.initialpurchase == record.saledate && record.form.start_year_2026_or_later?))
record.errors.add :initialpurchase, I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_saledate")
record.errors.add :saledate, :skip_bu_error, message: I18n.t("validations.sales.sale_information.saledate.must_be_after_initial_purchase_date")
end
@@ -55,11 +55,11 @@ module Validations::Sales::SaleInformationValidations
record.errors.add :lasttransaction, I18n.t("validations.sales.sale_information.lasttransaction.must_be_after_1980")
end
- if record.saledate.present? && record.lasttransaction > record.saledate
+ if record.saledate.present? && ((record.lasttransaction > record.saledate) || (record.lasttransaction == record.saledate && record.form.start_year_2026_or_later?))
record.errors.add :lasttransaction, I18n.t("validations.sales.sale_information.lasttransaction.must_be_before_saledate")
record.errors.add :saledate, :skip_bu_error, message: I18n.t("validations.sales.sale_information.saledate.must_be_after_last_transaction_date")
end
- if record.initialpurchase.present? && record.lasttransaction < record.initialpurchase
+ if record.initialpurchase.present? && ((record.lasttransaction < record.initialpurchase) || (record.lasttransaction == record.initialpurchase && record.form.start_year_2026_or_later?))
record.errors.add :initialpurchase, I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_last_transaction")
record.errors.add :lasttransaction, I18n.t("validations.sales.sale_information.lasttransaction.must_be_after_initial_purchase")
end
diff --git a/lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake b/lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake
new file mode 100644
index 000000000..11ff853a1
--- /dev/null
+++ b/lib/tasks/fix_sales_logs_with_invalid_initialpurchase_lasttransaction.rake
@@ -0,0 +1,15 @@
+desc "We tightened the validation between initial purchase date in 2026, last transaction date and sale date so the two can no longer be equal. To avoid invalid logs we clear initialpurchase if it equals saledate and if initialpurchase = lasttransaction we clear both"
+task fix_sales_logs_with_invalid_initialpurchase_lasttransaction: :environment do
+ initial_purchase_equal_lasttransaction_logs = SalesLog.filter_by_year_or_later(2026).where("initialpurchase = lasttransaction")
+ lasttransaction_equal_saledate_logs = SalesLog.filter_by_year_or_later(2026).where("lasttransaction = saledate")
+
+ # this one must happen first since this will always result in a log that passes date validations
+ puts "Updating #{initial_purchase_equal_lasttransaction_logs.count} logs where initialpurchase = lasttransaction, #{initial_purchase_equal_lasttransaction_logs.map(&:id)}"
+ initial_purchase_equal_lasttransaction_logs.update!(initialpurchase: nil, lasttransaction: nil)
+
+ # this one could fail if lasttransaction == saledate == initialpurchase, but the above case will have already reset these logs
+ puts "Updating #{lasttransaction_equal_saledate_logs.count} logs where lasttransaction = saledate, #{lasttransaction_equal_saledate_logs.map(&:id)}"
+ lasttransaction_equal_saledate_logs.update!(lasttransaction: nil)
+
+ puts "Done"
+end
diff --git a/spec/models/validations/sales/sale_information_validations_spec.rb b/spec/models/validations/sales/sale_information_validations_spec.rb
index 8d4eb4cea..04f71b198 100644
--- a/spec/models/validations/sales/sale_information_validations_spec.rb
+++ b/spec/models/validations/sales/sale_information_validations_spec.rb
@@ -252,12 +252,27 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when initial purchase date == saledate" do
- let(:record) { build(:sales_log, initialpurchase: current_collection_start_date, saledate: current_collection_start_date) }
+ let(:record) { build(:sales_log, initialpurchase: collection_start_date_for_year(start_year), saledate: collection_start_date_for_year(start_year)) }
- it "does not add an error" do
- sale_information_validator.validate_staircasing_initial_purchase_date(record)
+ context "and 2025", metadata: { year: 25 } do
+ let(:start_year) { 2025 }
- expect(record.errors[:initialpurchase]).not_to be_present
+ it "does not add an error" do
+ sale_information_validator.validate_staircasing_initial_purchase_date(record)
+
+ expect(record.errors[:lasttransaction]).not_to be_present
+ end
+ end
+
+ context "and 2026", metadata: { year: 26 } do
+ let(:start_year) { 2026 }
+
+ it "adds error" do
+ sale_information_validator.validate_staircasing_initial_purchase_date(record)
+
+ expect(record.errors[:initialpurchase]).to eq([I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_saledate")])
+ expect(record.errors[:saledate]).to eq([I18n.t("validations.sales.sale_information.saledate.must_be_after_initial_purchase_date")])
+ end
end
end
end
@@ -315,12 +330,27 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when last transaction date == saledate" do
- let(:record) { build(:sales_log, lasttransaction: current_collection_start_date, saledate: current_collection_start_date) }
+ let(:record) { build(:sales_log, lasttransaction: collection_start_date_for_year(start_year), saledate: collection_start_date_for_year(start_year)) }
- it "does not add an error" do
- sale_information_validator.validate_staircasing_last_transaction_date(record)
+ context "and 2025", metadata: { year: 25 } do
+ let(:start_year) { 2025 }
- expect(record.errors[:lasttransaction]).not_to be_present
+ it "does not add an error" do
+ sale_information_validator.validate_staircasing_last_transaction_date(record)
+
+ expect(record.errors[:lasttransaction]).not_to be_present
+ end
+ end
+
+ context "and 2026", metadata: { year: 26 } do
+ let(:start_year) { 2026 }
+
+ it "adds error" do
+ sale_information_validator.validate_staircasing_last_transaction_date(record)
+
+ expect(record.errors[:lasttransaction]).to eq([I18n.t("validations.sales.sale_information.lasttransaction.must_be_before_saledate")])
+ expect(record.errors[:saledate]).to eq([I18n.t("validations.sales.sale_information.saledate.must_be_after_last_transaction_date")])
+ end
end
end
@@ -346,12 +376,27 @@ RSpec.describe Validations::Sales::SaleInformationValidations do
end
context "when last transaction date == initial purchase date" do
- let(:record) { build(:sales_log, lasttransaction: current_collection_start_date, initialpurchase: current_collection_start_date) }
+ let(:record) { build(:sales_log, lasttransaction: collection_start_date_for_year(start_year), initialpurchase: collection_start_date_for_year(start_year), saledate: collection_start_date_for_year(start_year) + 1.day) }
- it "does not add an error" do
- sale_information_validator.validate_staircasing_last_transaction_date(record)
+ context "and 2025", metadata: { year: 25 } do
+ let(:start_year) { 2025 }
- expect(record.errors[:lasttransaction]).not_to be_present
+ it "does not add an error" do
+ sale_information_validator.validate_staircasing_last_transaction_date(record)
+
+ expect(record.errors[:lasttransaction]).not_to be_present
+ end
+ end
+
+ context "and 2026", metadata: { year: 26 } do
+ let(:start_year) { 2026 }
+
+ it "adds error" do
+ sale_information_validator.validate_staircasing_last_transaction_date(record)
+
+ expect(record.errors[:lasttransaction]).to eq([I18n.t("validations.sales.sale_information.lasttransaction.must_be_after_initial_purchase")])
+ expect(record.errors[:initialpurchase]).to eq([I18n.t("validations.sales.sale_information.initialpurchase.must_be_before_last_transaction")])
+ end
end
end
end
From e0c3938b6b7b1c94ec632d98f34ac633fd1a28b4 Mon Sep 17 00:00:00 2001
From: Nat Dean-Lewis <94526761+natdeanlewissoftwire@users.noreply.github.com>
Date: Wed, 3 Jun 2026 16:55:34 +0100
Subject: [PATCH 5/7] CLDC-4470: Update vulnerable packages (#3349)
* feat: update addressable
* feat: update rack-session
* feat: update other high criticality dependencies
* feat: update other high criticality dependencies
* feat: update other high criticality dependencies
* feat: align with goovuk-components 6.x
* feat: update navbar styling
* feat: use helpers where required and update misc tests
* feat: add title test for support users
* refactor: linting
* feat: update application helper spec
* feat: add missing helpers
* refactor: make specs more readable
* refactor: lint
---
Gemfile | 4 +-
Gemfile.lock | 106 +-
.../bulk_upload_error_row_component.html.erb | 4 +-
.../bulk_upload_error_row_component.rb | 9 +-
...oad_error_summary_table_component.html.erb | 4 +-
...lk_upload_error_summary_table_component.rb | 3 +-
.../bulk_upload_summary_component.rb | 16 +-
...swers_summary_list_card_component.html.erb | 6 +-
...eck_answers_summary_list_card_component.rb | 11 +-
.../create_log_actions_component.html.erb | 18 +-
.../create_log_actions_component.rb | 19 +-
...ion_confirmation_banner_component.html.erb | 2 +-
...rotection_confirmation_banner_component.rb | 5 +-
.../document_list_component.html.erb | 2 +-
app/components/document_list_component.rb | 2 +-
.../lettings_log_summary_component.html.erb | 2 +-
.../lettings_log_summary_component.rb | 2 +-
...ing_stock_owners_banner_component.html.erb | 2 +-
.../missing_stock_owners_banner_component.rb | 9 +-
.../primary_navigation_component.html.erb | 2 +-
.../primary_navigation_component.rb | 2 +-
.../sales_log_summary_component.html.erb | 2 +-
app/components/sales_log_summary_component.rb | 2 +-
app/components/search_component.html.erb | 4 +-
app/components/search_component.rb | 2 +-
.../search_result_caption_component.rb | 2 +-
.../sub_navigation_component.html.erb | 4 +-
app/components/sub_navigation_component.rb | 2 +-
app/frontend/styles/_filter.scss | 2 +-
app/frontend/styles/_header.scss | 11 -
app/frontend/styles/_related-navigation.scss | 2 +-
app/frontend/styles/_tag.scss | 2 +-
app/frontend/styles/_testing-tools.scss | 2 +-
app/frontend/styles/application.scss | 12 +
app/helpers/application_helper.rb | 13 +-
app/views/layouts/application.html.erb | 30 +-
.../layouts/rails_admin/_navigation.html.erb | 18 +-
app/views/users/_user_list.html.erb | 4 +-
package.json | 4 +-
spec/helpers/application_helper_spec.rb | 46 +-
spec/requests/users_controller_spec.rb | 54 +-
webpack.config.js | 4 +-
yarn.lock | 1377 +++++++++--------
43 files changed, 985 insertions(+), 844 deletions(-)
diff --git a/Gemfile b/Gemfile
index c76a48bec..d68325a7f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,7 +18,7 @@ gem "jsbundling-rails"
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.4.4", require: false
# GOV UK frontend components
-gem "govuk-components", "~> 5.7"
+gem "govuk-components", "~> 6.2"
# GOV UK component form builder DSL
gem "govuk_design_system_formbuilder", "~> 5.7"
# Convert Markdown into GOV.UK frontend-styled HTML
@@ -40,7 +40,7 @@ gem "devise_two_factor_authentication"
gem "uk_postcode"
# Get rich data from postcode lookups. Wraps postcodes.io
# Use Ruby objects to build reusable markup. A React inspired evolution of the presenter pattern
-gem "view_component", "~> 3.9"
+gem "view_component", "~> 4.9"
# Use the AWS S3 SDK as storage mechanism
gem "aws-sdk-s3"
# Track changes to models for auditing or versioning.
diff --git a/Gemfile.lock b/Gemfile.lock
index c83c95414..903824128 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -78,8 +78,8 @@ GEM
minitest (>= 5.1, < 6)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
- addressable (2.8.6)
- public_suffix (>= 2.0.2, < 6.0)
+ addressable (2.9.0)
+ public_suffix (>= 2.0.2, < 8.0)
ast (2.4.3)
auto_strip_attributes (2.6.0)
activerecord (>= 4.0)
@@ -123,7 +123,7 @@ GEM
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
- bigdecimal (4.0.1)
+ bigdecimal (4.1.2)
bindex (0.8.1)
bootsnap (1.18.3)
msgpack (~> 1.2)
@@ -155,18 +155,21 @@ GEM
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
concurrent-ruby (1.3.6)
- connection_pool (2.5.3)
+ connection_pool (2.5.5)
crack (1.0.0)
bigdecimal
rexml
crass (1.0.6)
+ cronex (0.15.0)
+ tzinfo
+ unicode (>= 0.4.4.5)
cssbundling-rails (1.4.0)
railties (>= 6.0.0)
csv (3.3.2)
date (3.5.1)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
- devise (5.0.3)
+ devise (5.0.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 7.0)
@@ -187,7 +190,7 @@ GEM
drb (2.2.3)
dumb_delegator (1.0.0)
encryptor (3.0.0)
- erb (6.0.2)
+ erb (6.0.4)
erb_lint (0.9.0)
activesupport
better_html (>= 2.0.1)
@@ -196,7 +199,7 @@ GEM
rubocop (>= 1)
smart_properties
erubi (1.13.1)
- et-orbi (1.2.11)
+ et-orbi (1.4.0)
tzinfo
event_stream_parser (1.0.0)
excon (0.111.0)
@@ -207,24 +210,24 @@ GEM
railties (>= 5.0.0)
faker (3.2.3)
i18n (>= 1.8.11, < 2)
- faraday (2.14.1)
+ faraday (2.14.2)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-multipart (1.0.4)
multipart-post (~> 2)
- faraday-net_http (3.1.0)
- net-http
+ faraday-net_http (3.4.3)
+ net-http (~> 0.5)
ffi (1.16.3)
- fugit (1.11.1)
- et-orbi (~> 1, >= 1.2.11)
+ fugit (1.12.2)
+ et-orbi (~> 1.4)
raabro (~> 1.4)
- globalid (1.2.1)
+ globalid (1.3.0)
activesupport (>= 6.1)
- govuk-components (5.7.0)
+ govuk-components (6.2.0)
html-attributes-utils (~> 1.0.0, >= 1.0.0)
pagy (>= 6, < 10)
- view_component (>= 3.9, < 3.17)
+ view_component (>= 4.9, < 4.10)
govuk_design_system_formbuilder (5.7.1)
actionview (>= 6.1)
activemodel (>= 6.1)
@@ -241,7 +244,7 @@ GEM
ice_nine (0.11.2)
iniparse (1.5.0)
io-console (0.8.2)
- irb (1.17.0)
+ irb (1.18.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
rdoc (>= 4.0.0)
@@ -249,10 +252,10 @@ GEM
jmespath (1.6.2)
jsbundling-rails (1.3.0)
railties (>= 6.0.0)
- json (2.19.2)
+ json (2.19.7)
json-schema (4.1.1)
addressable (>= 2.8)
- jwt (2.8.0)
+ jwt (3.2.0)
base64
kaminari (1.2.2)
activesupport (>= 4.1.0)
@@ -290,9 +293,9 @@ GEM
msgpack (1.7.2)
multipart-post (2.4.1)
nested_form (0.3.2)
- net-http (0.4.1)
- uri
- net-imap (0.5.7)
+ net-http (0.9.1)
+ uri (>= 0.11.1)
+ net-imap (0.6.4)
date
net-protocol
net-pop (0.1.2)
@@ -302,22 +305,22 @@ GEM
net-smtp (0.5.1)
net-protocol
nio4r (2.7.4)
- nokogiri (1.19.1-arm64-darwin)
+ nokogiri (1.19.3-arm64-darwin)
racc (~> 1.4)
- nokogiri (1.19.1-x86_64-darwin)
+ nokogiri (1.19.3-x86_64-darwin)
racc (~> 1.4)
- nokogiri (1.19.1-x86_64-linux-gnu)
+ nokogiri (1.19.3-x86_64-linux-gnu)
racc (~> 1.4)
- nokogiri (1.19.1-x86_64-linux-musl)
+ nokogiri (1.19.3-x86_64-linux-musl)
racc (~> 1.4)
- notifications-ruby-client (6.0.0)
- jwt (>= 1.5, < 3)
+ notifications-ruby-client (6.4.0)
+ jwt (>= 1.5, < 4)
orm_adapter (0.5.0)
overcommit (0.63.0)
childprocess (>= 0.6.3, < 6)
iniparse (~> 1.4)
rexml (~> 3.2)
- pagy (9.3.2)
+ pagy (9.4.0)
paper_trail (15.2.0)
activerecord (>= 6.1)
request_store (~> 1.4)
@@ -350,19 +353,19 @@ GEM
psych (5.3.1)
date
stringio
- public_suffix (5.0.4)
+ public_suffix (7.0.5)
puma (6.5.0)
nio4r (~> 2.0)
pundit (2.3.1)
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.8.1)
- rack (3.1.20)
+ rack (3.1.21)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-mini-profiler (3.3.1)
rack (>= 1.2.0)
- rack-session (2.1.1)
+ rack-session (2.1.2)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.2.0)
@@ -408,7 +411,7 @@ GEM
tsort (>= 0.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
- rake (13.3.1)
+ rake (13.4.2)
randexp (0.1.7)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
@@ -419,7 +422,7 @@ GEM
tsort
redcarpet (3.6.0)
redis (4.8.1)
- redis-client (0.22.1)
+ redis-client (0.29.0)
connection_pool
regexp_parser (2.11.3)
reline (0.6.3)
@@ -508,15 +511,17 @@ GEM
sentry-ruby (~> 5.16.1)
sentry-ruby (5.16.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
- sidekiq (7.2.4)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.19.0)
- sidekiq-cron (1.12.0)
- fugit (~> 1.8)
+ sidekiq (8.0.10)
+ connection_pool (>= 2.5.0)
+ json (>= 2.9.0)
+ logger (>= 1.6.2)
+ rack (>= 3.1.0)
+ redis-client (>= 0.23.2)
+ sidekiq-cron (2.4.0)
+ cronex (>= 0.13.0)
+ fugit (~> 1.8, >= 1.11.1)
globalid (>= 1.0.1)
- sidekiq (>= 6)
+ sidekiq (>= 6.5.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
@@ -530,7 +535,7 @@ GEM
thor (1.4.0)
thread_safe (0.3.6)
timecop (0.9.8)
- timeout (0.4.3)
+ timeout (0.6.1)
tsort (0.2.0)
turbo-rails (2.0.13)
actionpack (>= 7.1.0)
@@ -538,17 +543,18 @@ GEM
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uk_postcode (2.1.8)
+ unicode (0.4.4.5)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
unread (0.14.0)
activerecord (>= 6.1)
- uri (1.0.4)
+ uri (1.1.1)
useragent (0.16.11)
- view_component (3.10.0)
- activesupport (>= 5.2.0, < 8.0)
- concurrent-ruby (~> 1.0)
- method_source (~> 1.0)
+ view_component (4.9.0)
+ actionview (>= 7.1.0)
+ activesupport (>= 7.1.0)
+ concurrent-ruby (~> 1)
virtus (2.0.0)
axiom-types (~> 0.1)
coercible (~> 1.0)
@@ -571,7 +577,7 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.7.5)
+ zeitwerk (2.8.2)
PLATFORMS
arm64-darwin
@@ -599,7 +605,7 @@ DEPENDENCIES
factory_bot_rails
faker
faraday (>= 2.14.1)
- govuk-components (~> 5.7)
+ govuk-components (~> 6.2)
govuk_design_system_formbuilder (~> 5.7)
govuk_markdown
jsbundling-rails
@@ -643,7 +649,7 @@ DEPENDENCIES
tzinfo-data
uk_postcode
unread
- view_component (~> 3.9)
+ view_component (~> 4.9)
web-console (>= 4.1.0)
webmock
diff --git a/app/components/bulk_upload_error_row_component.html.erb b/app/components/bulk_upload_error_row_component.html.erb
index 8cfdb674e..4ce6e4f5c 100644
--- a/app/components/bulk_upload_error_row_component.html.erb
+++ b/app/components/bulk_upload_error_row_component.html.erb
@@ -13,7 +13,7 @@
<% if critical_errors.any? %>
Critical errors
These errors must be fixed to complete your logs.
- <%= govuk_table(html_attributes: { class: potential_errors.any? ? "" : "no-bottom-border" }) do |table| %>
+ <%= helpers.govuk_table(html_attributes: { class: potential_errors.any? ? "" : "no-bottom-border" }) do |table| %>
<%= table.with_head do |head| %>
<% head.with_row do |row| %>
<% row.with_cell(header: true, text: "Cell") %>
@@ -39,7 +39,7 @@
<% if potential_errors.any? %>
Confirmation needed
Potential data discrepancies exist in the following cells.
Please resolve all critical errors and review the cells with data discrepancies before re-uploading the file. Bulk confirmation of potential discrepancies is accessible only after all critical errors have been resolved.
- <%= govuk_table(html_attributes: { class: "no-bottom-border" }) do |table| %>
+ <%= helpers.govuk_table(html_attributes: { class: "no-bottom-border" }) do |table| %>
<%= table.with_head do |head| %>
<% head.with_row do |row| %>
<% row.with_cell(header: true, text: "Cell") %>
diff --git a/app/components/bulk_upload_error_row_component.rb b/app/components/bulk_upload_error_row_component.rb
index 1cb4de9d8..569f71b85 100644
--- a/app/components/bulk_upload_error_row_component.rb
+++ b/app/components/bulk_upload_error_row_component.rb
@@ -2,9 +2,8 @@ class BulkUploadErrorRowComponent < ViewComponent::Base
attr_reader :bulk_upload_errors
def initialize(bulk_upload_errors:)
+ super()
@bulk_upload_errors = bulk_upload_errors
-
- super
end
def row
@@ -18,7 +17,7 @@ class BulkUploadErrorRowComponent < ViewComponent::Base
def tenant_code_html
return if tenant_code.blank?
- content_tag :span, class: "govuk-!-margin-left-3" do
+ helpers.content_tag :span, class: "govuk-!-margin-left-3" do
"Tenant code: #{tenant_code}"
end
end
@@ -30,7 +29,7 @@ class BulkUploadErrorRowComponent < ViewComponent::Base
def purchaser_code_html
return if purchaser_code.blank?
- content_tag :span, class: "govuk-!-margin-left-3" do
+ helpers.content_tag :span, class: "govuk-!-margin-left-3" do
"Purchaser code: #{purchaser_code}"
end
end
@@ -42,7 +41,7 @@ class BulkUploadErrorRowComponent < ViewComponent::Base
def property_ref_html
return if property_ref.blank?
- content_tag :span, class: "govuk-!-margin-left-3" do
+ helpers.content_tag :span, class: "govuk-!-margin-left-3" do
"Property reference: #{property_ref}"
end
end
diff --git a/app/components/bulk_upload_error_summary_table_component.html.erb b/app/components/bulk_upload_error_summary_table_component.html.erb
index f9b42f34d..58489612a 100644
--- a/app/components/bulk_upload_error_summary_table_component.html.erb
+++ b/app/components/bulk_upload_error_summary_table_component.html.erb
@@ -3,7 +3,7 @@
<% sorted_errors.each do |error| %>
- <%= govuk_table do |table| %>
+ <%= helpers.govuk_table do |table| %>
<%= table.with_head do |head| %>
<% head.with_row do |row| %>
<% row.with_cell(text: question_for_field(error[0][1].to_sym), header: true) %>
@@ -13,7 +13,7 @@
<%= table.with_body do |body| %>
<% body.with_row do |row| %>
<% row.with_cell(text: error[0][2].html_safe) %>
- <% row.with_cell(text: pluralize(error[1], "error"), numeric: true) %>
+ <% row.with_cell(text: helpers.pluralize(error[1], "error"), numeric: true) %>
<% end %>
<% end %>
<% end %>
diff --git a/app/components/bulk_upload_error_summary_table_component.rb b/app/components/bulk_upload_error_summary_table_component.rb
index d15d5280e..db69fd9ec 100644
--- a/app/components/bulk_upload_error_summary_table_component.rb
+++ b/app/components/bulk_upload_error_summary_table_component.rb
@@ -6,9 +6,8 @@ class BulkUploadErrorSummaryTableComponent < ViewComponent::Base
delegate :question_for_field, to: :row_parser_class
def initialize(bulk_upload:)
+ super()
@bulk_upload = bulk_upload
-
- super
end
def sorted_errors
diff --git a/app/components/bulk_upload_summary_component.rb b/app/components/bulk_upload_summary_component.rb
index fa4cad414..2df854536 100644
--- a/app/components/bulk_upload_summary_component.rb
+++ b/app/components/bulk_upload_summary_component.rb
@@ -2,9 +2,9 @@ class BulkUploadSummaryComponent < ViewComponent::Base
attr_reader :bulk_upload
def initialize(bulk_upload:)
+ super()
@bulk_upload = bulk_upload
@bulk_upload_errors = bulk_upload.bulk_upload_errors
- super
end
def upload_status
@@ -27,9 +27,9 @@ class BulkUploadSummaryComponent < ViewComponent::Base
return if count.nil? || count <= 0
text = count > 1 ? (plural_text || singular_text.pluralize(count)) : singular_text
- content_tag(:p, class: "govuk-!-font-size-16 govuk-!-margin-bottom-1") do
- concat(content_tag(:strong, count))
- concat(" #{text}")
+ helpers.content_tag(:p, class: "govuk-!-font-size-16 govuk-!-margin-bottom-1") do
+ helpers.concat(helpers.content_tag(:strong, count))
+ helpers.concat(" #{text}")
end
end
@@ -44,11 +44,11 @@ class BulkUploadSummaryComponent < ViewComponent::Base
end
def download_lettings_file_link(bulk_upload)
- govuk_link_to "Download file", download_lettings_bulk_upload_path(bulk_upload), class: "govuk-link govuk-!-margin-right-2"
+ helpers.govuk_link_to "Download file", download_lettings_bulk_upload_path(bulk_upload), class: "govuk-link govuk-!-margin-right-2"
end
def download_sales_file_link(bulk_upload)
- govuk_link_to "Download file", download_sales_bulk_upload_path(bulk_upload), class: "govuk-link govuk-!-margin-right-2"
+ helpers.govuk_link_to "Download file", download_sales_bulk_upload_path(bulk_upload), class: "govuk-link govuk-!-margin-right-2"
end
def view_error_report_link(bulk_upload)
@@ -61,12 +61,12 @@ class BulkUploadSummaryComponent < ViewComponent::Base
"bulk_upload_#{bulk_upload.log_type}_result_path"
end
- govuk_link_to "View error report", send(path, bulk_upload), class: "govuk-link"
+ helpers.govuk_link_to "View error report", helpers.send(path, bulk_upload), class: "govuk-link"
end
def view_logs_link(bulk_upload)
return unless bulk_upload.status.to_s == "logs_uploaded_with_errors"
- govuk_link_to "View logs with errors", send("#{bulk_upload.log_type}_logs_path", bulk_upload_id: [bulk_upload.id]), class: "govuk-link"
+ helpers.govuk_link_to "View logs with errors", helpers.send("#{bulk_upload.log_type}_logs_path", bulk_upload_id: [bulk_upload.id]), class: "govuk-link"
end
end
diff --git a/app/components/check_answers_summary_list_card_component.html.erb b/app/components/check_answers_summary_list_card_component.html.erb
index 8b0c7d9b0..c9c974938 100644
--- a/app/components/check_answers_summary_list_card_component.html.erb
+++ b/app/components/check_answers_summary_list_card_component.html.erb
@@ -7,12 +7,12 @@
<% end %>
- <%= govuk_summary_list do |summary_list| %>
+ <%= helpers.govuk_summary_list do |summary_list| %>
<% applicable_questions.each do |question| %>
<% summary_list.with_row do |row| %>
<% row.with_key { get_question_label(question) } %>
<% row.with_value do %>
- <%= simple_format(
+ <%= helpers.simple_format(
get_answer_label(question),
wrapper_tag: "span",
class: "govuk-!-margin-right-4",
@@ -21,7 +21,7 @@
<% extra_value = question.get_extra_check_answer_value(log) %>
<% if extra_value && question.answer_label(log).present? %>
- <%= simple_format(
+ <%= helpers.simple_format(
extra_value,
wrapper_tag: "span",
class: "govuk-!-font-weight-regular app-!-colour-muted",
diff --git a/app/components/check_answers_summary_list_card_component.rb b/app/components/check_answers_summary_list_card_component.rb
index 1dc345f01..89345a0eb 100644
--- a/app/components/check_answers_summary_list_card_component.rb
+++ b/app/components/check_answers_summary_list_card_component.rb
@@ -2,12 +2,11 @@ class CheckAnswersSummaryListCardComponent < ViewComponent::Base
attr_reader :questions, :log, :user
def initialize(questions:, log:, user:, correcting_hard_validation: false)
+ super()
@questions = questions
@log = log
@user = user
@correcting_hard_validation = correcting_hard_validation
-
- super
end
def applicable_questions
@@ -34,16 +33,16 @@ class CheckAnswersSummaryListCardComponent < ViewComponent::Base
def action_href(question, log)
referrer = question.displayed_as_answered?(log) ? "check_answers" : "check_answers_new_answer"
- send("#{log.log_type}_#{question.page.id}_path", log, referrer:)
+ helpers.send("#{log.log_type}_#{question.page.id}_path", log, referrer:)
end
def correct_validation_action_href(question, log, _related_question_ids, correcting_hard_validation)
return action_href(question, log) unless correcting_hard_validation
if question.displayed_as_answered?(log)
- send("#{log.log_type}_confirm_clear_answer_path", log, question_id: question.id)
+ helpers.send("#{log.log_type}_confirm_clear_answer_path", log, question_id: question.id)
else
- send("#{log.log_type}_#{question.page.id}_path", log, referrer: "check_errors", related_question_ids: request.query_parameters["related_question_ids"], original_page_id: request.query_parameters["original_page_id"])
+ helpers.send("#{log.log_type}_#{question.page.id}_path", log, referrer: "check_errors", related_question_ids: request.query_parameters["related_question_ids"], original_page_id: request.query_parameters["original_page_id"])
end
end
@@ -56,7 +55,7 @@ private
"govuk-link govuk-link--no-visited-state"
end
- govuk_link_to question.check_answer_prompt, correct_validation_action_href(question, log, nil, @correcting_hard_validation), class: link_class
+ helpers.govuk_link_to question.check_answer_prompt, correct_validation_action_href(question, log, nil, @correcting_hard_validation), class: link_class
end
def number_of_buyers
diff --git a/app/components/create_log_actions_component.html.erb b/app/components/create_log_actions_component.html.erb
index 130072ec0..c8f557bce 100644
--- a/app/components/create_log_actions_component.html.erb
+++ b/app/components/create_log_actions_component.html.erb
@@ -1,11 +1,11 @@