Browse Source

Merge a4ec6d951b into 99e3d81093

pull/3136/merge
Samuel Young 4 days ago committed by GitHub
parent
commit
64920b88fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      app/models/form/lettings/subsections/property_information.rb
  2. 4
      app/services/exports/lettings_log_export_constants.rb
  3. 3
      app/services/exports/lettings_log_export_service.rb
  4. 175
      spec/fixtures/exports/general_needs_log_26_27.xml
  5. 3
      spec/fixtures/files/lettings_log_csv_export_codes_26.csv
  6. 3
      spec/fixtures/files/lettings_log_csv_export_labels_26.csv
  7. 3
      spec/fixtures/files/lettings_log_csv_export_non_support_codes_26.csv
  8. 3
      spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv
  9. 56
      spec/models/form/lettings/subsections/property_information_spec.rb
  10. 16
      spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb
  11. 192
      spec/services/csv/lettings_log_csv_service_spec.rb
  12. 36
      spec/services/exports/lettings_log_export_service_spec.rb

2
app/models/form/lettings/subsections/property_information.rb

@ -15,7 +15,7 @@ class Form::Lettings::Subsections::PropertyInformation < ::Form::Subsection
(first_let_questions unless form.start_year_2025_or_later?),
number_of_times_relet,
Form::Lettings::Pages::PropertyUnitType.new(nil, nil, self),
Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self),
(Form::Lettings::Pages::PropertyBuildingType.new(nil, nil, self) unless form.start_year_2026_or_later?),
Form::Lettings::Pages::PropertyWheelchairAccessible.new(nil, nil, self),
Form::Lettings::Pages::PropertyNumberOfBedrooms.new(nil, nil, self),
Form::Lettings::Pages::RentValueCheck.new("beds_rent_value_check", nil, self),

4
app/services/exports/lettings_log_export_constants.rb

@ -199,4 +199,8 @@ module Exports::LettingsLogExportConstants
"carehome_charges_value_check",
"chcharge"
]
PRE_2026_EXPORT_FIELDS = Set[
"builtype"
]
end

3
app/services/exports/lettings_log_export_service.rb

@ -170,7 +170,8 @@ module Exports
!EXPORT_FIELDS.include?(field_name) ||
(lettings_log.form.start_year_2024_or_later? && PRE_2024_EXPORT_FIELDS.include?(field_name)) ||
(!lettings_log.form.start_year_2024_or_later? && POST_2024_EXPORT_FIELDS.include?(field_name)) ||
(lettings_log.form.start_year_2025_or_later? && PRE_2025_EXPORT_FIELDS.include?(field_name))
(lettings_log.form.start_year_2025_or_later? && PRE_2025_EXPORT_FIELDS.include?(field_name)) ||
(lettings_log.form.start_year_2026_or_later? && PRE_2026_EXPORT_FIELDS.include?(field_name))
end
def build_export_xml(lettings_logs)

