Browse Source
* Add sales export to the export service * Update sales export * Remove comments and redundant or statement * Update sales export fields * Add mrentprestaircasing, update factory and tests * Enable sales export on test environments * Update fees mappings for the export * Upcase fields and rebase changes * Update some more fieldsCLDC-3939-test-tests-remotely
10 changed files with 1392 additions and 7 deletions
@ -1,5 +1,6 @@
|
||||
class Export < ApplicationRecord |
||||
scope :lettings, -> { where(collection: "lettings") } |
||||
scope :sales, -> { where(collection: "sales") } |
||||
scope :organisations, -> { where(collection: "organisations") } |
||||
scope :users, -> { where(collection: "users") } |
||||
end |
||||
|
@ -0,0 +1,146 @@
|
||||
module Exports::SalesLogExportConstants |
||||
MAX_XML_RECORDS = 10_000 |
||||
LOG_ID_OFFSET = 300_000_000_000 |
||||
|
||||
EXPORT_MODE = { |
||||
xml: 1, |
||||
csv: 2, |
||||
}.freeze |
||||
|
||||
EXPORT_FIELDS = Set["ID", |
||||
"STATUS", |
||||
"DAY", |
||||
"MONTH", |
||||
"YEAR", |
||||
"DUPLICATESET", |
||||
"CREATEDDATE", |
||||
"UPLOADDATE", |
||||
"OWNINGORGID", |
||||
"OWNINGORGNAME", |
||||
"MANINGORGID", |
||||
"MANINGORGNAME", |
||||
"USERNAME", |
||||
"USERNAMEID", |
||||
"PURCHID", |
||||
"TYPE", |
||||
"OWNERSHIP", |
||||
"COLLECTIONYEAR", |
||||
"JOINTMORE", |
||||
"JOINT", |
||||
"BEDS", |
||||
"ETHNIC", |
||||
"ETHNICGROUP1", |
||||
"LIVEINBUYER1", |
||||
"BUILTYPE", |
||||
"PROPTYPE", |
||||
"NOINT", |
||||
"LIVEINBUYER2", |
||||
"PRIVACYNOTICE", |
||||
"WHEEL", |
||||
"HHOLDCOUNT", |
||||
"LA", |
||||
"INCOME1", |
||||
"INC1NK", |
||||
"INC1MORT", |
||||
"INCOME2", |
||||
"INC2NK", |
||||
"SAVINGSNK", |
||||
"SAVINGS", |
||||
"PREVOWN", |
||||
"AMENDEDBY", |
||||
"AMENDEDBYID", |
||||
"MORTGAGE", |
||||
"INC2MORT", |
||||
"HB", |
||||
"FROMBEDS", |
||||
"STAIRCASE", |
||||
"STAIRBOUGHT", |
||||
"STAIROWNED", |
||||
"MRENT", |
||||
"MRENTPRESTAIRCASING", |
||||
"RESALE", |
||||
"DEPOSIT", |
||||
"CASHDIS", |
||||
"DISABLED", |
||||
"VALUE", |
||||
"EQUITY", |
||||
"DISCOUNT", |
||||
"GRANT", |
||||
"PPCODENK", |
||||
"PPOSTC1", |
||||
"PPOSTC2", |
||||
"PREVLOC", |
||||
"PREVLOCNAME", |
||||
"PREVIOUSLAKNOWN", |
||||
"HHREGRES", |
||||
"HHREGRESSTILL", |
||||
"PROPLEN", |
||||
"HASMSCHARGE", |
||||
"MSCHARGE", |
||||
"PREVTEN", |
||||
"MORTGAGEUSED", |
||||
"WCHAIR", |
||||
"ARMEDFORCESSPOUSE", |
||||
"HODAY", |
||||
"HOMONTH", |
||||
"HOYEAR", |
||||
"FROMPROP", |
||||
"SOCPREVTEN", |
||||
"MORTLEN1", |
||||
"EXTRABOR", |
||||
"HHTYPE", |
||||
"POSTCODE", |
||||
"ISLAINFERRED", |
||||
"BULKUPLOADID", |
||||
"VALUE_VALUE_CHECK", |
||||
"PREVSHARED", |
||||
"STAIRCASETOSALE", |
||||
"ETHNICGROUP2", |
||||
"ETHNIC2", |
||||
"BUY2LIVING", |
||||
"PREVTEN2", |
||||
"UPRN", |
||||
"ADDRESS1", |
||||
"ADDRESS2", |
||||
"TOWNCITY", |
||||
"COUNTY", |
||||
"LANAME", |
||||
"CREATIONMETHOD", |
||||
"NATIONALITYALL1", |
||||
"NATIONALITYALL2", |
||||
"MSCHARGE_VALUE_CHECK", |
||||
"ADDRESS1INPUT", |
||||
"POSTCODEINPUT", |
||||
"ADDRESS_SEARCH_VALUE_CHECK", |
||||
"UPRNSELECTED", |
||||
"BULKADDRESS1", |
||||
"BULKADDRESS2", |
||||
"BULKTOWNCITY", |
||||
"BULKCOUNTY", |
||||
"BULKPOSTCODE", |
||||
"BULKLA", |
||||
"CREATEDBY", |
||||
"CREATEDBYID", |
||||
"HASESTATEFEE", |
||||
"ESTATEFEE", |
||||
"FIRSTSTAIR", |
||||
"NUMSTAIR", |
||||
"STAIRLASTDAY", |
||||
"STAIRLASTMONTH", |
||||
"STAIRLASTYEAR", |
||||
"STAIRINITIALYEAR", |
||||
"STAIRINITIALMONTH", |
||||
"STAIRINITIALDAY", |
||||
"HASSERVICECHARGES", |
||||
"SERVICECHARGES",] |
||||
|
||||
(1..6).each do |index| |
||||
EXPORT_FIELDS << "AGE#{index}" |
||||
EXPORT_FIELDS << "ECSTAT#{index}" |
||||
EXPORT_FIELDS << "SEX#{index}" |
||||
end |
||||
|
||||
(2..6).each do |index| |
||||
EXPORT_FIELDS << "RELAT#{index}" |
||||
end |
||||
end |
@ -0,0 +1,156 @@
|
||||
module Exports |
||||
class SalesLogExportService < Exports::XmlExportService |
||||
include Exports::SalesLogExportConstants |
||||
include CollectionTimeHelper |
||||
|
||||
def export_xml_sales_logs(full_update: false, collection_year: nil) |
||||
archives_for_manifest = {} |
||||
collection_years_to_export(collection_year).each do |year| |
||||
recent_export = Export.sales.where(year:).order("started_at").last |
||||
base_number = Export.sales.where(empty_export: false, year:).maximum(:base_number) || 1 |
||||
export = build_export_run("sales", base_number, full_update, year) |
||||
archives = write_export_archive(export, year, recent_export, full_update) |
||||
|
||||
archives_for_manifest.merge!(archives) |
||||
|
||||
export.empty_export = archives.empty? |
||||
export.save! |
||||
end |
||||
|
||||
archives_for_manifest |
||||
end |
||||
|
||||
private |
||||
|
||||
def get_archive_name(year, base_number, increment) |
||||
return unless year |
||||
|
||||
base_number_str = "f#{base_number.to_s.rjust(4, '0')}" |
||||
increment_str = "inc#{increment.to_s.rjust(4, '0')}" |
||||
"core_sales_#{year}_#{year + 1}_apr_mar_#{base_number_str}_#{increment_str}".downcase |
||||
end |
||||
|
||||
def retrieve_resources(recent_export, full_update, year) |
||||
if !full_update && recent_export |
||||
params = { from: recent_export.started_at, to: @start_time } |
||||
SalesLog.exportable.where("(updated_at >= :from AND updated_at <= :to) OR (values_updated_at IS NOT NULL AND values_updated_at >= :from AND values_updated_at <= :to)", params).filter_by_year(year) |
||||
else |
||||
params = { to: @start_time } |
||||
SalesLog.exportable.where("updated_at <= :to", params).filter_by_year(year) |
||||
end |
||||
end |
||||
|
||||
def apply_cds_transformation(sales_log, _export_mode) |
||||
attribute_hash = sales_log.attributes_before_type_cast |
||||
|
||||
attribute_hash["day"] = sales_log.saledate&.day |
||||
attribute_hash["month"] = sales_log.saledate&.month |
||||
attribute_hash["year"] = sales_log.saledate&.year |
||||
|
||||
attribute_hash["createddate"] = sales_log.created_at&.iso8601 |
||||
attribute_hash["createdby"] = sales_log.created_by&.email |
||||
attribute_hash["createdbyid"] = sales_log.created_by_id |
||||
attribute_hash["username"] = sales_log.assigned_to&.email |
||||
attribute_hash["usernameid"] = sales_log.assigned_to_id |
||||
attribute_hash["uploaddate"] = sales_log.updated_at&.iso8601 |
||||
attribute_hash["amendedby"] = sales_log.updated_by&.email |
||||
attribute_hash["amendedbyid"] = sales_log.updated_by_id |
||||
|
||||
attribute_hash["owningorgid"] = sales_log.owning_organisation&.id |
||||
attribute_hash["owningorgname"] = sales_log.owning_organisation&.name |
||||
attribute_hash["maningorgid"] = sales_log.managing_organisation&.id |
||||
attribute_hash["maningorgname"] = sales_log.managing_organisation&.name |
||||
|
||||
attribute_hash["creationmethod"] = sales_log.creation_method_before_type_cast |
||||
attribute_hash["bulkuploadid"] = sales_log.bulk_upload_id |
||||
attribute_hash["collectionyear"] = sales_log.form.start_date.year |
||||
attribute_hash["ownership"] = sales_log.ownershipsch |
||||
attribute_hash["joint"] = sales_log.jointpur |
||||
attribute_hash["ethnicgroup1"] = sales_log.ethnic_group |
||||
attribute_hash["ethnicgroup2"] = sales_log.ethnic_group2 |
||||
attribute_hash["previouslaknown"] = sales_log.previous_la_known |
||||
attribute_hash["hasmscharge"] = sales_log.discounted_ownership_sale? ? sales_log.has_mscharge : nil |
||||
attribute_hash["mscharge"] = sales_log.discounted_ownership_sale? ? sales_log.mscharge : nil |
||||
attribute_hash["hasservicecharges"] = sales_log.shared_ownership_scheme? ? sales_log.has_mscharge : nil |
||||
attribute_hash["servicecharges"] = sales_log.shared_ownership_scheme? ? sales_log.mscharge : nil |
||||
|
||||
attribute_hash["hoday"] = sales_log.hodate&.day |
||||
attribute_hash["homonth"] = sales_log.hodate&.month |
||||
attribute_hash["hoyear"] = sales_log.hodate&.year |
||||
|
||||
attribute_hash["inc1nk"] = sales_log.income1nk |
||||
attribute_hash["inc2nk"] = sales_log.income2nk |
||||
attribute_hash["postcode"] = sales_log.postcode_full |
||||
attribute_hash["islainferred"] = sales_log.is_la_inferred |
||||
attribute_hash["mortlen1"] = sales_log.mortlen |
||||
attribute_hash["ethnic2"] = sales_log.ethnicbuy2 |
||||
attribute_hash["prevten2"] = sales_log.prevtenbuy2 |
||||
|
||||
attribute_hash["address1"] = sales_log.address_line1 |
||||
attribute_hash["address2"] = sales_log.address_line2 |
||||
attribute_hash["towncity"] = sales_log.town_or_city |
||||
attribute_hash["laname"] = LocalAuthority.find_by(code: sales_log.la)&.name |
||||
attribute_hash["address1input"] = sales_log.address_line1_input |
||||
attribute_hash["postcodeinput"] = sales_log.postcode_full_input |
||||
attribute_hash["uprnselected"] = sales_log.uprn_selection |
||||
|
||||
attribute_hash["bulkaddress1"] = sales_log.address_line1_as_entered |
||||
attribute_hash["bulkaddress2"] = sales_log.address_line2_as_entered |
||||
attribute_hash["bulktowncity"] = sales_log.town_or_city_as_entered |
||||
attribute_hash["bulkcounty"] = sales_log.county_as_entered |
||||
attribute_hash["bulkpostcode"] = sales_log.postcode_full_as_entered |
||||
attribute_hash["bulkla"] = sales_log.la_as_entered |
||||
attribute_hash["nationalityall1"] = sales_log.nationality_all |
||||
attribute_hash["nationalityall2"] = sales_log.nationality_all_buyer2 |
||||
attribute_hash["prevlocname"] = LocalAuthority.find_by(code: sales_log.prevloc)&.name |
||||
attribute_hash["liveinbuyer1"] = sales_log.buy1livein |
||||
attribute_hash["liveinbuyer2"] = sales_log.buy2livein |
||||
|
||||
attribute_hash["hasestatefee"] = sales_log.has_management_fee |
||||
attribute_hash["estatefee"] = sales_log.management_fee |
||||
|
||||
attribute_hash["stairlastday"] = sales_log.lasttransaction&.day |
||||
attribute_hash["stairlastmonth"] = sales_log.lasttransaction&.month |
||||
attribute_hash["stairlastyear"] = sales_log.lasttransaction&.year |
||||
|
||||
attribute_hash["stairinitialday"] = sales_log.initialpurchase&.day |
||||
attribute_hash["stairinitialmonth"] = sales_log.initialpurchase&.month |
||||
attribute_hash["stairinitialyear"] = sales_log.initialpurchase&.year |
||||
attribute_hash["mscharge_value_check"] = sales_log.monthly_charges_value_check |
||||
attribute_hash["duplicateset"] = sales_log.duplicate_set_id |
||||
attribute_hash["staircasetosale"] = sales_log.staircasesale |
||||
|
||||
attribute_hash.transform_keys!(&:upcase) |
||||
attribute_hash |
||||
end |
||||
|
||||
def is_omitted_field?(field_name, _sales_log) |
||||
!EXPORT_FIELDS.include?(field_name) |
||||
end |
||||
|
||||
def build_export_xml(sales_logs) |
||||
doc = Nokogiri::XML("<forms/>") |
||||
|
||||
sales_logs.each do |sales_log| |
||||
attribute_hash = apply_cds_transformation(sales_log, EXPORT_MODE[:xml]) |
||||
form = doc.create_element("form") |
||||
doc.at("forms") << form |
||||
attribute_hash.each do |key, value| |
||||
if is_omitted_field?(key, sales_log) |
||||
next |
||||
else |
||||
form << doc.create_element(key, value) |
||||
end |
||||
end |
||||
end |
||||
|
||||
xml_doc_to_temp_file(doc) |
||||
end |
||||
|
||||
def collection_years_to_export(collection_year) |
||||
return [collection_year] if collection_year.present? |
||||
|
||||
FormHandler.instance.sales_forms.values.map { |f| f.start_date.year }.uniq.select { |year| year > 2024 } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<forms> |
||||
<form> |
||||
<ID>{id}</ID> |
||||
<STATUS>1</STATUS> |
||||
<PURCHID>123</PURCHID> |
||||
<TYPE>8</TYPE> |
||||
<JOINTMORE>1</JOINTMORE> |
||||
<BEDS>2</BEDS> |
||||
<AGE1>27</AGE1> |
||||
<SEX1>F</SEX1> |
||||
<ETHNIC>17</ETHNIC> |
||||
<BUILTYPE>1</BUILTYPE> |
||||
<PROPTYPE>1</PROPTYPE> |
||||
<AGE2>33</AGE2> |
||||
<RELAT2>P</RELAT2> |
||||
<SEX2>X</SEX2> |
||||
<NOINT>2</NOINT> |
||||
<ECSTAT2>1</ECSTAT2> |
||||
<PRIVACYNOTICE>1</PRIVACYNOTICE> |
||||
<ECSTAT1>1</ECSTAT1> |
||||
<WHEEL>1</WHEEL> |
||||
<HHOLDCOUNT>4</HHOLDCOUNT> |
||||
<AGE3>14</AGE3> |
||||
<LA>E09000033</LA> |
||||
<INCOME1>10000</INCOME1> |
||||
<AGE4>18</AGE4> |
||||
<AGE5>40</AGE5> |
||||
<AGE6>40</AGE6> |
||||
<INC1MORT>1</INC1MORT> |
||||
<INCOME2>10000</INCOME2> |
||||
<SAVINGSNK>1</SAVINGSNK> |
||||
<SAVINGS/> |
||||
<PREVOWN>1</PREVOWN> |
||||
<SEX3>F</SEX3> |
||||
<MORTGAGE>20000.0</MORTGAGE> |
||||
<INC2MORT>1</INC2MORT> |
||||
<ECSTAT3>9</ECSTAT3> |
||||
<ECSTAT4>3</ECSTAT4> |
||||
<ECSTAT5>2</ECSTAT5> |
||||
<ECSTAT6>1</ECSTAT6> |
||||
<RELAT3>X</RELAT3> |
||||
<RELAT4>X</RELAT4> |
||||
<RELAT5>R</RELAT5> |
||||
<RELAT6>R</RELAT6> |
||||
<HB>4</HB> |
||||
<SEX4>X</SEX4> |
||||
<SEX5>M</SEX5> |
||||
<SEX6>X</SEX6> |
||||
<FROMBEDS/> |
||||
<STAIRCASE/> |
||||
<STAIRBOUGHT/> |
||||
<STAIROWNED/> |
||||
<MRENT/> |
||||
<RESALE/> |
||||
<DEPOSIT>80000.0</DEPOSIT> |
||||
<CASHDIS/> |
||||
<DISABLED>1</DISABLED> |
||||
<VALUE>110000.0</VALUE> |
||||
<EQUITY/> |
||||
<DISCOUNT/> |
||||
<GRANT>10000.0</GRANT> |
||||
<PPCODENK>0</PPCODENK> |
||||
<PPOSTC1>SW1A</PPOSTC1> |
||||
<PPOSTC2>1AA</PPOSTC2> |
||||
<PREVLOC>E09000033</PREVLOC> |
||||
<HHREGRES>7</HHREGRES> |
||||
<HHREGRESSTILL/> |
||||
<PROPLEN/> |
||||
<MSCHARGE>100.0</MSCHARGE> |
||||
<PREVTEN>1</PREVTEN> |
||||
<MORTGAGEUSED>1</MORTGAGEUSED> |
||||
<WCHAIR>1</WCHAIR> |
||||
<ARMEDFORCESSPOUSE>5</ARMEDFORCESSPOUSE> |
||||
<HODAY/> |
||||
<HOMONTH/> |
||||
<HOYEAR/> |
||||
<FROMPROP/> |
||||
<SOCPREVTEN/> |
||||
<EXTRABOR>1</EXTRABOR> |
||||
<HHTYPE>6</HHTYPE> |
||||
<VALUE_VALUE_CHECK/> |
||||
<PREVSHARED>2</PREVSHARED> |
||||
<BUY2LIVING>3</BUY2LIVING> |
||||
<UPRN/> |
||||
<COUNTY/> |
||||
<ADDRESS_SEARCH_VALUE_CHECK/> |
||||
<FIRSTSTAIR/> |
||||
<NUMSTAIR/> |
||||
<MRENTPRESTAIRCASING/> |
||||
<DAY>1</DAY> |
||||
<MONTH>3</MONTH> |
||||
<YEAR>2026</YEAR> |
||||
<CREATEDDATE>2026-03-01T00:00:00+00:00</CREATEDDATE> |
||||
<CREATEDBY>{created_by_email}</CREATEDBY> |
||||
<CREATEDBYID>{created_by_id}</CREATEDBYID> |
||||
<USERNAME>{assigned_to_email}</USERNAME> |
||||
<USERNAMEID>{assigned_to_id}</USERNAMEID> |
||||
<UPLOADDATE>2026-03-01T00:00:00+00:00</UPLOADDATE> |
||||
<AMENDEDBY/> |
||||
<AMENDEDBYID/> |
||||
<OWNINGORGID>{owning_org_id}</OWNINGORGID> |
||||
<OWNINGORGNAME>{owning_org_name}</OWNINGORGNAME> |
||||
<MANINGORGID>{managing_org_id}</MANINGORGID> |
||||
<MANINGORGNAME>{managing_org_name}</MANINGORGNAME> |
||||
<CREATIONMETHOD>1</CREATIONMETHOD> |
||||
<BULKUPLOADID/> |
||||
<COLLECTIONYEAR>2025</COLLECTIONYEAR> |
||||
<OWNERSHIP>2</OWNERSHIP> |
||||
<JOINT>1</JOINT> |
||||
<ETHNICGROUP1>17</ETHNICGROUP1> |
||||
<ETHNICGROUP2>17</ETHNICGROUP2> |
||||
<PREVIOUSLAKNOWN>1</PREVIOUSLAKNOWN> |
||||
<HASMSCHARGE>1</HASMSCHARGE> |
||||
<HASSERVICECHARGES/> |
||||
<SERVICECHARGES/> |
||||
<INC1NK>0</INC1NK> |
||||
<INC2NK>0</INC2NK> |
||||
<POSTCODE>SW1A 1AA</POSTCODE> |
||||
<ISLAINFERRED>true</ISLAINFERRED> |
||||
<MORTLEN1>10</MORTLEN1> |
||||
<ETHNIC2/> |
||||
<PREVTEN2/> |
||||
<ADDRESS1>Address line 1</ADDRESS1> |
||||
<ADDRESS2/> |
||||
<TOWNCITY>City</TOWNCITY> |
||||
<LANAME>Westminster</LANAME> |
||||
<ADDRESS1INPUT>Address line 1</ADDRESS1INPUT> |
||||
<POSTCODEINPUT>SW1A 1AA</POSTCODEINPUT> |
||||
<UPRNSELECTED/> |
||||
<BULKADDRESS1/> |
||||
<BULKADDRESS2/> |
||||
<BULKTOWNCITY/> |
||||
<BULKCOUNTY/> |
||||
<BULKPOSTCODE/> |
||||
<BULKLA/> |
||||
<NATIONALITYALL1>826</NATIONALITYALL1> |
||||
<NATIONALITYALL2>826</NATIONALITYALL2> |
||||
<PREVLOCNAME>Westminster</PREVLOCNAME> |
||||
<LIVEINBUYER1>1</LIVEINBUYER1> |
||||
<LIVEINBUYER2>1</LIVEINBUYER2> |
||||
<HASESTATEFEE/> |
||||
<ESTATEFEE/> |
||||
<STAIRLASTDAY/> |
||||
<STAIRLASTMONTH/> |
||||
<STAIRLASTYEAR/> |
||||
<STAIRINITIALDAY/> |
||||
<STAIRINITIALMONTH/> |
||||
<STAIRINITIALYEAR/> |
||||
<MSCHARGE_VALUE_CHECK/> |
||||
<DUPLICATESET/> |
||||
<STAIRCASETOSALE/> |
||||
</form> |
||||
</forms> |
@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<forms> |
||||
<form> |
||||
<ID>{id}</ID> |
||||
<STATUS>2</STATUS> |
||||
<PURCHID>123</PURCHID> |
||||
<TYPE>8</TYPE> |
||||
<JOINTMORE>1</JOINTMORE> |
||||
<BEDS>2</BEDS> |
||||
<AGE1>27</AGE1> |
||||
<SEX1>F</SEX1> |
||||
<ETHNIC>17</ETHNIC> |
||||
<BUILTYPE>1</BUILTYPE> |
||||
<PROPTYPE>1</PROPTYPE> |
||||
<AGE2>33</AGE2> |
||||
<RELAT2>P</RELAT2> |
||||
<SEX2>X</SEX2> |
||||
<NOINT>2</NOINT> |
||||
<ECSTAT2>1</ECSTAT2> |
||||
<PRIVACYNOTICE>1</PRIVACYNOTICE> |
||||
<ECSTAT1>1</ECSTAT1> |
||||
<WHEEL>1</WHEEL> |
||||
<HHOLDCOUNT>4</HHOLDCOUNT> |
||||
<AGE3>14</AGE3> |
||||
<LA>E09000033</LA> |
||||
<INCOME1>10000</INCOME1> |
||||
<AGE4>18</AGE4> |
||||
<AGE5>40</AGE5> |
||||
<AGE6>40</AGE6> |
||||
<INC1MORT>1</INC1MORT> |
||||
<INCOME2>10000</INCOME2> |
||||
<SAVINGSNK>1</SAVINGSNK> |
||||
<SAVINGS/> |
||||
<PREVOWN>1</PREVOWN> |
||||
<SEX3>F</SEX3> |
||||
<MORTGAGE>20000.0</MORTGAGE> |
||||
<INC2MORT>1</INC2MORT> |
||||
<ECSTAT3>9</ECSTAT3> |
||||
<ECSTAT4>3</ECSTAT4> |
||||
<ECSTAT5>2</ECSTAT5> |
||||
<ECSTAT6>1</ECSTAT6> |
||||
<RELAT3>X</RELAT3> |
||||
<RELAT4>X</RELAT4> |
||||
<RELAT5>R</RELAT5> |
||||
<RELAT6>R</RELAT6> |
||||
<HB>4</HB> |
||||
<SEX4>X</SEX4> |
||||
<SEX5>M</SEX5> |
||||
<SEX6>X</SEX6> |
||||
<FROMBEDS/> |
||||
<STAIRCASE/> |
||||
<STAIRBOUGHT/> |
||||
<STAIROWNED/> |
||||
<MRENT/> |
||||
<RESALE/> |
||||
<DEPOSIT>80000.0</DEPOSIT> |
||||
<CASHDIS/> |
||||
<DISABLED>1</DISABLED> |
||||
<VALUE>110000.0</VALUE> |
||||
<EQUITY/> |
||||
<DISCOUNT/> |
||||
<GRANT>10000.0</GRANT> |
||||
<PPCODENK>0</PPCODENK> |
||||
<PPOSTC1>SW1A</PPOSTC1> |
||||
<PPOSTC2>1AA</PPOSTC2> |
||||
<PREVLOC>E09000033</PREVLOC> |
||||
<HHREGRES>7</HHREGRES> |
||||
<HHREGRESSTILL/> |
||||
<PROPLEN/> |
||||
<MSCHARGE>100.0</MSCHARGE> |
||||
<PREVTEN>1</PREVTEN> |
||||
<MORTGAGEUSED>1</MORTGAGEUSED> |
||||
<WCHAIR>1</WCHAIR> |
||||
<ARMEDFORCESSPOUSE>5</ARMEDFORCESSPOUSE> |
||||
<HODAY/> |
||||
<HOMONTH/> |
||||
<HOYEAR/> |
||||
<FROMPROP/> |
||||
<SOCPREVTEN/> |
||||
<EXTRABOR>1</EXTRABOR> |
||||
<HHTYPE>6</HHTYPE> |
||||
<VALUE_VALUE_CHECK/> |
||||
<PREVSHARED>2</PREVSHARED> |
||||
<BUY2LIVING>3</BUY2LIVING> |
||||
<UPRN/> |
||||
<COUNTY/> |
||||
<ADDRESS_SEARCH_VALUE_CHECK/> |
||||
<FIRSTSTAIR/> |
||||
<NUMSTAIR/> |
||||
<MRENTPRESTAIRCASING/> |
||||
<DAY>3</DAY> |
||||
<MONTH>4</MONTH> |
||||
<YEAR>2024</YEAR> |
||||
<CREATEDDATE>2024-04-03T00:00:00+01:00</CREATEDDATE> |
||||
<CREATEDBY>{created_by_email}</CREATEDBY> |
||||
<CREATEDBYID>{created_by_id}</CREATEDBYID> |
||||
<USERNAME>{assigned_to_email}</USERNAME> |
||||
<USERNAMEID>{assigned_to_id}</USERNAMEID> |
||||
<UPLOADDATE>2024-04-03T00:00:00+01:00</UPLOADDATE> |
||||
<AMENDEDBY/> |
||||
<AMENDEDBYID/> |
||||
<OWNINGORGID>{owning_org_id}</OWNINGORGID> |
||||
<OWNINGORGNAME>{owning_org_name}</OWNINGORGNAME> |
||||
<MANINGORGID>{managing_org_id}</MANINGORGID> |
||||
<MANINGORGNAME>{managing_org_name}</MANINGORGNAME> |
||||
<CREATIONMETHOD>1</CREATIONMETHOD> |
||||
<BULKUPLOADID/> |
||||
<COLLECTIONYEAR>2024</COLLECTIONYEAR> |
||||
<OWNERSHIP>2</OWNERSHIP> |
||||
<JOINT>1</JOINT> |
||||
<ETHNICGROUP1>17</ETHNICGROUP1> |
||||
<ETHNICGROUP2>17</ETHNICGROUP2> |
||||
<PREVIOUSLAKNOWN>1</PREVIOUSLAKNOWN> |
||||
<HASMSCHARGE>1</HASMSCHARGE> |
||||
<HASSERVICECHARGES/> |
||||
<SERVICECHARGES/> |
||||
<INC1NK>0</INC1NK> |
||||
<INC2NK>0</INC2NK> |
||||
<POSTCODE>SW1A 1AA</POSTCODE> |
||||
<ISLAINFERRED>true</ISLAINFERRED> |
||||
<MORTLEN1>10</MORTLEN1> |
||||
<ETHNIC2/> |
||||
<PREVTEN2/> |
||||
<ADDRESS1>Address line 1</ADDRESS1> |
||||
<ADDRESS2/> |
||||
<TOWNCITY>City</TOWNCITY> |
||||
<LANAME>Westminster</LANAME> |
||||
<ADDRESS1INPUT>Address line 1</ADDRESS1INPUT> |
||||
<POSTCODEINPUT>SW1A 1AA</POSTCODEINPUT> |
||||
<UPRNSELECTED/> |
||||
<BULKADDRESS1/> |
||||
<BULKADDRESS2/> |
||||
<BULKTOWNCITY/> |
||||
<BULKCOUNTY/> |
||||
<BULKPOSTCODE/> |
||||
<BULKLA/> |
||||
<NATIONALITYALL1>826</NATIONALITYALL1> |
||||
<NATIONALITYALL2>826</NATIONALITYALL2> |
||||
<PREVLOCNAME>Westminster</PREVLOCNAME> |
||||
<LIVEINBUYER1>1</LIVEINBUYER1> |
||||
<LIVEINBUYER2>1</LIVEINBUYER2> |
||||
<HASESTATEFEE/> |
||||
<ESTATEFEE/> |
||||
<STAIRLASTDAY/> |
||||
<STAIRLASTMONTH/> |
||||
<STAIRLASTYEAR/> |
||||
<STAIRINITIALDAY/> |
||||
<STAIRINITIALMONTH/> |
||||
<STAIRINITIALYEAR/> |
||||
<MSCHARGE_VALUE_CHECK/> |
||||
<DUPLICATESET/> |
||||
<STAIRCASETOSALE/> |
||||
</form> |
||||
</forms> |
@ -0,0 +1,425 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe Exports::SalesLogExportService do |
||||
subject(:export_service) { described_class.new(storage_service, start_time) } |
||||
|
||||
let(:storage_service) { instance_double(Storage::S3Service) } |
||||
|
||||
let(:xml_export_file) { File.open("spec/fixtures/exports/sales_log.xml", "r:UTF-8") } |
||||
let(:local_manifest_file) { File.open("spec/fixtures/exports/manifest.xml", "r:UTF-8") } |
||||
|
||||
let(:expected_zip_filename) { "core_sales_2025_2026_apr_mar_f0001_inc0001.zip" } |
||||
let(:expected_data_filename) { "core_sales_2025_2026_apr_mar_f0001_inc0001_pt001.xml" } |
||||
let(:expected_manifest_filename) { "manifest.xml" } |
||||
let(:start_time) { Time.zone.local(2026, 3, 1) } |
||||
let(:organisation) { create(:organisation, name: "MHCLG", housing_registration_no: 1234) } |
||||
let(:user) { FactoryBot.create(:user, email: "test1@example.com", organisation:) } |
||||
|
||||
def replace_entity_ids(sales_log, export_template) |
||||
export_template.sub!(/\{owning_org_id\}/, sales_log["owning_organisation_id"].to_s) |
||||
export_template.sub!(/\{owning_org_name\}/, sales_log.owning_organisation.name.to_s) |
||||
export_template.sub!(/\{managing_org_id\}/, sales_log["managing_organisation_id"].to_s) |
||||
export_template.sub!(/\{managing_org_name\}/, sales_log.managing_organisation.name.to_s) |
||||
export_template.sub!(/\{assigned_to_id\}/, sales_log["assigned_to_id"].to_s) |
||||
export_template.sub!(/\{assigned_to_email\}/, sales_log.assigned_to&.email.to_s) |
||||
export_template.sub!(/\{created_by_id\}/, sales_log["created_by_id"].to_s) |
||||
export_template.sub!(/\{created_by_email\}/, sales_log.created_by&.email.to_s) |
||||
export_template.sub!(/\{id\}/, sales_log["id"].to_s) |
||||
end |
||||
|
||||
def replace_record_number(export_template, record_number) |
||||
export_template.sub!(/\{recno\}/, record_number.to_s) |
||||
end |
||||
|
||||
before do |
||||
Timecop.freeze(start_time) |
||||
Singleton.__init__(FormHandler) |
||||
allow(storage_service).to receive(:write_file) |
||||
end |
||||
|
||||
after do |
||||
Timecop.return |
||||
end |
||||
|
||||
context "when exporting daily sales logs in XML" do |
||||
context "and no sales logs are available for export" do |
||||
it "returns an empty archives list" do |
||||
expect(storage_service).not_to receive(:write_file) |
||||
expect(export_service.export_xml_sales_logs).to eq({}) |
||||
end |
||||
end |
||||
|
||||
context "when one pending sales log exists" do |
||||
before do |
||||
FactoryBot.create( |
||||
:sales_log, |
||||
:export, |
||||
status: "pending", |
||||
skip_update_status: true, |
||||
) |
||||
end |
||||
|
||||
it "returns empty archives list for archives manifest" do |
||||
expect(storage_service).not_to receive(:write_file) |
||||
expect(export_service.export_xml_sales_logs).to eq({}) |
||||
end |
||||
end |
||||
|
||||
context "and one sales log is available for export" do |
||||
let!(:sales_log) { FactoryBot.create(:sales_log, :export, assigned_to: user) } |
||||
|
||||
it "generates a ZIP export file with the expected filename" do |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) |
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
it "generates an XML export file with the expected filename within the ZIP file" do |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.name).to eq(expected_data_filename) |
||||
end |
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
it "generates an XML manifest file with the expected content within the ZIP file" do |
||||
expected_content = replace_record_number(local_manifest_file.read, 1) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_manifest_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
it "generates an XML export file with the expected content within the ZIP file" do |
||||
expected_content = replace_entity_ids(sales_log, xml_export_file.read) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
it "returns the list with correct archive" do |
||||
expect(export_service.export_xml_sales_logs).to eq({ expected_zip_filename.gsub(".zip", "") => start_time }) |
||||
end |
||||
end |
||||
|
||||
context "and multiple sales logs are available for export on different periods" do |
||||
let(:previous_zip_filename) { "core_sales_2024_2025_apr_mar_f0001_inc0001.zip" } |
||||
let(:next_zip_filename) { "core_sales_2026_2027_apr_mar_f0001_inc0001.zip" } |
||||
|
||||
before do |
||||
FactoryBot.create(:sales_log, :ignore_validation_errors, saledate: Time.zone.local(2024, 5, 1)) |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2025, 5, 1)) |
||||
FactoryBot.create(:sales_log, :ignore_validation_errors, saledate: Time.zone.local(2026, 4, 1)) |
||||
end |
||||
|
||||
context "when sales logs are across multiple years" do |
||||
it "generates multiple ZIP export files with the expected filenames" do |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) |
||||
expect(storage_service).not_to receive(:write_file).with(previous_zip_filename, any_args) |
||||
expect(storage_service).to receive(:write_file).with(next_zip_filename, any_args) |
||||
expect(Rails.logger).to receive(:info).with("Building export run for sales 2025") |
||||
expect(Rails.logger).to receive(:info).with("Creating core_sales_2025_2026_apr_mar_f0001_inc0001 - 1 resources") |
||||
expect(Rails.logger).to receive(:info).with("Added core_sales_2025_2026_apr_mar_f0001_inc0001_pt001.xml") |
||||
expect(Rails.logger).to receive(:info).with("Writing core_sales_2025_2026_apr_mar_f0001_inc0001.zip") |
||||
expect(Rails.logger).to receive(:info).with("Building export run for sales 2026") |
||||
expect(Rails.logger).to receive(:info).with("Creating core_sales_2026_2027_apr_mar_f0001_inc0001 - 1 resources") |
||||
expect(Rails.logger).to receive(:info).with("Added core_sales_2026_2027_apr_mar_f0001_inc0001_pt001.xml") |
||||
expect(Rails.logger).to receive(:info).with("Writing core_sales_2026_2027_apr_mar_f0001_inc0001.zip") |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
it "generates zip export files only for specified year" do |
||||
expect(storage_service).to receive(:write_file).with(next_zip_filename, any_args) |
||||
expect(Rails.logger).to receive(:info).with("Building export run for sales 2026") |
||||
expect(Rails.logger).to receive(:info).with("Creating core_sales_2026_2027_apr_mar_f0001_inc0001 - 1 resources") |
||||
expect(Rails.logger).to receive(:info).with("Added core_sales_2026_2027_apr_mar_f0001_inc0001_pt001.xml") |
||||
expect(Rails.logger).to receive(:info).with("Writing core_sales_2026_2027_apr_mar_f0001_inc0001.zip") |
||||
|
||||
export_service.export_xml_sales_logs(collection_year: 2026) |
||||
end |
||||
|
||||
context "and previous full exports are different for previous years" do |
||||
let(:expected_zip_filename) { "core_sales_2025_2026_apr_mar_f0007_inc0004.zip" } |
||||
let(:next_zip_filename) { "core_sales_2026_2027_apr_mar_f0001_inc0001.zip" } |
||||
|
||||
before do |
||||
Export.new(started_at: Time.zone.yesterday, base_number: 7, increment_number: 3, collection: "sales", year: 2025).save! |
||||
end |
||||
|
||||
it "generates multiple ZIP export files with different base numbers in the filenames" do |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) |
||||
expect(storage_service).to receive(:write_file).with(next_zip_filename, any_args) |
||||
expect(Rails.logger).to receive(:info).with("Building export run for sales 2025") |
||||
expect(Rails.logger).to receive(:info).with("Creating core_sales_2025_2026_apr_mar_f0007_inc0004 - 1 resources") |
||||
expect(Rails.logger).to receive(:info).with("Added core_sales_2025_2026_apr_mar_f0007_inc0004_pt001.xml") |
||||
expect(Rails.logger).to receive(:info).with("Writing core_sales_2025_2026_apr_mar_f0007_inc0004.zip") |
||||
expect(Rails.logger).to receive(:info).with("Building export run for sales 2026") |
||||
expect(Rails.logger).to receive(:info).with("Creating core_sales_2026_2027_apr_mar_f0001_inc0001 - 1 resources") |
||||
expect(Rails.logger).to receive(:info).with("Added core_sales_2026_2027_apr_mar_f0001_inc0001_pt001.xml") |
||||
expect(Rails.logger).to receive(:info).with("Writing core_sales_2026_2027_apr_mar_f0001_inc0001.zip") |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
context "and multiple sales logs are available for export on same quarter" do |
||||
before do |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2025, 4, 1)) |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2025, 4, 20)) |
||||
end |
||||
|
||||
it "generates an XML manifest file with the expected content within the ZIP file" do |
||||
expected_content = replace_record_number(local_manifest_file.read, 2) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_manifest_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
it "creates a logs export record in a database with correct time" do |
||||
expect { export_service.export_xml_sales_logs } |
||||
.to change(Export, :count).by(2) |
||||
expect(Export.last.started_at).to be_within(2.seconds).of(start_time) |
||||
end |
||||
|
||||
context "when this is the first export (full)" do |
||||
it "returns a ZIP archive for the master manifest (existing sales logs)" do |
||||
expect(export_service.export_xml_sales_logs).to eq({ expected_zip_filename.gsub(".zip", "").gsub(".zip", "") => start_time }) |
||||
end |
||||
end |
||||
|
||||
context "and underlying data changes between getting the logs and writting the manifest" do |
||||
before do |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2026, 2, 1)) |
||||
FactoryBot.create(:sales_log, :ignore_validation_errors, saledate: Time.zone.local(2026, 4, 1)) |
||||
end |
||||
|
||||
def remove_logs(logs) |
||||
logs.each(&:destroy) |
||||
file = Tempfile.new |
||||
doc = Nokogiri::XML("<forms/>") |
||||
doc.write_xml_to(file, encoding: "UTF-8") |
||||
file.rewind |
||||
file |
||||
end |
||||
|
||||
def create_fake_maifest |
||||
file = Tempfile.new |
||||
doc = Nokogiri::XML("<forms/>") |
||||
doc.write_xml_to(file, encoding: "UTF-8") |
||||
file.rewind |
||||
file |
||||
end |
||||
|
||||
it "maintains the same record number" do |
||||
# rubocop:disable RSpec/SubjectStub |
||||
allow(export_service).to receive(:build_export_xml) do |logs| |
||||
remove_logs(logs) |
||||
end |
||||
allow(export_service).to receive(:build_manifest_xml) do |
||||
create_fake_maifest |
||||
end |
||||
|
||||
expect(export_service).to receive(:build_manifest_xml).with(1) |
||||
# rubocop:enable RSpec/SubjectStub |
||||
export_service.export_xml_sales_logs |
||||
end |
||||
end |
||||
|
||||
context "when this is a second export (partial)" do |
||||
before do |
||||
start_time = Time.zone.local(2026, 6, 1) |
||||
Export.new(started_at: start_time, collection: "sales", year: 2025).save! |
||||
end |
||||
|
||||
it "does not add any entry for the master manifest (no sales logs)" do |
||||
expect(storage_service).not_to receive(:write_file) |
||||
expect(export_service.export_xml_sales_logs).to eq({}) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context "and a previous export has run the same day having sales logs" do |
||||
before do |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2025, 5, 1)) |
||||
export_service.export_xml_sales_logs |
||||
end |
||||
|
||||
context "and we trigger another full update" do |
||||
it "increments the base number" do |
||||
export_service.export_xml_sales_logs(full_update: true) |
||||
expect(Export.last.base_number).to eq(2) |
||||
end |
||||
|
||||
it "resets the increment number" do |
||||
export_service.export_xml_sales_logs(full_update: true) |
||||
expect(Export.last.increment_number).to eq(1) |
||||
end |
||||
|
||||
it "returns a correct archives list for manifest file" do |
||||
expect(export_service.export_xml_sales_logs(full_update: true)).to eq({ "core_sales_2025_2026_apr_mar_f0002_inc0001" => start_time }) |
||||
end |
||||
|
||||
it "generates a ZIP export file with the expected filename" do |
||||
expect(storage_service).to receive(:write_file).with("core_sales_2025_2026_apr_mar_f0002_inc0001.zip", any_args) |
||||
export_service.export_xml_sales_logs(full_update: true) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context "and a previous export has run having no sales logs" do |
||||
before { export_service.export_xml_sales_logs } |
||||
|
||||
it "doesn't increment the manifest number by 1" do |
||||
export_service.export_xml_sales_logs |
||||
|
||||
expect(Export.last.increment_number).to eq(1) |
||||
end |
||||
end |
||||
|
||||
context "and a log has been manually updated since the previous partial export" do |
||||
let(:expected_zip_filename) { "core_sales_2025_2026_apr_mar_f0001_inc0002.zip" } |
||||
|
||||
before do |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2026, 2, 1), updated_at: Time.zone.local(2026, 2, 27), values_updated_at: Time.zone.local(2026, 2, 29)) |
||||
FactoryBot.create(:sales_log, saledate: Time.zone.local(2026, 2, 1), updated_at: Time.zone.local(2026, 2, 27), values_updated_at: Time.zone.local(2026, 2, 29)) |
||||
Export.create!(started_at: Time.zone.local(2026, 2, 28), base_number: 1, increment_number: 1, collection: "sales", year: 2025) |
||||
end |
||||
|
||||
it "generates an XML manifest file with the expected content within the ZIP file" do |
||||
expected_content = replace_record_number(local_manifest_file.read, 2) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_manifest_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
expect(export_service.export_xml_sales_logs).to eq({ expected_zip_filename.gsub(".zip", "") => start_time }) |
||||
end |
||||
end |
||||
|
||||
context "and one sales log with duplicate reference is available for export" do |
||||
let!(:sales_log) { FactoryBot.create(:sales_log, :export, duplicate_set_id: 123) } |
||||
|
||||
def replace_duplicate_set_id(export_file) |
||||
export_file.sub!("<DUPLICATESET/>", "<DUPLICATESET>123</DUPLICATESET>") |
||||
end |
||||
|
||||
it "generates an XML export file with the expected content within the ZIP file" do |
||||
expected_content = replace_entity_ids(sales_log, xml_export_file.read) |
||||
expected_content = replace_duplicate_set_id(expected_content) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
end |
||||
|
||||
context "when exporting only 24/25 collection period" do |
||||
let(:start_time) { Time.zone.local(2024, 4, 3) } |
||||
|
||||
before do |
||||
Timecop.freeze(start_time) |
||||
Singleton.__init__(FormHandler) |
||||
end |
||||
|
||||
after do |
||||
Timecop.unfreeze |
||||
Singleton.__init__(FormHandler) |
||||
end |
||||
|
||||
context "and one sales log is available for export" do |
||||
let!(:sales_log) { FactoryBot.create(:sales_log, :export) } |
||||
let(:expected_zip_filename) { "core_sales_2024_2025_apr_mar_f0001_inc0001.zip" } |
||||
let(:expected_data_filename) { "core_sales_2024_2025_apr_mar_f0001_inc0001_pt001.xml" } |
||||
let(:xml_export_file) { File.open("spec/fixtures/exports/sales_log_2024.xml", "r:UTF-8") } |
||||
|
||||
it "generates an XML export file with the expected content within the ZIP file" do |
||||
expected_content = replace_entity_ids(sales_log, xml_export_file.read) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs(full_update: true, collection_year: 2024) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context "when exporting various fees, correctly maps the values" do |
||||
context "with discounted ownership and mscharge" do |
||||
let!(:sales_log) { FactoryBot.create(:sales_log, :export, mscharge: 123) } |
||||
|
||||
def replace_mscharge_value(export_file) |
||||
export_file.sub!("<MSCHARGE>100.0</MSCHARGE>", "<MSCHARGE>123.0</MSCHARGE>") |
||||
end |
||||
|
||||
it "exports mscharge fields as hasmscharge and mscharge" do |
||||
expected_content = replace_entity_ids(sales_log, xml_export_file.read) |
||||
expected_content = replace_mscharge_value(expected_content) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
end |
||||
|
||||
context "with shared ownership and mscharge" do |
||||
let!(:sales_log) { FactoryBot.create(:sales_log, :export, ownershipsch: 1, staircase: 2, type: 30, mscharge: 321, has_management_fee: 1, management_fee: 222) } |
||||
|
||||
def replace_mscharge_and_shared_ownership_values(export_file) |
||||
export_file.sub!("<HASSERVICECHARGES/>", "<HASSERVICECHARGES>1</HASSERVICECHARGES>") |
||||
export_file.sub!("<SERVICECHARGES/>", "<SERVICECHARGES>321.0</SERVICECHARGES>") |
||||
export_file.sub!("<HASESTATEFEE/>", "<HASESTATEFEE>1</HASESTATEFEE>") |
||||
export_file.sub!("<ESTATEFEE/>", "<ESTATEFEE>222.0</ESTATEFEE>") |
||||
export_file.sub!("<MSCHARGE>100.0</MSCHARGE>", "<MSCHARGE/>") |
||||
export_file.sub!("<HASMSCHARGE>1</HASMSCHARGE>", "<HASMSCHARGE/>") |
||||
|
||||
export_file.sub!("<TYPE>8</TYPE>", "<TYPE>30</TYPE>") |
||||
export_file.sub!("<STAIRCASE/>", "<STAIRCASE>2</STAIRCASE>") |
||||
export_file.sub!("<GRANT>10000.0</GRANT>", "<GRANT/>") |
||||
export_file.sub!("<PPCODENK>0</PPCODENK>", "<PPCODENK>1</PPCODENK>") |
||||
export_file.sub!("<PPOSTC1>SW1A</PPOSTC1>", "<PPOSTC1/>") |
||||
export_file.sub!("<PPOSTC2>1AA</PPOSTC2>", "<PPOSTC2/>") |
||||
export_file.sub!("<PREVLOC>E09000033</PREVLOC>", "<PREVLOC/>") |
||||
export_file.sub!("<EXTRABOR>1</EXTRABOR>", "<EXTRABOR/>") |
||||
export_file.sub!("<OWNERSHIP>2</OWNERSHIP>", "<OWNERSHIP>1</OWNERSHIP>") |
||||
export_file.sub!("<PREVIOUSLAKNOWN>1</PREVIOUSLAKNOWN>", "<PREVIOUSLAKNOWN>0</PREVIOUSLAKNOWN>") |
||||
export_file.sub!("<PREVLOCNAME>Westminster</PREVLOCNAME>", "<PREVLOCNAME/>") |
||||
end |
||||
|
||||
it "exports mscharge fields as hasmscharge and mscharge" do |
||||
expected_content = replace_entity_ids(sales_log, xml_export_file.read) |
||||
expected_content = replace_mscharge_and_shared_ownership_values(expected_content) |
||||
expect(storage_service).to receive(:write_file).with(expected_zip_filename, any_args) do |_, content| |
||||
entry = Zip::File.open_buffer(content).find_entry(expected_data_filename) |
||||
expect(entry).not_to be_nil |
||||
expect(entry.get_input_stream.read).to eq(expected_content) |
||||
end |
||||
|
||||
export_service.export_xml_sales_logs |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue