Browse Source

CLDC 2286 Delete logs (#1620)

* Add LogPolicy

* Add letting log delete flow

* Add delete sales log flow

* Test for presence of buttons

* Use govuk_button_link_to

* Move actions to helper

* Allow deletion of delete and in progress logs

* better handle 500 error

* Soft delete logs

* Move specs to shared spec file

* Address comments

* Add scoping for data coordinators
pull/1640/head
Jack 2 years ago committed by GitHub
parent
commit
7950f2ae14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 46
      app/controllers/lettings_logs_controller.rb
  2. 28
      app/controllers/sales_logs_controller.rb
  3. 46
      app/helpers/log_actions_helper.rb
  4. 8
      app/models/log.rb
  5. 27
      app/policies/lettings_log_policy.rb
  6. 27
      app/policies/sales_log_policy.rb
  7. 2
      app/services/csv/lettings_log_csv_service.rb
  8. 4
      app/services/exports/lettings_log_export_service.rb
  9. 28
      app/views/logs/delete_confirmation.html.erb
  10. 14
      app/views/logs/edit.html.erb
  11. 4
      config/routes.rb
  12. 6
      db/migrate/20230515114101_add_discarded_at_to_logs.rb
  13. 10
      db/schema.rb
  14. 4
      spec/factories/lettings_log.rb
  15. 4
      spec/factories/sales_log.rb
  16. 4
      spec/fixtures/files/lettings_logs_download.csv
  17. 4
      spec/fixtures/files/lettings_logs_download_codes_only.csv
  18. 82
      spec/models/lettings_log_spec.rb
  19. 85
      spec/models/sales_log_spec.rb
  20. 114
      spec/policies/lettings_log_policy_spec.rb
  21. 114
      spec/policies/sales_log_policy_spec.rb
  22. 96
      spec/requests/lettings_logs_controller_spec.rb
  23. 92
      spec/requests/sales_logs_controller_spec.rb
  24. 783
      spec/services/csv/lettings_log_csv_service_spec.rb
  25. 107
      spec/shared/shared_log_examples.rb

46
app/controllers/lettings_logs_controller.rb

@ -1,5 +1,8 @@
class LettingsLogsController < LogsController
before_action :find_resource, except: %i[create index edit]
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
before_action :find_resource, only: %i[update show]
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :authenticate_scope!, only: %i[download_csv email_csv]
@ -55,28 +58,31 @@ class LettingsLogsController < LogsController
end
def edit
@log = current_user.lettings_logs.find_by(id: params[:id])
if @log
if @log.unresolved
redirect_to(send(@log.form.unresolved_log_path, @log))
else
render("logs/edit", locals: { current_user: })
end
@log = current_user.lettings_logs.find(params[:id])
if @log.unresolved
redirect_to(send(@log.form.unresolved_log_path, @log))
else
render_not_found
render("logs/edit", locals: { current_user: })
end
end
def destroy
if @log
if @log.delete
head :no_content
else
render json: { errors: @log.errors.messages }, status: :unprocessable_entity
end
else
render_not_found_json("Log", params[:id])
end
@log = LettingsLog.visible.find(params[:id])
authorize @log
@log.discard!
redirect_to lettings_logs_path, notice: "Log #{@log.id} has been deleted"
end
def delete_confirmation
@log = LettingsLog.visible.find(params[:lettings_log_id])
authorize @log, :destroy?
render "logs/delete_confirmation"
end
def download_csv
@ -105,14 +111,14 @@ class LettingsLogsController < LogsController
end
end
private
def org_params
super.merge(
{ "managing_organisation_id" => current_user.organisation.id },
)
end
private
def authenticate_scope!
head :unauthorized and return if codes_only_export? && !current_user.support?
end

28
app/controllers/sales_logs_controller.rb

@ -1,4 +1,6 @@
class SalesLogsController < LogsController
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :authenticate_scope!, only: %i[download_csv email_csv]
@ -32,12 +34,26 @@ class SalesLogsController < LogsController
end
def edit
@log = current_user.sales_logs.visible.find_by(id: params[:id])
if @log
render "logs/edit", locals: { current_user: }
else
render_not_found
end
@log = current_user.sales_logs.visible.find(params[:id])
render "logs/edit", locals: { current_user: }
end
def destroy
@log = SalesLog.visible.find(params[:id])
authorize @log
@log.discard!
redirect_to sales_logs_path, notice: "Log #{@log.id} has been deleted"
end
def delete_confirmation
@log = SalesLog.visible.find(params[:sales_log_id])
authorize @log, :destroy?
render "logs/delete_confirmation"
end
def download_csv

46
app/helpers/log_actions_helper.rb

@ -0,0 +1,46 @@
module LogActionsHelper
include GovukLinkHelper
def edit_actions_for_log(log)
back = back_button_for(log)
delete = delete_button_for_log(log)
return if back.nil? && delete.nil?
content_tag(:div, class: "govuk-button-group") do
safe_join([back, delete])
end
end
private
def back_button_for(log)
if log.completed?
if log.bulk_uploaded?
if log.lettings?
govuk_button_link_to "Back to uploaded logs", resume_bulk_upload_lettings_result_path(log.bulk_upload)
else
govuk_button_link_to "Back to uploaded logs", resume_bulk_upload_sales_result_path(log.bulk_upload)
end
elsif log.lettings?
govuk_button_link_to "Back to lettings logs", lettings_logs_path
elsif log.sales?
govuk_button_link_to "Back to sales logs", sales_logs_path
end
end
end
def policy_class_for(log)
log.lettings? ? LettingsLogPolicy : SalesLogPolicy
end
def delete_button_for_log(log)
if policy_class_for(log).new(current_user, log).destroy?
govuk_button_link_to(
"Delete log",
lettings_log_delete_confirmation_path(log),
warning: true,
)
end
end
end

8
app/models/log.rb

@ -15,11 +15,13 @@ class Log < ApplicationRecord
"in_progress" => 1,
"completed" => 2,
"pending" => 3,
"deleted" => 4,
}.freeze
enum status: STATUS
enum status_cache: STATUS, _prefix: true
scope :visible, -> { where(status: %w[not_started in_progress completed]) }
scope :exportable, -> { where(status: %w[not_started in_progress completed deleted]) }
scope :filter_by_status, ->(status, _user = nil) { where status: }
scope :filter_by_years, lambda { |years, _user = nil|
@ -129,7 +131,13 @@ class Log < ApplicationRecord
end
end
def discard!
update!(status: "deleted", discarded_at: Time.zone.now)
end
def calculate_status
return "deleted" if discarded_at.present?
if all_fields_completed? && errors.empty?
"completed"
elsif all_fields_nil?

27
app/policies/lettings_log_policy.rb

@ -0,0 +1,27 @@
class LettingsLogPolicy
attr_reader :user, :log
def initialize(user, log)
@user = user
@log = log
end
def destroy?
return false unless log && user
# Can only delete editable logs
return false unless log.collection_period_open?
# Only delete logs with answered questions
return false unless log.in_progress? || log.completed?
# Support users can delete any log
return true if user.support?
# Data coordinators can delete any log visible to them
return true if user.data_coordinator? && user.lettings_logs.visible.include?(log)
# Data providers can only delete the log if it is assigned to them
log.created_by == user
end
end

27
app/policies/sales_log_policy.rb

@ -0,0 +1,27 @@
class SalesLogPolicy
attr_reader :user, :log
def initialize(user, log)
@user = user
@log = log
end
def destroy?
return false unless log && user
# Can only delete editable logs
return false unless log.collection_period_open?
# Only delete logs with answered questions
return false unless log.in_progress? || log.completed?
# Support users can delete any log
return true if user.support?
# Data coordinators can delete any log visible to them
return true if user.data_coordinator? && user.sales_logs.visible.include?(log)
# Data providers can only delete the log if it is assigned to them
log.created_by == user
end
end

2
app/services/csv/lettings_log_csv_service.rb

@ -1,6 +1,6 @@
module Csv
class LettingsLogCsvService
CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays la prevloc unresolved updated_by_id bulk_upload_id uprn_confirmed status_cache].freeze
CSV_FIELDS_TO_OMIT = %w[hhmemb net_income_value_check first_time_property_let_as_social_housing renttype needstype postcode_known is_la_inferred totchild totelder totadult net_income_known is_carehome previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 rent_type_detail wrent wscharge wpschrge wsupchrg wtcharge wtshortfall rent_value_check old_form_id old_id retirement_value_check tshortfall_known pregnancy_value_check hhtype new_old vacdays la prevloc unresolved updated_by_id bulk_upload_id uprn_confirmed status_cache discarded_at].freeze
def initialize(user, export_type:)
@user = user

