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. 38
      app/controllers/lettings_logs_controller.rb
  2. 24
      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. 80
      spec/requests/lettings_logs_controller_spec.rb
  23. 92
      spec/requests/sales_logs_controller_spec.rb
  24. 15
      spec/services/csv/lettings_log_csv_service_spec.rb
  25. 107
      spec/shared/shared_log_examples.rb

38
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
@log = current_user.lettings_logs.find(params[:id])
if @log.unresolved
redirect_to(send(@log.form.unresolved_log_path, @log))
else
render("logs/edit", locals: { current_user: })
end
else
render_not_found
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])
@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

24
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
@log = current_user.sales_logs.visible.find(params[:id])
render "logs/edit", locals: { current_user: }
else
render_not_found
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

80
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:
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
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
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)
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)
delete "/lettings-logs/#{id}", headers:
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

15
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,7 +11,8 @@ RSpec.describe Csv::LettingsLogCsvService do
end
it "sets csv attributes in correct order" do
expected_csv_attributes = %w[id
expected_csv_attributes = %w[
id
status
created_at
updated_at
@ -210,6 +211,7 @@ RSpec.describe Csv::LettingsLogCsvService do
town_or_city
county
status_cache
discarded_at
unittype_sh
scheme_code
scheme_service_name
@ -230,7 +232,8 @@ RSpec.describe Csv::LettingsLogCsvService do
location_type_of_unit
location_mobility_type
location_admin_district
location_startdate]
location_startdate
]
csv = CSV.parse(described_class.new(user, export_type: "labels").to_csv)
@ -248,7 +251,8 @@ RSpec.describe Csv::LettingsLogCsvService do
end
it "sets csv attributes in correct order and without omitted values" do
expected_csv_attributes = %w[id
expected_csv_attributes = %w[
id
status
created_at
updated_at
@ -415,7 +419,8 @@ RSpec.describe Csv::LettingsLogCsvService do
location_type_of_unit
location_mobility_type
location_admin_district
location_startdate]
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