Browse Source

Merge branch 'main' into CLDC-3919-Sales-BU-mortage-percentage-rounding-error

pull/2987/head
Manny Dinssa 2 months ago committed by GitHub
parent
commit
eda21a67b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      app/models/lettings_log.rb
  2. 6
      app/models/validations/sales/financial_validations.rb
  3. 4
      app/models/validations/sales/sale_information_validations.rb
  4. 52
      app/services/bulk_upload/sales/year2025/row_parser.rb
  5. 5
      app/services/csv/lettings_log_csv_service.rb
  6. 2
      app/services/exports/user_export_service.rb
  7. 2
      app/services/merge/merge_organisations_service.rb
  8. 5
      db/migrate/20250305092900_add_values_updated_at_to_user.rb
  9. 3
      db/schema.rb
  10. 48
      spec/services/bulk_upload/sales/year2025/row_parser_spec.rb
  11. 21
      spec/services/exports/user_export_service_spec.rb
  12. 2
      spec/services/merge/merge_organisations_service_spec.rb

4
app/models/lettings_log.rb

@ -148,7 +148,7 @@ class LettingsLog < Log
OPTIONAL_FIELDS = %w[tenancycode propcode chcharge].freeze OPTIONAL_FIELDS = %w[tenancycode propcode chcharge].freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent", 4 => "Specified accommodation" }.freeze RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent", 4 => "Specified accommodation" }.freeze
HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze HAS_BENEFITS_OPTIONS = [1, 6, 8, 7].freeze
NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 1 => 52, 10 => 53 }.freeze NUM_OF_WEEKS_FROM_PERIOD = { 2 => 26, 3 => 13, 4 => 12, 5 => 50, 6 => 49, 7 => 48, 8 => 47, 9 => 46, 11 => 51, 1 => 52, 10 => 53 }.freeze
SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze SUFFIX_FROM_PERIOD = { 2 => "every 2 weeks", 3 => "every 4 weeks", 4 => "every month" }.freeze
DUPLICATE_LOG_ATTRIBUTES = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 ecstat1 tcharge household_charge chcharge].freeze DUPLICATE_LOG_ATTRIBUTES = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 ecstat1 tcharge household_charge chcharge].freeze
RENT_TYPE = { RENT_TYPE = {
@ -626,7 +626,7 @@ class LettingsLog < Log
end end
def rent_and_charges_paid_weekly? def rent_and_charges_paid_weekly?
[1, 5, 6, 7, 8, 9, 10].include? period [1, 5, 6, 7, 8, 9, 10, 11].include? period
end end
def rent_and_charges_paid_every_4_weeks? def rent_and_charges_paid_every_4_weeks?

6
app/models/validations/sales/financial_validations.rb

@ -75,7 +75,7 @@ module Validations::Sales::FinancialValidations
if threshold && record.stairbought < threshold if threshold && record.stairbought < threshold
shared_ownership_type = record.form.get_question("type", record).label_from_value(record.type).downcase shared_ownership_type = record.form.get_question("type", record).label_from_value(record.type).downcase
record.errors.add :stairbought, I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold:, shared_ownership_type:) record.errors.add :stairbought, I18n.t("validations.sales.financial.stairbought.percentage_bought_must_be_at_least_threshold", threshold:, shared_ownership_type:)
record.errors.add :type, I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold:, shared_ownership_type:) record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.financial.type.percentage_bought_must_be_at_least_threshold", threshold:, shared_ownership_type:)
end end
end end
@ -96,10 +96,10 @@ module Validations::Sales::FinancialValidations
return unless (range = ranges[record.type]) return unless (range = ranges[record.type])
if record.equity < range.min if record.equity < range.min
record.errors.add :type, I18n.t("validations.sales.financial.type.equity_under_min", min_equity: range.min) record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.financial.type.equity_under_min", min_equity: range.min)
record.errors.add :equity, :under_min, message: I18n.t("validations.sales.financial.equity.equity_under_min", min_equity: range.min) record.errors.add :equity, :under_min, message: I18n.t("validations.sales.financial.equity.equity_under_min", min_equity: range.min)
elsif !record.is_resale? && record.equity > range.max elsif !record.is_resale? && record.equity > range.max
record.errors.add :type, I18n.t("validations.sales.financial.type.equity_over_max", max_equity: range.max) record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.financial.type.equity_over_max", max_equity: range.max)
record.errors.add :equity, :over_max, message: I18n.t("validations.sales.financial.equity.equity_over_max", max_equity: range.max) record.errors.add :equity, :over_max, message: I18n.t("validations.sales.financial.equity.equity_over_max", max_equity: range.max)
record.errors.add :resale, I18n.t("validations.sales.financial.resale.equity_over_max", max_equity: range.max) record.errors.add :resale, I18n.t("validations.sales.financial.resale.equity_over_max", max_equity: range.max)
end end

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