4
app/services/exports/lettings_log_export_service.rb

@ -119,10 +119,10 @@ module Exports
if !full_update && recent_export
params = { from: recent_export.started_at, to: start_time }
LettingsLog.visible.where("updated_at >= :from and updated_at <= :to", params)
LettingsLog.exportable.where("updated_at >= :from and updated_at <= :to", params)
else
params = { to: start_time }
LettingsLog.visible.where("updated_at <= :to", params)
LettingsLog.exportable.where("updated_at <= :to", params)
end
end

28
app/views/logs/delete_confirmation.html.erb

@ -0,0 +1,28 @@
<% content_for :before_content do %>
<% content_for :title, "Are you sure you want to delete this log?" %>
<%= govuk_back_link href: @log.lettings? ? lettings_logs_path : sales_logs_path %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">
<span class="govuk-caption-xl">Delete log <%= @log.id %></span>
<h1 class="govuk-heading-xl">
<%= content_for(:title) %>
</h1>
<%= govuk_warning_text(text: "You will not be able to undo this action.") %>
<div class="govuk-button-group">
<%= govuk_button_to(
"Delete this log",
@log.lettings? ? lettings_log_path(@log) : sales_log_path(@log),
method: :delete,
) %>
<%= govuk_button_link_to(
"Cancel",
@log.lettings? ? lettings_log_path(@log) : sales_log_path(@log),
secondary: true,
) %>
</div>
</div>
</div>

14
app/views/logs/edit.html.erb

@ -38,18 +38,6 @@
<%= render "tasklist" %>
<% if @log.completed? %>
<% if @log.bulk_uploaded? %>
<% if @log.lettings? %>
<%= govuk_button_link_to "Back to uploaded logs", resume_bulk_upload_lettings_result_path(@log.bulk_upload) %>
<% else %>
<%= govuk_button_link_to "Back to uploaded logs", resume_bulk_upload_sales_result_path(@log.bulk_upload) %>
<% end %>
<% elsif @log.lettings? %>
<%= govuk_button_link_to "Back to lettings logs", lettings_logs_path %>
<% elsif @log.sales? %>
<%= govuk_button_link_to "Back to sales logs", sales_logs_path %>
<% end %>
<% end %>
<%= edit_actions_for_log(@log) %>
</div>
</div>

4
config/routes.rb

@ -150,6 +150,8 @@ Rails.application.routes.draw do
end
resources :lettings_logs, path: "/lettings-logs" do
get "delete-confirmation", to: "lettings_logs#delete_confirmation"
collection do
post "bulk-upload", to: "bulk_upload#bulk_upload"
get "bulk-upload", to: "bulk_upload#show"
@ -208,6 +210,8 @@ Rails.application.routes.draw do
end
resources :sales_logs, path: "/sales-logs" do
get "delete-confirmation", to: "sales_logs#delete_confirmation"
collection do
get "csv-download", to: "sales_logs#download_csv"
post "email-csv", to: "sales_logs#email_csv"

6
db/migrate/20230515114101_add_discarded_at_to_logs.rb

@ -0,0 +1,6 @@
class AddDiscardedAtToLogs < ActiveRecord::Migration[7.0]
def change
add_column :sales_logs, :discarded_at, :datetime
add_column :lettings_logs, :discarded_at, :datetime
end
end

10
db/schema.rb

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_05_05_105327) do
ActiveRecord::Schema[7.0].define(version: 2023_05_15_114101) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -288,6 +288,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_05_105327) do
t.string "county"
t.integer "carehome_charges_value_check"
t.integer "status_cache", default: 0, null: false
t.datetime "discarded_at"
t.index ["bulk_upload_id"], name: "index_lettings_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_lettings_logs_on_created_by_id"
t.index ["location_id"], name: "index_lettings_logs_on_location_id"
@ -584,6 +585,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_05_105327) do
t.integer "ethnicbuy2"
t.integer "proplen_asked"
t.string "old_id"
t.integer "buy2living"
t.integer "prevtenbuy2"
t.integer "pregblank"
t.string "uprn"
t.integer "uprn_known"
@ -592,15 +595,14 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_05_105327) do
t.string "address_line2"
t.string "town_or_city"
t.string "county"
t.integer "buy2living"
t.integer "prevtenbuy2"
t.integer "nationalbuy2"
t.integer "discounted_sale_value_check"
t.integer "student_not_child_value_check"
t.integer "percentage_discount_value_check"
t.integer "buyer_livein_value_check"
t.integer "combined_income_value_check"
t.integer "status_cache", default: 0, null: false
t.integer "combined_income_value_check"
t.datetime "discarded_at"
t.index ["bulk_upload_id"], name: "index_sales_logs_on_bulk_upload_id"
t.index ["created_by_id"], name: "index_sales_logs_on_created_by_id"
t.index ["old_id"], name: "index_sales_logs_on_old_id", unique: true

4
spec/factories/lettings_log.rb

@ -164,6 +164,10 @@ FactoryBot.define do
trait :startdate_today do
startdate { Time.zone.today }
end
trait :deleted do
status { 4 }
discarded_at { Time.zone.now }
end
created_at { Time.zone.today }
updated_at { Time.zone.today }
end

4
spec/factories/sales_log.rb

@ -129,5 +129,9 @@ FactoryBot.define do
trait :with_uprn do
uprn { rand(999_999_999_999).to_s }
end
trait :deleted do
status { 4 }
discarded_at { Time.zone.now }
end
end
end

4
spec/fixtures/files/lettings_logs_download.csv vendored

@ -1,2 +1,2 @@
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate}
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,discarded_at,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,No,DLUHC,DLUHC,2021,,Supported housing,,2 October 2021,London Affordable Rent,,,,,,,,,,,,,,,,,,,,No,,,,,No,Westminster,E09000033,,SE1 1TE,,No,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},Missing,No,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,Bungalow,Fitted with equipment and adaptations,Westminster,{location_startdate}

