Browse Source
* 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 coordinatorspull/1640/head
Jack
2 years ago
committed by
GitHub
25 changed files with 1112 additions and 627 deletions
@ -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 |
@ -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 |
@ -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 |
@ -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> |
@ -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 |
|
|
@ -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 |
@ -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 |
@ -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…
Reference in new issue