Browse Source
# Conflicts: # config/locales/en.yml # db/schema.rbCLDC-1790-sales-validation-content-updates
natdeanlewissoftwire
2 years ago
86 changed files with 1268 additions and 105 deletions
@ -0,0 +1,25 @@
|
||||
<%= govuk_table do |table| %> |
||||
<% table.caption(size: "m", text: bulk_upload.filename) %> |
||||
|
||||
<% table.head do |head| %> |
||||
<% head.row do |row| %> |
||||
<% row.cell(text: "Column", header: true) %> |
||||
<% row.cell(text: "Number of rows", header: true) %> |
||||
<% row.cell(text: "Question", header: true) %> |
||||
<% row.cell(text: "Error", header: true) %> |
||||
<% row.cell(text: "Specification", header: true) %> |
||||
<% end %> |
||||
<% end %> |
||||
|
||||
<% table.body do |body| %> |
||||
<% sorted_errors.each do |error| %> |
||||
<% body.row do |row| %> |
||||
<% row.cell(text: error[0][0]) %> |
||||
<% row.cell(text: error[1]) %> |
||||
<% row.cell(text: BulkUpload::Lettings::Validator.question_for_field(error[0][1].to_sym)) %> |
||||
<% row.cell(text: error[0][2]) %> |
||||
<% row.cell(text: error[0][1]) %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
@ -0,0 +1,17 @@
|
||||
class BulkUploadErrorSummaryTableComponent < ViewComponent::Base |
||||
attr_reader :bulk_upload |
||||
|
||||
def initialize(bulk_upload:) |
||||
@bulk_upload = bulk_upload |
||||
|
||||
super |
||||
end |
||||
|
||||
def sorted_errors |
||||
@sorted_errors ||= bulk_upload |
||||
.bulk_upload_errors |
||||
.group(:col, :field, :error) |
||||
.count |
||||
.sort_by { |el| el[0][0].rjust(3, "0") } |
||||
end |
||||
end |
@ -0,0 +1,79 @@
|
||||
class BulkUploadMailer < NotifyMailer |
||||
BULK_UPLOAD_COMPLETE_TEMPLATE_ID = "83279578-c890-4168-838b-33c9cf0dc9f0".freeze |
||||
BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID = "e27abcd4-5295-48c2-b127-e9ee4b781b75".freeze |
||||
BULK_UPLOAD_FAILED_FILE_SETUP_ERROR_TEMPLATE_ID = "24c9f4c7-96ad-470a-ba31-eb51b7cbafd9".freeze |
||||
BULK_UPLOAD_FAILED_SERVICE_ERROR_TEMPLATE_ID = "c3f6288c-7a74-4e77-99ee-6c4a0f6e125a".freeze |
||||
BULK_UPLOAD_WITH_ERRORS_TEMPLATE_ID = "eb539005-6234-404e-812d-167728cf4274".freeze |
||||
|
||||
def send_bulk_upload_complete_mail(user, bulk_upload) |
||||
send_email( |
||||
user.email, |
||||
BULK_UPLOAD_COMPLETE_TEMPLATE_ID, |
||||
{ |
||||
title: "[#{bulk_upload} title]", |
||||
filename: "[#{bulk_upload} filename]", |
||||
upload_timestamp: "[#{bulk_upload} upload_timestamp]", |
||||
success_description: "[#{bulk_upload} success_description]", |
||||
logs_link: "[#{bulk_upload} logs_link]", |
||||
}, |
||||
) |
||||
end |
||||
|
||||
def send_bulk_upload_failed_csv_errors_mail(user, bulk_upload) |
||||
send_email( |
||||
user.email, |
||||
BULK_UPLOAD_FAILED_CSV_ERRORS_TEMPLATE_ID, |
||||
{ |
||||
filename: "[#{bulk_upload} filename]", |
||||
upload_timestamp: "[#{bulk_upload} upload_timestamp]", |
||||
year_combo: "[#{bulk_upload} year_combo]", |
||||
lettings_or_sales: "[#{bulk_upload} lettings_or_sales]", |
||||
error_description: "[#{bulk_upload} error_description]", |
||||
summary_report_link: "[#{bulk_upload} summary_report_link]", |
||||
}, |
||||
) |
||||
end |
||||
|
||||
def send_bulk_upload_failed_file_setup_error_mail(user, bulk_upload) |
||||
send_email( |
||||
user.email, |
||||
BULK_UPLOAD_FAILED_FILE_SETUP_ERROR_TEMPLATE_ID, |
||||
{ |
||||
filename: "[#{bulk_upload} filename]", |
||||
upload_timestamp: "[#{bulk_upload} upload_timestamp]", |
||||
lettings_or_sales: "[#{bulk_upload} lettings_or_sales]", |
||||
year_combo: "[#{bulk_upload} year_combo]", |
||||
errors_list: "[#{bulk_upload} errors_list]", |
||||
bulk_upload_link: "[#{bulk_upload} bulk_upload_link]", |
||||
}, |
||||
) |
||||
end |
||||
|
||||
def send_bulk_upload_failed_service_error_mail(user, bulk_upload) |
||||
send_email( |
||||
user.email, |
||||
BULK_UPLOAD_FAILED_SERVICE_ERROR_TEMPLATE_ID, |
||||
{ |
||||
filename: "[#{bulk_upload} filename]", |
||||
upload_timestamp: "[#{bulk_upload} upload_timestamp]", |
||||
lettings_or_sales: "[#{bulk_upload} lettings_or_sales]", |
||||
year_combo: "[#{bulk_upload} year_combo]", |
||||
bulk_upload_link: "[#{bulk_upload} bulk_upload_link]", |
||||
}, |
||||
) |
||||
end |
||||
|
||||
def send_bulk_upload_with_errors_mail(user, bulk_upload) |
||||
send_email( |
||||
user.email, |
||||
BULK_UPLOAD_WITH_ERRORS_TEMPLATE_ID, |
||||
{ |
||||
title: "[#{bulk_upload} title]", |
||||
filename: "[#{bulk_upload} filename]", |
||||
upload_timestamp: "[#{bulk_upload} upload_timestamp]", |
||||
error_description: "[#{bulk_upload} error_description]", |
||||
summary_report_link: "[#{bulk_upload} summary_report_link]", |
||||
}, |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,17 @@
|
||||
class Form::Sales::Pages::DepositAndMortgageValueCheck < ::Form::Page |
||||
def initialize(id, hsh, subsection) |
||||
super |
||||
@depends_on = [ |
||||
{ |
||||
"mortgage_plus_deposit_less_than_discounted_value?" => true, |
||||
}, |
||||
] |
||||
@informative_text = {} |
||||
end |
||||
|
||||
def questions |
||||
@questions ||= [ |
||||
Form::Sales::Questions::DepositAndMortgageValueCheck.new(nil, nil, self), |
||||
] |
||||
end |
||||
end |
@ -0,0 +1,21 @@
|
||||
class Form::Sales::Pages::MonthlyChargesValueCheck < ::Form::Page |
||||
def initialize(id, hsh, subsection) |
||||
super |
||||
@depends_on = [ |
||||
{ |
||||
"monthly_charges_over_soft_max?" => true, |
||||
}, |
||||
] |
||||
@title_text = { |
||||
"translation" => "soft_validations.monthly_charges_over_soft_max.title_text", |
||||
"arguments" => [], |
||||
} |
||||
@informative_text = {} |
||||
end |
||||
|
||||
def questions |
||||
@questions ||= [ |
||||
Form::Sales::Questions::MonthlyChargesValueCheck.new(nil, nil, self), |
||||
] |
||||
end |
||||
end |
@ -0,0 +1,27 @@
|
||||
class Form::Sales::Pages::StaircaseBoughtValueCheck < ::Form::Page |
||||
def initialize(id, hsh, subsection) |
||||
super |
||||
@id = "staircase_bought_value_check" |
||||
@depends_on = [ |
||||
{ |
||||
"staircase_bought_above_fifty?" => true, |
||||
}, |
||||
] |
||||
@title_text = { |
||||
"translation" => "soft_validations.staircase_bought_seems_high", |
||||
"arguments" => [ |
||||
{ |
||||
"key" => "stairbought", |
||||
"i18n_template" => "percentage", |
||||
}, |
||||
], |
||||
} |
||||
@informative_text = {} |
||||
end |
||||
|
||||
def questions |
||||
@questions ||= [ |
||||
Form::Sales::Questions::StaircaseBoughtValueCheck.new(nil, nil, self), |
||||
] |
||||
end |
||||
end |
@ -0,0 +1,23 @@
|
||||
class Form::Sales::Questions::DepositAndMortgageValueCheck < ::Form::Question |
||||
def initialize(id, hsh, page) |
||||
super |
||||
@id = "deposit_and_mortgage_value_check" |
||||
@check_answer_label = "Deposit and mortgage against discount confirmation" |
||||
@header = "Are you sure? Mortgage and deposit usually equal or are more than (value - discount)" |
||||
@type = "interruption_screen" |
||||
@answer_options = { |
||||
"0" => { "value" => "Yes" }, |
||||
"1" => { "value" => "No" }, |
||||
} |
||||
@hidden_in_check_answers = { |
||||
"depends_on" => [ |
||||
{ |
||||
"deposit_and_mortgage_value_check" => 0, |
||||
}, |
||||
{ |
||||
"deposit_and_mortgage_value_check" => 1, |
||||
}, |
||||
], |
||||
} |
||||
end |
||||
end |
@ -0,0 +1,23 @@
|
||||
class Form::Sales::Questions::MonthlyChargesValueCheck < ::Form::Question |
||||
def initialize(id, hsh, page) |
||||
super |
||||
@id = "monthly_charges_value_check" |
||||
@check_answer_label = "Monthly charges confirmation" |
||||
@header = "Are you sure this is correct?" |
||||
@type = "interruption_screen" |
||||
@answer_options = { |
||||
"0" => { "value" => "Yes" }, |
||||
"1" => { "value" => "No" }, |
||||
} |
||||
@hidden_in_check_answers = { |
||||
"depends_on" => [ |
||||
{ |
||||
"monthly_charges_value_check" => 0, |
||||
}, |
||||
{ |
||||
"monthly_charges_value_check" => 1, |
||||
}, |
||||
], |
||||
} |
||||
end |
||||
end |
@ -0,0 +1,23 @@
|
||||
class Form::Sales::Questions::StaircaseBoughtValueCheck < ::Form::Question |
||||
def initialize(id, hsh, page) |
||||
super |
||||
@id = "staircase_bought_value_check" |
||||
@check_answer_label = "Percentage bought confirmation" |
||||
@header = "Are you sure this is correct?" |
||||
@type = "interruption_screen" |
||||
@answer_options = { |
||||
"0" => { "value" => "Yes" }, |
||||
"1" => { "value" => "No" }, |
||||
} |
||||
@hidden_in_check_answers = { |
||||
"depends_on" => [ |
||||
{ |
||||
"staircase_bought_value_check" => 0, |
||||
}, |
||||
{ |
||||
"staircase_bought_value_check" => 1, |
||||
}, |
||||
], |
||||
} |
||||
end |
||||
end |
@ -0,0 +1,9 @@
|
||||
module Validations::Sales::SetupValidations |
||||
def validate_saledate(record) |
||||
return unless record.saledate |
||||
|
||||
unless Time.zone.local(2022, 4, 1) <= record.saledate && record.saledate < Time.zone.local(2023, 4, 1) |
||||
record.errors.add :saledate, I18n.t("validations.setup.saledate.financial_year") |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,22 @@
|
||||
<div class="govuk-grid-row"> |
||||
<div class="govuk-grid-column-two-thirds"> |
||||
<span class="govuk-caption-l">Bulk upload for lettings (<%= @bulk_upload.year_combo %>)</span> |
||||
<h1 class="govuk-heading-l">Correct data export and reupload</h1> |
||||
|
||||
<p class="govuk-body-l"> |
||||
We noticed that you have a lot of similar errors for some questions. You can download the specification which we reference below to understand how to correct the data. Once you have fixed these errors you can upload again. |
||||
</p> |
||||
</div> |
||||
</div> |
||||
|
||||
<%= render BulkUploadErrorSummaryTableComponent.new(bulk_upload: @bulk_upload) %> |
||||
|
||||
<div class="govuk-grid-row"> |
||||
<div class="govuk-grid-column-two-thirds"> |
||||
<p class="govuk-body"> |
||||
You also have other errors in your file which you can either fix them in the CSV file or you can reupload and fix on CORE. <%= govuk_link_to "View the full report", bulk_upload_lettings_result_path(@bulk_upload) %> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
|
||||
<%= govuk_button_link_to "Upload your file again", start_bulk_upload_lettings_logs_path %> |
@ -0,0 +1,5 @@
|
||||
class AddDepositAndMortgageValueCheckToSalesLogs < ActiveRecord::Migration[7.0] |
||||
def change |
||||
add_column :sales_logs, :deposit_and_mortgage_value_check, :integer |
||||
end |
||||
end |
@ -0,0 +1,5 @@
|
||||
class AddStaircaseBoughtValueCheckToSalesLog < ActiveRecord::Migration[7.0] |
||||
def change |
||||
add_column :sales_logs, :staircase_bought_value_check, :integer |
||||
end |
||||
end |
@ -0,0 +1,7 @@
|
||||
class AddMonthlyChargesValueCheck < ActiveRecord::Migration[7.0] |
||||
def change |
||||
change_table :sales_logs, bulk: true do |t| |
||||
t.column :monthly_charges_value_check, :integer |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,81 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe BulkUploadErrorSummaryTableComponent, type: :component do |
||||
subject(:component) { described_class.new(bulk_upload:) } |
||||
|
||||
let(:bulk_upload) { create(:bulk_upload) } |
||||
|
||||
context "when no errors" do |
||||
it "does not renders any rows" do |
||||
result = render_inline(component) |
||||
expect(result).not_to have_selector("tbody tr") |
||||
end |
||||
end |
||||
|
||||
context "when there are 2 independent errors" do |
||||
let!(:error_2) { create(:bulk_upload_error, bulk_upload:, col: "B", row: 2) } |
||||
let!(:error_1) { create(:bulk_upload_error, bulk_upload:, col: "A", row: 1) } |
||||
|
||||
it "renders rows for each error" do |
||||
result = render_inline(component) |
||||
expect(result).to have_selector("tbody tr", count: 2) |
||||
end |
||||
|
||||
it "renders rows by col order" do |
||||
result = render_inline(component) |
||||
order = result.css("tbody tr td:nth-of-type(1)").map(&:content) |
||||
expect(order).to eql(%w[A B]) |
||||
end |
||||
|
||||
it "render correct data" do |
||||
result = render_inline(component) |
||||
|
||||
row_1 = result.css("tbody tr:nth-of-type(1) td").map(&:content) |
||||
|
||||
expect(row_1).to eql([ |
||||
"A", |
||||
"1", |
||||
BulkUpload::Lettings::Validator.question_for_field(error_1.field.to_sym), |
||||
error_1.error, |
||||
error_1.field, |
||||
]) |
||||
|
||||
row_2 = result.css("tbody tr:nth-of-type(2) td").map(&:content) |
||||
|
||||
expect(row_2).to eql([ |
||||
"B", |
||||
"1", |
||||
BulkUpload::Lettings::Validator.question_for_field(error_2.field.to_sym), |
||||
error_2.error, |
||||
error_2.field, |
||||
]) |
||||
end |
||||
end |
||||
|
||||
context "when there are 2 grouped errors" do |
||||
let!(:error_1) { create(:bulk_upload_error, bulk_upload:, col: "A", row: 1, field: "field_1") } |
||||
|
||||
before do |
||||
create(:bulk_upload_error, bulk_upload:, col: "A", row: 2, field: "field_1") |
||||
end |
||||
|
||||
it "renders 1 row combining the errors" do |
||||
result = render_inline(component) |
||||
expect(result).to have_selector("tbody tr", count: 1) |
||||
end |
||||
|
||||
it "render correct data" do |
||||
result = render_inline(component) |
||||
|
||||
row_1 = result.css("tbody tr:nth-of-type(1) td").map(&:content) |
||||
|
||||
expect(row_1).to eql([ |
||||
"A", |
||||
"2", |
||||
BulkUpload::Lettings::Validator.question_for_field(error_1.field.to_sym), |
||||
error_1.error, |
||||
error_1.field, |
||||
]) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,48 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe Form::Sales::Pages::MonthlyChargesValueCheck, type: :model do |
||||
subject(:page) { described_class.new(page_id, page_definition, subsection) } |
||||
|
||||
let(:page_id) { "monthly_charges_value_check" } |
||||
let(:page_definition) { nil } |
||||
let(:subsection) { instance_double(Form::Subsection) } |
||||
|
||||
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[monthly_charges_value_check]) |
||||
end |
||||
|
||||
it "has the correct id" do |
||||
expect(page.id).to eq("monthly_charges_value_check") |
||||
end |
||||
|
||||
it "has the correct header" do |
||||
expect(page.header).to be_nil |
||||
end |
||||
|
||||
it "has correct depends_on" do |
||||
expect(page.depends_on).to eq([ |
||||
{ |
||||
"monthly_charges_over_soft_max?" => true, |
||||
}, |
||||
]) |
||||
end |
||||
|
||||
it "is interruption screen page" do |
||||
expect(page.interruption_screen?).to eq(true) |
||||
end |
||||
|
||||
it "has correct title_text" do |
||||
expect(page.title_text).to eq({ |
||||
"translation" => "soft_validations.monthly_charges_over_soft_max.title_text", |
||||
"arguments" => [], |
||||
}) |
||||
end |
||||
|
||||
it "has correct informative_text" do |
||||
expect(page.informative_text).to eq({}) |
||||
end |
||||
end |
@ -0,0 +1,57 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe Form::Sales::Questions::MonthlyChargesValueCheck, type: :model do |
||||
subject(:question) { described_class.new(question_id, question_definition, page) } |
||||
|
||||
let(:question_id) { "monthly_charges_value_check" } |
||||
let(:question_definition) { nil } |
||||
let(:page) { instance_double(Form::Page) } |
||||
|
||||
it "has correct page" do |
||||
expect(question.page).to eq(page) |
||||
end |
||||
|
||||
it "has the correct id" do |
||||
expect(question.id).to eq("monthly_charges_value_check") |
||||
end |
||||
|
||||
it "has the correct header" do |
||||
expect(question.header).to eq("Are you sure this is correct?") |
||||
end |
||||
|
||||
it "has the correct check_answer_label" do |
||||
expect(question.check_answer_label).to eq("Monthly charges confirmation") |
||||
end |
||||
|
||||
it "has the correct type" do |
||||
expect(question.type).to eq("interruption_screen") |
||||
end |
||||
|
||||
it "is not marked as derived" do |
||||
expect(question.derived?).to be false |
||||
end |
||||
|
||||
it "has the correct hint" do |
||||
expect(question.hint_text).to be_nil |
||||
end |
||||
|
||||
it "has the correct answer_options" do |
||||
expect(question.answer_options).to eq({ |
||||
"0" => { "value" => "Yes" }, |
||||
"1" => { "value" => "No" }, |
||||
}) |
||||
end |
||||
|
||||
it "has the correct hidden_in_check_answers" do |
||||
expect(question.hidden_in_check_answers).to eq({ |
||||
"depends_on" => [ |
||||
{ |
||||
"monthly_charges_value_check" => 0, |
||||
}, |
||||
{ |
||||
"monthly_charges_value_check" => 1, |
||||
}, |
||||
], |
||||
}) |
||||
end |
||||
end |
@ -0,0 +1,49 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe Validations::Sales::SetupValidations do |
||||
subject(:setup_validator) { validator_class.new } |
||||
|
||||
let(:validator_class) { Class.new { include Validations::Sales::SetupValidations } } |
||||
|
||||
describe "#validate_saledate" do |
||||
context "when saledate is blank" do |
||||
let(:record) { FactoryBot.build(:sales_log, saledate: nil) } |
||||
|
||||
it "does not add an error" do |
||||
setup_validator.validate_saledate(record) |
||||
|
||||
expect(record.errors).to be_empty |
||||
end |
||||
end |
||||
|
||||
context "when saledate is in the 22/23 financial year" do |
||||
let(:record) { FactoryBot.build(:sales_log, saledate: Time.zone.local(2023, 1, 1)) } |
||||
|
||||
it "does not add an error" do |
||||
setup_validator.validate_saledate(record) |
||||
|
||||
expect(record.errors).to be_empty |
||||
end |
||||
end |
||||
|
||||
context "when saledate is before the 22/23 financial year" do |
||||
let(:record) { FactoryBot.build(:sales_log, saledate: Time.zone.local(2022, 1, 1)) } |
||||
|
||||
it "adds error" do |
||||
setup_validator.validate_saledate(record) |
||||
|
||||
expect(record.errors[:saledate]).to include(I18n.t("validations.setup.saledate.financial_year")) |
||||
end |
||||
end |
||||
|
||||
context "when saledate is after the 22/23 financial year" do |
||||
let(:record) { FactoryBot.build(:sales_log, saledate: Time.zone.local(2023, 4, 1)) } |
||||
|
||||
it "adds error" do |
||||
setup_validator.validate_saledate(record) |
||||
|
||||
expect(record.errors[:saledate]).to include(I18n.t("validations.setup.saledate.financial_year")) |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue