Browse Source

Merge branch 'refs/heads/main' into CLDC-3928-logs-with-manually-entered-address-showing-the-property-information-as-incomplete

pull/3003/head
Manny Dinssa 1 week ago
parent
commit
f2a8feb01c
  1. 2
      app/models/derived_variables/lettings_log_variables.rb
  2. 2
      app/models/form/lettings/questions/previous_let_type.rb
  3. 4
      app/models/form/lettings/questions/reason.rb
  4. 4
      app/models/lettings_log.rb
  5. 6
      app/models/validations/sales/financial_validations.rb
  6. 4
      app/models/validations/sales/property_validations.rb
  7. 4
      app/models/validations/sales/sale_information_validations.rb
  8. 61
      app/services/bulk_upload/sales/year2025/row_parser.rb
  9. 12
      app/services/csv/lettings_log_csv_service.rb
  10. 7
      app/services/csv/sales_log_csv_service.rb
  11. 2
      app/services/exports/user_export_service.rb
  12. 2
      app/services/merge/merge_organisations_service.rb
  13. 11
      app/services/uprn_client.rb
  14. 3
      app/views/form/_date_question.html.erb
  15. 5
      db/migrate/20250305092900_add_values_updated_at_to_user.rb
  16. 3
      db/schema.rb
  17. 3
      spec/fixtures/files/lettings_log_csv_export_codes_25.csv
  18. 3
      spec/fixtures/files/lettings_log_csv_export_labels_25.csv
  19. 3
      spec/fixtures/files/lettings_log_csv_export_non_support_codes_25.csv
  20. 3
      spec/fixtures/files/lettings_log_csv_export_non_support_labels_25.csv
  21. 2
      spec/models/form/lettings/questions/previous_let_type_spec.rb
  22. 48
      spec/services/bulk_upload/sales/year2025/row_parser_spec.rb
  23. 193
      spec/services/csv/lettings_log_csv_service_spec.rb
  24. 21
      spec/services/exports/user_export_service_spec.rb
  25. 2
      spec/services/merge/merge_organisations_service_spec.rb
  26. 28
      spec/services/uprn_client_spec.rb

2
app/models/derived_variables/lettings_log_variables.rb

@ -18,7 +18,7 @@ module DerivedVariables::LettingsLogVariables
3 => 6, # "Rent to Buy" => "Rent to Buy basis" 3 => 6, # "Rent to Buy" => "Rent to Buy basis"
4 => 7, # "London Living Rent" => "London Living Rent basis" 4 => 7, # "London Living Rent" => "London Living Rent basis"
5 => 8, # "Other intermediate rent product" => "Another Intermediate Rent basis" 5 => 8, # "Other intermediate rent product" => "Another Intermediate Rent basis"
6 => 9, # "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" => "Specified accommodation - exempt accommodation, manged properties, refuges and local authority hostels" 6 => 9, # "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" => "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels"
}.freeze }.freeze
RENTTYPE_DETAIL_MAPPING = { RENTTYPE_DETAIL_MAPPING = {

2
app/models/form/lettings/questions/previous_let_type.rb

@ -36,7 +36,7 @@ class Form::Lettings::Questions::PreviousLetType < ::Form::Question
"6" => { "value" => "Rent to Buy basis" }, "6" => { "value" => "Rent to Buy basis" },
"7" => { "value" => "London Living Rent basis" }, "7" => { "value" => "London Living Rent basis" },
"8" => { "value" => "Another Intermediate Rent basis" }, "8" => { "value" => "Another Intermediate Rent basis" },
"9" => { "value" => "Specified accommodation - exempt accommodation, manged properties, refuges and local authority hostels" }, "9" => { "value" => "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" },
"divider" => { "value" => true }, "divider" => { "value" => true },
"3" => { "value" => "Don’t know" }, "3" => { "value" => "Don’t know" },
}.freeze }.freeze

4
app/models/form/lettings/questions/reason.rb

@ -46,9 +46,9 @@ class Form::Lettings::Questions::Reason < ::Form::Question
"18" => { "value" => "To move to accommodation with support" }, "18" => { "value" => "To move to accommodation with support" },
"19" => { "value" => "To move to independent accommodation" }, "19" => { "value" => "To move to independent accommodation" },
"20" => { "value" => "Other" }, "20" => { "value" => "Other" },
"28" => { "value" => "Don’t know" },
"divider" => { "value" => true },
"47" => { "value" => "Tenant prefers not to say" }, "47" => { "value" => "Tenant prefers not to say" },
"divider" => { "value" => true },
"28" => { "value" => "Don’t know" },
}.freeze }.freeze
end end

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/property_validations.rb

