From 61eb52516dbc4592a4507f0a12e1afe77b61699d Mon Sep 17 00:00:00 2001 From: Samuel Young Date: Mon, 16 Mar 2026 12:17:37 +0000 Subject: [PATCH] CLDC-4204: Update bulk upload fields sales (#3223) * CLDC-4204: Renumber fields to match spec * CLDC-4204: Update log to csv for 2026 * CLDC-4204: Cleanup BU todo commments * CLDC-4204: Replace BU example file * CLDC-4204: Update row parser question titles to match form * fixup! CLDC-4204: Update row parser question titles to match form fix sales log typo Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com> * fixup! CLDC-4204: Renumber fields to match spec update dupe fields comment Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com> * fixup! CLDC-4204: Renumber fields to match spec check the same field as before --------- Co-authored-by: Oscar Richardson <116292912+oscar-richardson-softwire@users.noreply.github.com> --- .../bulk_upload/lettings_log_to_csv.rb | 1 - app/helpers/bulk_upload/sales_log_to_csv.rb | 76 +- .../lettings/year2026/csv_parser.rb | 4 +- .../bulk_upload/sales/year2026/csv_parser.rb | 4 +- .../bulk_upload/sales/year2026/row_parser.rb | 1058 ++++++++--------- .../files/2026_27_sales_bulk_upload.csv | 32 +- .../bulk_upload/sales/validator_spec.rb | 2 +- .../sales/year2026/csv_parser_spec.rb | 2 +- .../sales/year2026/row_parser_spec.rb | 504 ++++---- 9 files changed, 837 insertions(+), 846 deletions(-) diff --git a/app/helpers/bulk_upload/lettings_log_to_csv.rb b/app/helpers/bulk_upload/lettings_log_to_csv.rb index 11f60773f..29c7b5ec5 100644 --- a/app/helpers/bulk_upload/lettings_log_to_csv.rb +++ b/app/helpers/bulk_upload/lettings_log_to_csv.rb @@ -94,7 +94,6 @@ class BulkUpload::LettingsLogToCsv end def to_2026_row - # TODO: CLDC-4162: Implement when 2026 format is known [ overrides[:organisation_id] || log.owning_organisation&.old_visible_id, # 1 overrides[:managing_organisation_id] || log.managing_organisation&.old_visible_id, diff --git a/app/helpers/bulk_upload/sales_log_to_csv.rb b/app/helpers/bulk_upload/sales_log_to_csv.rb index 19e34eea0..cf6d910ce 100644 --- a/app/helpers/bulk_upload/sales_log_to_csv.rb +++ b/app/helpers/bulk_upload/sales_log_to_csv.rb @@ -533,7 +533,6 @@ class BulkUpload::SalesLogToCsv end def to_2026_row - # TODO: CLDC-4162: Implement when 2026 template is available [ log.saledate&.day, log.saledate&.month, @@ -552,53 +551,66 @@ class BulkUpload::SalesLogToCsv log.privacynotice, log.uprn, - log.address_line1&.tr(",", " "), # 20 + log.address_line1&.tr(",", " "), log.address_line2&.tr(",", " "), log.town_or_city&.tr(",", " "), - log.county&.tr(",", " "), + log.county&.tr(",", " "), # 20 ((log.postcode_full || "").split(" ") || [""]).first, ((log.postcode_full || "").split(" ") || [""]).last, log.la, log.proptype, + log.buildheightclass, log.beds, log.builtype, log.wchair, log.age1, - log.sexrab1, - log.ethnic, # 30 + log.sexrab1, # 30 + log.gender_same_as_sex1, + log.gender_description1, + log.ethnic, log.nationality_all_group, log.ecstat1, log.buy1livein, { "P" => 1, "X" => 2, "R" => 3 }[log.relat2], log.age2, log.sexrab2, + log.gender_same_as_sex2, # 40 + log.gender_description2, log.ethnic_group2, log.nationality_all_buyer2_group, log.ecstat2, - log.buy2livein, # 40 + log.buy2livein, log.hholdcount, { "P" => 1, "X" => 2, "R" => 3 }[log.relat3], log.age3, log.sexrab3, + log.gender_same_as_sex3, # 50 + log.gender_description3, log.ecstat3, { "P" => 1, "X" => 2, "R" => 3 }[log.relat4], log.age4, log.sexrab4, + log.gender_same_as_sex4, + log.gender_description4, log.ecstat4, - { "P" => 1, "X" => 2, "R" => 3 }[log.relat5], # 50 - log.age5, + { "P" => 1, "X" => 2, "R" => 3 }[log.relat5], + log.age5, # 60 log.sexrab5, + log.gender_same_as_sex5, + log.gender_description5, log.ecstat5, { "P" => 1, "X" => 2, "R" => 3 }[log.relat6], log.age6, log.sexrab6, - log.ecstat6, + log.gender_same_as_sex6, + log.gender_description6, + log.ecstat6, # 70 log.prevten, log.ppcodenk, - ((log.ppostcode_full || "").split(" ") || [""]).first, # 60 + ((log.ppostcode_full || "").split(" ") || [""]).first, ((log.ppostcode_full || "").split(" ") || [""]).last, log.prevloc, log.buy2living, @@ -606,32 +618,32 @@ class BulkUpload::SalesLogToCsv log.hhregres, log.hhregresstill, - log.armedforcesspouse, + log.armedforcesspouse, # 80 log.disabled, log.wheel, - log.income1, # 70 + log.income1, log.inc1mort, log.income2, log.inc2mort, log.hb, log.savings.present? || "R", log.prevown, - log.prevshared, + log.prevshared, # 90 log.resale, log.proplen, - log.hodate&.day, # 80 + log.hodate&.day, log.hodate&.month, log.hodate&.strftime("%y"), log.frombeds, log.fromprop, log.socprevten, log.value, - log.equity, + log.equity, # 100 log.mortgageused, log.mortgage, - log.mortlen, # 90 + log.mortlen, log.deposit, log.cashdis, log.mrent, @@ -639,48 +651,34 @@ class BulkUpload::SalesLogToCsv log.management_fee, log.stairbought, - log.stairowned, + log.stairowned, # 110 log.staircasesale, log.firststair, - log.initialpurchase&.day, # 100 + log.initialpurchase&.day, log.initialpurchase&.month, log.initialpurchase&.strftime("%y"), log.numstair, log.lasttransaction&.day, log.lasttransaction&.month, log.lasttransaction&.strftime("%y"), - log.value, + log.value, # 120 log.equity, log.mortgageused, - log.mrentprestaircasing, # 110 + log.mrentprestaircasing, log.mrent, + log.hasservicechargeschanged, + log.newservicecharges, log.proplen, log.value, log.grant, - log.discount, + log.discount, # 130 log.mortgageused, log.mortgage, log.mortlen, log.extrabor, - log.deposit, # 120 - - log.mscharge, - log.buildheightclass, - log.gender_same_as_sex1, - log.gender_description1, - log.gender_same_as_sex2, - log.gender_description2, - log.gender_same_as_sex3, - log.gender_description3, - log.gender_same_as_sex4, - log.gender_description4, # 130 - log.gender_same_as_sex5, - log.gender_description5, - log.gender_same_as_sex6, - log.gender_description6, - log.hasservicechargeschanged, - log.newservicecharges, # 136 + log.deposit, + log.mscharge, # 136 ] end diff --git a/app/services/bulk_upload/lettings/year2026/csv_parser.rb b/app/services/bulk_upload/lettings/year2026/csv_parser.rb index d2f1ab7b1..90b009f50 100644 --- a/app/services/bulk_upload/lettings/year2026/csv_parser.rb +++ b/app/services/bulk_upload/lettings/year2026/csv_parser.rb @@ -3,7 +3,6 @@ require "csv" class BulkUpload::Lettings::Year2026::CsvParser include CollectionTimeHelper - # TODO: CLDC-4162: Update when 2026 format is known FIELDS = 147 FORM_YEAR = 2026 @@ -26,8 +25,7 @@ class BulkUpload::Lettings::Year2026::CsvParser end def cols - # TODO: CLDC-4162: Update when 2026 format is known - @cols ||= ("A".."FA").to_a + @cols ||= ("A".."ER").to_a end def row_parsers diff --git a/app/services/bulk_upload/sales/year2026/csv_parser.rb b/app/services/bulk_upload/sales/year2026/csv_parser.rb index efe89d1e8..9101959c1 100644 --- a/app/services/bulk_upload/sales/year2026/csv_parser.rb +++ b/app/services/bulk_upload/sales/year2026/csv_parser.rb @@ -3,7 +3,6 @@ require "csv" class BulkUpload::Sales::Year2026::CsvParser include CollectionTimeHelper - # TODO: CLDC-4162: Update when 2026 format is known FIELDS = 136 FORM_YEAR = 2026 @@ -26,8 +25,7 @@ class BulkUpload::Sales::Year2026::CsvParser end def cols - # TODO: CLDC-4162: Update when 2026 format is known - @cols ||= ("A".."EF").to_a + @cols ||= ("A".."EG").to_a end def row_parsers diff --git a/app/services/bulk_upload/sales/year2026/row_parser.rb b/app/services/bulk_upload/sales/year2026/row_parser.rb index ec1517f02..5d7fb8ad5 100644 --- a/app/services/bulk_upload/sales/year2026/row_parser.rb +++ b/app/services/bulk_upload/sales/year2026/row_parser.rb @@ -5,179 +5,177 @@ class BulkUpload::Sales::Year2026::RowParser include FormattingHelper QUESTIONS = { - field_1: "What is the day of the sale completion date? - DD", - field_2: "What is the month of the sale completion date? - MM", - field_3: "What is the year of the sale completion date? - YY", + field_1: "What is the sale completion date? - day DD", + field_2: "What is the sale completion date? - month MM", + field_3: "What is the sale completion date? - year YY", field_4: "Which organisation owned this property before the sale?", field_5: "Which organisation is reporting this sale?", - field_6: "Username", + field_6: "What is the CORE username of the account this sales log should be assigned to?", field_7: "What is the purchaser code?", field_8: "Is this a shared ownership or discounted ownership sale?", field_9: "What is the type of shared ownership sale?", field_10: "Is this a staircasing transaction?", field_11: "What is the type of discounted ownership sale?", field_12: "Is this a joint purchase?", - field_13: "Are there more than two joint purchasers of this property?", - field_14: "Was the buyer interviewed for any of the answers you will provide on this log?", - field_15: "Data Protection question", + field_13: "Are there more than 2 joint buyers of this property?", + field_14: "Did you interview the buyer to answer these questions?", + field_15: "Has the buyer seen or been given access to the MHCLG privacy notice?", field_16: "If known, enter this property’s UPRN", - field_17: "Address line 1", - field_18: "Address line 2", + field_17: "Address Line 1", + field_18: "Address Line 2", field_19: "Town or city", field_20: "County", - field_21: "Part 1 of postcode of property", - field_22: "Part 2 of postcode of property", - field_23: "What is the local authority of the property?", + field_21: "Part 1 of the property's postcode", + field_22: "Part 2 of the property's postcode", + field_23: "What is the property's local authority?", field_24: "What type of unit is the property?", - field_25: "How many bedrooms does the property have?", - field_26: "Which type of building is the property?", - field_27: "Is the property built or adapted to wheelchair user standards?", - - field_28: "Age of buyer 1", - field_29: "Buyer 1's sex, as registered at birth", - field_30: "What is buyer 1’s ethnic group?", - field_31: "What is buyer 1’s nationality?", - field_32: "Working situation of buyer 1", - field_33: "Will buyer 1 live in the property?", - field_34: "Is buyer 2 or person 2 the partner of buyer 1?", - field_35: "Age of person 2", - field_36: "Buyer/Person 2's sex, as registered at birth", - field_37: "Which of the following best describes buyer 2’s ethnic background?", - field_38: "What is buyer 2’s nationality?", - field_39: "What is buyer 2 or person 2’s working situation?", - field_40: "Will buyer 2 live in the property?", - field_41: "In total, how many people live in the property?", - - field_42: "Is person 3 the partner of buyer 1?", - field_43: "Age of person 3", - field_44: "Person 3's sex, as registered at birth", - field_45: "Working situation of person 3", - field_46: "Is person 4 the partner of buyer 1?", - field_47: "Age of person 4", - field_48: "Person 4's sex, as registered at birth", - field_49: "Working situation of person 4", - field_50: "Is person 5 the partner of buyer 1?", - field_51: "Age of person 5", - field_52: "Person 5's sex, as registered at birth", - field_53: "Working situation of person 5", - field_54: "Is person 6 the partner of buyer 1?", - field_55: "Age of person 6", - field_56: "Person 6's sex, as registered at birth", - field_57: "Working situation of person 6", - - field_58: "What was buyer 1’s previous tenure?", - field_59: "Do you know the postcode of buyer 1’s last settled home?", - field_60: "Part 1 of postcode of buyer 1’s last settled home", - field_61: "Part 2 of postcode of buyer 1’s last settled home", - field_62: "What is the local authority of buyer 1’s last settled home?", - field_63: "At the time of purchase, was buyer 2 living at the same address as buyer 1?", - field_64: "What was buyer 2’s previous tenure?", - - field_65: "Has the buyer ever served in the UK Armed Forces and for how long?", - field_66: "Is the buyer still serving in the UK armed forces?", - field_67: "Are any of the buyers a spouse or civil partner of a UK Armed Forces regular who died in service within the last 2 years?", - field_68: "Does anyone in the household consider themselves to have a disability?", - field_69: "Does anyone in the household use a wheelchair?", - - field_70: "What is buyer 1’s gross annual income?", - field_71: "Was buyer 1’s income used for a mortgage application?", - field_72: "What is buyer 2’s gross annual income?", - field_73: "Was buyer 2’s income used for a mortgage application?", - field_74: "Were the buyers receiving any of these housing-related benefits immediately before buying this property?", - field_75: "What is the total amount the buyers had in savings before they paid any deposit for the property?", - field_76: "Have any of the purchasers previously owned a property?", - field_77: "Was the previous property under shared ownership?", - - field_78: "Is this a resale?", - field_79: "How long have the buyers been living in the property before the purchase? - Shared ownership", - field_80: "What is the day of the practical completion or handover date?", - field_81: "What is the month of the practical completion or handover date?", - field_82: "What is the year of the practical completion or handover date?", - field_83: "How many bedrooms did the buyer’s previous property have?", - field_84: "What was the type of the buyer’s previous property?", - field_85: "What was the rent type of the buyer’s previous property?", - field_86: "What was the full purchase price?", - field_87: "What was the initial percentage share purchased?", - field_88: "Was a mortgage used for the purchase of this property? - Shared ownership", - field_89: "What is the mortgage amount?", - field_90: "What is the length of the mortgage in years? - Shared ownership", - field_91: "How much was the cash deposit paid on the property?", - field_92: "How much cash discount was given through Social Homebuy?", - field_93: "What is the basic monthly rent?", - field_94: "What are the total monthly service charges for the property?", - field_95: "What are the total monthly estate management fees for the property?", - - field_96: "What percentage of the property has been bought in this staircasing transaction?", - field_97: "What percentage of the property does the buyer now own in total?", - field_98: "Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?", - field_99: "Is this the first time the buyer has engaged in staircasing in the home?", - field_100: "What was the day of the initial purchase of a share in the property? DD", - field_101: "What was the month of the initial purchase of a share in the property? MM", - field_102: "What was the year of the initial purchase of a share in the property? YYYY", - field_103: "Including this time, how many times has the shared owner engaged in staircasing in the home?", - field_104: "What was the day of the last staircasing transaction? DD", - field_105: "What was the month of the last staircasing transaction? MM", - field_106: "What was the year of the last staircasing transaction? YYYY", - field_107: "What is the full purchase price for this staircasing transaction?", - field_108: "What was the percentage share purchased in the initial transaction?", - field_109: "Was a mortgage used for this staircasing transaction?", - field_110: "What was the basic monthly rent prior to staircasing?", - field_111: "What is the basic monthly rent after staircasing?", - - field_112: "How long have the buyers been living in the property before the purchase? - Discounted ownership", - field_113: "What was the full purchase price?", - field_114: "What was the amount of any loan, grant, discount or subsidy given?", - field_115: "What was the percentage discount?", - field_116: "Was a mortgage used for the purchase of this property? - Discounted ownership", - field_117: "What is the mortgage amount?", - field_118: "What is the length of the mortgage in years? - Discounted ownership", - field_119: "Does this include any extra borrowing?", - field_120: "How much was the cash deposit paid on the property?", - field_121: "What are the total monthly leasehold charges for the property?", - - field_122: "What is the building height classification?", - field_123: "Is the gender buyer 1 identifies with the same as their sex registered at birth?", - field_124: "If 'No', enter buyer 1's gender identity", - field_125: "Is the gender buyer/person 2 identifies with the same as their sex registered at birth?", - field_126: "If 'No', enter buyer/person 2's gender identity", - field_127: "Is the gender person 3 identifies with the same as their sex registered at birth?", - field_128: "If 'No', enter person 3's gender identity", - field_129: "Is the gender person 4 identifies with the same as their sex registered at birth?", - field_130: "If 'No', enter person 4's gender identity", - field_131: "Is the gender person 5 identifies with the same as their sex registered at birth?", - field_132: "If 'No', enter person 5's gender identity", - field_133: "Is the gender person 6 identifies with the same as their sex registered at birth?", - field_134: "If 'No', enter person 6's gender identity", - field_135: "Will the service charge change after this staircasing transaction takes place?", - field_136: "What are the new total monthly service charges for the property?", + field_25: "What is the building height classification?", + field_26: "How many bedrooms does the property have?", + field_27: "Which type of building is the property?", + field_28: "Is the property built or adapted to wheelchair-user standards?", + + field_29: "What is buyer 1’s age?", + field_30: "What is buyer 1's sex?", + field_31: "Is the gender buyer 1 identifies with the same as their sex registered at birth?", + field_32: "If 'No', enter buyer 1's gender identity", + field_33: "Which of the following best describes buyer 1's ethnic background?", + field_34: "What is buyer 1's nationality?", + field_35: "Which of these best describes buyer 1’s working situation?", + field_36: "Will buyer 1 live in the property?", + field_37: "Is buyer 2 or person 2 the partner of buyer 1?", + field_38: "What is buyer 2's or person 2's age?", + field_39: "What is buyer 2 or person 2's sex?", + field_40: "Is the gender buyer 2 or person 2 identifies with the same as their sex registered at birth?", + field_41: "If 'No', enter buyer 2 or person 2's gender identity", + field_42: "Which of the following best describes buyer 2 or person 2's ethnic background?", + field_43: "What is buyer 2 or person 2's nationality?", + field_44: "Which of these best describes buyer 2 or person 2’s working situation?", + field_45: "Will buyer 2 or person 2 live in the property?", + field_46: "In total, how many people live in the property?", + + field_47: "Is person 3 the partner of buyer 1?", + field_48: "What is person 3's age?", + field_49: "What is person 3's sex?", + field_50: "Is the gender person 3's identifies with the same as their sex registered at birth?", + field_51: "If 'No', enter person 3's gender identity", + field_52: "Which of these best describes person 3’s working situation?", + field_53: "Is person 4 the partner of buyer 1?", + field_54: "What is person 4's age?", + field_55: "What is person 4's sex?", + field_56: "Is the gender person 4's identifies with the same as their sex registered at birth?", + field_57: "If 'No', enter person 4's gender identity", + field_58: "Which of these best describes person 4’s working situation?", + field_59: "Is person 5 the partner of buyer 1?", + field_60: "What is person 5's age?", + field_61: "What is person 5's sex?", + field_62: "Is the gender person 5's identifies with the same as their sex registered at birth?", + field_63: "If 'No', enter person 5's gender identity", + field_64: "Which of these best describes person 5’s working situation?", + field_65: "Is person 6 the partner of buyer 1?", + field_66: "What is person 6's age?", + field_67: "What is person 6's sex?", + field_68: "Is the gender person 6's identifies with the same as their sex registered at birth?", + field_69: "If 'No', enter person 6's gender identity", + field_70: "Which of these best describes person 6’s working situation?", + field_71: "What was buyer 1's previous tenure?", + field_72: "Do you know the postcode of buyer 1's last settled accommodation?", + field_73: "Part 1 of postcode of buyer 1's last settled accommodation", + field_74: "Part 2 of postcode of buyer 1's last settled accommodation", + field_75: "What is the local authority of buyer 1's last settled accommodation?", + field_76: "At the time of purchase, was buyer 2 living at the same address as buyer 1?", + field_77: "What was buyer 2's previous tenure?", + + field_78: "Have any of the buyers ever served as a regular in the UK armed forces?", + field_79: "Is the buyer still serving in the UK armed forces?", + field_80: "Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?", + field_81: "Does anyone in the household consider themselves to have a disability?", + field_82: "Does anyone in the household use a wheelchair?", + + field_83: "What is buyer 1's annual income?", + field_84: "Was buyer 1's income used for a mortgage application?", + field_85: "What is buyer 2's annual income?", + field_86: "Was buyer 2's income used for a mortgage application?", + field_87: "Were the buyers receiving any of these housing-related benefits immediately before buying this property?", + field_88: "What is the total amount the buyers had in savings before they paid any deposit for the property?", + field_89: "Have any of the buyers previously owned a property?", + field_90: "Was the previous property under shared ownership?", + + field_91: "Is this a resale?", + field_92: "How long did the buyer(s) live in the property before purchasing it?", + field_93: "What is the day of the practical completion or handover date? - DD", + field_94: "What is the month of the practical completion or handover date? - MM", + field_95: "What is the year of the practical completion or handover date? - YY", + field_96: "How many bedrooms did the buyer's previous property have?", + field_97: "What was the previous property type?", + field_98: "What was the buyer’s previous tenure?", + field_99: "What is the full purchase price?", + field_100: "What was the initial percentage share purchased?", + field_101: "Was a mortgage used to buy this property?", + field_102: "What is the mortgage amount?", + field_103: "What is the length of the mortgage?", + field_104: "How much was the cash deposit paid on the property?", + field_105: "How much cash discount was given through Social HomeBuy?", + field_106: "What is the basic monthly rent?", + field_107: "What are the total monthly service charges for the property?", + field_108: "What are the total monthly estate management fees for the property?", + + field_109: "What percentage of the property has been bought in this staircasing transaction?", + field_110: "What percentage of the property do the buyers now own in total?", + field_111: "Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?", + field_112: "Is this the first time the buyer has engaged in staircasing in the home?", + field_113: "What was the day of the initial purchase of a share in the property? DD", + field_114: "What was the month of the initial purchase of a share in the property? MM", + field_115: "What was the year of the initial purchase of a share in the property? YYYY", + field_116: "Including this time, how many times has the shared owner engaged in staircasing in the home?", + field_117: "What was the day of the last staircasing transaction? DD", + field_118: "What was the month of the last staircasing transaction? MM", + field_119: "What was the year of the last staircasing transaction? YYYY", + field_120: "What is the full purchase price for this staircasing transaction?", + field_121: "What was the percentage share purchased in the initial transaction?", + field_122: "Was a mortgage used for this staircasing transaction?", + field_123: "What was the basic monthly rent prior to staircasing?", + field_124: "What is the basic monthly rent after staircasing?", + field_125: "What are the monthly service charges for the property?", + field_126: "If the service charges will change after this staircasing transaction takes places, what is the new monthly service charge amount?", + + field_127: "How long did the buyer(s) live in the property before purchasing it?", + field_128: "What is the full purchase price?", + field_129: "What was the amount of any loan, grant, discount or subsidy given?", + field_130: "What was the percentage discount?", + field_131: "Was a mortgage used to buy this property?", + field_132: "What is the mortgage amount?", + field_133: "What is the length of the mortgage?", + field_134: "Does this include any extra borrowing?", + field_135: "How much was the cash deposit paid on the property?", + field_136: "What are the total monthly leasehold charges for the property?", }.freeze ERROR_BASE_KEY = "validations.sales.2026.bulk_upload".freeze CASE_INSENSITIVE_FIELDS = [ - :field_28, # Age of buyer 1 - :field_35, # Age of person 2 - :field_43, # Age of person 3 - :field_47, # Age of person 4 - :field_51, # Age of person 5 - :field_55, # Age of person 6 - - :field_29, # Buyer 1's sex, as registered at birth - :field_36, # Buyer/Person 2's sex, as registered at birth - :field_44, # Person 3's sex, as registered at birth - :field_48, # Person 4's sex, as registered at birth - :field_52, # Person 5's sex, as registered at birth - :field_56, # Person 6's sex, as registered at birth - - :field_64, # What was buyer 2’s previous tenure? - - :field_75, # What is the total amount the buyers had in savings before they paid any deposit for the property? - :field_70, # What is buyer 1’s gross annual income? - :field_72, # What is buyer 2’s gross annual income? - - :field_90, # What is the length of the mortgage in years? - Shared ownership - :field_118, # What is the length of the mortgage in years? - Discounted ownership + :field_29, # Age of buyer 1 + :field_38, # Age of buyer/person 2 + :field_48, # Age of person 3 + :field_54, # Age of person 4 + :field_60, # Age of person 5 + :field_66, # Age of person 6 + + :field_30, # Buyer 1's sex, as registered at birth + :field_39, # Buyer/Person 2's sex, as registered at birth + :field_49, # Person 3's sex, as registered at birth + :field_55, # Person 4's sex, as registered at birth + :field_61, # Person 5's sex, as registered at birth + :field_67, # Person 6's sex, as registered at birth + + :field_77, # What was buyer 2’s previous tenure? + + :field_88, # What is the total amount the buyers had in savings before they paid any deposit for the property? + :field_83, # What is buyer 1’s gross annual income? + :field_85, # What is buyer 2’s gross annual income? + + :field_103, # What is the length of the mortgage in years? - Shared ownership + :field_133, # What is the length of the mortgage in years? - Discounted ownership ].freeze attribute :bulk_upload @@ -213,124 +211,122 @@ class BulkUpload::Sales::Year2026::RowParser attribute :field_25, :integer attribute :field_26, :integer attribute :field_27, :integer + attribute :field_28, :integer - attribute :field_28, :string attribute :field_29, :string - attribute :field_30, :integer + attribute :field_30, :string attribute :field_31, :integer - attribute :field_32, :integer + attribute :field_32, :string attribute :field_33, :integer attribute :field_34, :integer - attribute :field_35, :string - attribute :field_36, :string + attribute :field_35, :integer + attribute :field_36, :integer attribute :field_37, :integer - attribute :field_38, :integer - attribute :field_39, :integer + attribute :field_38, :string + attribute :field_39, :string attribute :field_40, :integer - attribute :field_41, :integer - + attribute :field_41, :string attribute :field_42, :integer - attribute :field_43, :string - attribute :field_44, :string + attribute :field_43, :integer + attribute :field_44, :integer attribute :field_45, :integer attribute :field_46, :integer - attribute :field_47, :string + + attribute :field_47, :integer attribute :field_48, :string - attribute :field_49, :integer + attribute :field_49, :string attribute :field_50, :integer attribute :field_51, :string - attribute :field_52, :string + attribute :field_52, :integer attribute :field_53, :integer - attribute :field_54, :integer + attribute :field_54, :string attribute :field_55, :string - attribute :field_56, :string - attribute :field_57, :integer - + attribute :field_56, :integer + attribute :field_57, :string attribute :field_58, :integer attribute :field_59, :integer attribute :field_60, :string attribute :field_61, :string - attribute :field_62, :string - attribute :field_63, :integer - attribute :field_64, :string - + attribute :field_62, :integer + attribute :field_63, :string + attribute :field_64, :integer attribute :field_65, :integer - attribute :field_66, :integer - attribute :field_67, :integer + attribute :field_66, :string + attribute :field_67, :string attribute :field_68, :integer - attribute :field_69, :integer + attribute :field_69, :string + attribute :field_70, :integer - attribute :field_70, :string attribute :field_71, :integer - attribute :field_72, :string - attribute :field_73, :integer - attribute :field_74, :integer + attribute :field_72, :integer + attribute :field_73, :string + attribute :field_74, :string attribute :field_75, :string attribute :field_76, :integer - attribute :field_77, :integer + attribute :field_77, :string attribute :field_78, :integer attribute :field_79, :integer attribute :field_80, :integer attribute :field_81, :integer attribute :field_82, :integer - attribute :field_83, :integer + + attribute :field_83, :string attribute :field_84, :integer - attribute :field_85, :integer - attribute :field_86, :decimal - attribute :field_87, :decimal - attribute :field_88, :integer - attribute :field_89, :decimal - attribute :field_90, :string - attribute :field_91, :decimal - attribute :field_92, :decimal - attribute :field_93, :decimal - attribute :field_94, :decimal - attribute :field_95, :decimal - - attribute :field_96, :decimal - attribute :field_97, :decimal + attribute :field_85, :string + attribute :field_86, :integer + attribute :field_87, :integer + attribute :field_88, :string + attribute :field_89, :integer + attribute :field_90, :integer + + attribute :field_91, :integer + attribute :field_92, :integer + attribute :field_93, :integer + attribute :field_94, :integer + attribute :field_95, :integer + attribute :field_96, :integer + attribute :field_97, :integer attribute :field_98, :integer - attribute :field_99, :integer - attribute :field_100, :integer + attribute :field_99, :decimal + attribute :field_100, :decimal attribute :field_101, :integer - attribute :field_102, :integer - attribute :field_103, :integer - attribute :field_104, :integer - attribute :field_105, :integer - attribute :field_106, :integer + attribute :field_102, :decimal + attribute :field_103, :string + attribute :field_104, :decimal + attribute :field_105, :decimal + attribute :field_106, :decimal attribute :field_107, :decimal attribute :field_108, :decimal - attribute :field_109, :integer - attribute :field_110, :decimal - attribute :field_111, :decimal + attribute :field_109, :decimal + attribute :field_110, :decimal + attribute :field_111, :integer attribute :field_112, :integer - attribute :field_113, :decimal + attribute :field_113, :integer attribute :field_114, :integer - attribute :field_115, :decimal + attribute :field_115, :integer attribute :field_116, :integer - attribute :field_117, :decimal - attribute :field_118, :string + attribute :field_117, :integer + attribute :field_118, :integer attribute :field_119, :integer attribute :field_120, :decimal attribute :field_121, :decimal attribute :field_122, :integer - - attribute :field_123, :integer - attribute :field_124, :string + attribute :field_123, :decimal + attribute :field_124, :decimal attribute :field_125, :integer - attribute :field_126, :string + attribute :field_126, :decimal + attribute :field_127, :integer - attribute :field_128, :string + attribute :field_128, :decimal attribute :field_129, :integer - attribute :field_130, :string + attribute :field_130, :decimal attribute :field_131, :integer - attribute :field_132, :string - attribute :field_133, :integer - attribute :field_134, :string - - attribute :field_135, :integer + attribute :field_132, :decimal + attribute :field_133, :string + attribute :field_134, :integer + attribute :field_135, :decimal attribute :field_136, :decimal validates :field_1, @@ -425,7 +421,7 @@ class BulkUpload::Sales::Year2026::RowParser }, on: :after_log - validates :field_115, + validates :field_130, numericality: { message: I18n.t("#{ERROR_BASE_KEY}.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%"), greater_than_or_equal_to: 0, @@ -451,7 +447,7 @@ class BulkUpload::Sales::Year2026::RowParser }, on: :after_log - validates :field_103, + validates :field_116, numericality: { greater_than_or_equal_to: 2, less_than_or_equal_to: 10, @@ -460,7 +456,7 @@ class BulkUpload::Sales::Year2026::RowParser }, on: :before_log - validates :field_90, + validates :field_103, if: :shared_ownership?, format: { with: /\A(\d+|R)\z/, @@ -468,7 +464,7 @@ class BulkUpload::Sales::Year2026::RowParser }, on: :after_log - validates :field_118, + validates :field_133, if: :discounted_ownership?, format: { with: /\A(\d+|R)\z/, @@ -590,9 +586,9 @@ class BulkUpload::Sales::Year2026::RowParser "field_17", # address_line1 "field_21", # postcode "field_22", # postcode - "field_28", # age1 - "field_29", # sexrab1 - "field_32", # ecstat1 + "field_29", # age1 + "field_30", # sexrab1 + "field_35", # ecstat1 ) end @@ -612,16 +608,16 @@ private end def prevtenbuy2 - case field_64 + case field_77 when "R" 0 else - field_64 + field_77 end end def infer_buyer2_ethnic_group_from_ethnic - case field_37 + case field_42 when 1, 2, 3, 18, 20 0 when 4, 5, 6, 7 @@ -633,7 +629,7 @@ private when 16, 19 4 else - field_37 + field_42 end end @@ -699,19 +695,19 @@ private end def two_buyers_share_address? - field_63 == 2 + field_76 == 2 end def not_resale? - field_78 == 2 + field_91 == 2 end def buyer_1_previous_tenure_not_1_or_2? - field_58 != 1 && field_58 != 2 + field_71 != 1 && field_71 != 2 end def mortgage_used? - field_88 == 2 + field_101 == 2 end def social_homebuy? @@ -719,11 +715,11 @@ private end def buyers_own_all? - field_97 == 100 + field_110 == 100 end def buyer_staircased_before? - field_99 == 1 + field_112 == 1 end def buyer_interviewed? @@ -739,89 +735,89 @@ private purchid: %i[field_7], saledate: %i[field_1 field_2 field_3], noint: %i[field_14], - age1_known: %i[field_28], - age1: %i[field_28], - age2_known: %i[field_35], - age2: %i[field_35], - age3_known: %i[field_43], - age3: %i[field_43], - age4_known: %i[field_47], - age4: %i[field_47], - age5_known: %i[field_51], - age5: %i[field_51], - age6_known: %i[field_55], - age6: %i[field_55], - relat2: %i[field_34], - relat3: %i[field_42], - relat4: %i[field_46], - relat5: %i[field_50], - relat6: %i[field_54], - - ecstat1: %i[field_32], - ecstat2: %i[field_39], - ecstat3: %i[field_45], - - ecstat4: %i[field_49], - ecstat5: %i[field_53], - ecstat6: %i[field_57], - ethnic_group: %i[field_30], - ethnic: %i[field_30], - nationality_all: %i[field_31], - nationality_all_group: %i[field_31], - income1nk: %i[field_70], - income1: %i[field_70], - income2nk: %i[field_72], - income2: %i[field_72], - inc1mort: %i[field_71], - inc2mort: %i[field_73], - savingsnk: %i[field_75], - savings: %i[field_75], - prevown: %i[field_76], - prevten: %i[field_58], - prevloc: %i[field_62], - previous_la_known: %i[field_62], - ppcodenk: %i[field_59], - ppostcode_full: %i[field_60 field_61], - disabled: %i[field_68], - - wheel: %i[field_69], - beds: %i[field_25], + age1_known: %i[field_29], + age1: %i[field_29], + age2_known: %i[field_38], + age2: %i[field_38], + age3_known: %i[field_48], + age3: %i[field_48], + age4_known: %i[field_54], + age4: %i[field_54], + age5_known: %i[field_60], + age5: %i[field_60], + age6_known: %i[field_66], + age6: %i[field_66], + relat2: %i[field_37], + relat3: %i[field_47], + relat4: %i[field_53], + relat5: %i[field_59], + relat6: %i[field_65], + + ecstat1: %i[field_35], + ecstat2: %i[field_44], + ecstat3: %i[field_52], + + ecstat4: %i[field_58], + ecstat5: %i[field_64], + ecstat6: %i[field_70], + ethnic_group: %i[field_33], + ethnic: %i[field_33], + nationality_all: %i[field_34], + nationality_all_group: %i[field_34], + income1nk: %i[field_83], + income1: %i[field_83], + income2nk: %i[field_85], + income2: %i[field_85], + inc1mort: %i[field_84], + inc2mort: %i[field_86], + savingsnk: %i[field_88], + savings: %i[field_88], + prevown: %i[field_89], + prevten: %i[field_71], + prevloc: %i[field_75], + previous_la_known: %i[field_75], + ppcodenk: %i[field_72], + ppostcode_full: %i[field_73 field_74], + disabled: %i[field_81], + + wheel: %i[field_82], + beds: %i[field_26], proptype: %i[field_24], - builtype: %i[field_26], + builtype: %i[field_27], la_known: %i[field_23], la: %i[field_23], is_la_inferred: %i[field_23], pcodenk: %i[field_21 field_22], postcode_full: %i[field_21 field_22], - wchair: %i[field_27], + wchair: %i[field_28], type: %i[field_9 field_11 field_8], - resale: %i[field_78], - hodate: %i[field_80 field_81 field_82], + resale: %i[field_91], + hodate: %i[field_93 field_94 field_95], - frombeds: %i[field_83], - fromprop: %i[field_84], + frombeds: %i[field_96], + fromprop: %i[field_97], value: value_fields, equity: equity_fields, mortgage: mortgage_fields, extrabor: extrabor_fields, deposit: deposit_fields, - cashdis: %i[field_92], + cashdis: %i[field_105], mrent: mrent_fields, has_mscharge: mscharge_fields, mscharge: mscharge_fields, - grant: %i[field_114], - discount: %i[field_115], + grant: %i[field_129], + discount: %i[field_130], owning_organisation_id: %i[field_4], managing_organisation_id: [:field_5], assigned_to: %i[field_6], - hhregres: %i[field_65], - hhregresstill: %i[field_66], - armedforcesspouse: %i[field_67], + hhregres: %i[field_78], + hhregresstill: %i[field_79], + armedforcesspouse: %i[field_80], - hb: %i[field_74], + hb: %i[field_87], mortlen: mortlen_fields, mortlen_known: mortlen_fields, proplen: proplen_fields, @@ -832,12 +828,12 @@ private ownershipsch: %i[field_8], jointpur: %i[field_12], - buy1livein: %i[field_33], - buy2livein: %i[field_40], - hholdcount: %i[field_41], - stairbought: %i[field_96], - stairowned: %i[field_97], - socprevten: %i[field_85], + buy1livein: %i[field_36], + buy2livein: %i[field_45], + hholdcount: %i[field_46], + stairbought: %i[field_109], + stairowned: %i[field_110], + socprevten: %i[field_98], mortgageused: mortgageused_fields, uprn: %i[field_16], @@ -847,47 +843,47 @@ private county: %i[field_20], uprn_selection: [:field_17], - ethnic_group2: %i[field_37], - ethnicbuy2: %i[field_37], - nationality_all_buyer2: %i[field_38], - nationality_all_buyer2_group: %i[field_38], - - buy2living: %i[field_63], - prevtenbuy2: %i[field_64], - - prevshared: %i[field_77], - - staircasesale: %i[field_98], - firststair: %i[field_99], - numstair: %i[field_103], - mrentprestaircasing: %i[field_110], - lasttransaction: %i[field_104 field_105 field_106], - initialpurchase: %i[field_100 field_101 field_102], - - sexrab1: %i[field_29], - sexrab2: %i[field_36], - sexrab3: %i[field_44], - sexrab4: %i[field_48], - sexrab5: %i[field_52], - sexrab6: %i[field_56], - - buildheightclass: %i[field_122], - - gender_same_as_sex1: %i[field_123], - gender_description1: %i[field_124], - gender_same_as_sex2: %i[field_125], - gender_description2: %i[field_126], - gender_same_as_sex3: %i[field_127], - gender_description3: %i[field_128], - gender_same_as_sex4: %i[field_129], - gender_description4: %i[field_130], - gender_same_as_sex5: %i[field_131], - gender_description5: %i[field_132], - gender_same_as_sex6: %i[field_133], - gender_description6: %i[field_134], - - hasservicechargeschanged: %i[field_135], - newservicecharges: %i[field_136], + ethnic_group2: %i[field_42], + ethnicbuy2: %i[field_42], + nationality_all_buyer2: %i[field_43], + nationality_all_buyer2_group: %i[field_43], + + buy2living: %i[field_76], + prevtenbuy2: %i[field_77], + + prevshared: %i[field_90], + + staircasesale: %i[field_111], + firststair: %i[field_112], + numstair: %i[field_116], + mrentprestaircasing: %i[field_123], + lasttransaction: %i[field_117 field_118 field_119], + initialpurchase: %i[field_113 field_114 field_115], + + sexrab1: %i[field_30], + sexrab2: %i[field_39], + sexrab3: %i[field_49], + sexrab4: %i[field_55], + sexrab5: %i[field_61], + sexrab6: %i[field_67], + + buildheightclass: %i[field_25], + + gender_same_as_sex1: %i[field_31], + gender_description1: %i[field_32], + gender_same_as_sex2: %i[field_40], + gender_description2: %i[field_41], + gender_same_as_sex3: %i[field_50], + gender_description3: %i[field_51], + gender_same_as_sex4: %i[field_56], + gender_description4: %i[field_57], + gender_same_as_sex5: %i[field_62], + gender_description5: %i[field_63], + gender_same_as_sex6: %i[field_68], + gender_description6: %i[field_69], + + hasservicechargeschanged: %i[field_125], + newservicecharges: %i[field_126], } end @@ -899,59 +895,59 @@ private attributes["noint"] = field_14 attributes["age1_known"] = age1_known? - attributes["age1"] = field_28 if attributes["age1_known"]&.zero? && field_28&.match(/\A\d{1,3}\z|\AR\z/) + attributes["age1"] = field_29 if attributes["age1_known"]&.zero? && field_29&.match(/\A\d{1,3}\z|\AR\z/) attributes["age2_known"] = age2_known? - attributes["age2"] = field_35 if attributes["age2_known"]&.zero? && field_35&.match(/\A\d{1,3}\z|\AR\z/) + attributes["age2"] = field_38 if attributes["age2_known"]&.zero? && field_38&.match(/\A\d{1,3}\z|\AR\z/) attributes["age3_known"] = age3_known? - attributes["age3"] = field_43 if attributes["age3_known"]&.zero? && field_43&.match(/\A\d{1,3}\z|\AR\z/) + attributes["age3"] = field_48 if attributes["age3_known"]&.zero? && field_48&.match(/\A\d{1,3}\z|\AR\z/) attributes["age4_known"] = age4_known? - attributes["age4"] = field_47 if attributes["age4_known"]&.zero? && field_47&.match(/\A\d{1,3}\z|\AR\z/) + attributes["age4"] = field_54 if attributes["age4_known"]&.zero? && field_54&.match(/\A\d{1,3}\z|\AR\z/) attributes["age5_known"] = age5_known? - attributes["age5"] = field_51 if attributes["age5_known"]&.zero? && field_51&.match(/\A\d{1,3}\z|\AR\z/) + attributes["age5"] = field_60 if attributes["age5_known"]&.zero? && field_60&.match(/\A\d{1,3}\z|\AR\z/) attributes["age6_known"] = age6_known? - attributes["age6"] = field_55 if attributes["age6_known"]&.zero? && field_55&.match(/\A\d{1,3}\z|\AR\z/) - - attributes["sexrab1"] = field_29 - attributes["sexrab2"] = field_36 - attributes["sexrab3"] = field_44 - attributes["sexrab4"] = field_48 - attributes["sexrab5"] = field_52 - attributes["sexrab6"] = field_56 - attributes["buildheightclass"] = field_122 - - attributes["gender_same_as_sex1"] = field_123 - attributes["gender_description1"] = field_124 - attributes["gender_same_as_sex2"] = field_125 - attributes["gender_description2"] = field_126 - attributes["gender_same_as_sex3"] = field_127 - attributes["gender_description3"] = field_128 - attributes["gender_same_as_sex4"] = field_129 - attributes["gender_description4"] = field_130 - attributes["gender_same_as_sex5"] = field_131 - attributes["gender_description5"] = field_132 - attributes["gender_same_as_sex6"] = field_133 - attributes["gender_description6"] = field_134 - - attributes["hasservicechargeschanged"] = field_135 - attributes["newservicecharges"] = field_136 - - attributes["relat2"] = relationship_from_is_partner(field_34) - attributes["relat3"] = relationship_from_is_partner(field_42) - attributes["relat4"] = relationship_from_is_partner(field_46) - attributes["relat5"] = relationship_from_is_partner(field_50) - attributes["relat6"] = relationship_from_is_partner(field_54) - - attributes["ecstat1"] = field_32 - attributes["ecstat2"] = field_39 - attributes["ecstat3"] = field_45 - attributes["ecstat4"] = field_49 - attributes["ecstat5"] = field_53 - attributes["ecstat6"] = field_57 + attributes["age6"] = field_66 if attributes["age6_known"]&.zero? && field_66&.match(/\A\d{1,3}\z|\AR\z/) + + attributes["sexrab1"] = field_30 + attributes["sexrab2"] = field_39 + attributes["sexrab3"] = field_49 + attributes["sexrab4"] = field_55 + attributes["sexrab5"] = field_61 + attributes["sexrab6"] = field_67 + attributes["buildheightclass"] = field_25 + + attributes["gender_same_as_sex1"] = field_31 + attributes["gender_description1"] = field_32 + attributes["gender_same_as_sex2"] = field_40 + attributes["gender_description2"] = field_41 + attributes["gender_same_as_sex3"] = field_50 + attributes["gender_description3"] = field_51 + attributes["gender_same_as_sex4"] = field_56 + attributes["gender_description4"] = field_57 + attributes["gender_same_as_sex5"] = field_62 + attributes["gender_description5"] = field_63 + attributes["gender_same_as_sex6"] = field_68 + attributes["gender_description6"] = field_69 + + attributes["hasservicechargeschanged"] = field_125 + attributes["newservicecharges"] = field_126 + + attributes["relat2"] = relationship_from_is_partner(field_37) + attributes["relat3"] = relationship_from_is_partner(field_47) + attributes["relat4"] = relationship_from_is_partner(field_53) + attributes["relat5"] = relationship_from_is_partner(field_59) + attributes["relat6"] = relationship_from_is_partner(field_65) + + attributes["ecstat1"] = field_35 + attributes["ecstat2"] = field_44 + attributes["ecstat3"] = field_52 + attributes["ecstat4"] = field_58 + attributes["ecstat5"] = field_64 + attributes["ecstat6"] = field_70 attributes["details_known_2"] = details_known?(2) attributes["details_known_3"] = details_known?(3) @@ -960,34 +956,34 @@ private attributes["details_known_6"] = details_known?(6) attributes["ethnic_group"] = ethnic_group_from_ethnic - attributes["ethnic"] = field_30 - attributes["nationality_all"] = field_31 if field_31.present? && valid_nationality_options.include?(field_31.to_s) + attributes["ethnic"] = field_33 + attributes["nationality_all"] = field_34 if field_34.present? && valid_nationality_options.include?(field_34.to_s) attributes["nationality_all_group"] = nationality_group(attributes["nationality_all"]) - attributes["income1nk"] = field_70 == "R" ? 1 : 0 - attributes["income1"] = field_70.to_i if attributes["income1nk"]&.zero? && field_70&.match(/\A\d+\z/) + attributes["income1nk"] = field_83 == "R" ? 1 : 0 + attributes["income1"] = field_83.to_i if attributes["income1nk"]&.zero? && field_83&.match(/\A\d+\z/) - attributes["income2nk"] = field_72 == "R" ? 1 : 0 - attributes["income2"] = field_72.to_i if attributes["income2nk"]&.zero? && field_72&.match(/\A\d+\z/) + attributes["income2nk"] = field_85 == "R" ? 1 : 0 + attributes["income2"] = field_85.to_i if attributes["income2nk"]&.zero? && field_85&.match(/\A\d+\z/) - attributes["inc1mort"] = field_71 - attributes["inc2mort"] = field_73 + attributes["inc1mort"] = field_84 + attributes["inc2mort"] = field_86 - attributes["savingsnk"] = field_75 == "R" ? 1 : 0 - attributes["savings"] = field_75.to_i if attributes["savingsnk"]&.zero? && field_75&.match(/\A\d+\z/) - attributes["prevown"] = field_76 + attributes["savingsnk"] = field_88 == "R" ? 1 : 0 + attributes["savings"] = field_88.to_i if attributes["savingsnk"]&.zero? && field_88&.match(/\A\d+\z/) + attributes["prevown"] = field_89 - attributes["prevten"] = field_58 - attributes["prevloc"] = field_62 + attributes["prevten"] = field_71 + attributes["prevloc"] = field_75 attributes["previous_la_known"] = previous_la_known attributes["ppcodenk"] = previous_postcode_known attributes["ppostcode_full"] = ppostcode_full - attributes["disabled"] = field_68 - attributes["wheel"] = field_69 - attributes["beds"] = field_25 + attributes["disabled"] = field_81 + attributes["wheel"] = field_82 + attributes["beds"] = field_26 attributes["proptype"] = field_24 - attributes["builtype"] = field_26 + attributes["builtype"] = field_27 attributes["la_known"] = field_23.present? ? 1 : 0 attributes["la"] = field_23 attributes["la_as_entered"] = field_23 @@ -995,15 +991,15 @@ private attributes["pcodenk"] = 0 if postcode_full.present? attributes["postcode_full"] = postcode_full attributes["postcode_full_as_entered"] = postcode_full - attributes["wchair"] = field_27 + attributes["wchair"] = field_28 attributes["type"] = sale_type - attributes["resale"] = field_78 + attributes["resale"] = field_91 attributes["hodate"] = hodate - attributes["frombeds"] = field_83 - attributes["fromprop"] = field_84 + attributes["frombeds"] = field_96 + attributes["fromprop"] = field_97 attributes["value"] = value attributes["equity"] = equity @@ -1011,22 +1007,22 @@ private attributes["extrabor"] = extrabor attributes["deposit"] = deposit - attributes["cashdis"] = field_92 + attributes["cashdis"] = field_105 attributes["mrent"] = mrent attributes["mscharge"] = mscharge if mscharge&.positive? attributes["has_mscharge"] = attributes["mscharge"].present? ? 1 : 0 - attributes["grant"] = field_114 - attributes["discount"] = field_115 + attributes["grant"] = field_129 + attributes["discount"] = field_130 attributes["owning_organisation"] = owning_organisation attributes["managing_organisation"] = managing_organisation attributes["assigned_to"] = assigned_to || (bulk_upload.user.support? ? nil : bulk_upload.user) attributes["created_by"] = bulk_upload.user - attributes["hhregres"] = field_65 - attributes["hhregresstill"] = field_66 - attributes["armedforcesspouse"] = field_67 + attributes["hhregres"] = field_78 + attributes["hhregresstill"] = field_79 + attributes["armedforcesspouse"] = field_80 - attributes["hb"] = field_74 + attributes["hb"] = field_87 attributes["mortlen"] = mortlen != "R" ? mortlen : nil attributes["mortlen_known"] = mortlen_known @@ -1038,12 +1034,12 @@ private attributes["privacynotice"] = field_15 attributes["ownershipsch"] = field_8 attributes["jointpur"] = field_12 - attributes["buy1livein"] = field_33 - attributes["buy2livein"] = field_40 - attributes["hholdcount"] = field_41 - attributes["stairbought"] = field_96 - attributes["stairowned"] = field_97 - attributes["socprevten"] = field_85 + attributes["buy1livein"] = field_36 + attributes["buy2livein"] = field_45 + attributes["hholdcount"] = field_46 + attributes["stairbought"] = field_109 + attributes["stairowned"] = field_110 + attributes["socprevten"] = field_98 attributes["soctenant"] = infer_soctenant_from_prevten_and_prevtenbuy2 attributes["mortgageused"] = mortgageused @@ -1064,25 +1060,25 @@ private attributes["select_best_address_match"] = true if field_16.blank? attributes["ethnic_group2"] = infer_buyer2_ethnic_group_from_ethnic - attributes["ethnicbuy2"] = field_37 - attributes["nationality_all_buyer2"] = field_38 if field_38.present? && valid_nationality_options.include?(field_38.to_s) + attributes["ethnicbuy2"] = field_42 + attributes["nationality_all_buyer2"] = field_43 if field_43.present? && valid_nationality_options.include?(field_43.to_s) attributes["nationality_all_buyer2_group"] = nationality_group(attributes["nationality_all_buyer2"]) - attributes["buy2living"] = field_63 + attributes["buy2living"] = field_76 attributes["prevtenbuy2"] = prevtenbuy2 - attributes["prevshared"] = field_77 + attributes["prevshared"] = field_90 - attributes["staircasesale"] = field_98 + attributes["staircasesale"] = field_111 - attributes["firststair"] = field_99 - attributes["numstair"] = field_103 - attributes["mrentprestaircasing"] = field_110 + attributes["firststair"] = field_112 + attributes["numstair"] = field_116 + attributes["mrentprestaircasing"] = field_123 attributes["lasttransaction"] = lasttransaction attributes["initialpurchase"] = initialpurchase - attributes["management_fee"] = field_95 - attributes["has_management_fee"] = field_95.present? && field_95.positive? ? 1 : 0 + attributes["management_fee"] = field_108 + attributes["has_management_fee"] = field_108.present? && field_108.positive? ? 1 : 0 attributes end @@ -1099,38 +1095,38 @@ private end def hodate - year = field_82.to_s.strip.length.between?(1, 2) ? field_82 + 2000 : field_82 - Date.new(year, field_81, field_80) if field_82.present? && field_81.present? && field_80.present? + year = field_95.to_s.strip.length.between?(1, 2) ? field_95 + 2000 : field_95 + Date.new(year, field_94, field_93) if field_95.present? && field_94.present? && field_93.present? rescue Date::Error Date.new end def lasttransaction - year = field_106.to_s.strip.length.between?(1, 2) ? field_106 + 2000 : field_106 - Date.new(year, field_105, field_104) if field_106.present? && field_105.present? && field_104.present? + year = field_119.to_s.strip.length.between?(1, 2) ? field_119 + 2000 : field_119 + Date.new(year, field_118, field_117) if field_119.present? && field_118.present? && field_117.present? rescue Date::Error Date.new end def initialpurchase - year = field_102.to_s.strip.length.between?(1, 2) ? field_102 + 2000 : field_102 - Date.new(year, field_101, field_100) if field_102.present? && field_101.present? && field_100.present? + year = field_115.to_s.strip.length.between?(1, 2) ? field_115 + 2000 : field_115 + Date.new(year, field_114, field_113) if field_115.present? && field_114.present? && field_113.present? rescue Date::Error Date.new end def age1_known? - return 1 if field_28 == "R" + return 1 if field_29 == "R" 0 end [ - { person: 2, field: :field_35 }, - { person: 3, field: :field_43 }, - { person: 4, field: :field_47 }, - { person: 5, field: :field_51 }, - { person: 6, field: :field_55 }, + { person: 2, field: :field_38 }, + { person: 3, field: :field_48 }, + { person: 4, field: :field_54 }, + { person: 5, field: :field_60 }, + { person: 6, field: :field_66 }, ].each do |hash| define_method("age#{hash[:person]}_known?") do return 1 if public_send(hash[:field]) == "R" @@ -1140,23 +1136,23 @@ private end def person_2_present? - field_35.present? || field_36.present? || field_34.present? || field_125.present? || field_126.present? + field_38.present? || field_39.present? || field_37.present? || field_40.present? || field_41.present? end def person_3_present? - field_43.present? || field_44.present? || field_42.present? || field_127.present? || field_128.present? + field_48.present? || field_49.present? || field_47.present? || field_50.present? || field_51.present? end def person_4_present? - field_47.present? || field_48.present? || field_46.present? || field_129.present? || field_130.present? + field_54.present? || field_55.present? || field_53.present? || field_56.present? || field_57.present? end def person_5_present? - field_51.present? || field_52.present? || field_50.present? || field_131.present? || field_132.present? + field_60.present? || field_61.present? || field_59.present? || field_62.present? || field_63.present? end def person_6_present? - field_55.present? || field_56.present? || field_54.present? || field_133.present? || field_134.present? + field_66.present? || field_67.present? || field_65.present? || field_68.present? || field_69.present? end def relationship_from_is_partner(is_partner) @@ -1175,9 +1171,9 @@ private end def ethnic_group_from_ethnic - return nil if field_30.blank? + return nil if field_33.blank? - case field_30 + case field_33 when 1, 2, 3, 18, 20 0 when 4, 5, 6, 7 @@ -1198,7 +1194,7 @@ private end def ppostcode_full - "#{field_60} #{field_61}" if field_60 && field_61 + "#{field_73} #{field_74}" if field_73 && field_74 end def sale_type @@ -1208,50 +1204,50 @@ private end def value - return field_86 if shared_ownership_initial_purchase? - return field_113 if discounted_ownership? + return field_99 if shared_ownership_initial_purchase? + return field_128 if discounted_ownership? - field_107 if staircasing? + field_120 if staircasing? end def equity - return field_87 if shared_ownership_initial_purchase? + return field_100 if shared_ownership_initial_purchase? - field_108 if staircasing? + field_121 if staircasing? end def mortgage - return field_89 if shared_ownership? + return field_102 if shared_ownership? - field_117 if discounted_ownership? + field_132 if discounted_ownership? end def extrabor - field_119 if discounted_ownership? + field_134 if discounted_ownership? end def deposit - return field_91 if shared_ownership? + return field_104 if shared_ownership? - field_120 if discounted_ownership? + field_135 if discounted_ownership? end def mrent - return field_93 if shared_ownership_initial_purchase? + return field_106 if shared_ownership_initial_purchase? - field_111 if staircasing? + field_124 if staircasing? end def mscharge - return field_94 if shared_ownership? + return field_107 if shared_ownership? - field_121 if discounted_ownership? + field_136 if discounted_ownership? end def mortlen - return field_90 if shared_ownership? + return field_103 if shared_ownership? - field_118 if discounted_ownership? + field_133 if discounted_ownership? end def mortlen_known @@ -1265,87 +1261,87 @@ private end def proplen - return field_79 if shared_ownership? + return field_92 if shared_ownership? - field_112 if discounted_ownership? + field_127 if discounted_ownership? end def mortgageused - return field_88 if shared_ownership_initial_purchase? - return field_116 if discounted_ownership? + return field_101 if shared_ownership_initial_purchase? + return field_131 if discounted_ownership? - field_109 if staircasing? + field_122 if staircasing? end def value_fields - return [:field_86] if shared_ownership_initial_purchase? - return [:field_113] if discounted_ownership? - return [:field_107] if staircasing? + return [:field_99] if shared_ownership_initial_purchase? + return [:field_128] if discounted_ownership? + return [:field_120] if staircasing? - %i[field_86 field_113 field_107] + %i[field_99 field_128 field_120] end def equity_fields - return [:field_87] if shared_ownership_initial_purchase? - return [:field_108] if staircasing? + return [:field_100] if shared_ownership_initial_purchase? + return [:field_121] if staircasing? - %i[field_87 field_108] + %i[field_100 field_121] end def mortgage_fields - return [:field_89] if shared_ownership? - return [:field_117] if discounted_ownership? + return [:field_102] if shared_ownership? + return [:field_132] if discounted_ownership? - %i[field_89 field_117] + %i[field_102 field_132] end def extrabor_fields - return [:field_119] if discounted_ownership? + return [:field_134] if discounted_ownership? - %i[field_119] + %i[field_134] end def deposit_fields - return [:field_91] if shared_ownership? - return [:field_120] if discounted_ownership? + return [:field_104] if shared_ownership? + return [:field_135] if discounted_ownership? - %i[field_91 field_120] + %i[field_104 field_135] end def mrent_fields - return [:field_93] if shared_ownership_initial_purchase? - return [:field_111] if staircasing? + return [:field_106] if shared_ownership_initial_purchase? + return [:field_124] if staircasing? - %i[field_93 field_111] + %i[field_106 field_124] end def mscharge_fields - return [:field_94] if shared_ownership? - return [:field_121] if discounted_ownership? + return [:field_107] if shared_ownership? + return [:field_136] if discounted_ownership? - %i[field_94 field_121] + %i[field_107 field_136] end def mortlen_fields - return [:field_90] if shared_ownership? - return [:field_118] if discounted_ownership? + return [:field_103] if shared_ownership? + return [:field_133] if discounted_ownership? - %i[field_90 field_118] + %i[field_103 field_133] end def proplen_fields - return [:field_79] if shared_ownership? - return [:field_112] if discounted_ownership? + return [:field_92] if shared_ownership? + return [:field_127] if discounted_ownership? - %i[field_79 field_112] + %i[field_92 field_127] end def mortgageused_fields - return [:field_88] if shared_ownership_initial_purchase? - return [:field_116] if discounted_ownership? - return [:field_109] if staircasing? + return [:field_101] if shared_ownership_initial_purchase? + return [:field_131] if discounted_ownership? + return [:field_122] if staircasing? - %i[field_88 field_116 field_109] + %i[field_101 field_131 field_122] end def owning_organisation @@ -1357,19 +1353,19 @@ private end def previous_la_known - field_62.present? ? 1 : 0 + field_75.present? ? 1 : 0 end def previous_postcode_known - return 1 if field_59 == 2 + return 1 if field_72 == 2 - 0 if field_59 == 1 + 0 if field_72 == 1 end def infer_soctenant_from_prevten_and_prevtenbuy2 return unless shared_ownership? - if [1, 2].include?(field_58) || [1, 2].include?(field_64.to_i) + if [1, 2].include?(field_71) || [1, 2].include?(field_77.to_i) 1 else 2 @@ -1568,9 +1564,9 @@ private errors.add(:field_17, error_message) # Address line 1 errors.add(:field_21, error_message) # Postcode errors.add(:field_22, error_message) # Postcode - errors.add(:field_28, error_message) # Buyer 1 age - errors.add(:field_29, error_message) # Buyer 1 sex registered at birth - errors.add(:field_32, error_message) # Buyer 1 working situation + errors.add(:field_29, error_message) # Buyer 1 age + errors.add(:field_30, error_message) # Buyer 1 sex registered at birth + errors.add(:field_35, error_message) # Buyer 1 working situation errors.add(:field_7, error_message) # Purchaser code end end @@ -1594,12 +1590,12 @@ private end def validate_buyer1_economic_status - if field_32 == 9 - if field_28.present? && field_28.to_i >= 16 - errors.add(:field_32, I18n.t("#{ERROR_BASE_KEY}.ecstat1.buyer_cannot_be_over_16_and_child")) - errors.add(:field_28, I18n.t("#{ERROR_BASE_KEY}.age1.buyer_cannot_be_over_16_and_child")) + if field_35 == 9 + if field_29.present? && field_29.to_i >= 16 + errors.add(:field_35, I18n.t("#{ERROR_BASE_KEY}.ecstat1.buyer_cannot_be_over_16_and_child")) + errors.add(:field_29, I18n.t("#{ERROR_BASE_KEY}.age1.buyer_cannot_be_over_16_and_child")) else - errors.add(:field_32, I18n.t("#{ERROR_BASE_KEY}.ecstat1.buyer_cannot_be_child")) + errors.add(:field_35, I18n.t("#{ERROR_BASE_KEY}.ecstat1.buyer_cannot_be_child")) end end end @@ -1607,25 +1603,25 @@ private def validate_buyer2_economic_status return unless joint_purchase? - if field_39 == 9 - if field_35.present? && field_35.to_i >= 16 - errors.add(:field_39, I18n.t("#{ERROR_BASE_KEY}.ecstat2.buyer_cannot_be_over_16_and_child")) - errors.add(:field_35, I18n.t("#{ERROR_BASE_KEY}.age2.buyer_cannot_be_over_16_and_child")) + if field_44 == 9 + if field_38.present? && field_38.to_i >= 16 + errors.add(:field_44, I18n.t("#{ERROR_BASE_KEY}.ecstat2.buyer_cannot_be_over_16_and_child")) + errors.add(:field_38, I18n.t("#{ERROR_BASE_KEY}.age2.buyer_cannot_be_over_16_and_child")) else - errors.add(:field_39, I18n.t("#{ERROR_BASE_KEY}.ecstat2.buyer_cannot_be_child")) + errors.add(:field_44, I18n.t("#{ERROR_BASE_KEY}.ecstat2.buyer_cannot_be_child")) end end end def validate_nationality - if field_31.present? && !valid_nationality_options.include?(field_31.to_s) - errors.add(:field_31, I18n.t("#{ERROR_BASE_KEY}.nationality.invalid")) + if field_34.present? && !valid_nationality_options.include?(field_34.to_s) + errors.add(:field_34, I18n.t("#{ERROR_BASE_KEY}.nationality.invalid")) end end def validate_buyer_2_nationality - if field_38.present? && !valid_nationality_options.include?(field_38.to_s) - errors.add(:field_38, I18n.t("#{ERROR_BASE_KEY}.nationality.invalid")) + if field_43.present? && !valid_nationality_options.include?(field_43.to_s) + errors.add(:field_43, I18n.t("#{ERROR_BASE_KEY}.nationality.invalid")) end end @@ -1635,8 +1631,8 @@ private def validate_mortlen_field_if_buyer_interviewed if buyer_interviewed? && mortlen == "R" - errors.add(:field_90, I18n.t("#{ERROR_BASE_KEY}.mortlen.invalid_for_interviewed")) if shared_ownership? - errors.add(:field_118, I18n.t("#{ERROR_BASE_KEY}.mortlen.invalid_for_interviewed")) if discounted_ownership? + errors.add(:field_103, I18n.t("#{ERROR_BASE_KEY}.mortlen.invalid_for_interviewed")) if shared_ownership? + errors.add(:field_133, I18n.t("#{ERROR_BASE_KEY}.mortlen.invalid_for_interviewed")) if discounted_ownership? end end diff --git a/spec/fixtures/files/2026_27_sales_bulk_upload.csv b/spec/fixtures/files/2026_27_sales_bulk_upload.csv index 3f4dc0142..01c78956c 100644 --- a/spec/fixtures/files/2026_27_sales_bulk_upload.csv +++ b/spec/fixtures/files/2026_27_sales_bulk_upload.csv @@ -1,23 +1,25 @@ -Section,Setting up this sales log,,,,,,,,,,,,,,,Property information,,,,,,,,,,,,Household characteristics,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Household situation,,,,,,,Other household information,,,,,"Income, benefits and outgoings",,,,,,,,Shared ownership - initial purchase,,,,,,,,,,,,,,,,,,Shared ownership - staircasing transaction,,,,,,,,,,,,,,,,Discounted ownership,,,,,,,,,,,,,,,,,,,,,,,, -Question,What is the sale completion date? - day DD,What is the sale completion date? - month MM,What is the sale completion date? - year YY,Which organisation owned this property before the sale?,Which organisation is reporting this sale?,What is the CORE username of the account this sale log should be assigned to? ,What is the purchaser code?,Is this a shared ownership or discounted ownership sale?,What is the type of shared ownership sale?,Is this a staircasing transaction?,What is the type of discounted ownership sale?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen or been given access to the MHCLG privacy notice?,"If known, enter this property’s UPRN",Address Line 1,Address Line 2,Town or city,County,Part 1 of the property's postcode,Part 2 of the property's postcode,What is the property's local authority?,What type of unit is the property?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1’s age?,What is buyer 1's sex?,Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1’s working situation? ,Will buyer 1 live in the property?,Is buyer 2 the partner of buyer 1?,What is buyer 2 or person 2's age?,What is buyer 2 or person 2's sex?,Which of the following best describes buyer 2's ethnic background?,What is buyer 2's nationality?,Which of these best describes buyer 2 or person 2’s working situation? ,Will buyer 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex?,Which of these best describes person 3’s working situation? ,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex?,Which of these best describes person 4’s working situation? ,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex?,Which of these best describes person 5’s working situation? ,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex?,Which of these best describes person 6’s working situation? ,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,What is the local authority of buyer 1's last settled accommodation?,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,"Have any of the buyers ever served as a regular in the UK armed forces? -",Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,What is the day of the practical completion or handover date? - DD,What is the month of the practical completion or handover date? - MM,What is the year of the practical completion or handover date? - YY,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the buyer’s previous tenure?,What is the full purchase price?,What was the initial percentage share purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,How much was the cash deposit paid on the property?,How much cash discount was given through Social HomeBuy?,What is the basic monthly rent?,What are the total monthly service charges for the property?,What are the total monthly estate management fees for the property?,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the buyer has engaged in staircasing in the home?,What was the day of the initial purchase of a share in the property? DD,What was the month of the initial purchase of a share in the property? MM,What was the year of the initial purchase of a share in the property? YYYY,"Including this time, how many times has the shared owner engaged in staircasing in the home?",What was the day of the last staircasing transaction? DD,What was the month of the last staircasing transaction? MM,What was the year of the last staircasing transaction? YYYY,What is the full purchase price for this staircasing transaction?,What was the percentage share purchased in the initial transaction?,Was a mortgage used for this staircasing transaction?,What was the basic monthly rent prior to staircasing?,What is the basic monthly rent after staircasing?,How long did the buyer(s) live in the property before purchasing it?,What is the full purchase price?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage in years?,Does this include any extra borrowing?,How much was the cash deposit paid on the property?,What are the total monthly leasehold charges for the property?,What is the building height classification?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",Is the gender buyer/person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer/person 2's gender identity",Is the gender person 3 identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",Is the gender person 4 identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",Is the gender person 5 identifies with the same as their sex registered at birth?,"If 'No', enter person 5's gender identity",Is the gender person 6 identifies with the same as their sex registered at birth?,"If 'No', enter person 6's gender identity",Will the service charge change after this staircasing transaction takes place?,What are the new total monthly service charges for the property? +Section,Setting up this sales log,,,,,,,,,,,,,,,Property information,,,,,,,,,,,,,Household characteristics,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Household situation,,,,,,,Other household information,,,,,"Income, benefits and outgoings",,,,,,,,Shared ownership - initial purchase,,,,,,,,,,,,,,,,,,Shared ownership - staircasing transaction,,,,,,,,,,,,,,,,,,Discounted ownership,,,,,,,,, +Question,What is the sale completion date? - day DD,What is the sale completion date? - month MM,What is the sale completion date? - year YY,Which organisation owned this property before the sale?,Which organisation is reporting this sale?,What is the CORE username of the account this sale log should be assigned to? ,What is the purchaser code?,Is this a shared ownership or discounted ownership sale?,What is the type of shared ownership sale?,Is this a staircasing transaction?,What is the type of discounted ownership sale?,Is this a joint purchase?,Are there more than 2 joint buyers of this property?,Did you interview the buyer to answer these questions?,Has the buyer seen or been given access to the MHCLG privacy notice?,"If known, enter this property’s UPRN",Address Line 1,Address Line 2,Town or city,County,Part 1 of the property's postcode,Part 2 of the property's postcode,What is the property's local authority?,What type of unit is the property?,What is the building height classification?,How many bedrooms does the property have?,Which type of building is the property?,Is the property built or adapted to wheelchair-user standards?,What is buyer 1’s age?,What is buyer 1's sex?,Is the gender buyer 1 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 1's gender identity",Which of the following best describes buyer 1's ethnic background?,What is buyer 1's nationality?,Which of these best describes buyer 1’s working situation? ,Will buyer 1 live in the property?,Is buyer 2 or person 2 the partner of buyer 1?,What is buyer 2's or person 2's age?,What is buyer 2 or person 2's sex?,Is the gender buyer 2 or person 2 identifies with the same as their sex registered at birth?,"If 'No', enter buyer 2 or person 2's gender identity",Which of the following best describes buyer 2 or person 2's ethnic background?,What is buyer 2 or person 2's nationality?,Which of these best describes buyer 2 or person 2’s working situation? ,Will buyer 2 or person 2 live in the property?,"In total, how many people live in the property?",Is person 3 the partner of buyer 1?,What is person 3's age?,What is person 3's sex?,Is the gender person 3's identifies with the same as their sex registered at birth?,"If 'No', enter person 3's gender identity",Which of these best describes person 3’s working situation? ,Is person 4 the partner of buyer 1?,What is person 4's age?,What is person 4's sex?,Is the gender person 4's identifies with the same as their sex registered at birth?,"If 'No', enter person 4's gender identity",Which of these best describes person 4’s working situation? ,Is person 5 the partner of buyer 1?,What is person 5's age?,What is person 5's sex?,Is the gender person 5's identifies with the same as their sex registered at birth?,Is the gender person 5's identifies with the same as their sex registered at birth?,Which of these best describes person 5’s working situation? ,Is person 6 the partner of buyer 1?,What is person 6's age?,What is person 6's sex?,Is the gender person 6's identifies with the same as their sex registered at birth?,Is the gender person 6's identifies with the same as their sex registered at birth?,Which of these best describes person 6’s working situation? ,What was buyer 1's previous tenure?,Do you know the postcode of buyer 1's last settled accommodation?,Part 1 of postcode of buyer 1's last settled accommodation,Part 2 of postcode of buyer 1's last settled accommodation,What is the local authority of buyer 1's last settled accommodation?,"At the time of purchase, was buyer 2 living at the same address as buyer 1?",What was buyer 2's previous tenure?,"Have any of the buyers ever served as a regular in the UK armed forces? +",Is the buyer still serving in the UK armed forces?,Are any of the buyers a spouse or civil partner of a UK armed forces regular who died in service within the last 2 years?,Does anyone in the household consider themselves to have a disability?,Does anyone in the household use a wheelchair?,What is buyer 1's annual income?,Was buyer 1's income used for a mortgage application?,What is buyer 2's annual income?,Was buyer 2's income used for a mortgage application?,Were the buyers receiving any of these housing-related benefits immediately before buying this property?,What is the total amount the buyers had in savings before they paid any deposit for the property?,Have any of the buyers previously owned a property?,Was the previous property under shared ownership?,Is this a resale?,How long did the buyer(s) live in the property before purchasing it?,What is the day of the practical completion or handover date? - DD,What is the month of the practical completion or handover date? - MM,What is the year of the practical completion or handover date? - YY,How many bedrooms did the buyer's previous property have?,What was the previous property type?,What was the buyer’s previous tenure?,What is the full purchase price?,What was the initial percentage share purchased?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage?,How much was the cash deposit paid on the property?,How much cash discount was given through Social HomeBuy?,What is the basic monthly rent?,What are the total monthly service charges for the property?,What are the total monthly estate management fees for the property?,What percentage of the property has been bought in this staircasing transaction?,What percentage of the property do the buyers now own in total?,Was this transaction part of a back-to-back staircasing transaction to facilitate sale of the home on the open market?,Is this the first time the buyer has engaged in staircasing in the home?,What was the day of the initial purchase of a share in the property? DD,What was the month of the initial purchase of a share in the property? MM,What was the year of the initial purchase of a share in the property? YYYY,"Including this time, how many times has the shared owner engaged in staircasing in the home?",What was the day of the last staircasing transaction? DD,What was the month of the last staircasing transaction? MM,What was the year of the last staircasing transaction? YYYY,What is the full purchase price for this staircasing transaction?,What was the percentage share purchased in the initial transaction?,Was a mortgage used for this staircasing transaction?,What was the basic monthly rent prior to staircasing?,What is the basic monthly rent after staircasing?,What are the monthly service charges for the property?,"If the service charges will change after this staircasing transaction takes places, what is the new monthly service charge amount?",How long did the buyer(s) live in the property before purchasing it?,What is the full purchase price?,"What was the amount of any loan, grant, discount or subsidy given?",What was the percentage discount?,Was a mortgage used to buy this property?,What is the mortgage amount?,What is the length of the mortgage?,Does this include any extra borrowing?,How much was the cash deposit paid on the property?,What are the total monthly leasehold charges for the property? Additional info,,,,"You can find the org ID on the CORE service under 'Stock owners' or, if your organisation is the stock owner, under 'About your organisation'","You can find the org ID on the CORE service under 'Managing agents' or, if your organisation is the managing agent, under 'About your organisation'","If left empty, the sales log will be assigned to the account used to upload the log.",This is how you usually refer to this buyer on your own systems,"Sales logs are no longer required for outright and other sales. A shared ownership sale is when the purchaser buys an initial share of up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion, or a subsequent staircasing transaction",See specification for full description of each scheme,"A staircasing transaction is when the household purchases more shares in their property, increasing the proportion they own and decreasing the proportion the housing association owns. Once the household purchases 100% of the shares, they own the property.",See specification for full description of each scheme,This is where two or more people are named as legal owners of the property after the purchase.,,You should still try to answer all questions even if the buyer wasn't interviewed in person,"Make sure the buyer has seen or been given access to the Ministry of Housing, Communities and Local Government (MHCLG) privacy notice before completing this log. This is a legal requirement under data protection legislation.","The Unique Property Reference Number (UPRN) is a unique number system created by Ordnance Survey and used by housing providers and various industries across the UK. An example UPRN is 10010457355. -The UPRN may not be the same as the property reference assigned by your organisation.",,,,,Combined with field 28 it should be a postcode which lies within the local authority given in field 29.,Combined with field 27 it should be a postcode which lies within the local authority given in field 29.,,,"For bedsits, enter ‘1’",,"This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property.","Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest.",,,"If buyer 1 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, they should decide which country to enter.","Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest.",,,,,,"If buyer 2 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, they should decide which country to enter.",,,"You can provide details for a maximum of 4 other people if there are 2 buyers, or 5 other people if there is only one buyer",,,,,,,,,,,,,,,,,,This is also known as the household's 'last settled home',,,,,,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or army full time and does not include reserve forces",,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or army full time and does not include reserve forces",This includes any long-term health condition that has an impact on the person's day-to-day life,This can be inside or outside the home,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,,Enter their total savings to the nearest £10,,For any buyer,"If the social landlord has previously sold the property to another buyer and is now reselling the property, enter 'yes'. If this is the first time the property has been sold, enter 'no'.","If the buyers haven't been living in the property, enter '0'",This is the date on which the building contractor hands over responsibility for the completed property to the private registered provider (PRP).,,,"For bedsits, enter ‘1’",,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,,Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage,Enter the total cash discount given on the property being purchased through the Social HomeBuy scheme,Before any charges,"This includes any charges for day-to-day maintenance and repairs, buildings insurance, and any contributions to a sinking or reserve fund. It does not include estate management fees.","Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks or play areas within new build estates.",,,Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer.,,,,,,,,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,,,,"For all schemes, including Right to Acquire (RTA), Right to Buy (RTB) or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount.","For all schemes except Right to Buy (RTB), Preserved Right to Buy (PRTB) and Rent to Buy","For Right to Buy (RTB) and Preserved Right to Buy (PRTB). For capped discount, enter capped %. If the property is sold to an existing tenant under RTB or PRTB, enter % discount from full market value.",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,,This is 'Yes' if the mortgage includes borrowing beyond the purchase price of the property,Enter the total cash sum paid by the purchaser towards the property that was not funded by the mortgage. This excludes any grant or loan.,"For example, service and management charges",,,,,,,,,,,,,,, -Values,Jan-31,01-Dec,25 - 26,Alphanumeric,Alphanumeric,Email format,"Alphanumeric, punctuation can be used as long as not the field separator. -Max 9 characters.",01-Feb,"2, 16, 18, 24, 28 or 30 - 32",01-Feb,"8, 9, 14, 21, 22, 27 or 29",01-Feb,01-Mar,01-Feb,1,"Numeric, max 12 digits",Alphanumeric,,Text,,"Alphanumeric, +The UPRN may not be the same as the property reference assigned by your organisation.",,,,,Combined with field 28 it should be a postcode which lies within the local authority given in field 29.,Combined with field 27 it should be a postcode which lies within the local authority given in field 29.,,,"Only complete this section if the answer to Q16 (What type of unit is the property?) is ‘Flat or maisonette’, ‘Bedsit’ or ‘Other’. + +High-rise residential buildings are those containing 2 or more residential units and either have 7 or more storeys or are at least 18 metres in height. If unsure, answer based on the number of storeys.","For bedsits, enter ‘1’",,"This is whether someone who uses a wheelchair is able to make full use of all of the property’s rooms and facilities, including use of both inside and outside space, and entering and exiting the property.","Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest.",This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,"If buyer 1 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, they should decide which country to enter.","Buyer 1 is the person in the household who does the most paid work. If it’s a joint purchase and the buyers do the same amount of paid work, buyer 1 is whoever is the oldest.",,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,"If buyer 2 is a dual national of the United Kingdom and another country, enter United Kingdom. If they are a dual national of two other countries, they should decide which country to enter.",,,Include all people who are living or will live in the property,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,,This is your sex that was registered at birth. The next question will ask about your gender identity.,,,,,This is also known as the household's 'last settled home',Combined with field 74 it should be a postcode which lies within the local authority given in field 75.,Combined with field 73 it should be a postcode which lies within the local authority given in field 75.,This is also known as the household's 'last settled home',,,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or Army full time and does not include reserve forces",,"A regular is somebody who has served in the Royal Navy, the Royal Marines, the Royal Air Force or army full time and does not include reserve forces",This includes any long-term health condition that has an impact on the person's day-to-day life,This can be inside or outside the home,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,"Provide the gross annual income (i.e. salary before tax) plus the annual amount of benefits, Universal Credit or pensions, and income from investments",,,Enter their total savings to the nearest £10,,For any buyer,"If the social landlord has previously sold the property to another buyer and is now reselling the property, enter 'yes'. If this is the first time the property has been sold, enter 'no'.","If the buyers haven't been living in the property, enter '0'",This is the date on which the building contractor hands over responsibility for the completed property to the private registered provider (PRP).,,,"For bedsits, enter ‘1’",,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,You should round up to the nearest year. Value should not exceed 60 years. You can only select ‘Don’t Know’ if you answered ‘No’ to Q11 'Did you interview the buyer to answer these questions?’.,Enter the total cash sum paid by the buyer towards the property that was not funded by the mortgage,Enter the total cash discount given on the property being purchased through the Social HomeBuy scheme,Before any charges,"This includes any charges for day-to-day maintenance and repairs, buildings insurance, and any contributions to a sinking or reserve fund. It does not include estate management fees.","Estate management fees are typically used for the maintenance of communal gardens, payments, private roads, car parks or play areas within new build estates.",,,Back-to-back staircasing transactions are used as a way for shared owners who own less than 100% of their property to sell on the open market. It involves the shared owner purchasing the remaining share from their landlord and immediately selling 100% of the property to a buyer on the open market. The landlord is then reimbursed for the staircasing transaction through the proceeds of sale to the buyer.,,,,,,,,,"Enter the full purchase price of the property before any discounts are applied. For shared ownership, enter the full purchase price paid for 100% equity (this is equal to the value of the share owned by the PRP plus the value bought by the purchaser).","This is the initial stake purchased. Enter the amount of initial equity held by the purchaser (for example, 25% or 50%)",,,,"This includes any charges for day-to-day maintenance and repairs, building insurance, and any contributions to a sinking or reserved fund. It does not include estate management fees","This includes any charges for day-to-day maintenance and repairs, building insurance, and any contributions to a sinking or reserved fund. It does not include estate management fees",,"For all schemes, including Right to Acquire (RTA), Right to Buy (RTB), or Preserved Right to Buy (PRTB) sales, enter the full price of the property without any discount.","For all schemes except Right to Buy (RTB), Preserved Right to Buy(PRTB) and Rent to Buy.","For Right to Buy (RTB) and Preserved Right to Buy (PRTB). For capped discount, enter capped %. If property is sold to an existing tenant under RTB or PRTB, enter % discount from full market value.",,Enter the amount of mortgage agreed with the mortgage lender. Exclude any deposits or cash payments.,You should round up to the nearest year. Value should not exceed 60 years. You can only select ‘Don’t Know’ if you answered ‘No’ to Q11 'Did you interview the buyer to answer these questions?’.,This is 'Yes' if the mortgage includes borrowing beyond the purchase price of the property,Enter the total cash sum paid by the purchaser towards the property that was not funded by the mortgage. This excludes any grant or loan.,"For example, service and management charges" +Values,1 - 31,1 - 12,26 - 27,Alphanumeric,Alphanumeric,Email format,"Alphanumeric, punctuation can be used as long as not the field separator. +Max 9 characters.",1 - 2,"2, 16, 18, 24, 28 or 30 - 32",1 - 2,"8, 9, 14, 21, 22, or 29",1 - 2,1 - 3,1 - 2,1,"Numeric, max 12 digits",Alphanumeric,,Text,,"Alphanumeric, 2 - 4 characters","Alphanumeric, - 3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ",1 - 4 or 9,01-Sep,01-Feb,01-Mar,"16 - 110 - or R",,Jan-20,"3 digit ISO country code, see specification",0 - 8 or 10,01-Feb,01-Mar,1 - 110 or R,,Jan-20,"3 digit ISO country code, see specification",0 - 10,01-Feb,0 - 5,01-Mar,1 - 110 or R,,0 - 10,01-Mar,1 - 110 or R,,0 - 10,01-Mar,1 - 110 or R,,0 - 10,01-Mar,1 - 110 or R,,0 - 10,"1 - 7, 9 or R",01-Feb,"Alphanumeric, + 3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ",1 - 4 or 9,"Numeric, range 1 - 3",1 - 9,1 - 2,1 - 3,"16 - 110 + or R","F, M, or R",1 - 3,Text,1 - 20,"3 digit ISO country code, see specification",0 - 8 or 10,1 - 2,1 - 3,1 - 110 or R,"F, M, or R",1 - 3,Text,1 - 20,"3 digit ISO country code, see specification",0 - 10,1 - 2,Integer > 0,1 - 3,1 - 110 or R,"F, M, or R",1 - 3,Text,0 - 10,1 - 3,1 - 110 or R,"F, M, or R",1 - 3,Text,0 - 10,1 - 3,1 - 110 or R,"F, M, or R",1 - 3,1 - 3,0 - 10,1 - 3,1 - 110 or R,"F, M, or R",1 - 3,1 - 3,0 - 10,"1 - 7, 9 or R",1 - 2,"Alphanumeric, 2 - 4 characters","Alphanumeric, - 3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ",01-Mar,"1 - 7, 9 or R","1, 3, 7 or 8",04-Jun,04-Jul,01-Mar,,0 - 99999 or R,01-Feb,0 - 99999 or R,01-Feb,01-Apr,0 - 999990 or R,01-Mar,,01-Feb,Integer <= 80,Jan-31,01-Dec,22 - 26,01-Sep,1 - 4 or 9,1 - 3 or 9 - 10,0 - 999999,0 - 100,01-Feb,0 - 999999,Integer <= 60,0 - 999999,,0 - 9999.99,,,1 - 100,,01-Mar,01-Feb,Jan-31,01-Dec,1980-2026,02-Oct,Jan-31,01-Dec,1980-2026,0 - 999999,0 - 100,01-Mar,0 - 9999.99,,Integer <= 80,0 - 999999,,0 - 100,01-Feb,0 - 999999,Integer <= 60,01-Mar,0 - 999999,0 - 9999.99,1-3,1-3,Text,1-3,Text,1-3,Text,1-3,Text,1-3,Text,1-3,Text,1-2,Text -Can be empty?,No,,,No,No,Yes,Yes,No,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)",No,"Yes, if the sale is not a joint purchase (if field 12 = 2)",No,No,"Yes, if property's full address is known (if fields 16, 18, 20 and 21 are not empty)","Yes, if property's UPRN is known (if field 16 is not empty)",Yes,"Yes, if property's UPRN is known (if field 16 is not empty)",Yes,"Yes, if property's UPRN and local authority are known (if fields 16 and 23 are not empty",,No,,,"Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,No,,"Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if sale is not a joint purchase (if field 12 = 2) and the other fields about this person (fields 35, 36 and 39) are also empty","Yes, if sale is not a joint purchase (if field 12 = 2) and the other fields about this person (fields 35, 36 and 39) are also empty",,"Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,"Yes, if sale is not a joint purchase (if field 12 = 2) and the other fields about this person (fields 35, 36 and 39) are also empty, or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if purchase is a staircasing transaction (if field 10 = 1)","Yes, if all fields about this person (fields 42 - 45) are also empty, or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if all fields about this person (fields 46 - 49) are also empty, or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if all fields about this person (fields 50 - 53) are also empty, or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if all fields about this person (fields 54 - 57) are also empty, or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is discounted ownership (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if postcode of buyer 1's last settled accommodation is unknown (if field 59 = 2) + 3 characters","9 character ONS code, beginning with 'E' (https://www.get-information-schools.service.gov.uk/Guidance/LaNameCodes) ",1 - 3,"1 - 7, 9 or R","1, 3, 7 or 8",4 - 6,4 - 7,1 - 3,,0 - 99999 or R,1 - 2,0 - 99999 or R,1 - 2,1 - 4,0 - 999990 or R,1 - 3,,1 - 2,Integer <= 80,1 - 31,1 - 12,22 - 27,1 - 9,1 - 4 or 9,1 - 3 or 9 - 10,15000 - 999999,0 - 100,1 - 3,0 - 999999,Integer <= 60 or text (upper case 'R'),0 - 999999,,0 - 9999.99,,,1 - 100,,1 - 3,1 - 2,1 - 31,1 - 12,1980-2027,2 - 10,1 - 31,1 - 12,1980-2027,0 - 99999999,0 - 100,1 - 3,0 - 9999.99,,0 - 9999.99 or R,0 - 9999.99 or R,Integer <= 80,15000 - 999999,0 - 999999,0 - 100,1 - 3,0 - 999999,Integer <= 60 or text (upper case 'R'),1 - 3,0 - 999999,0 - 9999.99 +Can be empty?,No,,,No,No,Yes,Yes,No,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)",No,"Yes, if the sale is not a joint purchase (if field 12 = 2)",No,No,"Yes, if property's full address is known (if fields 16, 18, 20 and 21 are not empty)","Yes, if property's UPRN is known (if field 16 is not empty)",Yes,"Yes, if property's UPRN is known (if field 16 is not empty)",Yes,"Yes, if property's UPRN and local authority are known (if fields 16 and 23 are not empty",,No,,"Yes, if the unit of the property is not 'Flat or maisonette', 'Bedsit' or 'Other' (if field 24 = 3 or 4)",No,"Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,No,,,"Yes, if 'No' is not selected for gender same as sex (if field 31 is not 2)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if sale is not a joint purchase (if field 12 = 2) and there is only one person living in the property (if field 46 = 1)",,,,"Yes, if sale is not a joint purchase (if field 12 = 2) and there is only one person living in the property (if field 46 = 1) and 'No' is not selected for gender same as sex (if field 40 is not 2)","Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,"Yes, if sale is not a joint purchase (if field 12 = 2), if there are fewer than 2 people living in the property (if field 46 < 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if purchase is a staircasing transaction (if field 10 = 1)","Yes, if there are fewer than 3 people living in the property (if field 46 < 3), or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"if there are fewer than 3 people living in the property (if field 46 < 3), or if the purchase is a staircasing transaction (if field 10 = 1), or if 'No' is not selected for gender same as sex (if field 50 is not 2)","Yes, if there are fewer than 3 people living in the property (if field 46 < 3), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if there are fewer than 4 people living in the property (if field 46 < 4), or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if there are fewer than 4 people living in the property (if field 46 < 4), or if the purchase is a staircasing transaction (if field 10 = 1), or if 'No' is not selected for gender same as sex (if field 56 is not 2)","Yes, if there are fewer than 4 people living in the property (if field 46 < 4), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if there are fewer than 5 people living in the property (if field 46 < 5), or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if there are fewer than 5 people living in the property (if field 46 < 5), or if the purchase is a staircasing transaction (if field 10 = 1), or if 'No' is not selected for gender same as sex (if field 62 is not 2)","Yes, if there are fewer than 5 people living in the property (if field 46 < 5), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if there are fewer than 6 people living in the property (if field 46 < 6), or if the purchase is a staircasing transaction (if field 10 = 1)",,,,"Yes, if there are fewer than 6 people living in the property (if field 46 < 6), or if the purchase is a staircasing transaction (if field 10 = 1), or if 'No' is not selected for gender same as sex (if field 68 is not 2)","Yes, if there are fewer than 6 people living in the property (if field 46 < 6), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is discounted ownership (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if postcode of buyer 1's last settled accommodation is unknown (if field 72 = 2) -Yes, if sale is discounted ownership (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,Yes,"Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2), or if buyer is not known to have been living at the same address as buyer 1 at the time of purchase (if field 63 = 1 or 3), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if none of the buyers are known to have served as a regular in the UK armed forces (if field 65 = 7, 3 or 8), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if buyer 1's income is not known (if field 70 = R), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2) or if buyer 2's income is not known (if field 70 = R), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if the purchasers did not previously own a property or if it is not known (if field 76 = 2 or 3), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2) or if this is a staircasing transaction (if field 10 = 1)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), if this is a resale (if field 78 = 1) or if this is a staircasing transaction (if field 10 = 1)",,,,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1) +Yes, if sale is discounted ownership (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,Yes,"Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2), or if buyer is not known to have been living at the same address as buyer 1 at the time of purchase (if field 76 = 1 or 3), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if none of the buyers are known to have served as a regular in the UK armed forces (if field 78 = 7, 3 or 8), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if buyer 1's income is not known (if field 83 = R), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if sale is not a joint purchase (if field 12 = 2) or if buyer 2's income is not known (if field 85 = R), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if the purchasers did not previously own a property or if it is not known (if field 89 = 2 or 3), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2) or if this is a staircasing transaction (if field 10 = 1)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), if this is a resale (if field 91 = 1) or if this is a staircasing transaction (if field 10 = 1)",,,,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1) -Yes, if the buyer was not a private registered provider, housing association or local authority tenant immediately before sale (if field 58 is not 1 or 2)",,,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), if the purchase is a staircasing transaction (if field 10 = 1) or if a mortgage was not used (if field 88 = 2)",,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), if the purchase is a staircasing transaction (if field 10 = 1), or if the type of shared ownership sale is not Social Homebuy (if field 9 is not 18)","Yes, if there are no monthy charges or fees, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2), or if the buyers do not own 100% of the property (if field 97 is less than 100)","Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2), and if this is the first time the buyer has engaged in staircasing in the home (if field 99 = 1)",,,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2), or if the buyers now own 100% of the property (if field 97 = 100)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)",,"Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1) or if the type of discounted sale is PRTB, RTB, or Rent to Buy (if field 11 is 9, 14 or 29)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1) or if the type of discounted sale is not PRTB, RTB, or Rent to Buy (if field 11 is 8, 21, 22)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1) or if a mortgage was not used (if field 116 = 2)",,,"Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)","Yes, if there are no leasehold charges or if the purchase was not made through a discounted ownership scheme (if field 8 = 1)",,No,"Yes, if 'No' is not selected for gender same as sex (if field 123 is not 2)",No,"Yes, if 'No' is not selected for gender same as sex (if field 125 is not 2)",No,"Yes, if 'No' is not selected for gender same as sex (if field 127 is not 2)",No,"Yes, if 'No' is not selected for gender same as sex (if field 129 is not 2)",No,"Yes, if 'No' is not selected for gender same as sex (if field 131 is not 2)",No,"Yes, if 'No' is not selected for gender same as sex (if field 133 is not 2)" -Types of sales the question applies to,All,,,,,,,,Shared ownership only,Shared ownership only,Discounted ownership only,All,Joint purchase only,All,,All,,,,,,,,,,Shared ownership - initial purchase and discounted ownership,,All,,Shared ownership - initial purchase and discounted ownership,,,,Joint purchases,,,Joint purchases which are shared ownership - initial purchase and discounted ownership,,,,Shared ownership - initial purchase and discounted ownership,,,,,,,,,,,,,,,,,Shared ownership - initial purchase and discounted ownership,Shared ownership - initial purchase,,,Shared ownership - initial purchase and discounted ownership,Joint purchases which are shared ownership - initial purchase and discounted ownership,,Shared ownership - initial purchase and discounted ownership,,,,,Shared ownership - initial purchase and discounted ownership,,Joint purchases which are shared ownership - initial purchase and discounted ownership,,Shared ownership - initial purchase and discounted ownership,,,,Shared ownership - initial purchase only,,,,,,,,,,,,,,,,,,Shared ownership - staircasing transaction only,,,,,,,,,,,,,,,,Discounted ownership only,,,,,,,,,,,All,All,All,All,All,All,All,All,All,All,All,All -Duplicate check field?,Yes,,,,,,,,,,,,,,,,,,,,Yes,,,,,,,Yes,,,,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +Yes, if the buyer was not a private registered provider, housing association or local authority tenant immediately before sale (if field 71 is not 1 or 2)",,,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), if the purchase is a staircasing transaction (if field 10 = 1) or if a mortgage was not used (if field 101 = 2)",,"Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)","Yes, if the purchase was not made through a shared ownership scheme (if field 8 = 2), if the purchase is a staircasing transaction (if field 10 = 1), or if the type of shared ownership sale is not Social Homebuy (if field 9 is not 18)","Yes, if there are no monthy charges or fees, if the purchase was not made through a shared ownership scheme (if field 8 = 2), or if the purchase is a staircasing transaction (if field 10 = 1)",,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2), or if the buyers do not own 100% of the property (if field 110 is less than 100)","Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2), and if this is the first time the buyer has engaged in staircasing in the home (if field 112 = 1)",,,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,,,"Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2), or if the buyers now own 100% of the property (if field 110 = 100)","Yes, if this is not a shared ownership transaction (if field 8 = 2), or if it is not a staircasing transaction (if field 10 = 2)",,"Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)",,"Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1) or if the type of discounted sale is PRTB, RTB, or Rent to Buy (if field 11 is 9, 14, or 29)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1) or if the type of discounted sale is not PRTB, RTB, or Rent to Buy (if field 11 is 8, 21, 22)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)","Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1) or if a mortgage was not used (if field 131 = 2)",,,"Yes, if the purchase was not made through a discounted ownership scheme (if field 8 = 1)","Yes, if there are no leasehold charges or if the purchase was not made through a discounted ownership scheme (if field 8 = 1)" +Types of sales the question applies to,All,,,,,,,,Shared ownership only,Shared ownership only,Discounted ownership only,All,Joint purchase only,All,,All,,,,,,,,,,,Shared ownership - initial purchase and discounted ownership,,All,,,,Shared ownership - initial purchase and discounted ownership,,,,Joint purchases,,,,,Joint purchases which are shared ownership - initial purchase and discounted ownership,,,,Shared ownership - initial purchase and discounted ownership,,,,,,,,,,,,,,,,,,,,,,,,,Shared ownership - initial purchase and discounted ownership,Shared ownership - initial purchase,,,Shared ownership - initial purchase and discounted ownership,Joint purchases which are shared ownership - initial purchase and discounted ownership,,Shared ownership - initial purchase and discounted ownership,,,,,Shared ownership - initial purchase and discounted ownership,,Joint purchases which are shared ownership - initial purchase and discounted ownership,,Shared ownership - initial purchase and discounted ownership,,,,Shared ownership - initial purchase only,,,,,,,,,,,,,,,,,,Shared ownership - staircasing transaction only,,,,,,,,,,,,,,,,,,Discounted ownership only,,,,,,,,, +Duplicate check field?,Yes,,,,,,Yes,,,,,,,,,"Yes, if UPRN is provided instead of address","Yes, if address is provided instead of UPRN",,,,"Yes, if address is provided instead of UPRN",,,,,,,,Yes,,,,,,Yes,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, Field number,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136 -,4,9,26,ORG1,ORG1,support@example.com,1,2,,,8,2,3,1,1,,a,a,a,a,aa1,1aa,E09000001,1,1,2,3,20,,20,GBR,10,1,,,,,,,,0,,,,,,,,,,,,,,,,,1,2,,,,,,8,,7,3,3,10000,2,,,4,20000,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,30,400000,10000,25,2,,,,390000,0,,1,,,,,,,,,,, +,4,9,26,ORG1,ORG1,support@example.com,9402238393,2,,,8,1,1,2,1,,Address line 1,,Port Thoraborough,Somerset,SW1A,1AA,E09000033,1,2,2,1,1,34,F,1,,17,826,1,1,1,42,F,2,Agender,17,826,1,1,4,2,14,M,1,,9,2,18,R,2,Bigender,3,3,40,M,3,,2,3,40,R,3,,1,1,0,SW1A,1AA,E09000033,3,,7,,5,1,1,13400,1,13400,1,4,R,1,2,,10,,,,,,,180000,,1,20000,10,150000,,,100,,,,,,,,,,,,,180000,,1,,,,,10,180000,10000,,1,20000,10,1,150000,100 diff --git a/spec/services/bulk_upload/sales/validator_spec.rb b/spec/services/bulk_upload/sales/validator_spec.rb index 9d4458a95..37115e609 100644 --- a/spec/services/bulk_upload/sales/validator_spec.rb +++ b/spec/services/bulk_upload/sales/validator_spec.rb @@ -127,7 +127,7 @@ RSpec.describe BulkUpload::Sales::Validator do before do log.owning_organisation = nil log.saledate = date - file.write(log_to_csv.default_field_numbers_row) + file.write(log_to_csv.default_field_numbers_row_for_year(year)) file.write(log_to_csv.to_csv_row) file.rewind end diff --git a/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb b/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb index 09e135400..abbcc7b9d 100644 --- a/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2026/csv_parser_spec.rb @@ -166,7 +166,7 @@ RSpec.describe BulkUpload::Sales::Year2026::CsvParser do it "returns correct column" do expect(service.column_for_field("field_1")).to eql("B") - expect(service.column_for_field("field_99")).to eql("CV") + expect(service.column_for_field("field_112")).to eql("DI") end end end diff --git a/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb b/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb index f53459664..b40c7e65d 100644 --- a/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb +++ b/spec/services/bulk_upload/sales/year2026/row_parser_spec.rb @@ -51,73 +51,73 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do field_22: "4BB", field_23: "E09000008", field_24: "1", - field_25: "2", - field_26: "1", - field_27: "3", - field_28: "32", - field_29: "M", - field_30: "12", - field_31: "28", - field_32: "1", - field_33: "1", - field_34: "3", - field_35: "32", - field_36: "F", - field_37: "17", - field_38: "28", - field_39: "2", - field_40: "1", - field_41: "1", - field_58: "1", - field_59: "1", - field_60: "A1", - field_61: "1AA", - field_62: "E09000008", - field_63: "3", - field_65: "3", - field_67: "5", - field_68: "3", - field_69: "3", - field_70: "30000", + field_26: "2", + field_27: "1", + field_28: "3", + field_29: "32", + field_30: "M", + field_33: "12", + field_34: "28", + field_35: "1", + field_36: "1", + field_37: "3", + field_38: "32", + field_39: "F", + field_42: "17", + field_43: "28", + field_44: "2", + field_45: "1", + field_46: "1", field_71: "1", - field_72: "15000", - field_73: "1", - field_74: "4", - field_75: "20000", + field_72: "1", + field_73: "A1", + field_74: "1AA", + field_75: "E09000008", field_76: "3", - field_79: "5", - field_80: "24", + field_78: "3", + field_80: "5", field_81: "3", - field_82: "2022", - field_83: "1", + field_82: "3", + field_83: "30000", field_84: "1", - field_85: "1", - field_107: "250000", - field_108: "25", - field_109: "1", - field_89: "5000", - field_90: "20", - field_96: "10", - field_97: "40", + field_85: "15000", + field_86: "1", + field_87: "4", + field_88: "20000", + field_89: "3", + field_92: "5", + field_93: "24", + field_94: "3", + field_95: "2022", + field_96: "1", + field_97: "1", field_98: "1", - field_99: "2", - field_94: "200", - field_91: "20000", - field_111: "800", - field_100: "05", - field_101: "04", - field_102: "2020", - field_103: "4", - field_104: "06", - field_105: "07", - field_106: "2023", - field_110: "900", + field_120: "250000", + field_121: "25", field_122: "1", - field_123: "1", - field_125: "2", - field_126: "Non-binary", - field_135: "1", - field_136: "150", + field_102: "5000", + field_103: "20", + field_109: "10", + field_110: "40", + field_111: "1", + field_112: "2", + field_107: "200", + field_104: "20000", + field_124: "800", + field_113: "05", + field_114: "04", + field_115: "2020", + field_116: "4", + field_117: "06", + field_118: "07", + field_119: "2023", + field_123: "900", + field_25: "1", + field_31: "1", + field_40: "2", + field_41: "Non-binary", + field_125: "1", + field_126: "150", } end @@ -150,7 +150,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do context "when the only populated fields are empty strings or whitespace" do before do parser.field_6 = " " - parser.field_25 = "" + parser.field_26 = "" end it "returns true" do @@ -179,11 +179,11 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "previous postcode known" do - context "when field_59 is 1" do + context "when field_72 is 1" do let(:attributes) do { bulk_upload:, - field_59: 1, + field_72: 1, } end @@ -192,11 +192,11 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_59 is 2" do + context "when field_72 is 2" do let(:attributes) do { bulk_upload:, - field_59: 2, + field_72: 2, } end @@ -211,9 +211,9 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do let(:attributes) do { bulk_upload:, - field_70: "R", # income 1 - field_72: "R", # income 2 - field_75: "R", # savings + field_83: "R", # income 1 + field_85: "R", # income 2 + field_88: "R", # savings } end @@ -234,9 +234,9 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do let(:attributes) do { bulk_upload:, - field_70: "30000", # income 1 - field_72: "0", # income 2 - field_75: "12420", # savings + field_83: "30000", # income 1 + field_85: "0", # income 2 + field_88: "12420", # savings } end @@ -300,8 +300,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "and case insensitive fields are set to lowercase" do - let(:case_insensitive_fields) { %w[field_29 field_36 field_44 field_48 field_52 field_56] } - let(:case_insensitive_integer_fields_with_r_option) { %w[field_28 field_35 field_43 field_47 field_51 field_55 field_64 field_75 field_70 field_72 field_90 field_118] } + let(:case_insensitive_fields) { %w[field_30 field_39 field_49 field_55 field_61 field_67] } + let(:case_insensitive_integer_fields_with_r_option) { %w[field_29 field_38 field_48 field_54 field_60 field_66 field_77 field_88 field_83 field_85 field_103 field_133] } let(:attributes) do valid_attributes .merge(case_insensitive_fields.each_with_object({}) { |field, h| h[field.to_sym] = valid_attributes[field.to_sym]&.downcase }) @@ -316,11 +316,11 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do describe "#validate_nulls" do context "when non-setup questions are null" do - let(:attributes) { setup_section_params.merge({ field_29: "" }) } + let(:attributes) { setup_section_params.merge({ field_30: "" }) } it "fetches the question's check_answer_label if it exists" do parser.valid? - expect(parser.errors[:field_29]).to eql([I18n.t("validations.not_answered", question: "buyer 1’s sex registered at birth.")]) + expect(parser.errors[:field_30]).to eql([I18n.t("validations.not_answered", question: "buyer 1’s sex registered at birth.")]) end end @@ -334,12 +334,12 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when an invalid value error has been added" do - let(:attributes) { setup_section_params.merge({ field_10: "2", field_32: "100" }) } + let(:attributes) { setup_section_params.merge({ field_10: "2", field_35: "100" }) } it "does not add an additional error" do parser.valid? - expect(parser.errors[:field_32].length).to eq(1) - expect(parser.errors[:field_32]).to include(match I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "")) + expect(parser.errors[:field_35].length).to eq(1) + expect(parser.errors[:field_35]).to include(match I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "")) end end end @@ -423,9 +423,9 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do let(:attributes) do { bulk_upload:, - field_28: "2", - field_43: "8", - field_36: "1", + field_29: "2", + field_48: "8", + field_39: "1", } end @@ -794,9 +794,9 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do :field_17, # Address line 1 :field_21, # Postcode :field_22, # Postcode - :field_28, # Buyer 1 age - :field_29, # Buyer 1 sex registered at birth - :field_32, # Buyer 1 working situation + :field_29, # Buyer 1 age + :field_30, # Buyer 1 sex registered at birth + :field_35, # Buyer 1 working situation :field_7, # Purchaser code ].each do |field| expect(parser.errors[field]).to include(error_message) @@ -826,9 +826,9 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do :field_17, # Address line 1 :field_21, # Postcode :field_22, # Postcode - :field_28, # Buyer 1 age - :field_29, # Buyer 1 sex registered at birth - :field_32, # Buyer 1 working situation + :field_29, # Buyer 1 age + :field_30, # Buyer 1 sex registered at birth + :field_35, # Buyer 1 working situation :field_7, # Purchaser code ].each do |field| expect(parser.errors[field]).to be_blank @@ -869,31 +869,31 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - describe "#field_115" do # percentage discount + describe "#field_130" do # percentage discount context "when percentage discount over 70" do - let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_11: "9", field_115: "71" }) } + let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_11: "9", field_130: "71" }) } it "returns correct error" do parser.valid? - expect(parser.errors.where(:field_115).map(&:message)).to include(I18n.t("validations.sales.2026.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%")) + expect(parser.errors.where(:field_130).map(&:message)).to include(I18n.t("validations.sales.2026.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%")) end end context "when percentage discount not over 70" do - let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_115: "70" }) } + let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_130: "70" }) } it "does not return error" do parser.valid? - expect(parser.errors.where(:field_115)).not_to be_present + expect(parser.errors.where(:field_130)).not_to be_present end end context "when percentage less than 0" do - let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_115: "-1" }) } + let(:attributes) { valid_attributes.merge({ field_8: "2", field_10: "2", field_130: "-1" }) } it "returns correct error" do parser.valid? - expect(parser.errors.where(:field_115).map(&:message)).to include(I18n.t("validations.sales.2026.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%")) + expect(parser.errors.where(:field_130).map(&:message)).to include(I18n.t("validations.sales.2026.bulk_upload.numeric.within_range", field: "Percentage discount", min: "0%", max: "70%")) end end end @@ -1092,17 +1092,17 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end [ - %w[age1_known details_known_1 age1 field_28 field_34 field_36], - %w[age2_known details_known_2 age2 field_35 field_34 field_36], - %w[age3_known details_known_3 age3 field_43 field_42 field_44], - %w[age4_known details_known_4 age4 field_47 field_46 field_48], - %w[age5_known details_known_5 age5 field_51 field_50 field_52], - %w[age6_known details_known_6 age6 field_55 field_54 field_56], + %w[age1_known details_known_1 age1 field_29 field_37 field_39], + %w[age2_known details_known_2 age2 field_38 field_37 field_39], + %w[age3_known details_known_3 age3 field_48 field_47 field_49], + %w[age4_known details_known_4 age4 field_54 field_53 field_55], + %w[age5_known details_known_5 age5 field_60 field_59 field_61], + %w[age6_known details_known_6 age6 field_66 field_65 field_67], ].each do |known, details_known, age, field, relationship, gender| describe "##{known} and ##{age}" do context "when #{field} is blank" do context "and person details are blank" do - let(:attributes) { setup_section_params.merge({ field.to_s => nil, relationship.to_sym => nil, gender.to_sym => nil, field_15: "1", field_41: "6" }) } + let(:attributes) { setup_section_params.merge({ field.to_s => nil, relationship.to_sym => nil, gender.to_sym => nil, field_15: "1", field_46: "6" }) } it "does not set ##{known}" do unless known == "age1_known" @@ -1122,7 +1122,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "and person details are given" do - let(:attributes) { setup_section_params.merge({ field.to_sym => nil, relationship.to_sym => "C", gender.to_sym => "X", field_15: "1", field_41: "6" }) } + let(:attributes) { setup_section_params.merge({ field.to_sym => nil, relationship.to_sym => "C", gender.to_sym => "X", field_15: "1", field_46: "6" }) } it "does not set ##{age}" do parser.valid? @@ -1132,7 +1132,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when #{field} is R" do - let(:attributes) { setup_section_params.merge({ field.to_s => "R", field_14: "1", field_41: "6", field_15: "1" }) } + let(:attributes) { setup_section_params.merge({ field.to_s => "R", field_14: "1", field_46: "6", field_15: "1" }) } it "sets ##{known} 1" do expect(parser.log.public_send(known)).to be(1) @@ -1144,7 +1144,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when #{field} is a number" do - let(:attributes) { setup_section_params.merge({ field.to_s => "50", field_14: "1", field_41: "6", field_15: "1" }) } + let(:attributes) { setup_section_params.merge({ field.to_s => "50", field_14: "1", field_46: "6", field_15: "1" }) } it "sets ##{known} to 0" do expect(parser.log.public_send(known)).to be(0) @@ -1156,7 +1156,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when #{field} is a non-sensical value" do - let(:attributes) { setup_section_params.merge({ field.to_s => "A", field_14: "1", field_41: "6", field_15: "1" }) } + let(:attributes) { setup_section_params.merge({ field.to_s => "A", field_14: "1", field_46: "6", field_15: "1" }) } it "sets ##{known} to 0" do expect(parser.log.public_send(known)).to be(0) @@ -1171,15 +1171,15 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do describe "relationship field mappings" do [ - %w[field_34 relat2 2], - %w[field_42 relat3 3], - %w[field_46 relat4 4], - %w[field_50 relat5 5], - %w[field_54 relat6 6], + %w[field_37 relat2 2], + %w[field_47 relat3 3], + %w[field_53 relat4 4], + %w[field_59 relat5 5], + %w[field_65 relat6 6], ].each do |input_field, relationship_attribute, person_num| describe input_field.to_s do context "when #{input_field} is 1" do - let(:attributes) { setup_section_params.merge({ input_field.to_sym => "1", field_41: "6" }) } + let(:attributes) { setup_section_params.merge({ input_field.to_sym => "1", field_46: "6" }) } it "sets relationship to P" do expect(parser.log.public_send(relationship_attribute)).to eq("P") @@ -1187,7 +1187,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when #{input_field} is 2" do - let(:attributes) { setup_section_params.merge({ input_field.to_sym => "2", field_41: "6" }) } + let(:attributes) { setup_section_params.merge({ input_field.to_sym => "2", field_46: "6" }) } it "sets relationship to X" do expect(parser.log.public_send(relationship_attribute)).to eq("X") @@ -1195,7 +1195,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when #{input_field} is 3" do - let(:attributes) { setup_section_params.merge({ input_field.to_sym => "3", field_41: "6" }) } + let(:attributes) { setup_section_params.merge({ input_field.to_sym => "3", field_46: "6" }) } it "sets relationship to R" do expect(parser.log.public_send(relationship_attribute)).to eq("R") @@ -1203,7 +1203,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when #{input_field} is 4" do - let(:attributes) { setup_section_params.merge({ input_field.to_sym => "4", field_41: "6" }) } + let(:attributes) { setup_section_params.merge({ input_field.to_sym => "4", field_46: "6" }) } it "gives a validation error" do parser.valid? @@ -1215,102 +1215,102 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - describe "field_39" do # ecstat2 + describe "field_44" do # ecstat2 context "when buyer 2 has no age but has ecstat as child" do - let(:attributes) { valid_attributes.merge({ field_35: nil, field_39: "9" }) } + let(:attributes) { valid_attributes.merge({ field_38: nil, field_44: "9" }) } it "a custom validation is applied" do parser.valid? - expect(parser.errors[:field_39]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat2.buyer_cannot_be_child") + expect(parser.errors[:field_44]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat2.buyer_cannot_be_child") end end context "when buyer 2 is under 16" do - let(:attributes) { valid_attributes.merge({ field_35: "9" }) } + let(:attributes) { valid_attributes.merge({ field_38: "9" }) } it "a custom validation is applied" do parser.valid? validation_message = "Buyer 2’s age must be between 16 and 110." - expect(parser.errors[:field_35]).to include validation_message + expect(parser.errors[:field_38]).to include validation_message end end context "when buyer 2 is over 16 but has ecstat as child" do - let(:attributes) { valid_attributes.merge({ field_35: "17", field_39: "9" }) } + let(:attributes) { valid_attributes.merge({ field_38: "17", field_44: "9" }) } it "a custom validation is applied" do parser.valid? - expect(parser.errors[:field_39]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat2.buyer_cannot_be_over_16_and_child") - expect(parser.errors[:field_35]).to include I18n.t("validations.sales.2026.bulk_upload.age2.buyer_cannot_be_over_16_and_child") + expect(parser.errors[:field_44]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat2.buyer_cannot_be_over_16_and_child") + expect(parser.errors[:field_38]).to include I18n.t("validations.sales.2026.bulk_upload.age2.buyer_cannot_be_over_16_and_child") end end context "when person 2 a child but not a buyer" do - let(:attributes) { valid_attributes.merge({ field_12: 2, field_35: "10", field_39: "9" }) } + let(:attributes) { valid_attributes.merge({ field_12: 2, field_38: "10", field_44: "9" }) } it "does not add errors to their age and ecstat fields" do parser.valid? - expect(parser.errors[:field_35]).to be_empty - expect(parser.errors[:field_39]).to be_empty + expect(parser.errors[:field_38]).to be_empty + expect(parser.errors[:field_44]).to be_empty end end end - describe "field_32" do # ecstat1 + describe "field_35" do # ecstat1 context "when buyer 1 has no age but has ecstat as child" do - let(:attributes) { valid_attributes.merge({ field_28: nil, field_32: "9" }) } + let(:attributes) { valid_attributes.merge({ field_29: nil, field_35: "9" }) } it "a custom validation is applied" do parser.valid? - expect(parser.errors[:field_32]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat1.buyer_cannot_be_child") + expect(parser.errors[:field_35]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat1.buyer_cannot_be_child") end end context "when buyer 1 is under 16" do - let(:attributes) { valid_attributes.merge({ field_28: "9" }) } + let(:attributes) { valid_attributes.merge({ field_29: "9" }) } it "a custom validation is applied" do parser.valid? validation_message = "Buyer 1’s age must be between 16 and 110." - expect(parser.errors[:field_28]).to include validation_message + expect(parser.errors[:field_29]).to include validation_message end end context "when buyer 1 is over 16 but has ecstat as child" do - let(:attributes) { valid_attributes.merge({ field_28: "17", field_32: "9" }) } + let(:attributes) { valid_attributes.merge({ field_29: "17", field_35: "9" }) } it "a custom validation is applied" do parser.valid? - expect(parser.errors[:field_32]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat1.buyer_cannot_be_over_16_and_child") - expect(parser.errors[:field_28]).to include I18n.t("validations.sales.2026.bulk_upload.age1.buyer_cannot_be_over_16_and_child") + expect(parser.errors[:field_35]).to include I18n.t("validations.sales.2026.bulk_upload.ecstat1.buyer_cannot_be_over_16_and_child") + expect(parser.errors[:field_29]).to include I18n.t("validations.sales.2026.bulk_upload.age1.buyer_cannot_be_over_16_and_child") end end end - describe "#field_33" do # will buyer1 live in property? + describe "#field_36" do # will buyer1 live in property? context "when not a possible value" do - let(:attributes) { valid_attributes.merge({ field_10: "2", field_33: "3" }) } + let(:attributes) { valid_attributes.merge({ field_10: "2", field_36: "3" }) } it "is not valid" do parser.valid? - expect(parser.errors).to include(:field_33) + expect(parser.errors).to include(:field_36) end end end - describe "#field_109" do # staircasing mortgageused + describe "#field_122" do # staircasing mortgageused context "when invalid value" do - let(:attributes) { setup_section_params.merge(field_109: "4") } + let(:attributes) { setup_section_params.merge(field_122: "4") } it "returns correct errors" do parser.valid? - expect(parser.errors[:field_109]).to include(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "was a mortgage used for this staircasing transaction?")) + expect(parser.errors[:field_122]).to include(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "was a mortgage used for this staircasing transaction?")) parser.log.blank_invalid_non_setup_fields! parser.log.save! @@ -1319,44 +1319,44 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when value is 3 and stairowned is not 100" do - let(:attributes) { setup_section_params.merge(field_109: "3", field_10: "1", field_96: "50", field_97: "99", field_120: nil) } + let(:attributes) { setup_section_params.merge(field_122: "3", field_10: "1", field_109: "50", field_110: "99", field_135: nil) } it "does not add errors" do parser.valid? - expect(parser.errors[:field_109]).to be_empty + expect(parser.errors[:field_122]).to be_empty end end context "when value is 3 and stairowned is not answered" do - let(:attributes) { setup_section_params.merge(field_109: "3", field_10: "1", field_96: "50", field_97: nil, field_120: nil) } + let(:attributes) { setup_section_params.merge(field_122: "3", field_10: "1", field_109: "50", field_110: nil, field_135: nil) } it "does not add errors" do parser.valid? - expect(parser.errors[:field_109]).to be_empty + expect(parser.errors[:field_122]).to be_empty end end context "when value is 3 and stairowned is 100" do - let(:attributes) { setup_section_params.merge(field_109: "3", field_10: "1", field_96: "50", field_97: "100", field_120: nil) } + let(:attributes) { setup_section_params.merge(field_122: "3", field_10: "1", field_109: "50", field_110: "100", field_135: nil) } it "does not add errors and sets mortgage used to 3" do parser.valid? expect(parser.log.mortgageused).to eq(3) expect(parser.log.stairowned).to eq(100) expect(parser.log.deposit).to be_nil - expect(parser.errors[:field_109]).to be_empty - expect(parser.errors[:field_120]).to be_empty + expect(parser.errors[:field_122]).to be_empty + expect(parser.errors[:field_135]).to be_empty end end end - describe "#field_88" do # shared ownership mortgageused + describe "#field_101" do # shared ownership mortgageused context "when invalid value" do - let(:attributes) { setup_section_params.merge(field_10: "2", field_88: "4") } + let(:attributes) { setup_section_params.merge(field_10: "2", field_101: "4") } it "returns correct errors" do parser.valid? - expect(parser.errors[:field_88]).to include(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "was a mortgage used for the purchase of this property? - Shared ownership.")) + expect(parser.errors[:field_101]).to include(I18n.t("validations.sales.2026.bulk_upload.invalid_option", question: "was a mortgage used to buy this property?")) parser.log.blank_invalid_non_setup_fields! parser.log.save! @@ -1365,16 +1365,16 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when value is 3 and stairowned is not answered" do - let(:attributes) { setup_section_params.merge(field_88: "3", field_10: "2", field_96: "50", field_97: nil, field_120: nil) } + let(:attributes) { setup_section_params.merge(field_101: "3", field_10: "2", field_109: "50", field_110: nil, field_135: nil) } it "does not add errors" do parser.valid? - expect(parser.errors[:field_88]).to be_empty + expect(parser.errors[:field_101]).to be_empty end end context "with non staircasing mortgage error" do - let(:attributes) { setup_section_params.merge(field_9: "30", field_88: "1", field_89: "10000", field_91: "5000", field_86: "30000", field_87: "28", field_10: "2") } + let(:attributes) { setup_section_params.merge(field_9: "30", field_101: "1", field_102: "10000", field_104: "5000", field_99: "30000", field_100: "28", field_10: "2") } it "does not add a BU error on type (because it's a setup field and would block log creation)" do parser.valid? @@ -1383,59 +1383,59 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do it "includes errors on other related fields" do parser.valid? - expect(parser.errors[:field_89]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") - expect(parser.errors[:field_91]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") - expect(parser.errors[:field_86]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") - expect(parser.errors[:field_87]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") + expect(parser.errors[:field_102]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") + expect(parser.errors[:field_104]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") + expect(parser.errors[:field_99]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") + expect(parser.errors[:field_100]).to include("The mortgage (£10,000.00) and cash deposit (£5,000.00) added together is £15,000.00.

The full purchase price (£30,000.00) multiplied by the percentage equity stake purchased (28.0%) is £8,400.00.

These two amounts should be the same.") end it "does not add errors to other ownership type fields" do parser.valid? - expect(parser.errors[:field_117]).to be_empty + expect(parser.errors[:field_132]).to be_empty + expect(parser.errors[:field_135]).to be_empty + expect(parser.errors[:field_128]).to be_empty expect(parser.errors[:field_120]).to be_empty - expect(parser.errors[:field_113]).to be_empty - expect(parser.errors[:field_107]).to be_empty - expect(parser.errors[:field_108]).to be_empty - expect(parser.errors[:field_116]).to be_empty - expect(parser.errors[:field_109]).to be_empty + expect(parser.errors[:field_121]).to be_empty + expect(parser.errors[:field_131]).to be_empty + expect(parser.errors[:field_122]).to be_empty end end end - describe "#field_116" do - let(:attributes) { valid_attributes.merge({ field_8: "2", field_11: "9", field_116: "3" }) } + describe "#field_131" do + let(:attributes) { valid_attributes.merge({ field_8: "2", field_11: "9", field_131: "3" }) } it "allows 3 (don't know) as an option for discounted ownership" do parser.valid? - expect(parser.errors[:field_116]).to be_empty + expect(parser.errors[:field_131]).to be_empty end context "when validate_discounted_ownership_value is triggered" do - let(:attributes) { setup_section_params.merge(field_113: 100, field_120: 100, field_8: 2, field_10: 2, field_11: 9, field_116: 2, field_115: 10) } + let(:attributes) { setup_section_params.merge(field_128: 100, field_135: 100, field_8: 2, field_10: 2, field_11: 9, field_131: 2, field_130: 10) } it "only adds errors to the discounted ownership field" do parser.valid? - expect(parser.errors[:field_88]).to be_empty - expect(parser.errors[:field_117]).to include("The mortgage and cash deposit (£100.00) added together is £100.00.

The full purchase price (£100.00) subtracted by the sum of the full purchase price (£100.00) multiplied by the percentage discount (10.0%) is £90.00.

These two amounts should be the same.") - expect(parser.errors[:field_126]).to be_empty + expect(parser.errors[:field_101]).to be_empty + expect(parser.errors[:field_132]).to include("The mortgage and cash deposit (£100.00) added together is £100.00.

The full purchase price (£100.00) subtracted by the sum of the full purchase price (£100.00) multiplied by the percentage discount (10.0%) is £90.00.

These two amounts should be the same.") + expect(parser.errors[:field_41]).to be_empty end end end describe "soft validations" do context "when soft validation is triggered" do - let(:attributes) { valid_attributes.merge({ field_10: 2, field_28: 22, field_32: 5 }) } + let(:attributes) { valid_attributes.merge({ field_10: 2, field_29: 22, field_35: 5 }) } it "adds an error to the relevant fields" do parser.valid? - expect(parser.errors.where(:field_28, category: :soft_validation)).to be_present - expect(parser.errors.where(:field_32, category: :soft_validation)).to be_present + expect(parser.errors.where(:field_29, category: :soft_validation)).to be_present + expect(parser.errors.where(:field_35, category: :soft_validation)).to be_present end it "populates with correct error message" do parser.valid? - expect(parser.errors.where(:field_28, category: :soft_validation).first.message).to eql("You told us this person is aged 22 years and retired. The minimum expected retirement age in England is 66.") - expect(parser.errors.where(:field_32, category: :soft_validation).first.message).to eql("You told us this person is aged 22 years and retired. The minimum expected retirement age in England is 66.") + expect(parser.errors.where(:field_29, category: :soft_validation).first.message).to eql("You told us this person is aged 22 years and retired. The minimum expected retirement age in England is 66.") + expect(parser.errors.where(:field_35, category: :soft_validation).first.message).to eql("You told us this person is aged 22 years and retired. The minimum expected retirement age in England is 66.") end end end @@ -1452,16 +1452,16 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - describe "field_90" do - context "when field_90 is a number" do - let(:field_90_number_attributes) { valid_attributes.merge({ field_90: 20 }) } + describe "field_103" do + context "when field_103 is a number" do + let(:field_90_number_attributes) { valid_attributes.merge({ field_103: 20 }) } context "and buyer was interviewed" do let(:attributes) { field_90_number_attributes.merge({ field_14: 2 }) } it "does not add an error" do parser.valid? - expect(parser.errors.where(:field_90)).not_to be_present + expect(parser.errors.where(:field_103)).not_to be_present end end @@ -1470,20 +1470,20 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do it "does not add an error" do parser.valid? - expect(parser.errors.where(:field_90)).not_to be_present + expect(parser.errors.where(:field_103)).not_to be_present end end end - context "when field_90 is R" do - let(:field_90_number_attributes) { valid_attributes.merge({ field_90: "R" }) } + context "when field_103 is R" do + let(:field_90_number_attributes) { valid_attributes.merge({ field_103: "R" }) } context "and buyer was interviewed" do let(:attributes) { field_90_number_attributes.merge({ field_14: 2 }) } it "adds an error" do parser.valid? - expect(parser.errors.where(:field_90)).to be_present + expect(parser.errors.where(:field_103)).to be_present end end @@ -1492,20 +1492,20 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do it "does not add an error" do parser.valid? - expect(parser.errors.where(:field_90)).not_to be_present + expect(parser.errors.where(:field_103)).not_to be_present end end end - context "when field_90 is neither a number nor R" do - let(:field_90_number_attributes) { valid_attributes.merge({ field_90: "something" }) } + context "when field_103 is neither a number nor R" do + let(:field_90_number_attributes) { valid_attributes.merge({ field_103: "something" }) } context "and buyer was interviewed" do let(:attributes) { field_90_number_attributes.merge({ field_14: 2 }) } it "adds an error" do parser.valid? - expect(parser.errors.where(:field_90)).to be_present + expect(parser.errors.where(:field_103)).to be_present end end @@ -1514,7 +1514,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do it "adds an error" do parser.valid? - expect(parser.errors.where(:field_90)).to be_present + expect(parser.errors.where(:field_103)).to be_present end end end @@ -1599,8 +1599,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#ethnic_group" do - context "when field_30 is 20" do - let(:attributes) { setup_section_params.merge({ field_30: "20" }) } + context "when field_33 is 20" do + let(:attributes) { setup_section_params.merge({ field_33: "20" }) } it "is correctly set" do expect(parser.log.ethnic_group).to be(0) @@ -1609,14 +1609,14 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#ethnic_group2" do - let(:attributes) { setup_section_params.merge({ field_37: "1" }) } + let(:attributes) { setup_section_params.merge({ field_42: "1" }) } it "is correctly set" do expect(parser.log.ethnic_group2).to be(0) end - context "when field_37 is 20" do - let(:attributes) { setup_section_params.merge({ field_37: "20" }) } + context "when field_42 is 20" do + let(:attributes) { setup_section_params.merge({ field_42: "20" }) } it "is correctly set" do expect(parser.log.ethnic_group2).to be(0) @@ -1625,7 +1625,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#ethnicbuy2" do - let(:attributes) { setup_section_params.merge({ field_37: "1" }) } + let(:attributes) { setup_section_params.merge({ field_42: "1" }) } it "is correctly set" do expect(parser.log.ethnicbuy2).to be(1) @@ -1633,8 +1633,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#nationality_all" do - context "when field_31 is a 3 digit nationality code" do - let(:attributes) { setup_section_params.merge({ field_31: "036" }) } + context "when field_34 is a 3 digit nationality code" do + let(:attributes) { setup_section_params.merge({ field_34: "036" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(36) @@ -1642,8 +1642,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is a nationality code without the trailing 0s" do - let(:attributes) { setup_section_params.merge({ field_31: "36" }) } + context "when field_34 is a nationality code without the trailing 0s" do + let(:attributes) { setup_section_params.merge({ field_34: "36" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(36) @@ -1651,8 +1651,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is a nationality code with trailing 0s" do - let(:attributes) { setup_section_params.merge({ field_31: "0036" }) } + context "when field_34 is a nationality code with trailing 0s" do + let(:attributes) { setup_section_params.merge({ field_34: "0036" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(36) @@ -1660,8 +1660,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is 0" do - let(:attributes) { setup_section_params.merge({ field_31: "0" }) } + context "when field_34 is 0" do + let(:attributes) { setup_section_params.merge({ field_34: "0" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(0) @@ -1669,8 +1669,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is 000" do - let(:attributes) { setup_section_params.merge({ field_31: "000" }) } + context "when field_34 is 000" do + let(:attributes) { setup_section_params.merge({ field_34: "000" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(0) @@ -1678,8 +1678,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is 0000" do - let(:attributes) { setup_section_params.merge({ field_31: "0000" }) } + context "when field_34 is 0000" do + let(:attributes) { setup_section_params.merge({ field_34: "0000" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(0) @@ -1687,8 +1687,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is 826" do - let(:attributes) { setup_section_params.merge({ field_31: "826" }) } + context "when field_34 is 826" do + let(:attributes) { setup_section_params.merge({ field_34: "826" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(826) @@ -1696,8 +1696,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is 826 with trailing 0s" do - let(:attributes) { setup_section_params.merge({ field_31: "0826" }) } + context "when field_34 is 826 with trailing 0s" do + let(:attributes) { setup_section_params.merge({ field_34: "0826" }) } it "is correctly set" do expect(parser.log.nationality_all).to be(826) @@ -1705,21 +1705,21 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_31 is not a valid option" do - let(:attributes) { setup_section_params.merge({ field_31: "123123" }) } + context "when field_34 is not a valid option" do + let(:attributes) { setup_section_params.merge({ field_34: "123123" }) } it "is correctly set" do parser.valid? expect(parser.log.nationality_all).to be_nil expect(parser.log.nationality_all_group).to be_nil - expect(parser.errors["field_31"]).to include(I18n.t("validations.sales.2026.bulk_upload.nationality.invalid")) + expect(parser.errors["field_34"]).to include(I18n.t("validations.sales.2026.bulk_upload.nationality.invalid")) end end end describe "#nationality_all_buyer2" do - context "when field_38 is a 3 digit nationality code" do - let(:attributes) { setup_section_params.merge({ field_38: "036" }) } + context "when field_43 is a 3 digit nationality code" do + let(:attributes) { setup_section_params.merge({ field_43: "036" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(36) @@ -1727,8 +1727,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is a nationality code without the trailing 0s" do - let(:attributes) { setup_section_params.merge({ field_38: "36" }) } + context "when field_43 is a nationality code without the trailing 0s" do + let(:attributes) { setup_section_params.merge({ field_43: "36" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(36) @@ -1736,8 +1736,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is a nationality code with trailing 0s" do - let(:attributes) { setup_section_params.merge({ field_38: "0036" }) } + context "when field_43 is a nationality code with trailing 0s" do + let(:attributes) { setup_section_params.merge({ field_43: "0036" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(36) @@ -1745,8 +1745,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is 0" do - let(:attributes) { setup_section_params.merge({ field_38: "0" }) } + context "when field_43 is 0" do + let(:attributes) { setup_section_params.merge({ field_43: "0" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(0) @@ -1754,8 +1754,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is 000" do - let(:attributes) { setup_section_params.merge({ field_38: "000" }) } + context "when field_43 is 000" do + let(:attributes) { setup_section_params.merge({ field_43: "000" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(0) @@ -1763,8 +1763,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is 0000" do - let(:attributes) { setup_section_params.merge({ field_38: "0000" }) } + context "when field_43 is 0000" do + let(:attributes) { setup_section_params.merge({ field_43: "0000" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(0) @@ -1772,8 +1772,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is 826" do - let(:attributes) { setup_section_params.merge({ field_38: "826" }) } + context "when field_43 is 826" do + let(:attributes) { setup_section_params.merge({ field_43: "826" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(826) @@ -1781,8 +1781,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is 826 with trailing 0s" do - let(:attributes) { setup_section_params.merge({ field_38: "0826" }) } + context "when field_43 is 826 with trailing 0s" do + let(:attributes) { setup_section_params.merge({ field_43: "0826" }) } it "is correctly set" do expect(parser.log.nationality_all_buyer2).to be(826) @@ -1790,20 +1790,20 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_38 is not a valid option" do - let(:attributes) { setup_section_params.merge({ field_38: "123123" }) } + context "when field_43 is not a valid option" do + let(:attributes) { setup_section_params.merge({ field_43: "123123" }) } it "is correctly set" do parser.valid? expect(parser.log.nationality_all_buyer2).to be_nil expect(parser.log.nationality_all_buyer2_group).to be_nil - expect(parser.errors["field_38"]).to include(I18n.t("validations.sales.2026.bulk_upload.nationality.invalid")) + expect(parser.errors["field_43"]).to include(I18n.t("validations.sales.2026.bulk_upload.nationality.invalid")) end end end describe "#buy2living" do - let(:attributes) { setup_section_params.merge({ field_63: "1" }) } + let(:attributes) { setup_section_params.merge({ field_76: "1" }) } it "is correctly set" do expect(parser.log.buy2living).to be(1) @@ -1811,7 +1811,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#prevtenbuy2" do - let(:attributes) { setup_section_params.merge({ field_64: "R" }) } + let(:attributes) { setup_section_params.merge({ field_77: "R" }) } it "is correctly set" do expect(parser.log.prevtenbuy2).to be(0) @@ -1819,7 +1819,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#hhregres" do - let(:attributes) { setup_section_params.merge({ field_65: "1" }) } + let(:attributes) { setup_section_params.merge({ field_78: "1" }) } it "is correctly set" do expect(parser.log.hhregres).to be(1) @@ -1827,7 +1827,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#hhregresstill" do - let(:attributes) { setup_section_params.merge({ field_66: "4" }) } + let(:attributes) { setup_section_params.merge({ field_79: "4" }) } it "is correctly set" do expect(parser.log.hhregresstill).to be(4) @@ -1835,7 +1835,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#prevshared" do - let(:attributes) { setup_section_params.merge({ field_77: "3" }) } + let(:attributes) { setup_section_params.merge({ field_90: "3" }) } it "is correctly set" do expect(parser.log.prevshared).to be(3) @@ -1843,7 +1843,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "#staircasesale" do - let(:attributes) { setup_section_params.merge({ field_98: "1" }) } + let(:attributes) { setup_section_params.merge({ field_111: "1" }) } it "is correctly set" do expect(parser.log.staircasesale).to be(1) @@ -1861,7 +1861,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do context "when shared ownership" do context "when prevten is a social housing type" do - let(:attributes) { valid_attributes.merge({ field_8: "1", field_58: "1" }) } + let(:attributes) { valid_attributes.merge({ field_8: "1", field_71: "1" }) } it "is set to yes" do expect(parser.log.soctenant).to be(1) @@ -1870,7 +1870,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do context "when prevten is not a social housing type" do context "and prevtenbuy2 is a social housing type" do - let(:attributes) { valid_attributes.merge({ field_8: "1", field_58: "3", field_64: "2" }) } + let(:attributes) { valid_attributes.merge({ field_8: "1", field_71: "3", field_77: "2" }) } it "is set to yes" do expect(parser.log.soctenant).to be(1) @@ -1878,7 +1878,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "and prevtenbuy2 is not a social housing type" do - let(:attributes) { valid_attributes.merge({ field_8: "1", field_58: "3", field_64: "4" }) } + let(:attributes) { valid_attributes.merge({ field_8: "1", field_71: "3", field_77: "4" }) } it "is set to no" do expect(parser.log.soctenant).to be(2) @@ -1886,7 +1886,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "and prevtenbuy2 is blank" do - let(:attributes) { valid_attributes.merge({ field_8: "1", field_58: "3", field_64: nil }) } + let(:attributes) { valid_attributes.merge({ field_8: "1", field_71: "3", field_77: nil }) } it "is set to no" do expect(parser.log.soctenant).to be(2) @@ -1897,7 +1897,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "with living before purchase years for shared ownership more than 0" do - let(:attributes) { setup_section_params.merge({ field_8: "1", field_79: "1" }) } + let(:attributes) { setup_section_params.merge({ field_8: "1", field_92: "1" }) } it "is sets living before purchase asked to yes and sets the correct living before purchase years" do expect(parser.log.proplen_asked).to be(0) @@ -1906,7 +1906,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "with living before purchase years for discounted ownership more than 0" do - let(:attributes) { setup_section_params.merge({ field_8: "2", field_112: "1" }) } + let(:attributes) { setup_section_params.merge({ field_8: "2", field_127: "1" }) } it "is sets living before purchase asked to yes and sets the correct living before purchase years" do expect(parser.log.proplen_asked).to be(0) @@ -1915,7 +1915,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "with living before purchase years for shared ownership set to 0" do - let(:attributes) { setup_section_params.merge({ field_8: "1", field_79: "0" }) } + let(:attributes) { setup_section_params.merge({ field_8: "1", field_92: "0" }) } it "is sets living before purchase asked to no" do expect(parser.log.proplen_asked).to be(1) @@ -1924,7 +1924,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "with living before purchase 0 years for discounted ownership set to 0" do - let(:attributes) { setup_section_params.merge({ field_8: "2", field_112: "0" }) } + let(:attributes) { setup_section_params.merge({ field_8: "2", field_127: "0" }) } it "is sets living before purchase asked to no" do expect(parser.log.proplen_asked).to be(1) @@ -1933,7 +1933,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when mscharge is given, but is set to 0 for shared ownership" do - let(:attributes) { valid_attributes.merge(field_94: "0") } + let(:attributes) { valid_attributes.merge(field_107: "0") } it "does not override variables correctly" do log = parser.log @@ -1943,7 +1943,7 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end context "when mscharge is given, but is set to 0 for discounted ownership" do - let(:attributes) { valid_attributes.merge(field_8: "2", field_121: "0") } + let(:attributes) { valid_attributes.merge(field_8: "2", field_136: "0") } it "does not override variables correctly" do log = parser.log @@ -2026,8 +2026,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end describe "mortlen amd mortlen_known" do - context "when field_90 is a number" do - let(:field_90_number_attributes) { valid_attributes.merge({ field_90: 20 }) } + context "when field_103 is a number" do + let(:field_90_number_attributes) { valid_attributes.merge({ field_103: 20 }) } context "and buyer was interviewed" do let(:attributes) { field_90_number_attributes.merge({ field_14: 2 }) } @@ -2058,8 +2058,8 @@ RSpec.describe BulkUpload::Sales::Year2026::RowParser do end end - context "when field_90 is R" do - let(:field_90_number_attributes) { valid_attributes.merge({ field_90: "R" }) } + context "when field_103 is R" do + let(:field_90_number_attributes) { valid_attributes.merge({ field_103: "R" }) } context "and buyer was not interviewed" do let(:attributes) { field_90_number_attributes.merge({ field_14: 1 }) }