1 id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name collection_start_year rent_value_check needstype renewal startdate rent_type_detail irproduct_other tenancycode propcode age1 sex1 ecstat1 hhmemb relat2 age2 sex2 retirement_value_check ecstat2 armedforces leftreg illness housingneeds_a housingneeds_b housingneeds_c housingneeds_h is_previous_la_inferred prevloc_label prevloc illness_type_1 illness_type_2 is_la_inferred la_label la postcode_known postcode_full previous_la_known wchair preg_occ cbl earnings incfreq net_income_value_check benefits hb period brent scharge pscharge supcharg tcharge offered layear ppostcode_full mrcdate declaration ethnic national prevten age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap reservist startertenancy tenancylength tenancy rsnvac unittype_gn beds waityear reasonpref chr cap reasonother housingneeds_f housingneeds_g illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother property_owner_organisation property_manager_organisation purchaser_code reason majorrepairs hbrentshortfall property_relet incref first_time_property_let_as_social_housing unitletas builtype voiddate renttype lettype totchild totelder totadult net_income_known nocharge is_carehome household_charge referral tshortfall chcharge ppcodenk age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known ethnic_group letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 has_benefits wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat3 relat4 relat5 relat6 relat7 relat8 old_form_id lar irproduct old_id joint tshortfall_known sheltered pregnancy_value_check hhtype new_old vacdays major_repairs_date_value_check void_date_value_check housingneeds_type housingneeds_other unresolved updated_by_id uprn uprn_known uprn_confirmed address_line1 address_line2 town_or_city county carehome_charges_value_check status_cache discarded_at unittype_sh scheme_code scheme_service_name scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_admin_district location_startdate
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 Danny Rojas No DLUHC DLUHC 2021 Supported housing 2 October 2021 London Affordable Rent No No Westminster E09000033 SE1 1TE No 2 8 0 0 0 0 0 0 9 1 not_started 6 {scheme_code} {scheme_service_name} {scheme_sensitive} Missing No DLUHC {scheme_primary_client_group} {scheme_secondary_client_group} {scheme_support_type} {scheme_intended_stay} 2021-04-01 00:00:00 +0100 {location_code} SE1 1TE Downing Street 20 Bungalow Fitted with equipment and adaptations Westminster {location_startdate}

4
spec/fixtures/files/lettings_logs_download_codes_only.csv vendored

@ -1,2 +1,2 @@
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate}
id,status,created_at,updated_at,created_by_name,is_dpo,owning_organisation_name,managing_organisation_name,collection_start_year,rent_value_check,needstype,renewal,startdate,rent_type_detail,irproduct_other,tenancycode,propcode,age1,sex1,ecstat1,hhmemb,relat2,age2,sex2,retirement_value_check,ecstat2,armedforces,leftreg,illness,housingneeds_a,housingneeds_b,housingneeds_c,housingneeds_h,is_previous_la_inferred,prevloc_label,prevloc,illness_type_1,illness_type_2,is_la_inferred,la_label,la,postcode_known,postcode_full,previous_la_known,wchair,preg_occ,cbl,earnings,incfreq,net_income_value_check,benefits,hb,period,brent,scharge,pscharge,supcharg,tcharge,offered,layear,ppostcode_full,mrcdate,declaration,ethnic,national,prevten,age3,sex3,ecstat3,age4,sex4,ecstat4,age5,sex5,ecstat5,age6,sex6,ecstat6,age7,sex7,ecstat7,age8,sex8,ecstat8,homeless,underoccupation_benefitcap,reservist,startertenancy,tenancylength,tenancy,rsnvac,unittype_gn,beds,waityear,reasonpref,chr,cap,reasonother,housingneeds_f,housingneeds_g,illness_type_3,illness_type_4,illness_type_8,illness_type_5,illness_type_6,illness_type_7,illness_type_9,illness_type_10,rp_homeless,rp_insan_unsat,rp_medwel,rp_hardship,rp_dontknow,tenancyother,property_owner_organisation,property_manager_organisation,purchaser_code,reason,majorrepairs,hbrentshortfall,property_relet,incref,first_time_property_let_as_social_housing,unitletas,builtype,voiddate,renttype,lettype,totchild,totelder,totadult,net_income_known,nocharge,is_carehome,household_charge,referral,tshortfall,chcharge,ppcodenk,age1_known,age2_known,age3_known,age4_known,age5_known,age6_known,age7_known,age8_known,ethnic_group,letting_allocation_unknown,details_known_2,details_known_3,details_known_4,details_known_5,details_known_6,details_known_7,details_known_8,has_benefits,wrent,wscharge,wpschrge,wsupchrg,wtcharge,wtshortfall,refused,housingneeds,wchchrg,newprop,relat3,relat4,relat5,relat6,relat7,relat8,old_form_id,lar,irproduct,old_id,joint,tshortfall_known,sheltered,pregnancy_value_check,hhtype,new_old,vacdays,major_repairs_date_value_check,void_date_value_check,housingneeds_type,housingneeds_other,unresolved,updated_by_id,uprn,uprn_known,uprn_confirmed,address_line1,address_line2,town_or_city,county,carehome_charges_value_check,status_cache,discarded_at,unittype_sh,scheme_code,scheme_service_name,scheme_sensitive,scheme_type,scheme_registered_under_care_act,scheme_owning_organisation_name,scheme_primary_client_group,scheme_has_other_client_group,scheme_secondary_client_group,scheme_support_type,scheme_intended_stay,scheme_created_at,location_code,location_postcode,location_name,location_units,location_type_of_unit,location_mobility_type,location_admin_district,location_startdate
{id},in_progress,2022-02-08 16:52:15 +0000,2022-02-08 16:52:15 +0000,Danny Rojas,false,DLUHC,DLUHC,2021,,2,,2 October 2021,2,,,,,,,,,,,,,,,,,,,,false,,,,,false,Westminster,E09000033,,SE1 1TE,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,8,0,0,0,,0,,,,,,,,,,,,,,,,,,,,,,,,0,,,,,,,0,,,,,,,,,,,,,,,,,,9,1,,,,,,,,,,,,,,,,not_started,,6,{scheme_code},{scheme_service_name},{scheme_sensitive},0,1,DLUHC,{scheme_primary_client_group},,{scheme_secondary_client_group},{scheme_support_type},{scheme_intended_stay},2021-04-01 00:00:00 +0100,{location_code},SE1 1TE,Downing Street,20,6,A,Westminster,{location_startdate}