@ -49,9 +49,9 @@ module Validations::Sales::PropertyValidations
record.errors.add :uprn_confirmation, I18n.t("validations.sales.property_information.uprn_confirmation.not_in_england") record.errors.add :uprn_confirmation, I18n.t("validations.sales.property_information.uprn_confirmation.not_in_england")
record.errors.add :uprn_selection, I18n.t("validations.sales.property_information.uprn_selection.not_in_england") record.errors.add :uprn_selection, I18n.t("validations.sales.property_information.uprn_selection.not_in_england")
if record.uprn.present? if record.uprn.present?
record.errors.add :saledate, I18n.t("validations.sales.property_information.saledate.address_not_in_england") record.errors.add :saledate, :skip_bu_error, message: I18n.t("validations.sales.property_information.saledate.address_not_in_england")
else else
record.errors.add :saledate, I18n.t("validations.sales.property_information.saledate.postcode_not_in_england") record.errors.add :saledate, :skip_bu_error, message: I18n.t("validations.sales.property_information.saledate.postcode_not_in_england")
end end
end end
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

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

@ -393,6 +393,15 @@ class BulkUpload::Sales::Year2025::RowParser
}, },
on: :after_log on: :after_log
validates :field_103,
numericality: {
greater_than_or_equal_to: 2,
less_than_or_equal_to: 10,
message: I18n.t("#{ERROR_BASE_KEY}.numeric.within_range", field: "Number of staircasing transactions", min: "2", max: "10"),
allow_blank: true,
},
on: :before_log
validate :validate_buyer1_economic_status, on: :before_log validate :validate_buyer1_economic_status, on: :before_log
validate :validate_buyer2_economic_status, on: :before_log validate :validate_buyer2_economic_status, on: :before_log
validate :validate_valid_radio_option, on: :before_log validate :validate_valid_radio_option, on: :before_log
@ -806,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
@ -1043,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
@ -1482,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

12
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 = {
@ -284,13 +289,12 @@ module Csv
end end
def lettings_log_definitions def lettings_log_definitions
CsvVariableDefinition.lettings.group_by { |record| [record.variable, record.definition] } CsvVariableDefinition.lettings.group_by(&:variable).map { |_, options|
.map do |_, options|
exact_match = options.find { |definition| definition.year == @year } exact_match = options.find { |definition| definition.year == @year }
next exact_match if exact_match next exact_match if exact_match
options.max_by(&:year) options.select { |opt| opt.year < @year }.max_by(&:year)
end }.compact
end end
def insert_derived_and_related_attributes(ordered_questions) def insert_derived_and_related_attributes(ordered_questions)

7
app/services/csv/sales_log_csv_service.rb

@ -179,13 +179,12 @@ module Csv
end end
def sales_log_definitions def sales_log_definitions
CsvVariableDefinition.sales.group_by { |record| [record.variable, record.definition] } CsvVariableDefinition.sales.group_by(&:variable).map { |_, options|
.map do |_, options|
exact_match = options.find { |definition| definition.year == @year } exact_match = options.find { |definition| definition.year == @year }
next exact_match if exact_match next exact_match if exact_match
options.max_by(&:year) options.select { |opt| opt.year < @year }.max_by(&:year)
end }.compact
end end
def insert_derived_and_related_attributes(ordered_questions) def insert_derived_and_related_attributes(ordered_questions)

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)

11
app/services/uprn_client.rb

@ -20,7 +20,16 @@ class UprnClient
end end
def result def result
@result ||= JSON.parse(response.body).dig("results", 0, "DPA") || JSON.parse(response.body).dig("results", 0, "LPI") @result ||= if response.is_a?(Net::HTTPSuccess)
parsed_response = JSON.parse(response.body)
parsed_response.dig("results", 0, "DPA") || parsed_response.dig("results", 0, "LPI")
else
@error = "UPRN client failed to return a valid result, try again later."
Sentry.capture_message("UPRN client failed to return a valid result with error code: #{response.code}.")
Rails.logger.error("UPRN client failed to return a valid result with error code: #{response.code}.")
Rails.logger.error("Response body: #{response.body}")
nil
end
end end
private private

3
app/views/form/_date_question.html.erb