@ -108,7 +108,7 @@ module Validations::Sales::SaleInformationValidations
if record.shared_ownership_scheme? && !record.old_persons_shared_ownership? && record.mrent > 9999 if record.shared_ownership_scheme? && !record.old_persons_shared_ownership? && record.mrent > 9999
record.errors.add :mrent, I18n.t("validations.sales.sale_information.mrent.monthly_rent_higher_than_expected") record.errors.add :mrent, I18n.t("validations.sales.sale_information.mrent.monthly_rent_higher_than_expected")
record.errors.add :type, I18n.t("validations.sales.sale_information.type.monthly_rent_higher_than_expected") record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.monthly_rent_higher_than_expected")
end end
end end
@ -136,7 +136,7 @@ module Validations::Sales::SaleInformationValidations
if max_stairbought && record.stairbought > max_stairbought if max_stairbought && record.stairbought > max_stairbought
record.errors.add :stairbought, I18n.t("validations.sales.sale_information.stairbought.stairbought_over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record)) record.errors.add :stairbought, I18n.t("validations.sales.sale_information.stairbought.stairbought_over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record))
record.errors.add :type, I18n.t("validations.sales.sale_information.type.stairbought_over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record)) record.errors.add :type, :skip_bu_error, message: I18n.t("validations.sales.sale_information.type.stairbought_over_max", max_stairbought:, type: record.form.get_question("type", record).answer_label(record))
end end
end end

52
app/services/bulk_upload/sales/year2025/row_parser.rb

@ -815,31 +815,11 @@ private
attributes["sex5"] = field_52 attributes["sex5"] = field_52
attributes["sex6"] = field_56 attributes["sex6"] = field_56
attributes["relat2"] = if field_34 == 1 attributes["relat2"] = relationship_from_is_partner(field_34)
"P" attributes["relat3"] = relationship_from_is_partner(field_42)
else attributes["relat4"] = relationship_from_is_partner(field_46)
(field_34 == 2 ? "X" : "R") attributes["relat5"] = relationship_from_is_partner(field_50)
end attributes["relat6"] = relationship_from_is_partner(field_54)
attributes["relat3"] = if field_42 == 1
"P"
else
(field_42 == 2 ? "X" : "R")
end
attributes["relat4"] = if field_46 == 1
"P"
else
(field_46 == 2 ? "X" : "R")
end
attributes["relat5"] = if field_50 == 1
"P"
else
(field_50 == 2 ? "X" : "R")
end
attributes["relat6"] = if field_54 == 1
"P"
else
(field_54 == 2 ? "X" : "R")
end
attributes["ecstat1"] = field_32 attributes["ecstat1"] = field_32
attributes["ecstat2"] = field_39 attributes["ecstat2"] = field_39
@ -1052,6 +1032,17 @@ private
field_55.present? || field_56.present? || field_54.present? field_55.present? || field_56.present? || field_54.present?
end end
def relationship_from_is_partner(is_partner)
case is_partner
when 1
"P"
when 2
"X"
when 3
"R"
end
end
def details_known?(person_n) def details_known?(person_n)
send("person_#{person_n}_present?") ? 1 : 2 send("person_#{person_n}_present?") ? 1 : 2
end end
@ -1491,6 +1482,17 @@ private
%w[0] + GlobalConstants::COUNTRIES_ANSWER_OPTIONS.keys # 0 is "Prefers not to say" %w[0] + GlobalConstants::COUNTRIES_ANSWER_OPTIONS.keys # 0 is "Prefers not to say"
end end
def validate_relat_fields
%i[field_34 field_42 field_46 field_50 field_54].each do |field|
value = send(field)
next if value.blank?
unless (1..3).cover?(value)
errors.add(field, I18n.t("#{ERROR_BASE_KEY}.invalid_option", question: format_ending(QUESTIONS[field])))
end
end
end
def bulk_upload_organisation def bulk_upload_organisation
Organisation.find(bulk_upload.organisation_id) Organisation.find(bulk_upload.organisation_id)
end end

5
app/services/csv/lettings_log_csv_service.rb

@ -177,6 +177,10 @@ module Csv
10 => "Intermediate rent supported housing private registered provider", 10 => "Intermediate rent supported housing private registered provider",
11 => "Intermediate rent general needs local authority", 11 => "Intermediate rent general needs local authority",
12 => "Intermediate rent supported housing local authority", 12 => "Intermediate rent supported housing local authority",
13 => "Specified accommodation general needs private registered provider",
14 => "Specified accommodation supported housing private registered provider",
15 => "Specified accommodation general needs local authority",
16 => "Specified accommodation supported housing local authority",
}.freeze }.freeze
IRPRODUCT_LABELS = { IRPRODUCT_LABELS = {
@ -206,6 +210,7 @@ module Csv
1 => "Social Rent", 1 => "Social Rent",
2 => "Affordable Rent", 2 => "Affordable Rent",
3 => "Intermediate Rent", 3 => "Intermediate Rent",
4 => "Specified accommodation",
}.freeze }.freeze
UPRN_KNOWN_LABELS = { UPRN_KNOWN_LABELS = {

2
app/services/exports/user_export_service.rb

@ -28,7 +28,7 @@ module Exports
def retrieve_resources(recent_export, full_update, _year) def retrieve_resources(recent_export, full_update, _year)
if !full_update && recent_export if !full_update && recent_export
params = { from: recent_export.started_at, to: @start_time } params = { from: recent_export.started_at, to: @start_time }
User.where("(updated_at >= :from AND updated_at <= :to)", params) User.where("(updated_at >= :from AND updated_at <= :to) OR (values_updated_at IS NOT NULL AND values_updated_at >= :from AND values_updated_at <= :to)", params)
else else
params = { to: @start_time } params = { to: @start_time }
User.where("updated_at <= :to", params) User.where("updated_at <= :to", params)

2
app/services/merge/merge_organisations_service.rb

@ -62,7 +62,7 @@ private
def merge_users(merging_organisation) def merge_users(merging_organisation)
users_to_merge = users_to_merge(merging_organisation) users_to_merge = users_to_merge(merging_organisation)
@merged_users[merging_organisation.name] = users_to_merge.map { |user| { name: user.name, email: user.email } } @merged_users[merging_organisation.name] = users_to_merge.map { |user| { name: user.name, email: user.email } }
users_to_merge.update_all(organisation_id: @absorbing_organisation.id) users_to_merge.update_all(organisation_id: @absorbing_organisation.id, values_updated_at: Time.zone.now)
end end
def merge_schemes_and_locations(merging_organisation) def merge_schemes_and_locations(merging_organisation)

5
db/migrate/20250305092900_add_values_updated_at_to_user.rb

@ -0,0 +1,5 @@
class AddValuesUpdatedAtToUser < ActiveRecord::Migration[7.2]
def change
add_column :users, :values_updated_at, :datetime
end
end

3
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2025_02_25_180643) do ActiveRecord::Schema[7.2].define(version: 2025_03_05_092900) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -853,6 +853,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_02_25_180643) do
t.boolean "reactivate_with_organisation" t.boolean "reactivate_with_organisation"
t.datetime "discarded_at" t.datetime "discarded_at"
t.string "phone_extension" t.string "phone_extension"
t.datetime "values_updated_at"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true