175
spec/fixtures/exports/general_needs_log_26_27.xml vendored

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<forms>
<form>
<status>2</status>
<tenancycode>BZ737</tenancycode>
<age1>35</age1>
<sex1>F</sex1>
<ethnic>2</ethnic>
<prevten>6</prevten>
<ecstat1>0</ecstat1>
<hhmemb>2</hhmemb>
<age2>32</age2>
<sex2>M</sex2>
<ecstat2>6</ecstat2>
<age3/>
<sex3/>
<ecstat3/>
<age4/>
<sex4/>
<ecstat4/>
<age5/>
<sex5/>
<ecstat5/>
<age6/>
<sex6/>
<ecstat6/>
<age7/>
<sex7/>
<ecstat7/>
<age8/>
<sex8/>
<ecstat8/>
<homeless>1</homeless>
<underoccupation_benefitcap>4</underoccupation_benefitcap>
<leftreg>4</leftreg>
<reservist>1</reservist>
<illness>1</illness>
<preg_occ>2</preg_occ>
<startertenancy>1</startertenancy>
<tenancylength>5</tenancylength>
<tenancy>4</tenancy>
<ppostcode_full>A1 1AA</ppostcode_full>
<rsnvac>6</rsnvac>
<unittype_gn>7</unittype_gn>
<beds>3</beds>
<wchair>1</wchair>
<earnings>268</earnings>
<incfreq>1</incfreq>
<benefits>1</benefits>
<period>2</period>
<layear>2</layear>
<waityear>7</waityear>
<postcode_full>AA1 1AA</postcode_full>
<reasonpref>1</reasonpref>
<cbl>0</cbl>
<chr>1</chr>
<cap>0</cap>
<reasonother/>
<housingneeds_a>1</housingneeds_a>
<housingneeds_b>0</housingneeds_b>
<housingneeds_c>0</housingneeds_c>
<housingneeds_f>0</housingneeds_f>
<housingneeds_g>0</housingneeds_g>
<housingneeds_h>0</housingneeds_h>
<illness_type_1>0</illness_type_1>
<illness_type_2>1</illness_type_2>
<illness_type_3>0</illness_type_3>
<illness_type_4>0</illness_type_4>
<illness_type_8>0</illness_type_8>
<illness_type_5>0</illness_type_5>
<illness_type_6>0</illness_type_6>
<illness_type_7>0</illness_type_7>
<illness_type_9>0</illness_type_9>
<illness_type_10>0</illness_type_10>
<rp_homeless>0</rp_homeless>
<rp_insan_unsat>1</rp_insan_unsat>
<rp_medwel>0</rp_medwel>
<rp_hardship>0</rp_hardship>
<rp_dontknow>0</rp_dontknow>
<tenancyother/>
<net_income_value_check/>
<irproduct_other/>
<reason>4</reason>
<propcode>123</propcode>
<la>E09000033</la>
<prevloc>E07000105</prevloc>
<hb>6</hb>
<hbrentshortfall>1</hbrentshortfall>
<mrcdate>2022-05-05T10:36:49+01:00</mrcdate>
<incref>0</incref>
<startdate>2026-04-03T00:00:00+01:00</startdate>
<armedforces>1</armedforces>
<unitletas>2</unitletas>
<voiddate>2021-11-03T00:00:00+00:00</voiddate>
<renttype>2</renttype>
<needstype>1</needstype>
<lettype>7</lettype>
<totchild>0</totchild>
<totelder>0</totelder>
<totadult>2</totadult>
<nocharge/>
<referral>2</referral>
<brent>200.0</brent>
<scharge>50.0</scharge>
<pscharge>40.0</pscharge>
<supcharg>35.0</supcharg>
<tcharge>325.0</tcharge>
<tshortfall>12.0</tshortfall>
<ppcodenk>0</ppcodenk>
<has_benefits>1</has_benefits>
<renewal>0</renewal>
<wrent>100.0</wrent>
<wscharge>25.0</wscharge>
<wpschrge>20.0</wpschrge>
<wsupchrg>17.5</wsupchrg>
<wtcharge>162.5</wtcharge>
<wtshortfall>6.0</wtshortfall>
<refused>0</refused>
<housingneeds>1</housingneeds>
<wchchrg/>
<newprop>2</newprop>
<relat2>P</relat2>
<relat3/>
<relat4/>
<relat5/>
<relat6/>
<relat7/>
<relat8/>
<rent_value_check/>
<lar>2</lar>
<irproduct/>
<joint>3</joint>
<sheltered/>
<hhtype>4</hhtype>
<new_old>2</new_old>
<vacdays>1429</vacdays>
<bulk_upload_id>1</bulk_upload_id>
<uprn>1</uprn>
<uprn_known>1</uprn_known>
<uprn_confirmed>1</uprn_confirmed>
<address_line1>1, Test Street</address_line1>
<address_line2/>
<town_or_city>Test Town</town_or_city>
<county/>
<discarded_at/>
<creation_method>2</creation_method>
<supcharg_value_check/>
<scharge_value_check/>
<pscharge_value_check/>
<duplicate_set_id/>
<accessible_register>0</accessible_register>
<nationality_all>826</nationality_all>
<address_line1_as_entered>address line 1 as entered</address_line1_as_entered>
<address_line2_as_entered>address line 2 as entered</address_line2_as_entered>
<town_or_city_as_entered>town or city as entered</town_or_city_as_entered>
<county_as_entered>county as entered</county_as_entered>
<postcode_full_as_entered>AB1 2CD</postcode_full_as_entered>
<la_as_entered>la as entered</la_as_entered>
<formid>{id}</formid>
<owningorgid>{owning_org_id}</owningorgid>
<owningorgname>{owning_org_name}</owningorgname>
<hcnum>1234</hcnum>
<maningorgid>{managing_org_id}</maningorgid>
<maningorgname>{managing_org_name}</maningorgname>
<manhcnum>1234</manhcnum>
<createddate>2026-04-03T00:00:00+01:00</createddate>
<uploaddate>2026-04-03T00:00:00+01:00</uploaddate>
<log_id>{log_id}</log_id>
<assigned_to>test1@example.com</assigned_to>
<created_by>test1@example.com</created_by>
<amended_by/>
<renttype_detail>2</renttype_detail>
<providertype>1</providertype>
</form>
</forms>