1 id status created_at updated_at created_by_name is_dpo owning_organisation_name managing_organisation_name collection_start_year rent_value_check needstype renewal startdate rent_type_detail irproduct_other tenancycode propcode age1 sex1 ecstat1 hhmemb relat2 age2 sex2 retirement_value_check ecstat2 armedforces leftreg illness housingneeds_a housingneeds_b housingneeds_c housingneeds_h is_previous_la_inferred prevloc_label prevloc illness_type_1 illness_type_2 is_la_inferred la_label la postcode_known postcode_full previous_la_known wchair preg_occ cbl earnings incfreq net_income_value_check benefits hb period brent scharge pscharge supcharg tcharge offered layear ppostcode_full mrcdate declaration ethnic national prevten age3 sex3 ecstat3 age4 sex4 ecstat4 age5 sex5 ecstat5 age6 sex6 ecstat6 age7 sex7 ecstat7 age8 sex8 ecstat8 homeless underoccupation_benefitcap reservist startertenancy tenancylength tenancy rsnvac unittype_gn beds waityear reasonpref chr cap reasonother housingneeds_f housingneeds_g illness_type_3 illness_type_4 illness_type_8 illness_type_5 illness_type_6 illness_type_7 illness_type_9 illness_type_10 rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow tenancyother property_owner_organisation property_manager_organisation purchaser_code reason majorrepairs hbrentshortfall property_relet incref first_time_property_let_as_social_housing unitletas builtype voiddate renttype lettype totchild totelder totadult net_income_known nocharge is_carehome household_charge referral tshortfall chcharge ppcodenk age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known ethnic_group letting_allocation_unknown details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 has_benefits wrent wscharge wpschrge wsupchrg wtcharge wtshortfall refused housingneeds wchchrg newprop relat3 relat4 relat5 relat6 relat7 relat8 old_form_id lar irproduct old_id joint tshortfall_known sheltered pregnancy_value_check hhtype new_old vacdays major_repairs_date_value_check void_date_value_check housingneeds_type housingneeds_other unresolved updated_by_id uprn uprn_known uprn_confirmed address_line1 address_line2 town_or_city county carehome_charges_value_check status_cache discarded_at unittype_sh scheme_code scheme_service_name scheme_sensitive scheme_type scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_admin_district location_startdate
2 {id} in_progress 2022-02-08 16:52:15 +0000 2022-02-08 16:52:15 +0000 Danny Rojas false DLUHC DLUHC 2021 2 2 October 2021 2 false false Westminster E09000033 SE1 1TE 2 2 8 0 0 0 0 0 0 9 1 not_started 6 {scheme_code} {scheme_service_name} {scheme_sensitive} 0 1 DLUHC {scheme_primary_client_group} {scheme_secondary_client_group} {scheme_support_type} {scheme_intended_stay} 2021-04-01 00:00:00 +0100 {location_code} SE1 1TE Downing Street 20 6 A Westminster {location_startdate}

82
spec/models/lettings_log_spec.rb

@ -1,8 +1,8 @@
require "rails_helper"
require "shared/shared_examples_for_derived_fields"
require "shared/shared_log_examples"
# rubocop:disable RSpec/MessageChain
# rubocop:disable RSpec/AnyInstance
RSpec.describe LettingsLog do
let(:different_managing_organisation) { create(:organisation) }
let(:created_by_user) { create(:user) }
@ -23,6 +23,7 @@ RSpec.describe LettingsLog do
end
include_examples "shared examples for derived fields", :lettings_log
include_examples "shared log examples", :lettings_log
it "inherits from log" do
expect(described_class).to be < Log
@ -183,27 +184,7 @@ RSpec.describe LettingsLog do
end
describe "status" do
let!(:empty_lettings_log) { create(:lettings_log) }
let!(:in_progress_lettings_log) { create(:lettings_log, :in_progress) }
let!(:completed_lettings_log) { create(:lettings_log, :completed) }
it "is set to not started for an empty lettings log" do
expect(empty_lettings_log.not_started?).to be(true)
expect(empty_lettings_log.in_progress?).to be(false)
expect(empty_lettings_log.completed?).to be(false)
end
it "is set to in progress for a started lettings log" do
expect(in_progress_lettings_log.in_progress?).to be(true)
expect(in_progress_lettings_log.not_started?).to be(false)
expect(in_progress_lettings_log.completed?).to be(false)
end
it "is set to completed for a completed lettings log" do
expect(completed_lettings_log.in_progress?).to be(false)
expect(completed_lettings_log.not_started?).to be(false)
expect(completed_lettings_log.completed?).to be(true)
end
let(:completed_lettings_log) { create(:lettings_log, :completed) }
context "when only a subsection that is hidden in tasklist is not completed" do
let(:household_characteristics_subsection) { completed_lettings_log.form.get_subsection("household_characteristics") }
@ -3009,62 +2990,6 @@ RSpec.describe LettingsLog do
end
end
describe "#process_uprn_change!" do
context "when UPRN set to a value" do
let(:lettings_log) do
create(
:lettings_log,
uprn: "123456789",
uprn_confirmed: 1,
county: "county",
)
end
it "updates sales log fields" do
lettings_log.uprn = "1111111"
allow_any_instance_of(UprnClient).to receive(:call)
allow_any_instance_of(UprnClient).to receive(:result).and_return({
"UPRN" => "UPRN",
"UDPRN" => "UDPRN",
"ADDRESS" => "full address",
"SUB_BUILDING_NAME" => "0",
"BUILDING_NAME" => "building name",
"THOROUGHFARE_NAME" => "thoroughfare",
"POST_TOWN" => "posttown",
"POSTCODE" => "postcode",
})
expect { lettings_log.process_uprn_change! }.to change(lettings_log, :address_line1).from(nil).to("0, Building Name, Thoroughfare")
.and change(lettings_log, :town_or_city).from(nil).to("Posttown")
.and change(lettings_log, :postcode_full).from(nil).to("POSTCODE")
.and change(lettings_log, :uprn_confirmed).from(1).to(nil)
.and change(lettings_log, :county).from("county").to(nil)
.and change(lettings_log, :uprn_known).from(nil).to(1)
end
end
context "when UPRN nil" do
let(:lettings_log) { create(:lettings_log, uprn: nil) }
it "does not update sales log" do
expect { lettings_log.process_uprn_change! }.not_to change(lettings_log, :attributes)
end
end
context "when service errors" do
let(:lettings_log) { create(:lettings_log, uprn: "123456789", uprn_confirmed: 1) }
let(:error_message) { "error" }
it "adds error to sales log" do
allow_any_instance_of(UprnClient).to receive(:call)
allow_any_instance_of(UprnClient).to receive(:error).and_return(error_message)
expect { lettings_log.process_uprn_change! }.to change { lettings_log.errors[:uprn] }.from([]).to([error_message])
end
end
end
describe "#beds_for_la_rent_range" do
context "when beds nil" do
let(:lettings_log) { build(:lettings_log, beds: nil) }
@ -3140,5 +3065,4 @@ RSpec.describe LettingsLog do
end
end
end
# rubocop:enable RSpec/AnyInstance
# rubocop:enable RSpec/MessageChain

85
spec/models/sales_log_spec.rb