48
spec/services/bulk_upload/sales/year2025/row_parser_spec.rb

@ -60,7 +60,7 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
field_31: "28", field_31: "28",
field_32: "1", field_32: "1",
field_33: "1", field_33: "1",
field_34: "R", field_34: "3",
field_35: "32", field_35: "32",
field_36: "F", field_36: "F",
field_37: "17", field_37: "17",
@ -1145,6 +1145,52 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
end end
end end
describe "relationship field mappings" do
[
%w[field_34 relat2 2],
%w[field_42 relat3 3],
%w[field_46 relat4 4],
%w[field_50 relat5 5],
%w[field_54 relat6 6],
].each do |input_field, relationship_attribute, person_num|
describe input_field.to_s do
context "when #{input_field} is 1" do
let(:attributes) { setup_section_params.merge({ input_field.to_sym => "1", field_41: "5" }) }
it "sets relationship to P" do
expect(parser.log.public_send(relationship_attribute)).to eq("P")
end
end
context "when #{input_field} is 2" do
let(:attributes) { setup_section_params.merge({ input_field.to_sym => "2", field_41: "5" }) }
it "sets relationship to X" do
expect(parser.log.public_send(relationship_attribute)).to eq("X")
end
end
context "when #{input_field} is 3" do
let(:attributes) { setup_section_params.merge({ input_field.to_sym => "3", field_41: "5" }) }
it "sets relationship to R" do
expect(parser.log.public_send(relationship_attribute)).to eq("R")
end
end
context "when #{input_field} is 4" do
let(:attributes) { setup_section_params.merge({ input_field.to_sym => "4", field_41: "5" }) }
it "gives a validation error" do
parser.valid?
validation_message = "You must answer person #{person_num} is the partner of buyer 1."
expect(parser.errors[input_field]).to include validation_message
end
end
end
end
end
describe "field_39" do # ecstat2 describe "field_39" do # ecstat2
context "when buyer 2 has no age but has ecstat as child" do context "when buyer 2 has no age but has ecstat as child" do
let(:attributes) { valid_attributes.merge({ field_35: nil, field_39: "9" }) } let(:attributes) { valid_attributes.merge({ field_35: nil, field_39: "9" }) }

21
spec/services/exports/user_export_service_spec.rb

@ -202,7 +202,26 @@ RSpec.describe Exports::UserExportService do
before do before do
create(:user, updated_at: Time.zone.local(2022, 4, 27), organisation:) create(:user, updated_at: Time.zone.local(2022, 4, 27), organisation:)
create(:user, updated_at: Time.zone.local(2022, 4, 27), organisation:) create(:user, updated_at: Time.zone.local(2022, 4, 27), organisation:)
Export.create!(started_at: Time.zone.local(2022, 4, 26), base_number: 1, increment_number: 1) Export.create!(started_at: Time.zone.local(2022, 4, 26), base_number: 1, increment_number: 1, empty_export: true, collection: "users")
end
it "generates an XML manifest file with the expected content within the ZIP file" do
expected_content = replace_record_number(local_manifest_file.read, 2)
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content|
entry = Zip::File.open_buffer(content).find_entry(expected_manifest_filename)
expect(entry).not_to be_nil
expect(entry.get_input_stream.read).to eq(expected_content)
end
expect(export_service.export_xml_users).to eq({ expected_zip_filename.gsub(".zip", "") => start_time })
end
end
context "and a user has been manually updated since the previous partial export" do
before do
create(:user, updated_at: Time.zone.local(2022, 4, 25), values_updated_at: Time.zone.local(2022, 4, 27), organisation:)
create(:user, updated_at: Time.zone.local(2022, 4, 25), values_updated_at: Time.zone.local(2022, 4, 27), organisation:)
Export.create!(started_at: Time.zone.local(2022, 4, 26), base_number: 1, increment_number: 1, empty_export: true, collection: "users")
end end
it "generates an XML manifest file with the expected content within the ZIP file" do it "generates an XML manifest file with the expected content within the ZIP file" do

2
spec/services/merge/merge_organisations_service_spec.rb

@ -31,10 +31,12 @@ RSpec.describe Merge::MergeOrganisationsService do
expect(Rails.logger).to receive(:info).with("\t#{merging_organisation.data_protection_officers.first.name} (#{merging_organisation.data_protection_officers.first.email})") expect(Rails.logger).to receive(:info).with("\t#{merging_organisation.data_protection_officers.first.name} (#{merging_organisation.data_protection_officers.first.email})")
expect(Rails.logger).to receive(:info).with("\tfake name (fake@email.com)") expect(Rails.logger).to receive(:info).with("\tfake name (fake@email.com)")
expect(Rails.logger).to receive(:info).with("New schemes from fake org:") expect(Rails.logger).to receive(:info).with("New schemes from fake org:")
expect(merging_organisation_user.values_updated_at).to be_nil
merge_organisations_service.call merge_organisations_service.call
merging_organisation_user.reload merging_organisation_user.reload
expect(merging_organisation_user.organisation).to eq(absorbing_organisation) expect(merging_organisation_user.organisation).to eq(absorbing_organisation)
expect(merging_organisation_user.values_updated_at).not_to be_nil
end end
it "sets merge date on merged organisation" do it "sets merge date on merged organisation" do

Loading…
Cancel
Save