@ -1,9 +1,10 @@
<%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %> <%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %>
<% legend = legend(question, page_header, conditional) %>
<%= render partial: "components/date_picker", locals: <%= render partial: "components/date_picker", locals:
{ {
resource: @log, resource: @log,
question_id: question.id, question_id: question.id,
legend: { text: legend(question, page_header, conditional)[:text], size: "l", caption: caption(caption_text, page_header, conditional) }, legend: { text: legend[:text], size: legend[:size], caption: caption(caption_text, page_header, conditional) },
resource_type: @log.log_type, resource_type: @log.log_type,
hint: (question.hint_text.blank? ? "" : (question.hint_text.html_safe + "</br></br>".html_safe)) + "For example, #{date_mid_collection_year_formatted(@log.startdate).tr(' ', '/')}", hint: (question.hint_text.blank? ? "" : (question.hint_text.html_safe + "</br></br>".html_safe)) + "For example, #{date_mid_collection_year_formatted(@log.startdate).tr(' ', '/')}",
f:, f:,

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

3
spec/fixtures/files/lettings_log_csv_export_codes_25.csv vendored

File diff suppressed because one or more lines are too long

3
spec/fixtures/files/lettings_log_csv_export_labels_25.csv vendored

File diff suppressed because one or more lines are too long

3
spec/fixtures/files/lettings_log_csv_export_non_support_codes_25.csv vendored

File diff suppressed because one or more lines are too long

3
spec/fixtures/files/lettings_log_csv_export_non_support_labels_25.csv vendored

File diff suppressed because one or more lines are too long

2
spec/models/form/lettings/questions/previous_let_type_spec.rb

@ -83,7 +83,7 @@ RSpec.describe Form::Lettings::Questions::PreviousLetType, type: :model do
"6" => { "value" => "Rent to Buy basis" }, "6" => { "value" => "Rent to Buy basis" },
"7" => { "value" => "London Living Rent basis" }, "7" => { "value" => "London Living Rent basis" },
"8" => { "value" => "Another Intermediate Rent basis" }, "8" => { "value" => "Another Intermediate Rent basis" },
"9" => { "value" => "Specified accommodation - exempt accommodation, manged properties, refuges and local authority hostels" }, "9" => { "value" => "Specified accommodation - exempt accommodation, managed properties, refuges and local authority hostels" },
"divider" => { "value" => true }, "divider" => { "value" => true },
"3" => { "value" => "Don’t know" }, "3" => { "value" => "Don’t know" },
}) })

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" }) }

193
spec/services/csv/lettings_log_csv_service_spec.rb

