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
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
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
DUPLICATE_LOG_ATTRIBUTES = %w[owning_organisation_id tenancycode startdate age1_known age1 sex1 ecstat1 tcharge household_charge chcharge].freeze
RENT_TYPE = {
@ -626,7 +626,7 @@ class LettingsLog < Log
end
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
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
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 :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
@ -96,10 +96,10 @@ module Validations::Sales::FinancialValidations
return unless (range = ranges[record.type])
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)
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 :resale, I18n.t("validations.sales.financial.resale.equity_over_max", max_equity: range.max)
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
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
@ -136,7 +136,7 @@ module Validations::Sales::SaleInformationValidations
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 :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

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

@ -815,31 +815,11 @@ private
attributes["sex5"] = field_52
attributes["sex6"] = field_56
attributes["relat2"] = if field_34 == 1
"P"
else
(field_34 == 2 ? "X" : "R")
end
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["relat2"] = relationship_from_is_partner(field_34)
attributes["relat3"] = relationship_from_is_partner(field_42)
attributes["relat4"] = relationship_from_is_partner(field_46)
attributes["relat5"] = relationship_from_is_partner(field_50)
attributes["relat6"] = relationship_from_is_partner(field_54)
attributes["ecstat1"] = field_32
attributes["ecstat2"] = field_39
@ -1052,6 +1032,17 @@ private
field_55.present? || field_56.present? || field_54.present?
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)
send("person_#{person_n}_present?") ? 1 : 2
end
@ -1491,6 +1482,17 @@ private
%w[0] + GlobalConstants::COUNTRIES_ANSWER_OPTIONS.keys # 0 is "Prefers not to say"
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
Organisation.find(bulk_upload.organisation_id)
end

5
app/services/csv/lettings_log_csv_service.rb

@ -177,6 +177,10 @@ module Csv
10 => "Intermediate rent supported housing private registered provider",
11 => "Intermediate rent general needs 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
IRPRODUCT_LABELS = {
@ -206,6 +210,7 @@ module Csv
1 => "Social Rent",
2 => "Affordable Rent",
3 => "Intermediate Rent",
4 => "Specified accommodation",
}.freeze
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)
if !full_update && recent_export
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
params = { to: @start_time }
User.where("updated_at <= :to", params)

2
app/services/merge/merge_organisations_service.rb

@ -62,7 +62,7 @@ private
def merge_users(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 } }
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
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.
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
enable_extension "plpgsql"
@ -853,6 +853,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_02_25_180643) do
t.boolean "reactivate_with_organisation"
t.datetime "discarded_at"
t.string "phone_extension"
t.datetime "values_updated_at"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", 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

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_32: "1",
field_33: "1",
field_34: "R",
field_34: "3",
field_35: "32",
field_36: "F",
field_37: "17",
@ -1145,6 +1145,52 @@ RSpec.describe BulkUpload::Sales::Year2025::RowParser do
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
context "when buyer 2 has no age but has ecstat as child" do
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
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
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("\tfake name (fake@email.com)")
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
merging_organisation_user.reload
expect(merging_organisation_user.organisation).to eq(absorbing_organisation)
expect(merging_organisation_user.values_updated_at).not_to be_nil
end
it "sets merge date on merged organisation" do

Loading…
Cancel
Save