@ -1,13 +1,14 @@
require "rails_helper"
require "shared/shared_examples_for_derived_fields"
require "shared/shared_log_examples"
# rubocop:disable RSpec/MessageChain
# rubocop:disable RSpec/AnyInstance
RSpec.describe SalesLog, type: :model do
let(:owning_organisation) { create(:organisation) }
let(:created_by_user) { create(:user) }
include_examples "shared examples for derived fields", :sales_log
include_examples "shared log examples", :sales_log
it "inherits from log" do
expect(described_class).to be < Log
@ -112,27 +113,7 @@ RSpec.describe SalesLog, type: :model do
end
describe "status" do
let!(:empty_sales_log) { create(:sales_log) }
let!(:in_progress_sales_log) { create(:sales_log, :in_progress) }
let!(:completed_sales_log) { create(:sales_log, :completed) }
it "is set to not started for an empty sales log" do
expect(empty_sales_log.not_started?).to be(true)
expect(empty_sales_log.in_progress?).to be(false)
expect(empty_sales_log.completed?).to be(false)
end
it "is set to in progress for a started sales log" do
expect(in_progress_sales_log.in_progress?).to be(true)
expect(in_progress_sales_log.not_started?).to be(false)
expect(in_progress_sales_log.completed?).to be(false)
end
it "is set to completed for a completed sales log" do
expect(completed_sales_log.in_progress?).to be(false)
expect(completed_sales_log.not_started?).to be(false)
expect(completed_sales_log.completed?).to be(true)
end
let(:completed_sales_log) { create(:sales_log, :completed) }
context "when proplen is not given" do
before do
@ -148,6 +129,7 @@ RSpec.describe SalesLog, type: :model do
expect(completed_sales_log.in_progress?).to be(false)
expect(completed_sales_log.not_started?).to be(false)
expect(completed_sales_log.completed?).to be(true)
expect(completed_sales_log.deleted?).to be(false)
end
it "is set to in_progress for a log with a saledate after 23/24" do
@ -155,6 +137,7 @@ RSpec.describe SalesLog, type: :model do
expect(completed_sales_log.in_progress?).to be(true)
expect(completed_sales_log.not_started?).to be(false)
expect(completed_sales_log.completed?).to be(false)
expect(completed_sales_log.deleted?).to be(false)
end
end
end
@ -591,63 +574,6 @@ RSpec.describe SalesLog, type: :model do
end
end
describe "#process_uprn_change!" do
context "when UPRN set to a value" do
let(:sales_log) do
create(
:sales_log,
uprn: "123456789",
uprn_confirmed: 1,
county: "county",
)
end
it "updates sales log fields" do
sales_log.uprn = "1111111"
sales_log.uprn_confirmed = 1
allow_any_instance_of(UprnClient).to receive(:call)
allow_any_instance_of(UprnClient).to receive(:result).and_return({
"UPRN" => "UPRN",
"UDPRN" => "UDPRN",
"ADDRESS" => "full address",
"SUB_BUILDING_NAME" => "0",
"BUILDING_NAME" => "building name",
"THOROUGHFARE_NAME" => "thoroughfare",
"POST_TOWN" => "posttown",
"POSTCODE" => "postcode",
})
expect { sales_log.process_uprn_change! }.to change(sales_log, :address_line1).from(nil).to("0, Building Name, Thoroughfare")
.and change(sales_log, :town_or_city).from(nil).to("Posttown")
.and change(sales_log, :postcode_full).from(nil).to("POSTCODE")
.and change(sales_log, :uprn_confirmed).from(1).to(nil)
.and change(sales_log, :county).from("county").to(nil)
.and change(sales_log, :uprn_known).from(nil).to(1)
end
end
context "when UPRN nil" do
let(:sales_log) { create(:sales_log, uprn: nil) }
it "does not update sales log" do
expect { sales_log.process_uprn_change! }.not_to change(sales_log, :attributes)
end
end
context "when the API returns an error" do
let(:sales_log) { build(:sales_log, :outright_sale_setup_complete, uprn_known: 1, uprn: "123456789", uprn_confirmed: 1) }
let(:error_message) { "error" }
it "adds error to sales log" do
allow_any_instance_of(UprnClient).to receive(:call)
allow_any_instance_of(UprnClient).to receive(:error).and_return(error_message)
expect { sales_log.process_uprn_change! }.to change { sales_log.errors[:uprn] }.from([]).to([error_message])
end
end
end
describe "#beds_for_la_sale_range" do
context "when beds nil" do
let(:sales_log) { build(:sales_log, beds: nil) }
@ -723,5 +649,4 @@ RSpec.describe SalesLog, type: :model do
end
end
end
# rubocop:enable RSpec/AnyInstance
# rubocop:enable RSpec/MessageChain

114
spec/policies/lettings_log_policy_spec.rb

@ -0,0 +1,114 @@
require "rails_helper"
RSpec.describe LettingsLogPolicy do
subject(:policy) { described_class }
permissions :destroy? do
let(:log) { create(:lettings_log, :in_progress) }
context "when log nil" do
before do
allow(log).to receive(:collection_period_open?).and_return(false)
end
it "does not allow deletion of log" do
expect(policy).not_to permit(build(:user, :support), nil)
end
end
context "when user nil" do
before do
allow(log).to receive(:collection_period_open?).and_return(false)
end
it "does not allow deletion of log" do
expect(policy).not_to permit(nil, build(:lettings_log, :in_progress))
end
end
context "when collection period closed" do
before do
allow(log).to receive(:collection_period_open?).and_return(false)
end
it "does not allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(build(:user, :support), log)
end
end
context "when collection period open" do
before do
allow(log).to receive(:collection_period_open?).and_return(true)
end
context "when not started" do
before do
allow(log).to receive(:in_progress?).and_return(false)
allow(log).to receive(:completed?).and_return(false)
end
it "does not allow deletion of log" do
expect(log).to receive(:in_progress?)
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(build(:user, :support), log)
end
end
[
%i[lettings_log in_progress],
%i[lettings_log completed],
].each do |type, status|
let(:log) { create(type, status) }
context "when #{type} status: #{status}" do
context "when user is data coordinator" do
let(:user) { create(:user, :data_coordinator) }
let(:user_of_managing_org) { create(:user, :data_coordinator, organisation: log.managing_organisation) }
it "does not allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(user, log)
end
it "allows deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).to permit(user_of_managing_org, log)
end
end
context "when user is support" do
let(:user) { create(:user, :support) }
it "does allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).to permit(user, log)
end
end
context "when user is data provider" do
let(:user) { create(:user) }
it "does not allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(user, log)
end
context "when the log is assigned to the user" do
let(:log) { create(:lettings_log, :in_progress, created_by: user) }
it "does allow deletion of log" do
expect(policy).to permit(user, log)
end
end
end
end
end
end
end
end

114
spec/policies/sales_log_policy_spec.rb

@ -0,0 +1,114 @@
require "rails_helper"
RSpec.describe SalesLogPolicy do
subject(:policy) { described_class }
permissions :destroy? do
let(:log) { create(:sales_log, :in_progress) }
context "when log nil" do
before do
allow(log).to receive(:collection_period_open?).and_return(false)
end
it "does not allow deletion of log" do
expect(policy).not_to permit(build(:user, :support), nil)
end
end
context "when user nil" do
before do
allow(log).to receive(:collection_period_open?).and_return(false)
end
it "does not allow deletion of log" do
expect(policy).not_to permit(nil, build(:sales_log, :in_progress))
end
end
context "when collection period closed" do
before do
allow(log).to receive(:collection_period_open?).and_return(false)
end
it "does not allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(build(:user, :support), log)
end
end
context "when collection period open" do
before do
allow(log).to receive(:collection_period_open?).and_return(true)
end
context "when not started" do
before do
allow(log).to receive(:in_progress?).and_return(false)
allow(log).to receive(:completed?).and_return(false)
end
it "does not allow deletion of log" do
expect(log).to receive(:in_progress?)
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(build(:user, :support), log)
end
end
[
%i[sales_log in_progress],
%i[sales_log completed],
].each do |type, status|
let(:log) { create(type, status) }
context "when #{type} status: #{status}" do
context "when user is data coordinator" do
let(:user) { create(:user, :data_coordinator) }
let(:user_of_owning_org) { create(:user, :data_coordinator, organisation: log.owning_organisation) }
it "does not allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(user, log)
end
it "allows deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).to permit(user_of_owning_org, log)
end
end
context "when user is support" do
let(:user) { create(:user, :support) }
it "does allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).to permit(user, log)
end
end
context "when user is data provider" do
let(:user) { create(:user) }
it "does not allow deletion of log" do
expect(log).to receive(:collection_period_open?)
expect(policy).not_to permit(user, log)
end
context "when the log is assigned to the user" do
let(:log) { create(:sales_log, :in_progress, created_by: user) }
it "does allow deletion of log" do
expect(policy).to permit(user, log)
end
end
end
end
end
end
end
end

