diff --git a/app/components/bulk_upload_error_summary_table_component.html.erb b/app/components/bulk_upload_error_summary_table_component.html.erb new file mode 100644 index 000000000..f1749ee92 --- /dev/null +++ b/app/components/bulk_upload_error_summary_table_component.html.erb @@ -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 %> diff --git a/app/components/bulk_upload_error_summary_table_component.rb b/app/components/bulk_upload_error_summary_table_component.rb new file mode 100644 index 000000000..ac236af4d --- /dev/null +++ b/app/components/bulk_upload_error_summary_table_component.rb @@ -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 diff --git a/app/controllers/bulk_upload_lettings_results_controller.rb b/app/controllers/bulk_upload_lettings_results_controller.rb index 1c5342cd8..6f92c0375 100644 --- a/app/controllers/bulk_upload_lettings_results_controller.rb +++ b/app/controllers/bulk_upload_lettings_results_controller.rb @@ -19,6 +19,10 @@ class BulkUploadLettingsResultsController < ApplicationController end end + def summary + @bulk_upload = current_user.bulk_uploads.lettings.find(params[:id]) + end + private def reset_logs_filters diff --git a/app/mailers/bulk_upload_mailer.rb b/app/mailers/bulk_upload_mailer.rb new file mode 100644 index 000000000..5ab0d70b0 --- /dev/null +++ b/app/mailers/bulk_upload_mailer.rb @@ -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 diff --git a/app/models/bulk_upload.rb b/app/models/bulk_upload.rb index 7933ac204..3d0ef93f0 100644 --- a/app/models/bulk_upload.rb +++ b/app/models/bulk_upload.rb @@ -3,7 +3,8 @@ class BulkUpload < ApplicationRecord belongs_to :user - has_many :bulk_upload_errors + has_many :bulk_upload_errors, dependent: :destroy + has_many :lettings_logs has_many :sales_logs diff --git a/app/models/derived_variables/sales_log_variables.rb b/app/models/derived_variables/sales_log_variables.rb index 245361a80..16fb6ebd7 100644 --- a/app/models/derived_variables/sales_log_variables.rb +++ b/app/models/derived_variables/sales_log_variables.rb @@ -15,6 +15,9 @@ module DerivedVariables::SalesLogVariables if mscharge_known.present? && mscharge_known.zero? self.mscharge = 0 end + if mortgage_not_used? + self.mortgage = 0 + end self.pcode1, self.pcode2 = postcode_full.split(" ") if postcode_full.present? self.totchild = total_child self.totadult = total_adult + total_elder diff --git a/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb b/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb new file mode 100644 index 000000000..338889030 --- /dev/null +++ b/app/models/form/sales/pages/deposit_and_mortgage_value_check.rb @@ -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 diff --git a/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb b/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb new file mode 100644 index 000000000..9198af18a --- /dev/null +++ b/app/models/form/sales/pages/old_persons_shared_ownership_value_check.rb @@ -0,0 +1,21 @@ +class Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck < ::Form::Page + def initialize(id, hsh, subsection) + super + @depends_on = [ + { + "buyers_age_for_old_persons_shared_ownership_invalid?" => true, + }, + ] + @title_text = { + "translation" => "soft_validations.old_persons_shared_ownership", + "arguments" => [], + } + @informative_text = {} + end + + def questions + @questions ||= [ + Form::Sales::Questions::OldPersonsSharedOwnershipValueCheck.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb b/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb new file mode 100644 index 000000000..907968028 --- /dev/null +++ b/app/models/form/sales/pages/shared_ownership_deposit_value_check.rb @@ -0,0 +1,27 @@ +class Form::Sales::Pages::SharedOwnershipDepositValueCheck < ::Form::Page + def initialize(id, hsh, subsection) + super + @depends_on = [ + { + "shared_ownership_deposit_invalid?" => true, + }, + ] + @informative_text = {} + @title_text = { + "translation" => "soft_validations.shared_owhership_deposit.title_text", + "arguments" => [ + { + "key" => "expected_shared_ownership_deposit_value", + "label" => false, + "i18n_template" => "expected_shared_ownership_deposit_value", + }, + ], + } + end + + def questions + @questions ||= [ + Form::Sales::Questions::SharedOwnershipDepositValueCheck.new(nil, nil, self), + ] + end +end diff --git a/app/models/form/sales/pages/staircase_bought_value_check.rb b/app/models/form/sales/pages/staircase_bought_value_check.rb new file mode 100644 index 000000000..b7b8178ff --- /dev/null +++ b/app/models/form/sales/pages/staircase_bought_value_check.rb @@ -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 diff --git a/app/models/form/sales/questions/deposit_and_mortgage_value_check.rb b/app/models/form/sales/questions/deposit_and_mortgage_value_check.rb new file mode 100644 index 000000000..263cf4342 --- /dev/null +++ b/app/models/form/sales/questions/deposit_and_mortgage_value_check.rb @@ -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 diff --git a/app/models/form/sales/questions/old_persons_shared_ownership_value_check.rb b/app/models/form/sales/questions/old_persons_shared_ownership_value_check.rb new file mode 100644 index 000000000..c869dd3be --- /dev/null +++ b/app/models/form/sales/questions/old_persons_shared_ownership_value_check.rb @@ -0,0 +1,23 @@ +class Form::Sales::Questions::OldPersonsSharedOwnershipValueCheck < ::Form::Question + def initialize(id, hsh, page) + super + @id = "old_persons_shared_ownership_value_check" + @check_answer_label = "Shared ownership 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" => [ + { + "old_persons_shared_ownership_value_check" => 0, + }, + { + "old_persons_shared_ownership_value_check" => 1, + }, + ], + } + end +end diff --git a/app/models/form/sales/questions/shared_ownership_deposit_value_check.rb b/app/models/form/sales/questions/shared_ownership_deposit_value_check.rb new file mode 100644 index 000000000..3f8111881 --- /dev/null +++ b/app/models/form/sales/questions/shared_ownership_deposit_value_check.rb @@ -0,0 +1,23 @@ +class Form::Sales::Questions::SharedOwnershipDepositValueCheck < ::Form::Question + def initialize(id, hsh, page) + super + @id = "shared_ownership_deposit_value_check" + @check_answer_label = "Shared ownership deposit confirmation" + @type = "interruption_screen" + @header = "Are you sure this is correct?" + @answer_options = { + "0" => { "value" => "Yes" }, + "1" => { "value" => "No" }, + } + @hidden_in_check_answers = { + "depends_on" => [ + { + "shared_ownership_deposit_value_check" => 0, + }, + { + "shared_ownership_deposit_value_check" => 1, + }, + ], + } + end +end diff --git a/app/models/form/sales/questions/staircase_bought.rb b/app/models/form/sales/questions/staircase_bought.rb index bdb5ac965..385cfb782 100644 --- a/app/models/form/sales/questions/staircase_bought.rb +++ b/app/models/form/sales/questions/staircase_bought.rb @@ -8,6 +8,6 @@ class Form::Sales::Questions::StaircaseBought < ::Form::Question @width = 5 @min = 0 @max = 100 - @suffix = " percent" + @suffix = "%" end end diff --git a/app/models/form/sales/questions/staircase_bought_value_check.rb b/app/models/form/sales/questions/staircase_bought_value_check.rb new file mode 100644 index 000000000..65fe02e66 --- /dev/null +++ b/app/models/form/sales/questions/staircase_bought_value_check.rb @@ -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 diff --git a/app/models/form/sales/questions/staircase_owned.rb b/app/models/form/sales/questions/staircase_owned.rb index 6f9e13b14..7e6dab5d6 100644 --- a/app/models/form/sales/questions/staircase_owned.rb +++ b/app/models/form/sales/questions/staircase_owned.rb @@ -8,6 +8,6 @@ class Form::Sales::Questions::StaircaseOwned < ::Form::Question @width = 5 @min = 0 @max = 100 - @suffix = " percent" + @suffix = "%" end end diff --git a/app/models/form/sales/subsections/discounted_ownership_scheme.rb b/app/models/form/sales/subsections/discounted_ownership_scheme.rb index 2ab47e729..925f651e8 100644 --- a/app/models/form/sales/subsections/discounted_ownership_scheme.rb +++ b/app/models/form/sales/subsections/discounted_ownership_scheme.rb @@ -14,9 +14,11 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::AboutPriceNotRtb.new(nil, nil, self), Form::Sales::Pages::GrantValueCheck.new(nil, nil, self), Form::Sales::Pages::PurchasePrice.new("purchase_price_discounted_ownership", nil, self), + Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_value_and_discount", nil, self), Form::Sales::Pages::Mortgageused.new("mortgage_used_discounted_ownership", nil, self), Form::Sales::Pages::MortgageAmount.new("mortgage_amount_discounted_ownership", nil, self), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_mortgage_value_check", nil, self), + Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_mortgage", nil, self), Form::Sales::Pages::MortgageLender.new("mortgage_lender_discounted_ownership", nil, self), Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_discounted_ownership", nil, self), Form::Sales::Pages::MortgageLength.new("mortgage_length_discounted_ownership", nil, self), @@ -25,6 +27,7 @@ class Form::Sales::Subsections::DiscountedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_discounted_ownership", nil, self), Form::Sales::Pages::ExtraBorrowingValueCheck.new("extra_borrowing_deposit_value_check", nil, self), Form::Sales::Pages::DepositValueCheck.new("discounted_ownership_deposit_value_check", nil, self), + Form::Sales::Pages::DepositAndMortgageValueCheck.new("discounted_ownership_deposit_and_mortgage_value_check_after_deposit", nil, self), Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_discounted_ownership", nil, self), ] end diff --git a/app/models/form/sales/subsections/household_characteristics.rb b/app/models/form/sales/subsections/household_characteristics.rb index b4c8badcf..bfccf0fa2 100644 --- a/app/models/form/sales/subsections/household_characteristics.rb +++ b/app/models/form/sales/subsections/household_characteristics.rb @@ -13,6 +13,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection Form::Sales::Pages::Age1.new(nil, nil, self), Form::Sales::Pages::RetirementValueCheck.new("age_1_retirement_value_check", nil, self, person_index: 1), Form::Sales::Pages::RetirementValueCheck.new("age_1_retirement_value_check_joint_purchase", nil, self, person_index: 1), + Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_1_old_persons_shared_ownership_value_check", nil, self), Form::Sales::Pages::GenderIdentity1.new(nil, nil, self), Form::Sales::Pages::RetirementValueCheck.new("gender_1_retirement_value_check", nil, self, person_index: 1), Form::Sales::Pages::RetirementValueCheck.new("gender_1_retirement_value_check_joint_purchase", nil, self, person_index: 1), @@ -31,6 +32,7 @@ class Form::Sales::Subsections::HouseholdCharacteristics < ::Form::Subsection Form::Sales::Pages::Buyer2RelationshipToBuyer1.new(nil, nil, self), Form::Sales::Pages::Age2.new(nil, nil, self), Form::Sales::Pages::RetirementValueCheck.new("age_2_retirement_value_check_joint_purchase", nil, self, person_index: 2), + Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("age_2_old_persons_shared_ownership_value_check", nil, self), Form::Sales::Pages::GenderIdentity2.new(nil, nil, self), Form::Sales::Pages::RetirementValueCheck.new("gender_2_retirement_value_check_joint_purchase", nil, self, person_index: 2), Form::Sales::Pages::Buyer2WorkingSituation.new(nil, nil, self), diff --git a/app/models/form/sales/subsections/household_needs.rb b/app/models/form/sales/subsections/household_needs.rb index b5d4d528e..a419cff95 100644 --- a/app/models/form/sales/subsections/household_needs.rb +++ b/app/models/form/sales/subsections/household_needs.rb @@ -2,7 +2,7 @@ class Form::Sales::Subsections::HouseholdNeeds < ::Form::Subsection def initialize(id, hsh, section) super @id = "household_needs" - @label = "Household needs" + @label = "Other household information" @depends_on = [{ "setup_completed?" => true }] end diff --git a/app/models/form/sales/subsections/setup.rb b/app/models/form/sales/subsections/setup.rb index d2261a33b..cb147e9c2 100644 --- a/app/models/form/sales/subsections/setup.rb +++ b/app/models/form/sales/subsections/setup.rb @@ -15,6 +15,7 @@ class Form::Sales::Subsections::Setup < ::Form::Subsection Form::Sales::Pages::SharedOwnershipType.new(nil, nil, self), Form::Sales::Pages::DiscountedOwnershipType.new(nil, nil, self), Form::Sales::Pages::OutrightOwnershipType.new(nil, nil, self), + Form::Sales::Pages::OldPersonsSharedOwnershipValueCheck.new("ownership_type_old_persons_shared_ownership_value_check", nil, self), Form::Sales::Pages::BuyerCompany.new(nil, nil, self), Form::Sales::Pages::BuyerLive.new(nil, nil, self), Form::Sales::Pages::JointPurchase.new(nil, nil, self), diff --git a/app/models/form/sales/subsections/shared_ownership_scheme.rb b/app/models/form/sales/subsections/shared_ownership_scheme.rb index 8c05cd82d..0e2e3fdda 100644 --- a/app/models/form/sales/subsections/shared_ownership_scheme.rb +++ b/app/models/form/sales/subsections/shared_ownership_scheme.rb @@ -11,6 +11,7 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::LivingBeforePurchase.new("living_before_purchase_shared_ownership", nil, self), Form::Sales::Pages::Staircase.new(nil, nil, self), Form::Sales::Pages::AboutStaircase.new(nil, nil, self), + Form::Sales::Pages::StaircaseBoughtValueCheck.new(nil, nil, self), Form::Sales::Pages::Resale.new(nil, nil, self), Form::Sales::Pages::ExchangeDate.new(nil, nil, self), Form::Sales::Pages::HandoverDate.new(nil, nil, self), @@ -21,15 +22,18 @@ class Form::Sales::Subsections::SharedOwnershipScheme < ::Form::Subsection Form::Sales::Pages::PreviousPropertyType.new(nil, nil, self), Form::Sales::Pages::PreviousTenure.new(nil, nil, self), Form::Sales::Pages::AboutPriceSharedOwnership.new(nil, nil, self), + Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_equity_value_check", nil, self), Form::Sales::Pages::Mortgageused.new("mortgage_used_shared_ownership", nil, self), Form::Sales::Pages::MortgageAmount.new("mortgage_amount_shared_ownership", nil, self), + Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_mortgage_amount_value_check", nil, self), Form::Sales::Pages::MortgageLender.new("mortgage_lender_shared_ownership", nil, self), Form::Sales::Pages::MortgageLenderOther.new("mortgage_lender_other_shared_ownership", nil, self), Form::Sales::Pages::MortgageLength.new("mortgage_length_shared_ownership", nil, self), Form::Sales::Pages::ExtraBorrowing.new("extra_borrowing_shared_ownership", nil, self), Form::Sales::Pages::AboutDepositWithDiscount.new(nil, nil, self), Form::Sales::Pages::AboutDepositWithoutDiscount.new("about_deposit_shared_ownership", nil, self), - Form::Sales::Pages::DepositValueCheck.new("shared_ownership_deposit_value_check", nil, self), + Form::Sales::Pages::DepositValueCheck.new("deposit_value_check", nil, self), + Form::Sales::Pages::SharedOwnershipDepositValueCheck.new("shared_ownership_deposit_value_check", nil, self), Form::Sales::Pages::MonthlyRent.new(nil, nil, self), Form::Sales::Pages::LeaseholdCharges.new("leasehold_charges_shared_ownership", nil, self), ] diff --git a/app/models/log.rb b/app/models/log.rb index fcde08bdb..45f3e9607 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -108,7 +108,6 @@ private return unless form form.reset_not_routed_questions(self) - reset_created_by! end diff --git a/app/models/sales_log.rb b/app/models/sales_log.rb index aa3eb1c09..7a57be8ee 100644 --- a/app/models/sales_log.rb +++ b/app/models/sales_log.rb @@ -1,4 +1,5 @@ class SalesLogValidator < ActiveModel::Validator + include Validations::Sales::SetupValidations include Validations::Sales::HouseholdValidations include Validations::Sales::PropertyValidations include Validations::SharedValidations @@ -23,18 +24,18 @@ class SalesLog < Log has_paper_trail validates_with SalesLogValidator - before_validation :set_derived_fields! before_validation :reset_invalidated_dependent_fields! before_validation :process_postcode_changes!, if: :postcode_full_changed? before_validation :process_previous_postcode_changes!, if: :ppostcode_full_changed? before_validation :reset_location_fields!, unless: :postcode_known? before_validation :reset_previous_location_fields!, unless: :previous_postcode_known? + before_validation :set_derived_fields! scope :filter_by_year, ->(year) { where(saledate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) } scope :search_by, ->(param) { filter_by_id(param) } scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org) } - OPTIONAL_FIELDS = %w[purchid].freeze + OPTIONAL_FIELDS = %w[purchid old_persons_shared_ownership_value_check].freeze RETIREMENT_AGES = { "M" => 65, "F" => 60, "X" => 65 }.freeze def startdate @@ -159,6 +160,12 @@ class SalesLog < Log end end + def expected_shared_ownership_deposit_value + return unless value && equity + + (value * equity / 100).round(2) + end + def process_postcode(postcode, postcode_known_key, la_inferred_key, la_key) return if postcode.blank? @@ -211,4 +218,18 @@ class SalesLog < Log def old_persons_shared_ownership? type == 24 end + + def shared_owhership_scheme? + ownershipsch == 1 + end + + def buyers_age_for_old_persons_shared_ownership_invalid? + return unless old_persons_shared_ownership? + + (joint_purchase? && ages_unknown_or_under_64?([1, 2])) || (not_joint_purchase? && ages_unknown_or_under_64?([1])) + end + + def ages_unknown_or_under_64?(person_indexes) + person_indexes.all? { |person_num| self["age#{person_num}"].present? && self["age#{person_num}"] < 64 || self["age#{person_num}_known"] == 1 } + end end diff --git a/app/models/validations/sales/financial_validations.rb b/app/models/validations/sales/financial_validations.rb index 116ecf301..df7dc8df5 100644 --- a/app/models/validations/sales/financial_validations.rb +++ b/app/models/validations/sales/financial_validations.rb @@ -3,11 +3,19 @@ module Validations::Sales::FinancialValidations # or 'validate_' to run on submit as well def validate_income1(record) - if record.ecstat1 && record.income1 && record.ownershipsch == 1 + if record.ecstat1 && record.income1 && record.la && record.ownershipsch == 1 if record.london_property? record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000 + record.errors.add :ecstat1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000 + record.errors.add :ownershipsch, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000 + record.errors.add :la, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000 + record.errors.add :postcode_full, I18n.t("validations.financial.income1.over_hard_max", hard_max: 90_000) if record.income1 > 90_000 elsif record.income1 > 80_000 record.errors.add :income1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) + record.errors.add :ecstat1, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) + record.errors.add :ownershipsch, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) + record.errors.add :la, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) if record.income1 > 80_000 + record.errors.add :postcode_full, I18n.t("validations.financial.income1.over_hard_max", hard_max: 80_000) if record.income1 > 80_000 end end end @@ -19,4 +27,21 @@ module Validations::Sales::FinancialValidations record.errors.add :cashdis, I18n.t("validations.financial.cash_discount_invalid") end end + + def validate_percentage_bought_not_greater_than_percentage_owned(record) + return unless record.stairbought && record.stairowned + + if record.stairbought > record.stairowned + record.errors.add :stairowned, I18n.t("validations.financial.staircasing.percentage_bought_must_be_greater_than_percentage_owned") + end + end + + def validate_percentage_owned_not_too_much_if_older_person(record) + return unless record.old_persons_shared_ownership? && record.stairowned + + if record.stairowned > 75 + record.errors.add :stairowned, I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75") + record.errors.add :type, I18n.t("validations.financial.staircasing.older_person_percentage_owned_maximum_75") + end + end end diff --git a/app/models/validations/sales/household_validations.rb b/app/models/validations/sales/household_validations.rb index c7af23299..f9318ab0f 100644 --- a/app/models/validations/sales/household_validations.rb +++ b/app/models/validations/sales/household_validations.rb @@ -10,20 +10,6 @@ module Validations::Sales::HouseholdValidations shared_validate_partner_count(record, 6) end - def validate_buyers_age_for_old_persons_shared_ownership(record) - if record.old_persons_shared_ownership? - if record.joint_purchase? && ages_unknown_or_under_64?(record, [1, 2]) - record.errors.add :age1, I18n.t("validations.household.old_persons_shared_ownership") - record.errors.add :age2, I18n.t("validations.household.old_persons_shared_ownership") - record.errors.add :type, I18n.t("validations.household.old_persons_shared_ownership") - end - if record.not_joint_purchase? && ages_unknown_or_under_64?(record, [1]) - record.errors.add :age1, I18n.t("validations.household.old_persons_shared_ownership") - record.errors.add :type, I18n.t("validations.household.old_persons_shared_ownership") - end - end - end - def validate_previous_postcode(record) return unless record.postcode_full && record.ppostcode_full && record.discounted_ownership_sale? @@ -108,8 +94,4 @@ private def tenant_is_economic_child?(economic_status) economic_status == 9 end - - def ages_unknown_or_under_64?(record, person_indexes) - person_indexes.all? { |person_num| record["age#{person_num}"].present? && record["age#{person_num}"] < 64 || record["age#{person_num}_known"] == 1 } - end end diff --git a/app/models/validations/sales/sale_information_validations.rb b/app/models/validations/sales/sale_information_validations.rb index c42c42ccc..24fdff3ea 100644 --- a/app/models/validations/sales/sale_information_validations.rb +++ b/app/models/validations/sales/sale_information_validations.rb @@ -3,7 +3,7 @@ module Validations::Sales::SaleInformationValidations return if record.saledate.blank? || record.hodate.blank? unless record.saledate > record.hodate - record.errors.add :hodate, "Practical completion or handover date must be before exchange date" + record.errors.add :hodate, I18n.t("validations.sale_information.hodate.must_be_before_exdate") end end @@ -23,11 +23,15 @@ module Validations::Sales::SaleInformationValidations def validate_exchange_date(record) return unless record.exdate && record.saledate - record.errors.add(:exdate, I18n.t("validations.sale_information.exdate.must_be_before_saledate")) if record.exdate > record.saledate - - return if (record.saledate.to_date - record.exdate.to_date).to_i / 365 < 1 + if record.exdate > record.saledate + record.errors.add :exdate, I18n.t("validations.sale_information.exdate.must_be_before_saledate") + record.errors.add :saledate, I18n.t("validations.sale_information.saledate.must_be_after_exdate") + end - record.errors.add(:exdate, I18n.t("validations.sale_information.exdate.must_be_less_than_1_year_from_saledate")) + if record.saledate - record.exdate >= 1.year + record.errors.add :exdate, I18n.t("validations.sale_information.exdate.must_be_less_than_1_year_from_saledate") + record.errors.add :saledate, I18n.t("validations.sale_information.saledate.must_be_less_than_1_year_from_exdate") + end end def validate_previous_property_unit_type(record) @@ -54,4 +58,13 @@ module Validations::Sales::SaleInformationValidations end end end + + def validate_basic_monthly_rent(record) + return unless record.mrent && record.ownershipsch && record.type + + if record.shared_owhership_scheme? && !record.old_persons_shared_ownership? && record.mrent > 9999 + record.errors.add :mrent, I18n.t("validations.sale_information.monthly_rent.higher_than_expected") + record.errors.add :type, I18n.t("validations.sale_information.monthly_rent.higher_than_expected") + end + end end diff --git a/app/models/validations/sales/setup_validations.rb b/app/models/validations/sales/setup_validations.rb new file mode 100644 index 000000000..0e7a759ee --- /dev/null +++ b/app/models/validations/sales/setup_validations.rb @@ -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 diff --git a/app/models/validations/sales/soft_validations.rb b/app/models/validations/sales/soft_validations.rb index 742e505a5..0bffe1234 100644 --- a/app/models/validations/sales/soft_validations.rb +++ b/app/models/validations/sales/soft_validations.rb @@ -13,6 +13,10 @@ module Validations::Sales::SoftValidations income1 < ALLOWED_INCOME_RANGES[ecstat1][:soft_min] end + def staircase_bought_above_fifty? + stairbought && stairbought > 50 + end + def mortgage_over_soft_max? return false unless mortgage && inc1mort && inc2mort return false if income1_used_for_mortgage? && income1.blank? || income2_used_for_mortgage? && income2.blank? @@ -43,6 +47,23 @@ module Validations::Sales::SoftValidations extrabor != 1 && mortgage + deposit > value - value * discount / 100 end + def shared_ownership_deposit_invalid? + return unless mortgage || mortgageused == 2 + return unless cashdis || !is_type_discount? + return unless deposit && value && equity + + cash_discount = cashdis || 0 + mortgage_value = mortgage || 0 + mortgage_value + deposit + cash_discount != value * equity / 100 + end + + def mortgage_plus_deposit_less_than_discounted_value? + return unless mortgage && deposit && value && discount + + discounted_value = value * (100 - discount) / 100 + mortgage + deposit < discounted_value + end + def hodate_3_years_or_more_saledate? return unless hodate && saledate diff --git a/app/services/bulk_upload/lettings/validator.rb b/app/services/bulk_upload/lettings/validator.rb index 8cfdee684..b96739836 100644 --- a/app/services/bulk_upload/lettings/validator.rb +++ b/app/services/bulk_upload/lettings/validator.rb @@ -164,6 +164,7 @@ class BulkUpload::Lettings::Validator property_ref: row_parser.field_100, row:, cell: "#{cols[field_number_for_attribute(error.attribute) - col_offset + 1]}#{row}", + col: cols[field_number_for_attribute(error.attribute) - col_offset + 1], ) 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 452926787..84374e26a 100644 --- a/app/views/bulk_upload_lettings_results/show.html.erb +++ b/app/views/bulk_upload_lettings_results/show.html.erb @@ -1,6 +1,10 @@ +<% content_for :before_content do %> + <%= govuk_back_link(text: "Back", href: summary_bulk_upload_lettings_result_path(@bulk_upload)) %> +<% end %> +
+ 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. +
++ 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) %> +
+