3
spec/fixtures/files/lettings_log_csv_export_codes_26.csv vendored

File diff suppressed because one or more lines are too long

3
spec/fixtures/files/lettings_log_csv_export_labels_26.csv vendored

File diff suppressed because one or more lines are too long

3
spec/fixtures/files/lettings_log_csv_export_non_support_codes_26.csv vendored

File diff suppressed because one or more lines are too long

3
spec/fixtures/files/lettings_log_csv_export_non_support_labels_26.csv vendored

File diff suppressed because one or more lines are too long

56
spec/models/form/lettings/subsections/property_information_spec.rb

@ -15,22 +15,23 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
allow(form).to receive(:start_year_2026_or_later?).and_return(true)
end
context "when 2023" do
let(:start_date) { Time.utc(2023, 2, 8) }
context "when 2024" do
let(:start_date) { Time.utc(2024, 4, 8) }
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(false)
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(form).to receive(:start_year_2026_or_later?).and_return(false)
end
it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq(
%w[
uprn
uprn_confirmation
address_search
address
property_local_authority
local_authority_rent_value_check
@ -38,7 +39,6 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
property_let_type
property_vacancy_reason_not_first_let
property_vacancy_reason_first_let
property_number_of_times_relet
property_unit_type
property_building_type
property_wheelchair_accessible
@ -51,27 +51,36 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
],
)
end
context "when it is supported housing and a renewal" do
let(:log) { FactoryBot.build(:lettings_log, needstype: 2, renewal: 1) }
it "is not displayed in tasklist" do
expect(property_information.displayed_in_tasklist?(log)).to eq(false)
end
end
end
context "when 2024" do
let(:start_date) { Time.utc(2024, 2, 8) }
context "when 2025" do
let(:start_date) { Time.utc(2025, 4, 8) }
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(false)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
allow(form).to receive(:start_year_2026_or_later?).and_return(false)
end
it "has correct pages" do
expect(property_information.pages.map(&:id)).to eq(
%w[
address_search
address
property_local_authority
local_authority_rent_value_check
first_time_property_let_as_social_housing
property_let_type
property_vacancy_reason_not_first_let
property_vacancy_reason_first_let
address_search
address
property_local_authority
local_authority_rent_value_check
property_unit_type
property_building_type
property_wheelchair_accessible
@ -81,6 +90,7 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
void_date_value_check
property_major_repairs
property_major_repairs_value_check
sheltered_accommodation
],
)
end
@ -88,18 +98,19 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
context "when it is supported housing and a renewal" do
let(:log) { FactoryBot.build(:lettings_log, needstype: 2, renewal: 1) }
it "is not displayed in tasklist" do
expect(property_information.displayed_in_tasklist?(log)).to eq(false)
it "is displayed in tasklist" do
expect(property_information.displayed_in_tasklist?(log)).to eq(true)
end
end
end
context "when 2025" do
let(:start_date) { Time.utc(2025, 2, 8) }
context "when 2026" do
let(:start_date) { Time.utc(2026, 4, 8) }
before do
allow(form).to receive(:start_year_2024_or_later?).and_return(true)
allow(form).to receive(:start_year_2025_or_later?).and_return(true)
allow(form).to receive(:start_year_2026_or_later?).and_return(true)
end
it "has correct pages" do
@ -114,7 +125,6 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
property_local_authority
local_authority_rent_value_check
property_unit_type
property_building_type
property_wheelchair_accessible
property_number_of_bedrooms
beds_rent_value_check
@ -126,14 +136,6 @@ RSpec.describe Form::Lettings::Subsections::PropertyInformation, type: :model do
],
)
end
context "when it is supported housing and a renewal" do
let(:log) { FactoryBot.build(:lettings_log, needstype: 2, renewal: 1) }
it "is displayed in tasklist" do
expect(property_information.displayed_in_tasklist?(log)).to eq(true)
end
end
end
end

16
spec/services/bulk_upload/lettings/year2026/row_parser_spec.rb