96
spec/requests/lettings_logs_controller_spec.rb

@ -1309,55 +1309,93 @@ RSpec.describe LettingsLogsController, type: :request do
end
describe "DELETE" do
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { create(:user, :support) }
let!(:lettings_log) do
FactoryBot.create(:lettings_log, :in_progress)
create(:lettings_log, :completed)
end
let(:id) { lettings_log.id }
let(:delete_request) { delete "/lettings-logs/#{id}", headers: }
context "when deleting a lettings log" do
before do
delete "/lettings-logs/#{id}", headers:
end
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end
it "returns http success" do
expect(response).to have_http_status(:success)
context "when delete permitted" do
it "redirects to lettings logs and shows message" do
delete_request
expect(response).to redirect_to(lettings_logs_path)
follow_redirect!
expect(page).to have_content("Log #{id} has been deleted")
end
it "deletes the lettings log" do
expect { LettingsLog.find(id) }.to raise_error(ActiveRecord::RecordNotFound)
it "marks the log as deleted" do
expect { delete_request }.to change { lettings_log.reload.status }.from("completed").to("deleted")
end
end
context "with an invalid lettings log id" do
let(:id) { (LettingsLog.order(:id).last&.id || 0) + 1 }
context "when log does not exist" do
let(:id) { -1 }
it "returns 404" do
expect(response).to have_http_status(:not_found)
end
it "returns 404" do
delete_request
expect(response).to have_http_status(:not_found)
end
end
context "with a request containing invalid credentials" do
let(:basic_credentials) do
ActionController::HttpAuthentication::Basic.encode_credentials(api_username, "Oops")
end
context "when user not authorised" do
let(:user) { create(:user) }
it "returns 401" do
expect(response).to have_http_status(:unauthorized)
end
it "returns 404" do
delete_request
expect(response).to have_http_status(:unauthorized)
end
end
end
context "when a lettings log deletion fails" do
let(:mock_scope) { instance_double("LettingsLog::ActiveRecord_Relation", find_by: lettings_log) }
describe "GET delete-confirmation" do
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { create(:user, :support) }
let!(:lettings_log) do
create(:lettings_log, :completed)
end
let(:id) { lettings_log.id }
let(:request) { get "/lettings-logs/#{id}/delete-confirmation", headers: }
before do
allow(LettingsLog).to receive(:visible).and_return(mock_scope)
allow(lettings_log).to receive(:delete).and_return(false)
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end
delete "/lettings-logs/#{id}", headers:
context "when delete permitted" do
it "renders page" do
request
expect(response).to have_http_status(:ok)
expect(page).to have_content("Are you sure you want to delete this log?")
expect(page).to have_button(text: "Delete this log")
expect(page).to have_link(text: "Cancel", href: lettings_log_path(id))
end
end
it "returns an unprocessable entity 422" do
expect(response).to have_http_status(:unprocessable_entity)
context "when log does not exist" do
let(:id) { -1 }
it "returns 404" do
request
expect(response).to have_http_status(:not_found)
end
end
context "when user not authorised" do
let(:user) { create(:user) }
it "returns 404" do
request
expect(response).to have_http_status(:unauthorized)
end
end
end

92
spec/requests/sales_logs_controller_spec.rb

@ -652,4 +652,96 @@ RSpec.describe SalesLogsController, type: :request do
end
end
end
describe "DELETE" do
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { create(:user, :support) }
let!(:sales_log) do
create(:sales_log, :completed)
end
let(:id) { sales_log.id }
let(:delete_request) { delete "/sales-logs/#{id}", headers: }
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end
context "when delete permitted" do
it "redirects to sales logs and shows message" do
delete_request
expect(response).to redirect_to(sales_logs_path)
follow_redirect!
expect(page).to have_content("Log #{id} has been deleted")
end
it "marks the log as deleted" do
expect { delete_request }.to change { sales_log.reload.status }.from("completed").to("deleted")
end
end
context "when log does not exist" do
let(:id) { -1 }
it "returns 404" do
delete_request
expect(response).to have_http_status(:not_found)
end
end
context "when user not authorised" do
let(:user) { create(:user) }
it "returns 404" do
delete_request
expect(response).to have_http_status(:unauthorized)
end
end
end
describe "GET delete-confirmation" do
let(:headers) { { "Accept" => "text/html" } }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:user) { create(:user, :support) }
let!(:sales_log) do
create(:sales_log, :completed)
end
let(:id) { sales_log.id }
let(:request) { get "/sales-logs/#{id}/delete-confirmation", headers: }
before do
allow(user).to receive(:need_two_factor_authentication?).and_return(false)
sign_in user
end
context "when delete permitted" do
it "renders page" do
request
expect(response).to have_http_status(:ok)
expect(page).to have_content("Are you sure you want to delete this log?")
expect(page).to have_button(text: "Delete this log")
expect(page).to have_link(text: "Cancel", href: sales_log_path(id))
end
end
context "when log does not exist" do
let(:id) { -1 }
it "returns 404" do
request
expect(response).to have_http_status(:not_found)
end
end
context "when user not authorised" do
let(:user) { create(:user) }
it "returns 404" do
request
expect(response).to have_http_status(:unauthorized)
end
end
end
end

783
spec/services/csv/lettings_log_csv_service_spec.rb

