diff --git a/app/jobs/email_csv_job.rb b/app/jobs/email_csv_job.rb index 426c3430e..55fdcc824 100644 --- a/app/jobs/email_csv_job.rb +++ b/app/jobs/email_csv_job.rb @@ -6,15 +6,15 @@ class EmailCsvJob < ApplicationJob EXPIRATION_TIME = 3.hours.to_i def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, codes_only_export = false, log_type = "lettings") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params + export_type = codes_only_export ? "codes" : "labels" case log_type when "lettings" unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible filtered_logs = FilterManager.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) - csv_string = filtered_logs.to_csv(user, codes_only_export:) + csv_string = Csv::LettingsLogCsvService.new(user:, export_type:).prepare_csv(filtered_logs) when "sales" unfiltered_logs = organisation.present? && user.support? ? SalesLog.visible.where(owning_organisation_id: organisation.id) : user.sales_logs.visible filtered_logs = FilterManager.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) - export_type = codes_only_export ? "codes" : "labels" csv_string = Csv::SalesLogCsvService.new(export_type:).prepare_csv(filtered_logs) end diff --git a/app/models/form_handler.rb b/app/models/form_handler.rb index 7f858c645..c4b3c51d4 100644 --- a/app/models/form_handler.rb +++ b/app/models/form_handler.rb @@ -52,6 +52,18 @@ class FormHandler ordered_questions end + def ordered_lettings_questions_for_all_years + lettings_forms = forms.filter { |name, _form| name.end_with? "lettings" }.values + ordered_questions = lettings_forms.pop.questions.uniq(&:id) + question_ids = ordered_questions.map(&:id) + all_questions_from_previous_forms = lettings_forms.flat_map(&:questions) + deprecated_questions_by_preceding_question_id(question_ids, all_questions_from_previous_forms).each do |preceding_question_id, deprecated_question| + index_of_preceding_question = ordered_questions.index { |q| q.id == preceding_question_id } + ordered_questions.insert(index_of_preceding_question + 1, deprecated_question) + end + ordered_questions + end + def deprecated_questions_by_preceding_question_id(current_form_question_ids, all_questions_from_previous_forms) deprecated_questions = {} all_questions_from_previous_forms.each_cons(2) do |preceding_question, question| diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index b1de54bbf..b2f3023a5 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -411,23 +411,6 @@ class LettingsLog < Log managing_organisation&.name end - def created_by_name - created_by&.name - end - - def is_dpo - created_by&.is_dpo - end - - def scheme_code - scheme&.id ? "S#{scheme.id}" : nil - end - - def self.to_csv(user = nil, codes_only_export:) - export_type = codes_only_export ? "codes" : "labels" - Csv::LettingsLogCsvService.new(user, export_type:).to_csv - end - def beds_for_la_rent_range return 0 if is_supported_housing? diff --git a/app/models/log.rb b/app/models/log.rb index f2c67771a..c17bd2c44 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -20,6 +20,12 @@ class Log < ApplicationRecord enum status: STATUS enum status_cache: STATUS, _prefix: true + CREATION_METHOD = { + "single log" => 1, + "bulk upload" => 2, + }.freeze + enum creation_method: CREATION_METHOD + scope :visible, -> { where(status: %w[not_started in_progress completed]) } scope :exportable, -> { where(status: %w[not_started in_progress completed deleted]) } @@ -178,10 +184,6 @@ class Log < ApplicationRecord end end - def creation_method - bulk_uploaded? ? "bulk upload" : "single log" - end - def bulk_uploaded? bulk_upload_id.present? end diff --git a/app/services/bulk_upload/lettings/log_creator.rb b/app/services/bulk_upload/lettings/log_creator.rb index 8d3a6cbd7..02852a2a5 100644 --- a/app/services/bulk_upload/lettings/log_creator.rb +++ b/app/services/bulk_upload/lettings/log_creator.rb @@ -14,6 +14,7 @@ class BulkUpload::Lettings::LogCreator row_parser.log.blank_invalid_non_setup_fields! row_parser.log.bulk_upload = bulk_upload + row_parser.log.creation_method = "bulk upload" row_parser.log.skip_update_status = true row_parser.log.status = "pending" row_parser.log.status_cache = row_parser.log.calculate_status diff --git a/app/services/bulk_upload/sales/log_creator.rb b/app/services/bulk_upload/sales/log_creator.rb index db389fc5c..5aa9d01c8 100644 --- a/app/services/bulk_upload/sales/log_creator.rb +++ b/app/services/bulk_upload/sales/log_creator.rb @@ -14,6 +14,7 @@ class BulkUpload::Sales::LogCreator row_parser.log.blank_invalid_non_setup_fields! row_parser.log.bulk_upload = bulk_upload + row_parser.log.creation_method = "bulk upload" row_parser.log.skip_update_status = true row_parser.log.status = "pending" row_parser.log.status_cache = row_parser.log.calculate_status diff --git a/app/services/csv/lettings_log_csv_service.rb b/app/services/csv/lettings_log_csv_service.rb index e8c2815f5..a4daf6d1a 100644 --- a/app/services/csv/lettings_log_csv_service.rb +++ b/app/services/csv/lettings_log_csv_service.rb @@ -1,26 +1,32 @@ module Csv class LettingsLogCsvService - CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays la prevloc unresolved updated_by_id bulk_upload_id uprn_confirmed status_cache discarded_at].freeze - - def initialize(user, export_type:) + def initialize(user:, export_type:) @user = user @export_type = export_type - set_csv_attributes + @attributes = lettings_log_attributes end - def to_csv + def prepare_csv(logs) CSV.generate(headers: true) do |csv| csv << @attributes - LettingsLog.all.find_each do |record| - csv << @attributes.map { |attribute| get_value(attribute, record) } + logs.find_each do |log| + csv << @attributes.map { |attribute| value(attribute, log) } end end end private - ATTRIBUTES_OF_RELATED_OBJECTS = { + CUSTOM_CALL_CHAINS = { + created_by: { + labels: %i[created_by email], + codes: %i[created_by email], + }, + updated_by: { + labels: %i[updated_by email], + codes: %i[updated_by email], + }, location_code: { labels: %i[location id], codes: %i[location id], @@ -61,7 +67,7 @@ module Csv labels: %i[scheme sensitive], codes: %i[scheme sensitive_before_type_cast], }, - scheme_type: { + SCHTYPE: { labels: %i[scheme scheme_type], codes: %i[scheme scheme_type_before_type_cast], }, @@ -97,37 +103,67 @@ module Csv labels: %i[scheme created_at], codes: %i[scheme created_at], }, + scheme_code: { + labels: %i[scheme id_to_display], + codes: %i[scheme id_to_display], + }, + creation_method: { + labels: %i[creation_method], + codes: %i[creation_method_before_type_cast], + }, + is_dpo: { + labels: %i[created_by is_dpo?], + codes: %i[created_by is_dpo?], + }, }.freeze - def get_value(attribute, record) - attribute = "rent_type" if attribute == "rent_type_detail" # rent_type_detail is the requested column header for rent_type, so as not to confuse with renttype - if ATTRIBUTES_OF_RELATED_OBJECTS.key? attribute.to_sym - call_chain = ATTRIBUTES_OF_RELATED_OBJECTS[attribute.to_sym][@export_type.to_sym] - call_chain.reduce(record) { |object, next_call| object&.send(next_call) } - elsif %w[la prevloc].include? attribute # for all exports we output both the codes and labels for these location attributes - record.send(attribute) - elsif %w[la_label prevloc_label].include? attribute # as above - attribute = attribute.remove("_label") - field_value = record.send(attribute) - get_label(field_value, attribute, record) - elsif %w[mrcdate startdate voiddate].include? attribute - record.send(attribute)&.to_formatted_s(:govuk_date) + FIELDS_ALWAYS_EXPORTED_AS_CODES = %w[ + la + prevloc + ].freeze + + FIELDS_ALWAYS_EXPORTED_AS_LABELS = { + "la_label" => "la", + "prevloc_label" => "prevloc", + }.freeze + + DATE_FIELDS = %w[ + mrcdate + startdate + voiddate + created_at + updated_at + ].freeze + + def value(attribute, log) + attribute = "rent_type" if attribute == "rent_type_detail" # rent_type_detail is the requested column header for rent_type, so as not to confuse with renttype. It can be exported as label or code. + if CUSTOM_CALL_CHAINS.key? attribute.to_sym + call_chain = CUSTOM_CALL_CHAINS[attribute.to_sym][@export_type.to_sym] + call_chain.reduce(log) { |object, next_call| object&.public_send(next_call) } + elsif FIELDS_ALWAYS_EXPORTED_AS_CODES.include? attribute + log.public_send(attribute) + elsif FIELDS_ALWAYS_EXPORTED_AS_LABELS.key? attribute + attribute = FIELDS_ALWAYS_EXPORTED_AS_LABELS[attribute] + value = log.public_send(attribute) + get_label(value, attribute, log) + elsif DATE_FIELDS.include? attribute + log.public_send(attribute)&.iso8601 else - field_value = record.send(attribute) + value = log.public_send(attribute) case @export_type when "codes" - field_value + value when "labels" - answer_label = get_label(field_value, attribute, record) - answer_label || label_if_boolean_value(field_value) || field_value + answer_label = get_label(value, attribute, log) + answer_label || label_if_boolean_value(value) || value end end end - def get_label(value, attribute, record) - record.form - .get_question(attribute, record) - &.label_from_value(value) + def get_label(value, attribute, log) + log.form + .get_question(attribute, log) + &.label_from_value(value) end def label_if_boolean_value(value) @@ -135,60 +171,52 @@ module Csv return "No" if value == false end - def set_csv_attributes - metadata_fields = %w[id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name collection_start_year] - metadata_id_fields = %w[managing_organisation_id owning_organisation_id created_by_id bulk_upload_id] - scheme_and_location_ids = %w[scheme_id location_id] - scheme_attributes = %w[scheme_code scheme_service_name scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at] - location_attributes = %w[location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_admin_district location_startdate] - intersecting_attributes = ordered_form_questions & LettingsLog.attribute_names - scheme_and_location_ids - remaining_attributes = LettingsLog.attribute_names - intersecting_attributes - scheme_and_location_ids - - @attributes = (metadata_fields + intersecting_attributes + remaining_attributes - metadata_id_fields + %w[unittype_sh] + scheme_attributes + location_attributes).uniq - move_la_fields - rename_attributes - - @attributes -= CSV_FIELDS_TO_OMIT if @user.present? && !@user.support? - end - - def ordered_form_questions - downloaded_form_years = LettingsLog.all.map(&:collection_start_year).uniq.compact - - if downloaded_form_years.count == 1 && downloaded_form_years[0].present? - form_name = FormHandler.instance.form_name_from_start_year(downloaded_form_years[0], "lettings") - downloaded_form_fields = FormHandler.instance.get_form(form_name).questions - else - downloaded_form_fields = FormHandler.instance.current_lettings_form.questions - end - move_checkbox_answer_options(downloaded_form_fields) - end - - def move_checkbox_answer_options(form_questions) - checkboxes = form_questions.filter { |question| question.type == "checkbox" }.map { |question| { "#{question.id}": question.answer_options.keys } } - attributes = form_questions.map(&:id).uniq - - checkboxes.each do |checkbox_question| - checkbox_question.values[0].each do |answer_option| - attributes.insert(attributes.find_index(checkbox_question.keys[0].to_s), answer_option) - end - end - attributes - end + ATTRIBUTE_MAPPINGS = { + "owning_organisation_id" => %w[owning_organisation_name], + "managing_organisation_id" => %w[managing_organisation_name], + "created_by_id" => [], + "scheme_id" => [], + "location_id" => [], + "rent_type" => %w[renttype rent_type_detail], + "hb" => %w[hb has_benefits], + "age1" => %w[refused hhtype totchild totelder totadult age1], + "housingneeds_type" => %w[housingneeds_type housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h], + "net_income_known" => %w[net_income_known incref], + "irproduct_other" => %w[irproduct irproduct_other lar], + "la" => %w[is_la_inferred la_label la], + "prevloc" => %w[is_previous_la_inferred prevloc_label prevloc], + "needstype" => %w[needstype lettype], + "prevten" => %w[prevten new_old], + "voiddate" => %w[voiddate vacdays], + "rsnvac" => %w[rsnvac newprop], + "household_charge" => %w[household_charge nocharge], + "brent" => %w[brent wrent rent_value_check], + "scharge" => %w[scharge wscharge], + "pscharge" => %w[pscharge wpschrge], + "supcharg" => %w[supcharg wsupchrg], + "tcharge" => %w[tcharge wtcharge], + "chcharge" => %w[chcharge wchchrg], + "tshortfall" => %w[tshortfall wtshortfall], + }.freeze - def move_la_fields - { la: %w[is_la_inferred la_label], prevloc: %w[is_previous_la_inferred prevloc_label] }.each do |inferred_field, fields| - fields.each do |field| - @attributes.delete(field) - @attributes.insert(@attributes.find_index(inferred_field.to_s), field) + SUPPORT_ONLY_ATTRIBUTES = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays la prevloc updated_by_id bulk_upload_id uprn_confirmed].freeze + + def lettings_log_attributes + ordered_questions = FormHandler.instance.ordered_lettings_questions_for_all_years + ordered_questions.reject! { |q| q.id.match?(/rent_value_check/) } + attributes = ordered_questions.flat_map do |question| + if question.type == "checkbox" + question.answer_options.keys.reject { |key| key == "divider" } + elsif ATTRIBUTE_MAPPINGS.key? question.id + ATTRIBUTE_MAPPINGS[question.id] + else + question.id end end - end - - def rename_attributes - { "rent_type" => "rent_type_detail" }.each do |original_field, new_field| - @attributes.insert(@attributes.find_index(original_field), new_field) - @attributes.delete(original_field) - end + non_question_fields = %w[id status created_by is_dpo created_at updated_by updated_at creation_method old_id old_form_id collection_start_year] + scheme_and_location_attributes = %w[scheme_code scheme_service_name scheme_sensitive SCHTYPE scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_admin_district location_startdate] + final_attributes = non_question_fields + attributes + scheme_and_location_attributes + @user.support? ? final_attributes : final_attributes - SUPPORT_ONLY_ATTRIBUTES end end end diff --git a/app/services/csv/sales_log_csv_service.rb b/app/services/csv/sales_log_csv_service.rb index a9573df41..895b544cf 100644 --- a/app/services/csv/sales_log_csv_service.rb +++ b/app/services/csv/sales_log_csv_service.rb @@ -17,13 +17,35 @@ module Csv private - ATTRIBUTES_OF_RELATED_OBJECTS = { - day: %i[saledate day], - month: %i[saledate month], - year: %i[saledate year], - is_dpo: %i[created_by is_dpo], - created_by_name: %i[created_by name], - owning_organisation_name: %i[owning_organisation name], + CUSTOM_CALL_CHAINS = { + day: { + labels: %i[saledate day], + codes: %i[saledate day], + }, + month: { + labels: %i[saledate month], + codes: %i[saledate month], + }, + year: { + labels: %i[saledate year], + codes: %i[saledate year], + }, + is_dpo: { + labels: %i[created_by is_dpo], + codes: %i[created_by is_dpo], + }, + created_by: { + labels: %i[created_by email], + codes: %i[created_by email], + }, + owning_organisation_name: { + labels: %i[owning_organisation name], + codes: %i[owning_organisation name], + }, + creation_method: { + labels: %i[creation_method], + codes: %i[creation_method_before_type_cast], + }, }.freeze FIELDS_ALWAYS_EXPORTED_AS_CODES = %w[ @@ -42,15 +64,15 @@ module Csv ].freeze def value(attribute, log) - if ATTRIBUTES_OF_RELATED_OBJECTS.key? attribute.to_sym - call_chain = ATTRIBUTES_OF_RELATED_OBJECTS[attribute.to_sym] + if CUSTOM_CALL_CHAINS.key? attribute.to_sym + call_chain = CUSTOM_CALL_CHAINS[attribute.to_sym][@export_type.to_sym] call_chain.reduce(log) { |object, next_call| object&.public_send(next_call) } elsif FIELDS_ALWAYS_EXPORTED_AS_CODES.include? attribute log.send(attribute) elsif FIELDS_ALWAYS_EXPORTED_AS_LABELS.key? attribute attribute = FIELDS_ALWAYS_EXPORTED_AS_LABELS[attribute] - field_value = log.send(attribute) - get_label(field_value, attribute, log) + value = log.send(attribute) + get_label(value, attribute, log) elsif DATE_FIELDS.include? attribute log.send(attribute)&.iso8601 else @@ -84,7 +106,7 @@ module Csv "ppostcode_full" => %w[ppostc1 ppostc2], "la" => %w[la la_label], "prevloc" => %w[prevloc prevloc_label], - "created_by_id" => %w[created_by_name], + "created_by_id" => %w[created_by], "owning_organisation_id" => %w[owning_organisation_name], }.freeze diff --git a/db/migrate/20230629124739_add_creation_method_to_lettings_logs.rb b/db/migrate/20230629124739_add_creation_method_to_lettings_logs.rb new file mode 100644 index 000000000..3b96a5af9 --- /dev/null +++ b/db/migrate/20230629124739_add_creation_method_to_lettings_logs.rb @@ -0,0 +1,5 @@ +class AddCreationMethodToLettingsLogs < ActiveRecord::Migration[7.0] + def change + add_column :lettings_logs, :creation_method, :integer, default: 1 + end +end diff --git a/db/migrate/20230629125541_add_creation_method_to_sales_logs.rb b/db/migrate/20230629125541_add_creation_method_to_sales_logs.rb new file mode 100644 index 000000000..5292973a0 --- /dev/null +++ b/db/migrate/20230629125541_add_creation_method_to_sales_logs.rb @@ -0,0 +1,5 @@ +class AddCreationMethodToSalesLogs < ActiveRecord::Migration[7.0] + def change + add_column :sales_logs, :creation_method, :integer, default: 1 + end +end diff --git a/db/schema.rb b/db/schema.rb index 625290d8a..3a47ae8aa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_06_21_142422) do +ActiveRecord::Schema[7.0].define(version: 2023_06_29_125541) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -290,6 +290,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_21_142422) do t.integer "carehome_charges_value_check" t.integer "status_cache", default: 0, null: false t.datetime "discarded_at" + t.integer "creation_method", default: 1 t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id" t.index ["location_id"], name: "index_lettings_logs_on_location_id" @@ -600,11 +601,12 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_21_142422) do t.integer "discounted_sale_value_check" t.integer "student_not_child_value_check" t.integer "percentage_discount_value_check" - t.integer "combined_income_value_check" t.integer "buyer_livein_value_check" t.integer "status_cache", default: 0, null: false + t.integer "combined_income_value_check" t.datetime "discarded_at" t.integer "stairowned_value_check" + t.integer "creation_method", default: 1 t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id" t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id" t.index ["old_id"], name: "index_sales_logs_on_old_id", unique: true diff --git a/spec/fixtures/files/lettings_log_csv_export_codes.csv b/spec/fixtures/files/lettings_log_csv_export_codes.csv new file mode 100644 index 000000000..e4a1c53e3 --- /dev/null +++ b/spec/fixtures/files/lettings_log_csv_export_codes.csv @@ -0,0 +1,2 @@ +id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,age1_known,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,ecstat1,details_known_2,relat2,age2_known,age2,sex2,ecstat2,details_known_3,relat3,age3_known,age3,sex3,ecstat3,details_known_4,relat4,age4_known,age4,sex4,ecstat4,details_known_5,relat5,age5_known,age5,sex5,ecstat5,details_known_6,relat6,age6_known,age6,sex6,ecstat6,details_known_7,relat7,age7_known,age7,sex7,ecstat7,details_known_8,relat8,age8_known,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_unknown,referral,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +,completed,s.port@jeemayle.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,1,,,2023,DLUHC,DLUHC,1,7,0,2023-06-26T00:00:00+01:00,2,1,,,,HIJKLMN,ABCDEFG,0,,,fake address,,London,,NW9 5LL,false,Barnet,E09000003,0,2,6,2,2,7,1,1,3,2023-06-24T00:00:00+01:00,,,1,2023-06-25T00:00:00+01:00,,3,1,4,,2,,1,2,,0,0,4,0,0,2,35,,F,0,2,13,0,0,P,0,32,M,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,2,1,1,TN23 6LZ,1,false,Ashford,E07000105,1,0,1,0,0,0,0,0,1,,2,0,0,68,1,,6,1,1,,0,2,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,1,0,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_labels.csv b/spec/fixtures/files/lettings_log_csv_export_labels.csv new file mode 100644 index 000000000..3c50ecff0 --- /dev/null +++ b/spec/fixtures/files/lettings_log_csv_export_labels.csv @@ -0,0 +1,2 @@ +id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,old_id,old_form_id,collection_start_year,owning_organisation_name,managing_organisation_name,needstype,lettype,renewal,startdate,renttype,rent_type_detail,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,postcode_full,is_la_inferred,la_label,la,first_time_property_let_as_social_housing,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,vacdays,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,hhmemb,pregnancy_value_check,age1_known,refused,hhtype,totchild,totelder,totadult,age1,retirement_value_check,sex1,ethnic_group,ethnic,national,ecstat1,details_known_2,relat2,age2_known,age2,sex2,ecstat2,details_known_3,relat3,age3_known,age3,sex3,ecstat3,details_known_4,relat4,age4_known,age4,sex4,ecstat4,details_known_5,relat5,age5_known,age5,sex5,ecstat5,details_known_6,relat6,age6_known,age6,sex6,ecstat6,details_known_7,relat7,age7_known,age7,sex7,ecstat7,details_known_8,relat8,age8_known,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,new_old,homeless,ppcodenk,ppostcode_full,previous_la_known,is_previous_la_inferred,prevloc_label,prevloc,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,letting_allocation_unknown,referral,net_income_known,incref,earnings,incfreq,net_income_value_check,hb,has_benefits,benefits,household_charge,nocharge,period,is_carehome,chcharge,wchchrg,carehome_charges_value_check,brent,wrent,rent_value_check,scharge,wscharge,pscharge,wpschrge,supcharg,wsupchrg,tcharge,wtcharge,hbrentshortfall,tshortfall_known,tshortfall,wtshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +,completed,s.port@jeemayle.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,single log,,,2023,DLUHC,DLUHC,General needs,7,No,2023-06-26T00:00:00+01:00,2,Affordable Rent,,,,HIJKLMN,ABCDEFG,No,,,fake address,,London,,NW9 5LL,No,Barnet,E09000003,No,Affordable rent basis,Tenant abandoned property,2,2,House,Purpose built,Yes,3,2023-06-24T00:00:00+01:00,,,Yes,2023-06-25T00:00:00+01:00,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,1,2,,Yes,0,4,0,0,2,35,,Female,White,Irish,Tenant prefers not to say,Other,Yes,Partner,Yes,32,Male,Not seeking work,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,1,0,0,0,0,0,No,Yes,0,0,1,0,0,0,0,0,0,0,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,2,No,Yes,TN23 6LZ,Yes,No,Ashford,E07000105,Yes,0,1,0,0,0,0,0,1,,Tenant applied directly (no referral or nomination),Yes,0,68,Weekly,,Universal Credit housing element,1,All,,0,Every 2 weeks,,,,,200.0,100.0,,50.0,25.0,40.0,20.0,35.0,17.5,325.0,162.5,Yes,Yes,12.0,6.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv new file mode 100644 index 000000000..4cfdc963c --- /dev/null +++ b/spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv @@ -0,0 +1,2 @@ +id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,lettype,renewal,startdate,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,refused,age1,sex1,ethnic_group,ethnic,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,referral,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +,completed,choreographer@owtluk.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,1,2023,DLUHC,DLUHC,7,0,2023-06-26T00:00:00+01:00,,,,HIJKLMN,ABCDEFG,0,,fake address,,London,,NW9 5LL,Barnet,2,6,2,2,7,1,1,3,2023-06-24T00:00:00+01:00,,1,2023-06-25T00:00:00+01:00,,3,1,4,,2,,1,0,35,F,0,2,13,0,P,32,M,6,,,,,,,,,,,,,,,,,,,,,,,,,1,4,1,2,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,2,7,4,,6,1,1,TN23 6LZ,Ashford,1,0,1,0,0,0,0,0,1,2,0,68,1,6,1,1,,0,2,,,,200.0,50.0,40.0,35.0,325.0,1,12.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv b/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv new file mode 100644 index 000000000..bb2230668 --- /dev/null +++ b/spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv @@ -0,0 +1,2 @@ +id,status,created_by,is_dpo,created_at,updated_by,updated_at,creation_method,collection_start_year,owning_organisation_name,managing_organisation_name,lettype,renewal,startdate,irproduct,irproduct_other,lar,tenancycode,propcode,uprn_known,uprn,address_line1,address_line2,town_or_city,county,postcode_full,la_label,unitletas,rsnvac,newprop,offered,unittype_gn,builtype,wchair,beds,voiddate,void_date_value_check,majorrepairs,mrcdate,major_repairs_date_value_check,joint,startertenancy,tenancy,tenancyother,tenancylength,sheltered,declaration,refused,age1,sex1,ethnic_group,ethnic,national,ecstat1,relat2,age2,sex2,ecstat2,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,relat7,age7,sex7,ecstat7,relat8,age8,sex8,ecstat8,armedforces,leftreg,reservist,preg_occ,housingneeds,housingneeds_type,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_f,housingneeds_g,housingneeds_h,housingneeds_other,illness,illness_type_4,illness_type_5,illness_type_2,illness_type_6,illness_type_7,illness_type_3,illness_type_9,illness_type_8,illness_type_1,illness_type_10,layear,waityear,reason,reasonother,prevten,homeless,ppcodenk,ppostcode_full,prevloc_label,reasonpref,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,cbl,cap,chr,referral,incref,earnings,incfreq,hb,has_benefits,benefits,household_charge,nocharge,period,chcharge,wchchrg,carehome_charges_value_check,brent,scharge,pscharge,supcharg,tcharge,hbrentshortfall,tshortfall,scheme_code,scheme_service_name,scheme_sensitive,SCHTYPE,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate +,completed,choreographer@owtluk.com,false,2023-06-26T00:00:00+01:00,,2023-06-26T00:00:00+01:00,single log,2023,DLUHC,DLUHC,7,No,2023-06-26T00:00:00+01:00,,,,HIJKLMN,ABCDEFG,No,,fake address,,London,,NW9 5LL,Barnet,Affordable rent basis,Tenant abandoned property,2,2,House,Purpose built,Yes,3,2023-06-24T00:00:00+01:00,,Yes,2023-06-25T00:00:00+01:00,,Don’t know,Yes,Assured Shorthold Tenancy (AST) – Fixed term,,2,,1,0,35,Female,White,Irish,Tenant prefers not to say,Other,Partner,32,Male,Not seeking work,,,,,,,,,,,,,,,,,,,,,,,,,Yes – the person is a current or former regular,No – they left up to and including 5 years ago,Yes,No,Yes,Fully wheelchair accessible housing,1,0,0,0,0,0,No,Yes,0,0,1,0,0,0,0,0,0,0,Less than 1 year,1 year but under 2 years,Loss of tied accommodation,,Other supported housing,No,Yes,TN23 6LZ,Ashford,Yes,0,1,0,0,0,0,0,1,Tenant applied directly (no referral or nomination),0,68,Weekly,Universal Credit housing element,1,All,,0,Every 2 weeks,,,,200.0,50.0,40.0,35.0,325.0,Yes,12.0,,,,,,,,,,,,,,,,,,,, diff --git a/spec/fixtures/files/lettings_logs_download.csv b/spec/fixtures/files/lettings_logs_download.csv deleted file mode 100644 index 24568524b..000000000 --- a/spec/fixtures/files/lettings_logs_download.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,discarded_at,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/fixtures/files/lettings_logs_download_codes_only.csv b/spec/fixtures/files/lettings_logs_download_codes_only.csv deleted file mode 100644 index 15d671976..000000000 --- a/spec/fixtures/files/lettings_logs_download_codes_only.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,rent_value_check,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,discarded_at,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate} diff --git a/spec/fixtures/files/lettings_logs_download_non_support.csv b/spec/fixtures/files/lettings_logs_download_non_support.csv deleted file mode 100644 index c82dfa589..000000000 --- a/spec/fixtures/files/lettings_logs_download_non_support.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,renewal,startdate,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,relat2,age2,sex2,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,prevloc_label,illness_type_1,illness_type_2,la_label,postcode_full,wchair,preg_occ,cbl,earnings,incfreq,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,unitletas,builtype,voiddate,lettype,nocharge,household_charge,referral,tshortfall,chcharge,ppcodenk,ethnic_group,has_benefits,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,lar,irproduct,joint,sheltered,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,uprn,uprn_known,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate -{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,2 October 2021,,,,,,,,,,,,,,,,,,,,,Westminster,SE1 1TE,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,8,0,,,,,,,0,0,,,,,,,,,,,,,,,,,,,,,,,,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate} diff --git a/spec/fixtures/files/sales_logs_csv_export_codes.csv b/spec/fixtures/files/sales_logs_csv_export_codes.csv index 31d964afe..3467917d6 100644 --- a/spec/fixtures/files/sales_logs_csv_export_codes.csv +++ b/spec/fixtures/files/sales_logs_csv_export_codes.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,old_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,created_by_name,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant -,completed,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,single log,false,DLUHC,Danny Rojas,8,2,2023,,2,8,,,,1,1,2,1,1,0,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,1,2,1,30,X,17,17,18,1,1,P,35,X,17,,13,1,1,6,C,14,X,9,X,18,X,3,R,40,X,2,R,40,X,1,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0 +id,status,created_at,updated_at,old_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant +,completed,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,1,false,DLUHC,billyboy@eyeklaud.com,8,2,2023,,2,8,,,,1,1,2,1,1,0,,,Address line 1,,Town or city,,SW1A,1AA,1,E09000003,Barnet,1,2,1,30,X,17,17,18,1,1,P,35,X,17,,13,1,1,6,C,14,X,9,X,18,X,3,R,40,X,2,R,40,X,1,1,1,,,0,,,1,1,1,1,,3,,1,4,5,1,1,0,10000,1,0,10000,1,4,1,,1,2,10,,,,,,,,,,,,,,,,,110000.0,,1,20000.0,5,,10,1,80000.0,,,1,100.0,,10000.0 diff --git a/spec/fixtures/files/sales_logs_csv_export_labels.csv b/spec/fixtures/files/sales_logs_csv_export_labels.csv index aeda373f3..dc0acf9d2 100644 --- a/spec/fixtures/files/sales_logs_csv_export_labels.csv +++ b/spec/fixtures/files/sales_logs_csv_export_labels.csv @@ -1,2 +1,2 @@ -id,status,created_at,updated_at,old_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,created_by_name,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant -,completed,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,single log,false,DLUHC,Danny Rojas,8,2,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,Yes,,,Address line 1,,Town or city,,SW1A,1AA,Yes,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,17,,13,Full-time - 30 hours or more,Yes,6,Child,14,Non-binary,Child under 16,Other,18,Non-binary,"In government training into work, such as New Deal",Person prefers not to say,40,Non-binary,Part-time - Less than 30 hours,Person prefers not to say,40,Non-binary,Full-time - 30 hours or more,Local authority tenant,No,,,No,,,1,1,1,1,,3,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,Don’t know ,No,,Yes,2,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 +id,status,created_at,updated_at,old_id,collection_start_year,creation_method,is_dpo,owning_organisation_name,created_by,day,month,year,purchid,ownershipsch,type,othtype,companybuy,buylivein,jointpur,jointmore,beds,proptype,builtype,pcodenk,uprn,uprn_confirmed,address_line1,address_line2,town_or_city,county,pcode1,pcode2,la_known,la,la_label,wchair,noint,privacynotice,age1,sex1,ethnic_group,ethnic,national,ecstat1,buy1livein,relat2,age2,sex2,ethnic_group2,ethnicbuy2,nationalbuy2,ecstat2,buy2livein,hholdcount,relat3,age3,sex3,ecstat3,relat4,age4,sex4,ecstat4,relat5,age5,sex5,ecstat5,relat6,age6,sex6,ecstat6,prevten,ppcodenk,ppostc1,ppostc2,previous_la_known,prevloc,prevloc_label,pregyrha,pregother,pregla,pregghb,pregblank,buy2living,prevtenbuy2,hhregres,hhregresstill,armedforcesspouse,disabled,wheel,income1nk,income1,inc1mort,income2nk,income2,inc2mort,hb,savingsnk,savings,prevown,prevshared,proplen,staircase,stairbought,stairowned,staircasesale,resale,exday,exmonth,exyear,hoday,homonth,hoyear,lanomagr,soctenant,frombeds,fromprop,socprevten,value,equity,mortgageused,mortgage,mortgagelender,mortgagelenderother,mortlen,extrabor,deposit,cashdis,mrent,has_mscharge,mscharge,discount,grant +,completed,2023-02-08T00:00:00+00:00,2023-02-08T00:00:00+00:00,,2022,single log,false,DLUHC,billyboy@eyeklaud.com,8,2,2023,,Yes - a discounted ownership scheme,Right to Acquire (RTA),,,,Yes,Yes,2,Flat or maisonette,Purpose built,Yes,,,Address line 1,,Town or city,,SW1A,1AA,Yes,E09000003,Barnet,Yes,Yes,1,30,Non-binary,Buyer 1 prefers not to say,17,United Kingdom,Full-time - 30 hours or more,Yes,Partner,35,Non-binary,17,,13,Full-time - 30 hours or more,Yes,6,Child,14,Non-binary,Child under 16,Other,18,Non-binary,"In government training into work, such as New Deal",Person prefers not to say,40,Non-binary,Part-time - Less than 30 hours,Person prefers not to say,40,Non-binary,Full-time - 30 hours or more,Local authority tenant,No,,,No,,,1,1,1,1,,3,,Yes,Yes,No,Yes,Yes,Yes,10000,Yes,Yes,10000,Yes,"Don’t know ",No,,Yes,2,10,,,,,,,,,,,,,,,,,110000.0,,Yes,20000.0,Cambridge Building Society,,10,Yes,80000.0,,,Yes,100.0,,10000.0 diff --git a/spec/jobs/email_csv_job_spec.rb b/spec/jobs/email_csv_job_spec.rb index 40f13e022..f8677e493 100644 --- a/spec/jobs/email_csv_job_spec.rb +++ b/spec/jobs/email_csv_job_spec.rb @@ -7,186 +7,105 @@ describe EmailCsvJob do let(:job) { described_class.new } let(:user) { FactoryBot.create(:user) } - let(:organisation) { user.organisation } - let(:other_organisation) { FactoryBot.create(:organisation) } - let(:fake_2021_2022_form) { Form.new("spec/fixtures/forms/2021_2022.json") } + let(:storage_service) { instance_double(Storage::S3Service) } + let(:mailer) { instance_double(CsvDownloadMailer) } + let(:sales_log_csv_service) { instance_double(Csv::SalesLogCsvService) } + let(:lettings_log_csv_service) { instance_double(Csv::LettingsLogCsvService) } + let(:search_term) { "meaning" } + let(:filters) { { "user" => "yours", "status" => %w[in_progress] } } + let(:all_orgs) { false } + let(:organisation) { build(:organisation) } + let(:codes_only_export) { true } + let(:lettings_logs) { build_list(:lettings_log, 5) } + let(:sales_logs) { build_list(:sales_log, 5) } before do - allow(FormHandler.instance).to receive(:current_lettings_form).and_return(fake_2021_2022_form) + allow(Storage::S3Service).to receive(:new).and_return(storage_service) + allow(storage_service).to receive(:write_file) + allow(storage_service).to receive(:get_presigned_url).and_return(test_url) + + allow(Csv::SalesLogCsvService).to receive(:new).and_return(sales_log_csv_service) + allow(sales_log_csv_service).to receive(:prepare_csv).and_return("") + + allow(Csv::LettingsLogCsvService).to receive(:new).and_return(lettings_log_csv_service) + allow(lettings_log_csv_service).to receive(:prepare_csv).and_return("") + + allow(CsvDownloadMailer).to receive(:new).and_return(mailer) + allow(mailer).to receive(:send_csv_download_mail) end - context "when a log exists" do - let!(:lettings_log) do - FactoryBot.create( - :lettings_log, - created_by: user, - ecstat1: 1, - ) + context "when exporting lettings logs" do + before do + allow(FilterManager).to receive(:filter_logs).and_return(lettings_logs) + end + + it "uses an appropriate filename in S3" do + expect(storage_service).to receive(:write_file).with(/lettings-logs-.*\.csv/, anything) + job.perform(user) end - let(:storage_service) { instance_double(Storage::S3Service) } - let(:mailer) { instance_double(CsvDownloadMailer) } - let(:sales_log_csv_service) { instance_double(Csv::SalesLogCsvService) } + it "includes the organisation name in the filename when one is provided" do + expect(storage_service).to receive(:write_file).with(/lettings-logs-#{organisation.name}-.*\.csv/, anything) + job.perform(user, nil, {}, nil, organisation) + end + + it "calls the filter manager with the arguments provided" do + expect(FilterManager).to receive(:filter_logs).with(a_kind_of(ActiveRecord::Relation), search_term, filters, all_orgs, user) + job.perform(user, search_term, filters, all_orgs, organisation, codes_only_export) + end + it "creates a LettingsLogCsvService with the correct export type" do + expect(Csv::LettingsLogCsvService).to receive(:new).with(user:, export_type: "labels") + codes_only = false + job.perform(user, nil, {}, nil, nil, codes_only, "lettings") + expect(Csv::LettingsLogCsvService).to receive(:new).with(user:, export_type: "codes") + codes_only = true + job.perform(user, nil, {}, nil, nil, codes_only, "lettings") + end + + it "passes the logs returned by the filter manager to the csv service" do + expect(lettings_log_csv_service).to receive(:prepare_csv).with(lettings_logs) + job.perform(user, nil, {}, nil, nil, codes_only_export) + end + end + + context "when exporting sales logs" do before do - FactoryBot.create(:lettings_log, - :completed, - created_by: user, - startdate: Time.zone.local(2022, 5, 1), - voiddate: Time.zone.local(2022, 5, 1), - mrcdate: Time.zone.local(2022, 5, 1)) - - allow(Storage::S3Service).to receive(:new).and_return(storage_service) - allow(storage_service).to receive(:write_file) - allow(storage_service).to receive(:get_presigned_url).and_return(test_url) - - allow(Csv::SalesLogCsvService).to receive(:new).and_return(sales_log_csv_service) - allow(sales_log_csv_service).to receive(:prepare_csv).and_return("") - - allow(CsvDownloadMailer).to receive(:new).and_return(mailer) - allow(mailer).to receive(:send_csv_download_mail) + allow(FilterManager).to receive(:filter_logs).and_return(sales_logs) end - context "when exporting lettings logs" do - it "uses an appropriate filename in S3" do - expect(storage_service).to receive(:write_file).with(/lettings-logs-.*\.csv/, anything) - job.perform(user) - end + it "uses an appropriate filename in S3" do + expect(storage_service).to receive(:write_file).with(/sales-logs-.*\.csv/, anything) + job.perform(user, nil, {}, nil, nil, nil, "sales") + end - it "includes the organisation name in the filename when one is provided" do - expect(storage_service).to receive(:write_file).with(/lettings-logs-#{organisation.name}-.*\.csv/, anything) - job.perform(user, nil, {}, nil, organisation) - end + it "includes the organisation name in the filename when one is provided" do + expect(storage_service).to receive(:write_file).with(/sales-logs-#{organisation.name}-.*\.csv/, anything) + job.perform(user, nil, {}, nil, organisation, nil, "sales") end - context "when exporting sales logs" do - it "uses an appropriate filename in S3" do - expect(storage_service).to receive(:write_file).with(/sales-logs-.*\.csv/, anything) - job.perform(user, nil, {}, nil, nil, nil, "sales") - end - - it "includes the organisation name in the filename when one is provided" do - expect(storage_service).to receive(:write_file).with(/sales-logs-#{organisation.name}-.*\.csv/, anything) - job.perform(user, nil, {}, nil, organisation, nil, "sales") - end - - it "creates a SalesLogCsvService with the correct export type" do - expect(Csv::SalesLogCsvService).to receive(:new).with(export_type: "labels") - codes_only = false - job.perform(user, nil, {}, nil, nil, codes_only, "sales") - expect(Csv::SalesLogCsvService).to receive(:new).with(export_type: "codes") - codes_only = true - job.perform(user, nil, {}, nil, nil, codes_only, "sales") - end + it "calls the filter manager with the arguments provided" do + expect(FilterManager).to receive(:filter_logs).with(a_kind_of(ActiveRecord::Relation), search_term, filters, all_orgs, user) + job.perform(user, search_term, filters, all_orgs, organisation, codes_only_export, "sales") end - it "sends an E-mail with the presigned URL and duration" do - expect(mailer).to receive(:send_csv_download_mail).with(user, test_url, instance_of(Integer)) - job.perform(user) + it "creates a SalesLogCsvService with the correct export type" do + expect(Csv::SalesLogCsvService).to receive(:new).with(export_type: "labels") + codes_only = false + job.perform(user, nil, {}, nil, nil, codes_only, "sales") + expect(Csv::SalesLogCsvService).to receive(:new).with(export_type: "codes") + codes_only = true + job.perform(user, nil, {}, nil, nil, codes_only, "sales") end - context "when writing to S3" do - before do - FactoryBot.create_list(:lettings_log, 4, owning_organisation: other_organisation) - FactoryBot.create(:lettings_log, owning_organisation: other_organisation, status: "pending", skip_update_status: true) - end - - def expect_csv - expect(storage_service).to receive(:write_file) do |_filename, data| - # Ignore byte order marker - csv = CSV.parse(data[1..]) - yield(csv) - end - end - - it "writes CSV data with headers" do - expect_csv do |csv| - expect(csv.first.first).to eq("id") - expect(csv.second.first).to eq(lettings_log.id.to_s) - end - - job.perform(user) - end - - context "when there is no organisation provided" do - it "only writes logs from the user's organisation" do - expect_csv do |csv| - # Headings + 2 rows - expect(csv.count).to eq(3) - end - - job.perform(user) - end - end - - context "when the user is support and an organisation is provided" do - let(:user) { FactoryBot.create(:user, :support) } - - it "only writes logs from that organisation" do - expect_csv do |csv| - # other organisation => Headings + 4 rows - expect(csv.count).to eq(5) - end - - job.perform(user, nil, {}, nil, other_organisation) - end - end - - it "writes answer labels rather than values" do - expect_csv do |csv| - expect(csv.second[19]).to eq("Full-time – 30 hours or more") - end - - job.perform(user) - end - - it "writes filtered logs" do - expect_csv do |csv| - expect(csv.count).to eq(2) - end - - job.perform(user, nil, { status: "completed" }) - end - - it "writes searched logs" do - expect_csv do |csv| - expect(csv.count).to eq(LettingsLog.search_by(lettings_log.id.to_s).count + 1) - end - - job.perform(user, lettings_log.id.to_s) - end - - context "when both filter and search applied" do - let(:postcode) { "XX1 1TG" } - - before do - FactoryBot.create(:lettings_log, :in_progress, postcode_full: postcode, owning_organisation: organisation, created_by: user) - FactoryBot.create(:lettings_log, :completed, postcode_full: postcode, owning_organisation: organisation, created_by: user) - end - - it "downloads logs matching both csv and filter logs" do - expect_csv do |csv| - expect(csv.count).to eq(2) - end - - job.perform(user, postcode, { status: "completed" }) - end - end - - context "when there are more than 20 logs" do - before do - FactoryBot.create_list(:lettings_log, 26, owning_organisation: organisation) - end - - it "does not paginate, it downloads all the user's logs" do - expect_csv do |csv| - # Heading + 2 + 26 - expect(csv.count).to eq(29) - end - - job.perform(user) - end - end + it "passes the logs returned by the filter manager to the csv service" do + expect(sales_log_csv_service).to receive(:prepare_csv).with(sales_logs) + job.perform(user, nil, {}, nil, nil, codes_only_export, "sales") end end + + it "sends an E-mail with the presigned URL and duration" do + expect(mailer).to receive(:send_csv_download_mail).with(user, test_url, instance_of(Integer)) + job.perform(user) + end end diff --git a/spec/models/form_handler_spec.rb b/spec/models/form_handler_spec.rb index 547ed2f21..0c06e84fd 100644 --- a/spec/models/form_handler_spec.rb +++ b/spec/models/form_handler_spec.rb @@ -270,5 +270,56 @@ RSpec.describe FormHandler do expect(result.map(&:id)).to eq %w[1 1a_deprecated 2 2a_new 3] end end + + describe "#ordered_lettings_questions_for_all_years" do + let(:result) { described_class.instance.ordered_lettings_questions_for_all_years } + let(:now) { Time.zone.now } + + it "returns an array of questions" do + section = build(:section, :with_questions, question_ids: %w[1 2 3]) + lettings_form = FormFactory.new(year: 2936, type: "lettings") + .with_sections([section]) + .build + described_class.instance.use_fake_forms!({ only_lettings: lettings_form }) + expect(result).to(satisfy { |result| result.all? { |element| element.is_a?(Form::Question) } }) + end + + it "does not return multiple questions with the same id" do + first_section = build(:section, :with_questions, question_ids: %w[1 2 3]) + second_section = build(:section, :with_questions, question_ids: %w[2 3 4 5]) + lettings_form = FormFactory.new(year: 2936, type: "lettings") + .with_sections([first_section, second_section]) + .build + described_class.instance.use_fake_forms!({ only_lettings: lettings_form }) + expect(result.map(&:id)).to eq %w[1 2 3 4 5] + end + + it "returns the questions in the same order as the form" do + first_section = build(:section, :with_questions, question_ids: %w[1 2 3]) + second_section = build(:section, :with_questions, question_ids: %w[4 5 6]) + lettings_form = FormFactory.new(year: 2945, type: "lettings") + .with_sections([first_section, second_section]) + .build + described_class.instance.use_fake_forms!({ only_lettings: lettings_form }) + expect(result.map(&:id)).to eq %w[1 2 3 4 5 6] + end + + it "inserts questions from all years in their correct positions" do + original_section = build(:section, :with_questions, question_ids: %w[1 1a_deprecated 2 3]) + new_section = build(:section, :with_questions, question_ids: %w[1 2 2a_new 3]) + original_form = FormFactory.new(year: 2066, type: "lettings") + .with_sections([original_section]) + .build + new_form = FormFactory.new(year: 2485, type: "lettings") + .with_sections([new_section]) + .build + fake_forms = { + earlier_lettings: original_form, + newer_lettings: new_form, + } + described_class.instance.use_fake_forms!(fake_forms) + expect(result.map(&:id)).to eq %w[1 1a_deprecated 2 2a_new 3] + end + end # rubocop:enable RSpec/PredicateMatcher end diff --git a/spec/models/lettings_log_spec.rb b/spec/models/lettings_log_spec.rb index f050e7fcd..990ef2b03 100644 --- a/spec/models/lettings_log_spec.rb +++ b/spec/models/lettings_log_spec.rb @@ -2857,115 +2857,6 @@ RSpec.describe LettingsLog do end end - describe "csv download" do - let(:scheme) { create(:scheme) } - let(:location) do - create( - :location, - :export, - scheme:, - type_of_unit: 6, - postcode: "SE11TE", - startdate: Time.zone.local(2021, 10, 1), - ) - end - let(:user) { create(:user, organisation: location.scheme.owning_organisation) } - let(:expected_content) { csv_export_file.read } - - after do - Timecop.unfreeze - end - - context "with values represented as human readable labels" do - before do - Timecop.freeze(Time.utc(2022, 6, 5)) - lettings_log = FactoryBot.create( - :lettings_log, - needstype: 2, - scheme:, - location:, - owning_organisation: scheme.owning_organisation, - created_by: user, - rent_type: 2, - startdate: Time.zone.local(2021, 10, 2), - created_at: Time.zone.local(2022, 2, 8, 16, 52, 15), - updated_at: Time.zone.local(2022, 2, 8, 16, 52, 15), - ) - expected_content.sub!(/\{id\}/, lettings_log["id"].to_s) - expected_content.sub!(/\{scheme_code\}/, "S#{scheme['id']}") - expected_content.sub!(/\{scheme_service_name\}/, scheme["service_name"].to_s) - expected_content.sub!(/\{scheme_sensitive\}/, scheme["sensitive"].to_s) - expected_content.sub!(/\{scheme_primary_client_group\}/, scheme["primary_client_group"].to_s) - expected_content.sub!(/\{scheme_secondary_client_group\}/, scheme["secondary_client_group"].to_s) - expected_content.sub!(/\{scheme_support_type\}/, scheme["support_type"].to_s) - expected_content.sub!(/\{scheme_intended_stay\}/, scheme["intended_stay"].to_s) - expected_content.sub!(/\{location_code\}/, location["id"].to_s) - expected_content.sub!(/\{location_startdate\}/, location["startdate"].to_s) - expected_content.sub!(/\{scheme_id\}/, scheme["service_name"].to_s) - expected_content.sub!(/\{location_id\}/, location["id"].to_s) - end - - around do |example| - Timecop.freeze(Time.zone.local(2022, 6, 5)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end - - context "with a support user" do - let(:csv_export_file) { File.open("spec/fixtures/files/lettings_logs_download.csv", "r:UTF-8") } - - it "generates a correct csv from a lettings log" do - expect(described_class.to_csv(codes_only_export: false)).to eq(expected_content) - end - end - - context "with a non support user" do - let(:csv_export_file) { File.open("spec/fixtures/files/lettings_logs_download_non_support.csv", "r:UTF-8") } - - it "generates a correct csv from a lettings log" do - expect(described_class.to_csv(user, codes_only_export: false)).to eq(expected_content) - end - end - end - - context "with values represented as codes" do - before do - Timecop.freeze(Time.utc(2022, 6, 5)) - lettings_log = FactoryBot.create(:lettings_log, needstype: 2, scheme:, location:, owning_organisation: scheme.owning_organisation, created_by: user, rent_type: 2, startdate: Time.zone.local(2021, 10, 2), created_at: Time.zone.local(2022, 2, 8, 16, 52, 15), updated_at: Time.zone.local(2022, 2, 8, 16, 52, 15)) - expected_content.sub!(/\{id\}/, lettings_log["id"].to_s) - expected_content.sub!(/\{scheme_code\}/, "S#{scheme.id}") - expected_content.sub!(/\{scheme_service_name\}/, scheme.service_name.to_s) - expected_content.sub!(/\{scheme_sensitive\}/, scheme.sensitive_before_type_cast.to_s) - expected_content.sub!(/\{scheme_primary_client_group\}/, scheme.primary_client_group_before_type_cast.to_s) - expected_content.sub!(/\{scheme_secondary_client_group\}/, scheme.secondary_client_group_before_type_cast.to_s) - expected_content.sub!(/\{scheme_support_type\}/, scheme.support_type_before_type_cast.to_s) - expected_content.sub!(/\{scheme_intended_stay\}/, scheme.intended_stay_before_type_cast.to_s) - expected_content.sub!(/\{location_code\}/, location.id.to_s) - expected_content.sub!(/\{location_startdate\}/, location.startdate.to_s) - expected_content.sub!(/\{scheme_id\}/, scheme.service_name.to_s) - expected_content.sub!(/\{location_id\}/, location.id.to_s) - end - - let(:csv_export_file) { File.open("spec/fixtures/files/lettings_logs_download_codes_only.csv", "r:UTF-8") } - - around do |example| - Timecop.freeze(Time.zone.local(2022, 6, 5)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end - - it "generates a correct csv from a lettings log" do - expect(described_class.to_csv(codes_only_export: true)).to eq(expected_content) - end - end - end - describe "#blank_invalid_non_setup_fields!" do context "when a setup field is invalid" do subject(:model) { described_class.new(needstype: 404) } diff --git a/spec/services/bulk_upload/lettings/log_creator_spec.rb b/spec/services/bulk_upload/lettings/log_creator_spec.rb index 87ac6e320..7739da110 100644 --- a/spec/services/bulk_upload/lettings/log_creator_spec.rb +++ b/spec/services/bulk_upload/lettings/log_creator_spec.rb @@ -27,6 +27,12 @@ RSpec.describe BulkUpload::Lettings::LogCreator do expect(log.bulk_upload).to eql(bulk_upload) expect(bulk_upload.lettings_logs).to include(log) end + + it "sets the creation method" do + service.call + + expect(LettingsLog.last.creation_method).to eq "bulk upload" + end end context "when a valid csv with several blank rows" do diff --git a/spec/services/bulk_upload/sales/log_creator_spec.rb b/spec/services/bulk_upload/sales/log_creator_spec.rb index 7e0082311..16bf25c66 100644 --- a/spec/services/bulk_upload/sales/log_creator_spec.rb +++ b/spec/services/bulk_upload/sales/log_creator_spec.rb @@ -15,8 +15,6 @@ RSpec.describe BulkUpload::Sales::LogCreator do Singleton.__init__(FormHandler) example.run end - Timecop.return - Singleton.__init__(FormHandler) end context "when a valid csv with new log" do @@ -36,6 +34,12 @@ RSpec.describe BulkUpload::Sales::LogCreator do expect(log.bulk_upload).to eql(bulk_upload) expect(bulk_upload.sales_logs).to include(log) end + + it "sets the creation method" do + service.call + + expect(SalesLog.last.creation_method).to eq "bulk upload" + end end context "when a valid csv with several blank rows" do diff --git a/spec/services/csv/lettings_log_csv_service_spec.rb b/spec/services/csv/lettings_log_csv_service_spec.rb index 04d52065d..f21ebfe4e 100644 --- a/spec/services/csv/lettings_log_csv_service_spec.rb +++ b/spec/services/csv/lettings_log_csv_service_spec.rb @@ -1,430 +1,227 @@ require "rails_helper" RSpec.describe Csv::LettingsLogCsvService do - context "when the user is support" do - let(:user) { create(:user, :support) } - let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } + let(:form_handler_mock) { instance_double(FormHandler) } + let(:organisation) { create(:organisation) } + let(:fixed_time) { Time.zone.local(2023, 6, 26) } + let(:log) do + create( + :lettings_log, + :completed, + startdate: fixed_time, + created_at: fixed_time, + updated_at: fixed_time, + mrcdate: fixed_time - 1.day, + voiddate: fixed_time - 2.days, + propcode: "ABCDEFG", + tenancycode: "HIJKLMN", + postcode_full: "NW9 5LL", + ppostcode_full: "TN23 6LZ", + created_by: user, + managing_organisation: organisation, + ) + end + let(:user) { create(:user, :support, email: "s.port@jeemayle.com") } + let(:service) { described_class.new(user:, export_type:) } + let(:export_type) { "labels" } + let(:csv) { CSV.parse(service.prepare_csv(LettingsLog.where(id: logs.map(&:id)))) } + let(:logs) { [log] } + let(:headers) { csv.first } + + it "calls the form handler to get all questions in order when initialized" do + allow(FormHandler).to receive(:instance).and_return(form_handler_mock) + allow(form_handler_mock).to receive(:ordered_lettings_questions_for_all_years).and_return([]) + service + expect(form_handler_mock).to have_received(:ordered_lettings_questions_for_all_years) + end + + it "returns a string" do + result = service.prepare_csv(LettingsLog.all) + expect(result).to be_a String + end + + it "returns a csv with headers" do + expect(csv.first.first).to eq "id" + end + + context "when stubbing :ordered_lettings_questions_for_all_years" do + let(:lettings_form) do + FormFactory.new(year: 2050, type: "lettings") + .with_sections([build(:section, :with_questions, question_ids:, questions:)]) + .build + end + let(:question_ids) { nil } + let(:questions) { nil } before do - LettingsLog.create!(startdate: Time.zone.today, created_at: Time.utc(2022, 2, 8, 16, 52, 15)) - allow(FormHandler.instance).to receive(:get_form).and_return(real_2021_2022_form) + allow(FormHandler).to receive(:instance).and_return(form_handler_mock) + allow(form_handler_mock).to receive(:form_name_from_start_year) + allow(form_handler_mock).to receive(:get_form).and_return(lettings_form) + allow(form_handler_mock).to receive(:ordered_lettings_questions_for_all_years).and_return(lettings_form.questions) + allow(form_handler_mock).to receive(:lettings_in_crossover_period?).and_return false + end + + context "when it returns questions with particular ids" do + let(:question_ids) { %w[prevten startdate brent rent_type] } + + it "includes log attributes related to questions to the headers" do + expect(headers).to include(*question_ids.first(3)) + end + + it "removes some log attributes related to questions from the headers and replaces them with their derived values in the correct order" do + expect(headers).not_to include "rent_type" + expect(headers).to include(*%w[wrent renttype rent_type_detail]) + end end - it "sets csv attributes in correct order" do - expected_csv_attributes = %w[ - id - status - created_at - updated_at - created_by_name - is_dpo - owning_organisation_name - managing_organisation_name - collection_start_year - needstype - renewal - startdate - rent_type_detail - irproduct_other - tenancycode - propcode - postcode_known - postcode_full - is_la_inferred - la_label - la - first_time_property_let_as_social_housing - unitletas - rsnvac - offered - unittype_gn - builtype - wchair - beds - voiddate - void_date_value_check - majorrepairs - mrcdate - major_repairs_date_value_check - startertenancy - tenancy - tenancyother - tenancylength - sheltered - declaration - hhmemb - pregnancy_value_check - age1_known - age1 - sex1 - ethnic_group - ethnic - national - ecstat1 - retirement_value_check - details_known_2 - relat2 - age2_known - age2 - sex2 - ecstat2 - details_known_3 - relat3 - age3_known - age3 - sex3 - ecstat3 - details_known_4 - relat4 - age4_known - age4 - sex4 - ecstat4 - details_known_5 - relat5 - age5_known - age5 - sex5 - ecstat5 - details_known_6 - relat6 - age6_known - age6 - sex6 - ecstat6 - details_known_7 - relat7 - age7_known - age7 - sex7 - ecstat7 - details_known_8 - relat8 - age8_known - age8 - sex8 - ecstat8 - armedforces - leftreg - reservist - preg_occ - housingneeds - housingneeds_type - housingneeds_other - illness - illness_type_4 - illness_type_5 - illness_type_2 - illness_type_6 - illness_type_7 - illness_type_3 - illness_type_9 - illness_type_8 - illness_type_1 - illness_type_10 - layear - waityear - reason - reasonother - prevten - underoccupation_benefitcap - homeless - ppcodenk - ppostcode_full - previous_la_known - is_previous_la_inferred - prevloc_label - prevloc - reasonpref - rp_homeless - rp_insan_unsat - rp_medwel - rp_hardship - rp_dontknow - cbl - cap - chr - letting_allocation_unknown - referral - net_income_known - earnings - incfreq - net_income_value_check - hb - benefits - household_charge - period - is_carehome - chcharge - carehome_charges_value_check - brent - scharge - pscharge - supcharg - tcharge - rent_value_check - hbrentshortfall - tshortfall_known - tshortfall - housingneeds_a - housingneeds_b - housingneeds_c - housingneeds_f - housingneeds_g - housingneeds_h - property_owner_organisation - property_manager_organisation - purchaser_code - property_relet - incref - renttype - lettype - totchild - totelder - totadult - nocharge - has_benefits - wrent - wscharge - wpschrge - wsupchrg - wtcharge - wtshortfall - refused - wchchrg - newprop - old_form_id - lar - irproduct - old_id - joint - hhtype - new_old - vacdays - unresolved - updated_by_id - uprn - uprn_known - uprn_confirmed - address_line1 - address_line2 - town_or_city - county - status_cache - discarded_at - unittype_sh - scheme_code - scheme_service_name - scheme_sensitive - scheme_type - scheme_registered_under_care_act - scheme_owning_organisation_name - scheme_primary_client_group - scheme_has_other_client_group - scheme_secondary_client_group - scheme_support_type - scheme_intended_stay - scheme_created_at - location_code - location_postcode - location_name - location_units - location_type_of_unit - location_mobility_type - location_admin_district - location_startdate - ] - - csv = CSV.parse(described_class.new(user, export_type: "labels").to_csv) - - expect(csv.first).to eq(expected_csv_attributes) + context "when it returns checkbox questions" do + let(:questions) do + [ + build(:question, id: "condition_effects", type: "checkbox", answer_options: { "illness_type_1" => {}, "illness_type_2" => {}, "illness_type_3" => {} }), + build(:question, id: "letting_allocation", type: "checkbox", answer_options: { "cbl" => {}, "cap" => {}, "chr" => {} }), + ] + end + + it "does not add the id of the checkbox question to the headers" do + question_ids = questions.map(&:id) + expect(headers).not_to include(*question_ids) + end + + it "adds the related log attributes from the answer options to the headers" do + log_attributes = questions.flat_map { |q| q.answer_options.keys } + expect(headers).to include(*log_attributes) + end end end - context "when the user is not support" do - let(:user) { FactoryBot.create(:user) } - let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") } + it "adds log attributes not related to questions to the headers" do + expect(headers.first(5)).to eq %w[id status created_by is_dpo created_at] + end - before do - LettingsLog.create!(startdate: Time.zone.today, created_at: Time.utc(2022, 2, 8, 16, 52, 15)) - allow(FormHandler.instance).to receive(:get_form).and_return(real_2021_2022_form) + it "adds attributes related to associated schemes and locations to the headers" do + expect(headers).to include(*%w[scheme_service_name scheme_sensitive SCHTYPE scheme_registered_under_care_act]) + expect(headers.last(5)).to eq %w[location_units location_type_of_unit location_mobility_type location_admin_district location_startdate] + end + + context "when there are many logs" do + let(:logs) { create_list(:lettings_log, log_count) } + let(:log_count) { 30 } + + it "creates a CSV with the correct number of logs" do + expected_row_count_with_headers = log_count + 1 + expect(csv.size).to be expected_row_count_with_headers + end + end + + context "when exporting with human readable labels" do + let(:export_type) { "labels" } + + it "gives answer to radio questions as labels" do + relat2_column_index = csv.first.index("relat2") + relat2_value = csv.second[relat2_column_index] + expect(relat2_value).to eq "Partner" + end + + it "gives answers to free input questions as the user input" do + age1_column_index = csv.first.index("age1") + age1_value = csv.second[age1_column_index] + expect(age1_value).to eq 35.to_s end - it "sets csv attributes in correct order and without omitted values" do - expected_csv_attributes = %w[ - id - status - created_at - updated_at - created_by_name - is_dpo - owning_organisation_name - managing_organisation_name - collection_start_year - renewal - startdate - irproduct_other - tenancycode - propcode - postcode_full - la_label - unitletas - rsnvac - offered - unittype_gn - builtype - wchair - beds - voiddate - void_date_value_check - majorrepairs - mrcdate - major_repairs_date_value_check - startertenancy - tenancy - tenancyother - tenancylength - sheltered - declaration - age1 - sex1 - ethnic_group - ethnic - national - ecstat1 - relat2 - age2 - sex2 - ecstat2 - relat3 - age3 - sex3 - ecstat3 - relat4 - age4 - sex4 - ecstat4 - relat5 - age5 - sex5 - ecstat5 - relat6 - age6 - sex6 - ecstat6 - relat7 - age7 - sex7 - ecstat7 - relat8 - age8 - sex8 - ecstat8 - armedforces - leftreg - reservist - preg_occ - housingneeds - housingneeds_type - housingneeds_other - illness - illness_type_4 - illness_type_5 - illness_type_2 - illness_type_6 - illness_type_7 - illness_type_3 - illness_type_9 - illness_type_8 - illness_type_1 - illness_type_10 - layear - waityear - reason - reasonother - prevten - underoccupation_benefitcap - homeless - ppcodenk - ppostcode_full - prevloc_label - reasonpref - rp_homeless - rp_insan_unsat - rp_medwel - rp_hardship - rp_dontknow - cbl - cap - chr - referral - earnings - incfreq - hb - benefits - household_charge - period - chcharge - carehome_charges_value_check - brent - scharge - pscharge - supcharg - tcharge - hbrentshortfall - tshortfall - housingneeds_a - housingneeds_b - housingneeds_c - housingneeds_f - housingneeds_g - housingneeds_h - property_owner_organisation - property_manager_organisation - purchaser_code - property_relet - incref - lettype - nocharge - has_benefits - refused - wchchrg - newprop - lar - irproduct - joint - uprn - uprn_known - address_line1 - address_line2 - town_or_city - county - unittype_sh - scheme_code - scheme_service_name - scheme_sensitive - scheme_type - scheme_registered_under_care_act - scheme_owning_organisation_name - scheme_primary_client_group - scheme_has_other_client_group - scheme_secondary_client_group - scheme_support_type - scheme_intended_stay - scheme_created_at - location_code - location_postcode - location_name - location_units - location_type_of_unit - location_mobility_type - location_admin_district - location_startdate - ] - - csv = CSV.parse(described_class.new(user, export_type: "labels").to_csv) - - expect(csv.first).to eq(expected_csv_attributes) + it "exports the code for the local authority under the heading 'la'" do + la_column_index = csv.first.index("la") + la_value = csv.second[la_column_index] + expect(la_value).to eq "E09000003" + end + + it "exports the label for the local authority under the heading 'la_label'" do + la_label_column_index = csv.first.index("la_label") + la_label_value = csv.second[la_label_column_index] + expect(la_label_value).to eq "Barnet" + end + + it "exports the CSV with all values correct" do + expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_labels.csv") + values_to_delete = %w[id vacdays] + values_to_delete.each do |attribute| + index = csv.first.index(attribute) + csv.second[index] = nil + end + expect(csv).to eq expected_content + end + end + + context "when exporting as codes" do + let(:export_type) { "codes" } + + it "gives answer to radio questions as labels" do + relat2_column_index = csv.first.index("relat2") + relat2_value = csv.second[relat2_column_index] + expect(relat2_value).to eq "P" + end + + it "gives answers to free input questions as the user input" do + age1_column_index = csv.first.index("age1") + age1_value = csv.second[age1_column_index] + expect(age1_value).to eq 35.to_s + end + + it "exports the code for the local authority under the heading 'la'" do + la_column_index = csv.first.index("la") + la_value = csv.second[la_column_index] + expect(la_value).to eq "E09000003" + end + + it "exports the label for the local authority under the heading 'la_label'" do + la_label_column_index = csv.first.index("la_label") + la_label_value = csv.second[la_label_column_index] + expect(la_label_value).to eq "Barnet" + end + + it "exports the CSV with all values correct" do + expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_codes.csv") + values_to_delete = %w[id vacdays] + values_to_delete.each do |attribute| + index = csv.first.index(attribute) + csv.second[index] = nil + end + expect(csv).to eq expected_content + end + end + + context "when the user is not a support user" do + let(:user) { create(:user, email: "choreographer@owtluk.com") } + + it "does not include certain attributes in the headers" do + expect(headers).not_to include(*%w[rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge]) + end + + context "and exporting with labels" do + let(:export_type) { "labels" } + + it "exports the CSV with all values correct" do + expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_labels.csv") + values_to_delete = %w[id] + values_to_delete.each do |attribute| + index = csv.first.index(attribute) + csv.second[index] = nil + end + expect(csv).to eq expected_content + end + end + + context "and exporting values as codes" do + let(:export_type) { "codes" } + + it "exports the CSV with all values correct" do + expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_non_support_codes.csv") + values_to_delete = %w[id] + values_to_delete.each do |attribute| + index = csv.first.index(attribute) + csv.second[index] = nil + end + expect(csv).to eq expected_content + end end end end diff --git a/spec/services/csv/sales_log_csv_service_spec.rb b/spec/services/csv/sales_log_csv_service_spec.rb index 2c24bf7b1..f037603ff 100644 --- a/spec/services/csv/sales_log_csv_service_spec.rb +++ b/spec/services/csv/sales_log_csv_service_spec.rb @@ -3,23 +3,27 @@ require "rails_helper" RSpec.describe Csv::SalesLogCsvService do let(:form_handler_mock) { instance_double(FormHandler) } let(:organisation) { create(:organisation) } - let!(:log) { create(:sales_log, :completed, owning_organisation: organisation, purchid: nil) } + let(:fixed_time) { Time.zone.local(2023, 2, 8) } + let(:user) { create(:user, email: "billyboy@eyeKLAUD.com") } + let!(:log) do + create( + :sales_log, + :completed, + created_by: user, + saledate: fixed_time, + created_at: fixed_time, + updated_at: fixed_time, + owning_organisation: organisation, + purchid: nil, + ) + end let(:service) { described_class.new(export_type: "labels") } let(:csv) { CSV.parse(service.prepare_csv(SalesLog.all)) } - around do |example| - Timecop.freeze(Time.utc(2023, 2, 8)) do - Singleton.__init__(FormHandler) - example.run - end - Timecop.return - Singleton.__init__(FormHandler) - end - it "calls the form handler to get all questions in order when initialized" do allow(FormHandler).to receive(:instance).and_return(form_handler_mock) allow(form_handler_mock).to receive(:ordered_sales_questions_for_all_years).and_return([]) - described_class.new(export_type: "codes") + service expect(form_handler_mock).to have_received(:ordered_sales_questions_for_all_years) end @@ -30,7 +34,6 @@ RSpec.describe Csv::SalesLogCsvService do it "returns a csv with headers" do expect(csv.first.first).to eq "id" - expect(csv.second.first).not_to be log.id.to_s end context "when stubbing :ordered_sales_questions_for_all_years" do