@ -1678,22 +1678,6 @@ RSpec.describe BulkUpload::Lettings::Year2026::RowParser do
end
end
describe "#field_27" do
context "when null" do
let(:attributes) { setup_section_params.merge({ field_27: nil }) }
it "returns an error" do
parser.valid?
expect(parser.errors[:field_27]).to be_present
end
it "populates with correct error message" do
parser.valid?
expect(parser.errors[:field_27]).to eql([I18n.t("validations.lettings.2026.bulk_upload.not_answered", question: "type of building.")])
end
end
end
describe "#field_48" do # age2
context "when null but gender given" do
let(:attributes) { setup_section_params.merge({ field_48: "", field_49: "F" }) }

192
spec/services/csv/lettings_log_csv_service_spec.rb

@ -194,6 +194,198 @@ RSpec.describe Csv::LettingsLogCsvService do
end
describe "the full CSV output" do
context "when the requested log year is 2026" do
let(:year) { 2026 }
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(2026, 4, 1),
updated_at: Time.zone.local(2026, 4, 1),
owning_organisation: organisation,
managing_organisation: organisation,
needstype: 1,
renewal: 0,
startdate: Time.zone.local(2026, 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,
wchair: 1,
beds: 3,
voiddate: Time.zone.local(2026, 3, 30),
majorrepairs: 1,
mrcdate: Time.zone.local(2026, 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 all values correct" do
expected_content = CSV.read("spec/fixtures/files/lettings_log_csv_export_labels_26.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_26.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_26.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_26.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 2025" do
let(:year) { 2025 }
let(:organisation) { create(:organisation, provider_type: "LA", name: "MHCLG") }

36
spec/services/exports/lettings_log_export_service_spec.rb

@ -419,7 +419,7 @@ RSpec.describe Exports::LettingsLogExportService do
end
end
context "with 24/25 collection period" do
context "with 24/25 collection period", metadata: { year: 24 } do
let(:start_time) { Time.zone.local(2024, 4, 3) }
before do
@ -451,7 +451,7 @@ RSpec.describe Exports::LettingsLogExportService do
end
end
context "with 25/26 collection period" do
context "with 25/26 collection period", metadata: { year: 25 } do
let(:start_time) { Time.zone.local(2025, 4, 3) }
before do
@ -483,6 +483,38 @@ RSpec.describe Exports::LettingsLogExportService do
end
end
context "with 26/27 collection period", metadata: { year: 26 } do
let(:start_time) { Time.zone.local(2026, 4, 3) }
before do
Timecop.freeze(start_time)
Singleton.__init__(FormHandler)
end
after do
Timecop.unfreeze
Singleton.__init__(FormHandler)
end
context "and one lettings log is available for export" do
let!(:lettings_log) { FactoryBot.create(:lettings_log, :completed, startdate: Time.zone.local(2026, 4, 3), assigned_to: user, age1: 35, sex1: "F", age2: 32, sex2: "M", ppostcode_full: "A1 1AA", nationality_all_group: 13, propcode: "123", postcode_full: "SE2 6RT", tenancycode: "BZ737", voiddate: Time.zone.local(2021, 11, 3), mrcdate: Time.zone.local(2022, 5, 5, 10, 36, 49), tenancylength: 5, underoccupation_benefitcap: 4, creation_method: 2, bulk_upload_id: 1, 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", manual_address_entry_selected: false, uprn: "1", uprn_known: 1) }
let(:expected_zip_filename) { "core_2026_2027_apr_mar_f0001_inc0001.zip" }
let(:expected_data_filename) { "core_2026_2027_apr_mar_f0001_inc0001_pt001.xml" }
let(:xml_export_file) { File.open("spec/fixtures/exports/general_needs_log_26_27.xml", "r:UTF-8") }
it "generates an XML export file with the expected content within the ZIP file" do
expected_content = replace_entity_ids(lettings_log, xml_export_file.read)
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content|
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename)
expect(entry).not_to be_nil
expect(entry.get_input_stream.read).to have_same_xml_contents_as(expected_content)
end
export_service.export_xml_lettings_logs
end
end
end
context "and one lettings log has not been updated in the time range" do
let(:expected_zip_filename) { "core_#{current_collection_start_year}_#{current_collection_end_year}_apr_mar_f0001_inc0001.zip" }
let(:start_time) { current_collection_start_date }

Loading…
Cancel
Save