@ -2,7 +2,7 @@ require "rails_helper"
RSpec.describe Csv::LettingsLogCsvService do
context "when the user is support" do
let(:user) { FactoryBot.create(:user, :support) }
let(:user) { create(:user, :support) }
let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json") }
before do
@ -11,226 +11,229 @@ RSpec.describe Csv::LettingsLogCsvService do
end
it "sets csv attributes in correct order" do
expected_csv_attributes = %w[id
status
created_at
updated_at
created_by_name
is_dpo
owning_organisation_name
managing_organisation_name
collection_start_year
rent_value_check
needstype
renewal
startdate
rent_type_detail
irproduct_other
tenancycode
propcode
postcode_known
postcode_full
is_la_inferred
la_label
la
first_time_property_let_as_social_housing
unitletas
rsnvac
offered
unittype_gn
builtype
wchair
beds
voiddate
void_date_value_check
majorrepairs
mrcdate
major_repairs_date_value_check
startertenancy
tenancy
tenancyother
tenancylength
sheltered
declaration
hhmemb
pregnancy_value_check
age1_known
age1
sex1
ethnic_group
ethnic
national
ecstat1
retirement_value_check
details_known_2
relat2
age2_known
age2
sex2
ecstat2
details_known_3
relat3
age3_known
age3
sex3
ecstat3
details_known_4
relat4
age4_known
age4
sex4
ecstat4
details_known_5
relat5
age5_known
age5
sex5
ecstat5
details_known_6
relat6
age6_known
age6
sex6
ecstat6
details_known_7
relat7
age7_known
age7
sex7
ecstat7
details_known_8
relat8
age8_known
age8
sex8
ecstat8
armedforces
leftreg
reservist
preg_occ
housingneeds
housingneeds_type
housingneeds_other
illness
illness_type_4
illness_type_5
illness_type_2
illness_type_6
illness_type_7
illness_type_3
illness_type_9
illness_type_8
illness_type_1
illness_type_10
layear
waityear
reason
reasonother
prevten
underoccupation_benefitcap
homeless
ppcodenk
ppostcode_full
previous_la_known
is_previous_la_inferred
prevloc_label
prevloc
reasonpref
rp_homeless
rp_insan_unsat
rp_medwel
rp_hardship
rp_dontknow
cbl
cap
chr
letting_allocation_unknown
referral
net_income_known
earnings
incfreq
net_income_value_check
hb
benefits
household_charge
period
is_carehome
chcharge
carehome_charges_value_check
brent
scharge
pscharge
supcharg
tcharge
hbrentshortfall
tshortfall_known
tshortfall
housingneeds_a
housingneeds_b
housingneeds_c
housingneeds_f
housingneeds_g
housingneeds_h
property_owner_organisation
property_manager_organisation
purchaser_code
property_relet
incref
renttype
lettype
totchild
totelder
totadult
nocharge
has_benefits
wrent
wscharge
wpschrge
wsupchrg
wtcharge
wtshortfall
refused
wchchrg
newprop
old_form_id
lar
irproduct
old_id
joint
hhtype
new_old
vacdays
unresolved
updated_by_id
uprn
uprn_known
uprn_confirmed
address_line1
address_line2
town_or_city
county
status_cache
unittype_sh
scheme_code
scheme_service_name
scheme_sensitive
scheme_type
scheme_registered_under_care_act
scheme_owning_organisation_name
scheme_primary_client_group
scheme_has_other_client_group
scheme_secondary_client_group
scheme_support_type
scheme_intended_stay
scheme_created_at
location_code
location_postcode
location_name
location_units
location_type_of_unit
location_mobility_type
location_admin_district
location_startdate]
expected_csv_attributes = %w[
id
status
created_at
updated_at
created_by_name
is_dpo
owning_organisation_name
managing_organisation_name
collection_start_year
rent_value_check
needstype
renewal
startdate
rent_type_detail
irproduct_other
tenancycode
propcode
postcode_known
postcode_full
is_la_inferred
la_label
la
first_time_property_let_as_social_housing
unitletas
rsnvac
offered
unittype_gn
builtype
wchair
beds
voiddate
void_date_value_check
majorrepairs
mrcdate
major_repairs_date_value_check
startertenancy
tenancy
tenancyother
tenancylength
sheltered
declaration
hhmemb
pregnancy_value_check
age1_known
age1
sex1
ethnic_group
ethnic
national
ecstat1
retirement_value_check
details_known_2
relat2
age2_known
age2
sex2
ecstat2
details_known_3
relat3
age3_known
age3
sex3
ecstat3
details_known_4
relat4
age4_known
age4
sex4
ecstat4
details_known_5
relat5
age5_known
age5
sex5
ecstat5
details_known_6
relat6
age6_known
age6
sex6
ecstat6
details_known_7
relat7
age7_known
age7
sex7
ecstat7
details_known_8
relat8
age8_known
age8
sex8
ecstat8
armedforces
leftreg
reservist
preg_occ
housingneeds
housingneeds_type
housingneeds_other
illness
illness_type_4
illness_type_5
illness_type_2
illness_type_6
illness_type_7
illness_type_3
illness_type_9
illness_type_8
illness_type_1
illness_type_10
layear
waityear
reason
reasonother
prevten
underoccupation_benefitcap
homeless
ppcodenk
ppostcode_full
previous_la_known
is_previous_la_inferred
prevloc_label
prevloc
reasonpref
rp_homeless
rp_insan_unsat
rp_medwel
rp_hardship
rp_dontknow
cbl
cap
chr
letting_allocation_unknown
referral
net_income_known
earnings
incfreq
net_income_value_check
hb
benefits
household_charge
period
is_carehome
chcharge
carehome_charges_value_check
brent
scharge
pscharge
supcharg
tcharge
hbrentshortfall
tshortfall_known
tshortfall
housingneeds_a
housingneeds_b
housingneeds_c
housingneeds_f
housingneeds_g
housingneeds_h
property_owner_organisation
property_manager_organisation
purchaser_code
property_relet
incref
renttype
lettype
totchild
totelder
totadult
nocharge
has_benefits
wrent
wscharge
wpschrge
wsupchrg
wtcharge
wtshortfall
refused
wchchrg
newprop
old_form_id
lar
irproduct
old_id
joint
hhtype
new_old
vacdays
unresolved
updated_by_id
uprn
uprn_known
uprn_confirmed
address_line1
address_line2
town_or_city
county
status_cache
discarded_at
unittype_sh
scheme_code
scheme_service_name
scheme_sensitive
scheme_type
scheme_registered_under_care_act
scheme_owning_organisation_name
scheme_primary_client_group
scheme_has_other_client_group
scheme_secondary_client_group
scheme_support_type
scheme_intended_stay
scheme_created_at
location_code
location_postcode
location_name
location_units
location_type_of_unit
location_mobility_type
location_admin_district
location_startdate
]
csv = CSV.parse(described_class.new(user, export_type: "labels").to_csv)
@ -248,174 +251,176 @@ RSpec.describe Csv::LettingsLogCsvService do
end
it "sets csv attributes in correct order and without omitted values" do
expected_csv_attributes = %w[id
status
created_at
updated_at
created_by_name
is_dpo
owning_organisation_name
managing_organisation_name
collection_start_year
renewal
startdate
irproduct_other
tenancycode
propcode
postcode_full
la_label
unitletas
rsnvac
offered
unittype_gn
builtype
wchair
beds
voiddate
void_date_value_check
majorrepairs
mrcdate
major_repairs_date_value_check
startertenancy
tenancy
tenancyother
tenancylength
sheltered
declaration
age1
sex1
ethnic_group
ethnic
national
ecstat1
relat2
age2
sex2
ecstat2
relat3
age3
sex3
ecstat3
relat4
age4
sex4
ecstat4
relat5
age5
sex5
ecstat5
relat6
age6
sex6
ecstat6
relat7
age7
sex7
ecstat7
relat8
age8
sex8
ecstat8
armedforces
leftreg
reservist
preg_occ
housingneeds
housingneeds_type
housingneeds_other
illness
illness_type_4
illness_type_5
illness_type_2
illness_type_6
illness_type_7
illness_type_3
illness_type_9
illness_type_8
illness_type_1
illness_type_10
layear
waityear
reason
reasonother
prevten
underoccupation_benefitcap
homeless
ppcodenk
ppostcode_full
prevloc_label
reasonpref
rp_homeless
rp_insan_unsat
rp_medwel
rp_hardship
rp_dontknow
cbl
cap
chr
referral
earnings
incfreq
hb
benefits
household_charge
period
chcharge
carehome_charges_value_check
brent
scharge
pscharge
supcharg
tcharge
hbrentshortfall
tshortfall
housingneeds_a
housingneeds_b
housingneeds_c
housingneeds_f
housingneeds_g
housingneeds_h
property_owner_organisation
property_manager_organisation
purchaser_code
property_relet
incref
lettype
nocharge
has_benefits
refused
wchchrg
newprop
lar
irproduct
joint
uprn
uprn_known
address_line1
address_line2
town_or_city
county
unittype_sh
scheme_code
scheme_service_name
scheme_sensitive
scheme_type
scheme_registered_under_care_act
scheme_owning_organisation_name
scheme_primary_client_group
scheme_has_other_client_group
scheme_secondary_client_group
scheme_support_type
scheme_intended_stay
scheme_created_at
location_code
location_postcode
location_name
location_units
location_type_of_unit
location_mobility_type
location_admin_district
location_startdate]
expected_csv_attributes = %w[
id
status
created_at
updated_at
created_by_name
is_dpo
owning_organisation_name
managing_organisation_name
collection_start_year
renewal
startdate
irproduct_other
tenancycode
propcode
postcode_full
la_label
unitletas
rsnvac
offered
unittype_gn
builtype
wchair
beds
voiddate
void_date_value_check
majorrepairs
mrcdate
major_repairs_date_value_check
startertenancy
tenancy
tenancyother
tenancylength
sheltered
declaration
age1
sex1
ethnic_group
ethnic
national
ecstat1
relat2
age2
sex2
ecstat2
relat3
age3
sex3
ecstat3
relat4
age4
sex4
ecstat4
relat5
age5
sex5
ecstat5
relat6
age6
sex6
ecstat6
relat7
age7
sex7
ecstat7
relat8
age8
sex8
ecstat8
armedforces
leftreg
reservist
preg_occ
housingneeds
housingneeds_type
housingneeds_other
illness
illness_type_4
illness_type_5
illness_type_2
illness_type_6
illness_type_7
illness_type_3
illness_type_9
illness_type_8
illness_type_1
illness_type_10
layear
waityear
reason
reasonother
prevten
underoccupation_benefitcap
homeless
ppcodenk
ppostcode_full
prevloc_label
reasonpref
rp_homeless
rp_insan_unsat
rp_medwel
rp_hardship
rp_dontknow
cbl
cap
chr
referral
earnings
incfreq
hb
benefits
household_charge
period
chcharge
carehome_charges_value_check
brent
scharge
pscharge
supcharg
tcharge
hbrentshortfall
tshortfall
housingneeds_a
housingneeds_b
housingneeds_c
housingneeds_f
housingneeds_g
housingneeds_h
property_owner_organisation
property_manager_organisation
purchaser_code
property_relet
incref
lettype
nocharge
has_benefits
refused
wchchrg
newprop
lar
irproduct
joint
uprn
uprn_known
address_line1
address_line2
town_or_city
county
unittype_sh
scheme_code
scheme_service_name
scheme_sensitive
scheme_type
scheme_registered_under_care_act
scheme_owning_organisation_name
scheme_primary_client_group
scheme_has_other_client_group
scheme_secondary_client_group
scheme_support_type
scheme_intended_stay
scheme_created_at
location_code
location_postcode
location_name
location_units
location_type_of_unit
location_mobility_type
location_admin_district
location_startdate
]
csv = CSV.parse(described_class.new(user, export_type: "labels").to_csv)