@ -194,6 +194,199 @@ RSpec.describe Csv::LettingsLogCsvService do
end end
describe "the full CSV output" do describe "the full CSV output" do
context "when the requested log year is 2025" do
let(:year) { 2025 }
let(:organisation) { create(:organisation, provider_type: "LA", name: "MHCLG") }
let(:log) do
create(
:lettings_log,
:ignore_validation_errors,
created_by: user,
assigned_to: user,
created_at: Time.zone.local(2025, 4, 1),
updated_at: Time.zone.local(2025, 4, 1),
owning_organisation: organisation,
managing_organisation: organisation,
needstype: 1,
renewal: 0,
startdate: Time.zone.local(2025, 4, 1),
rent_type: 1,
tenancycode: "HIJKLMN",
propcode: "ABCDEFG",
declaration: 1,
address_line1: "Address line 1",
town_or_city: "London",
postcode_full: "NW9 5LL",
la: "E09000003",
is_la_inferred: false,
address_line1_as_entered: "address line 1 as entered",
address_line2_as_entered: "address line 2 as entered",
town_or_city_as_entered: "town or city as entered",
county_as_entered: "county as entered",
postcode_full_as_entered: "AB1 2CD",
la_as_entered: "la as entered",
first_time_property_let_as_social_housing: 0,
unitletas: 2,
rsnvac: 6,
unittype_gn: 7,
builtype: 1,
wchair: 1,
beds: 3,
voiddate: Time.zone.local(2025, 3, 30),
majorrepairs: 1,
mrcdate: Time.zone.local(2025, 3, 31),
joint: 3,
startertenancy: 1,
tenancy: 4,
tenancylength: 2,
hhmemb: 4,
age1_known: 0,
age1: 35,
sex1: "F",
ethnic_group: 0,
ethnic: 2,
nationality_all: 36,
ecstat1: 0,
details_known_2: 0,
relat2: "P",
age2_known: 0,
age2: 32,
sex2: "M",
ecstat2: 6,
details_known_3: 1,
details_known_4: 0,
relat4: "R",
age4_known: 1,
sex4: "R",
ecstat4: 10,
armedforces: 1,
leftreg: 4,
reservist: 1,
preg_occ: 2,
housingneeds: 1,
housingneeds_type: 0,
housingneeds_a: 1,
housingneeds_b: 0,
housingneeds_c: 0,
housingneeds_f: 0,
housingneeds_g: 0,
housingneeds_h: 0,
housingneeds_other: 0,
illness: 1,
illness_type_1: 0,
illness_type_2: 1,
illness_type_3: 0,
illness_type_4: 0,
illness_type_5: 0,
illness_type_6: 0,
illness_type_7: 0,
illness_type_8: 0,
illness_type_9: 0,
illness_type_10: 0,
layear: 2,
waityear: 7,
reason: 4,
prevten: 6,
homeless: 1,
ppcodenk: 1,
ppostcode_full: "TN23 6LZ",
previous_la_known: 1,
prevloc: "E07000105",
reasonpref: 1,
rp_homeless: 0,
rp_insan_unsat: 1,
rp_medwel: 0,
rp_hardship: 0,
rp_dontknow: 0,
cbl: 0,
chr: 1,
cap: 0,
accessible_register: 0,
referral: 2,
net_income_known: 0,
incref: 0,
incfreq: 1,
earnings: 268,
hb: 6,
has_benefits: 1,
benefits: 1,
period: 2,
brent: 200,
scharge: 50,
pscharge: 40,
supcharg: 35,
tcharge: 325,
hbrentshortfall: 1,
tshortfall_known: 1,
tshortfall: 12,
)
end
context "when exporting with human readable labels" do
let(:export_type) { "labels" }
context "when the current user is a support user" do
let(:user) { create(:user, :support, organisation:, email: "s.port@jeemayle.com") }
it "exports the CSV with 2025 ordering and all values correct" do
expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_labels_25.csv")
values_to_delete = %w[id]
values_to_delete.each do |attribute|
index = attribute_line.index(attribute)
content_line[index] = nil
end
expect(csv).to eq expected_content
end
end
context "when the current user is not a support user" do
let(:user) { create(:user, :data_provider, organisation:, email: "choreographer@owtluk.com") }
it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_labels_25.csv")
values_to_delete = %w[id]
values_to_delete.each do |attribute|
index = attribute_line.index(attribute)
content_line[index] = nil
end
expect(csv).to eq expected_content
end
end
end
context "when exporting values as codes" do
let(:export_type) { "codes" }
context "when the current user is a support user" do
let(:user) { create(:user, :support, organisation:, email: "s.port@jeemayle.com") }
it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_codes_25.csv")
values_to_delete = %w[id]
values_to_delete.each do |attribute|
index = attribute_line.index(attribute)
content_line[index] = nil
end
expect(csv).to eq expected_content
end
end
context "when the current user is not a support user" do
let(:user) { create(:user, :data_provider, organisation:, email: "choreographer@owtluk.com") }
it "exports the CSV with all values correct" do
expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_codes_25.csv")
values_to_delete = %w[id]
values_to_delete.each do |attribute|
index = attribute_line.index(attribute)
content_line[index] = nil
end
expect(csv).to eq expected_content
end
end
end
end
context "when the requested log year is 2024" do context "when the requested log year is 2024" do
let(:year) { 2024 } let(:year) { 2024 }
let(:organisation) { create(:organisation, provider_type: "LA", name: "MHCLG") } let(:organisation) { create(:organisation, provider_type: "LA", name: "MHCLG") }

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

28
spec/services/uprn_client_spec.rb

@ -86,5 +86,33 @@ describe UprnClient do
expect(client.error).to be_nil expect(client.error).to be_nil
end end
end end
describe "result" do
context "when response is successful" do
before do
stub_api_request(body: valid_response)
client.call
end
it "returns parsed result" do
expect(client.result).to eq({ "postcode" => "12345" })
expect(client.send(:response).code.to_i).to eq(200)
end
end
context "when response is not successful" do
before do
stub_api_request(body: valid_response, status: 500)
client.call
end
it "returns nil" do
expect(client.result).to be_nil
expect(client.error).to eq("UPRN client failed to return a valid result, try again later.")
end
end
end
end end
end end

Loading…
Cancel
Save