107
spec/shared/shared_log_examples.rb

@ -0,0 +1,107 @@
require "rails_helper"
# rubocop:disable RSpec/AnyInstance
RSpec.shared_examples "shared log examples" do |log_type|
describe "status" do
let(:empty_log) { create(log_type) }
let(:in_progress_log) { create(log_type, :in_progress) }
let(:completed_log) { create(log_type, :completed) }
it "is set to not started for an empty #{log_type} log" do
expect(empty_log.not_started?).to be(true)
expect(empty_log.in_progress?).to be(false)
expect(empty_log.completed?).to be(false)
expect(empty_log.deleted?).to be(false)
end
it "is set to in progress for a started #{log_type} log" do
expect(in_progress_log.in_progress?).to be(true)
expect(in_progress_log.not_started?).to be(false)
expect(in_progress_log.completed?).to be(false)
expect(in_progress_log.deleted?).to be(false)
end
it "is set to completed for a completed #{log_type} log" do
expect(completed_log.in_progress?).to be(false)
expect(completed_log.not_started?).to be(false)
expect(completed_log.completed?).to be(true)
expect(completed_log.deleted?).to be(false)
end
end
describe "discard!" do
around do |example|
Timecop.freeze(Time.zone.local(2022, 1, 1)) do
example.run
end
Timecop.return
end
let(:log) { create(log_type) }
it "updates discarded at with current time" do
expect { log.discard! }.to change { log.reload.discarded_at }.from(nil).to(Time.zone.now)
end
it "updates status to deleted" do
expect { log.discard! }.to change { log.reload.status }.to("deleted")
end
end
describe "#process_uprn_change!" do
context "when UPRN set to a value" do
let(:log) do
create(
log_type,
uprn: "123456789",
uprn_confirmed: 1,
county: "county",
)
end
it "updates log fields" do
log.uprn = "1111111"
allow_any_instance_of(UprnClient).to receive(:call)
allow_any_instance_of(UprnClient).to receive(:result).and_return({
"UPRN" => "UPRN",
"UDPRN" => "UDPRN",
"ADDRESS" => "full address",
"SUB_BUILDING_NAME" => "0",
"BUILDING_NAME" => "building name",
"THOROUGHFARE_NAME" => "thoroughfare",
"POST_TOWN" => "posttown",
"POSTCODE" => "postcode",
})
expect { log.process_uprn_change! }.to change(log, :address_line1).from(nil).to("0, Building Name, Thoroughfare")
.and change(log, :town_or_city).from(nil).to("Posttown")
.and change(log, :postcode_full).from(nil).to("POSTCODE")
.and change(log, :uprn_confirmed).from(1).to(nil)
.and change(log, :county).from("county").to(nil)
.and change(log, :uprn_known).from(nil).to(1)
end
end
context "when UPRN nil" do
let(:log) { create(log_type, uprn: nil) }
it "does not update log" do
expect { log.process_uprn_change! }.not_to change(log, :attributes)
end
end
context "when service errors" do
let(:log) { create(log_type, uprn: "123456789", uprn_confirmed: 1) }
let(:error_message) { "error" }
it "adds error to log" do
allow_any_instance_of(UprnClient).to receive(:call)
allow_any_instance_of(UprnClient).to receive(:error).and_return(error_message)
expect { log.process_uprn_change! }.to change { log.errors[:uprn] }.from([]).to([error_message])
end
end
end
end
# rubocop:enable RSpec/AnyInstance
Loading…
Cancel
Save