Skip to content

Commit 702a876

Browse files
committed
[WW][Sutton] Add flow for container request cancellations.
1 parent 17be1dd commit 702a876

File tree

9 files changed

+273
-2
lines changed

9 files changed

+273
-2
lines changed

perllib/FixMyStreet/App/Controller/Waste.pm

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use FixMyStreet::App::Form::Waste::AboutYou;
1414
use FixMyStreet::App::Form::Waste::Report;
1515
use FixMyStreet::App::Form::Waste::Problem;
1616
use FixMyStreet::App::Form::Waste::Enquiry;
17+
use FixMyStreet::App::Form::Waste::Request::Cancel;
1718
use Memcached;
1819
use JSON::MaybeXS;
1920

@@ -788,6 +789,46 @@ sub process_request_data : Private {
788789
return 1;
789790
}
790791

792+
sub cancel_request : Chained('property') : PathPart('request/cancel') : Args(1) {
793+
my ($self, $c, $request_report_id) = @_;
794+
$c->detach( '/auth/redirect' ) unless $c->user_exists;
795+
796+
my $request_report = $c->model('DB::Problem')->find( { id => $request_report_id } )
797+
|| $c->detach('/waste/property_redirect');
798+
799+
$c->detach('/waste/property_redirect')
800+
if !$c->cobrand->waste_can_cancel_request($request_report);
801+
802+
803+
# Try to get the service name
804+
my $service_id = $request_report->get_extra_field_value('service_id');
805+
my $service = $c->stash->{services}->{$service_id};
806+
my $service_name;
807+
if ($service) {
808+
$service_name = $service->{service_name};
809+
}
810+
811+
$c->stash->{request_to_cancel} = $request_report;
812+
$c->stash->{request_to_cancel_service_name} = $service_name;
813+
$c->stash->{form_class} = "FixMyStreet::App::Form::Waste::Request::Cancel";
814+
$c->forward('form');
815+
}
816+
817+
sub process_request_cancellation : Private {
818+
my ( $self, $c, $form ) = @_;
819+
my $report = $c->stash->{request_to_cancel};
820+
$report->add_to_comments({
821+
text => "Request cancelled",
822+
user => $c->cobrand->body->comment_user || $report->user,
823+
extra => { request_cancellation => 1 },
824+
problem_state => 'cancelled',
825+
});
826+
$report->state('cancelled');
827+
$report->update;
828+
return 1;
829+
}
830+
831+
791832
sub group_reports {
792833
my ($c, @reports) = @_;
793834
my $report = shift @reports;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package FixMyStreet::App::Form::Waste::Request::Cancel;
2+
3+
use utf8;
4+
use HTML::FormHandler::Moose;
5+
extends 'FixMyStreet::App::Form::Waste';
6+
7+
has_page intro => (
8+
fields => ['confirm'],
9+
finished => sub {
10+
return $_[0]->wizard_finished('process_request_cancellation');
11+
},
12+
next => 'done',
13+
);
14+
15+
has title => ( is => 'ro', 'isa' => 'Str', lazy => 1, builder => '_build_title' );
16+
17+
has_page done => (
18+
title => 'Container request cancelled',
19+
template => 'waste/request_cancellation.html',
20+
);
21+
22+
has_field confirm => (
23+
type => 'Checkbox',
24+
required => 1,
25+
label => "Confirm",
26+
option_label => "I acknowledge that the payment will not be refunded and would like to cancel my request",
27+
);
28+
29+
has_field submit => (
30+
type => 'Submit',
31+
value => 'Cancel request',
32+
element_attr => { class => 'govuk-button' },
33+
order => 999,
34+
);
35+
36+
sub _build_title {
37+
my $self = shift;
38+
my $c = $self->form->{c};
39+
my $service_name = $c->stash->{request_to_cancel_service_name} || "";
40+
if ($service_name) {
41+
$service_name = lc $service_name;
42+
$service_name .= " ";
43+
}
44+
return "Cancel your $service_name" . "container request";
45+
}
46+
47+
1;

perllib/FixMyStreet/Cobrand/Sutton.pm

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,4 +804,34 @@ sub waste_munge_small_items_data {
804804
$self->waste_munge_bulky_data($data);
805805
}
806806

807+
=head2 waste_show_cancel_request
808+
809+
Show the option to cancel a request for all users, if the report hasn't
810+
already been cancelled.
811+
812+
=cut
813+
814+
sub waste_show_cancel_request {
815+
my ($self, $request_report) = @_;
816+
return $request_report->state ne 'cancelled';
817+
}
818+
819+
=head2 waste_can_cancel_request
820+
821+
Allows cancelling a request if the user is staff or the person that
822+
cancelled the request, and the report is not already cancelled.
823+
824+
=cut
825+
826+
sub waste_can_cancel_request {
827+
my ($self, $request_report) = @_;
828+
return unless $request_report->state ne 'cancelled';
829+
830+
# Staff members and the person who made the request can cancel it.
831+
my $c = $self->{c};
832+
return $c->user->is_superuser ||
833+
$c->user->belongs_to_body($self->body->id) ||
834+
$c->user->id == $request_report->user_id;
835+
}
836+
807837
1;

perllib/FixMyStreet/Roles/Cobrand/Echo.pm

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,14 @@ sub bin_services_for_address {
286286
}
287287

288288
# $containers should contain them all now (garden may only have been added just above here)
289-
my $open_requests = { map { $_->{container} => $_ } $events->filter({ type => 'request', containers => $containers })->list };
289+
my $open_requests = { map { $_->{container} => $_ } $events->filter(
290+
{
291+
type => 'request',
292+
containers => $containers,
293+
report_not_cancelled => 1,
294+
}
295+
)->list };
296+
290297
$self->call_hook(waste_munge_bin_services_open_requests => $open_requests);
291298

292299
my $any_request_max = ref $request_max ? sum(values %$request_max) : $request_max;

perllib/FixMyStreet/Roles/Cobrand/Waste.pm

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,28 @@ sub waste_sub_due {
114114
return $now >= $due_date;
115115
}
116116

117+
=head2 waste_show_cancel_request
118+
119+
Whether the option to cancel a request is shown.
120+
Defaults to false.
121+
122+
=cut
123+
124+
sub waste_show_cancel_request {
125+
my ($self, $request_report) = @_;
126+
return 0;
127+
}
128+
129+
=head2 waste_can_cancel_request
130+
131+
Whether or not the given request can be cancelled.
132+
Defaults to false.
133+
134+
=cut
135+
136+
sub waste_can_cancel_request {
137+
my ($self, $request_report) = @_;
138+
return 0;
139+
}
140+
117141
1;

perllib/Integrations/Echo/Events.pm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ sub filter {
9595
&& ($params->{type} ? $_->{type} eq $params->{type} : 1)
9696
&& ($params->{containers} ? $containers{$_->{container}} : 1)
9797
&& (defined $params->{closed} ? $_->{closed} == $params->{closed} : 1)
98+
&& (defined $params->{report_not_cancelled} ? !$_->{report} || $_->{report}->state ne 'cancelled': 1)
9899
} $self->list;
99100
return $self->new(%$self, _events => \@events);
100101
}

t/app/controller/waste_sutton_r.t

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ my $body = $mech->create_body_ok(2498, 'Sutton Council', $params, {
3939
});
4040
my $kingston = $mech->create_body_ok(2480, 'Kingston Council', { %$params, cobrand => 'kingston' });
4141
my $user = $mech->create_user_ok('test@example.net', name => 'Normal User');
42+
my $user2 = $mech->create_user_ok('test2@example.net', name => 'Normal User The Second');
4243
my $staff = $mech->create_user_ok('staff@example.net', name => 'Staff User', from_body => $body->id);
4344
$staff->user_body_permissions->create({ body => $body, permission_type => 'report_edit' });
4445

@@ -945,6 +946,105 @@ FixMyStreet::override_config {
945946
$mech->content_like(qr/Complaint against time.*LBS-123/);
946947
$mech->content_like(qr/Failure to Deliver.*LBS-789/);
947948
};
949+
950+
subtest "Container request cancellations" => sub {
951+
my ($container_request_report) = $mech->create_problems_for_body(
952+
1, $body->id,
953+
'Container request', {
954+
cobrand => 'sutton',
955+
external_id => 'container-request-event-guid',
956+
cobrand_data => 'waste',
957+
user => $user,
958+
}
959+
);
960+
$container_request_report->set_extra_fields({
961+
name => 'service_id',
962+
value => 940, # Domestic Refuse Collection
963+
});
964+
$container_request_report->update;
965+
my $open_container_request_event = {
966+
Id => '112112321',
967+
ServiceId => 940, # Domestic Refuse Collection
968+
ClientReference => 'LBS-789',
969+
EventTypeId => 3129, # Container request
970+
EventDate => { DateTime => "2025-02-03T08:00:00Z" },
971+
Data => { ExtensibleDatum => [
972+
{ Value => 2, DatatypeName => 'Source' },
973+
{
974+
ChildData => { ExtensibleDatum => [
975+
{ Value => 1, DatatypeName => 'Action' },
976+
{ Value => 1, DatatypeName => 'Container Type' }, # Refuse container
977+
] },
978+
},
979+
] },
980+
Guid => 'container-request-event-guid',
981+
};
982+
$e->mock('GetServiceUnitsForObject', sub { $bin_data });
983+
$e->mock('GetEventsForObject', sub { [$open_container_request_event] });
984+
985+
my $cancellation_url = "/waste/12345/request/cancel/" . $container_request_report->id;
986+
my $cancel_form_title = "Cancel your non-recyclable refuse container request";
987+
set_fixed_time('2025-02-05T08:00:00Z');
988+
989+
subtest "Link shown" => sub {
990+
$mech->get_ok('/waste/12345');
991+
$mech->content_contains($cancellation_url);
992+
};
993+
994+
foreach ((
995+
{
996+
scenario => "Staff",
997+
can_cancel => 1,
998+
user => $staff,
999+
},
1000+
{
1001+
scenario => "The user that made the request",
1002+
can_cancel => 1,
1003+
user => $user,
1004+
},
1005+
{
1006+
scenario => "Random user",
1007+
can_cancel => 0,
1008+
user => $user2,
1009+
},
1010+
)) {
1011+
my $can_cancel = $_->{can_cancel};
1012+
my $can_cancel_text = $can_cancel ? 'can cancel' : "can't cancel";
1013+
my $scenario = $_->{scenario};
1014+
my $u = $_->{user};
1015+
subtest "$scenario $can_cancel_text" => sub {
1016+
$mech->log_in_ok($u->email);
1017+
$mech->get_ok($cancellation_url);
1018+
if ($can_cancel) {
1019+
$mech->content_contains($cancel_form_title);
1020+
} else {
1021+
$mech->content_lacks($cancel_form_title);
1022+
}
1023+
};
1024+
}
1025+
1026+
subtest "Cancel" => sub {
1027+
$mech->log_in_ok($user->email);
1028+
$mech->get_ok($cancellation_url);
1029+
$mech->content_contains($cancel_form_title);
1030+
$mech->content_contains("I acknowledge that the payment will not be refunded");
1031+
$mech->submit_form_ok( { with_fields => { confirm => 1 } } );
1032+
$mech->content_contains("Your non-recyclable refuse container request has been cancelled.");
1033+
$container_request_report->discard_changes;
1034+
is $container_request_report->state, 'cancelled';
1035+
my $latest_comment = $container_request_report->comments->search(
1036+
{},
1037+
{ order_by => { -desc => 'id' } },
1038+
)->first;
1039+
is $latest_comment->text, "Request cancelled";
1040+
};
1041+
1042+
subtest "Link not shown after already cancelled" => sub {
1043+
$mech->log_in_ok($user->email);
1044+
$mech->content_lacks($cancellation_url);
1045+
};
1046+
};
1047+
9481048
};
9491049

9501050
sub get_report_from_redirect {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[% PROCESS 'waste/header.html' %]
2+
3+
<div class="govuk-panel govuk-panel--confirmation">
4+
<h1 class="govuk-panel__title">
5+
Request cancelled
6+
</h1>
7+
<div class="govuk-panel__body">
8+
<p>Your [% request_to_cancel_service_name FILTER lower %] container request has been cancelled.</p>
9+
</div>
10+
</div>
11+
12+
[% INCLUDE 'waste/_button_show_upcoming.html' %]
13+
14+
[% INCLUDE footer.html %]

templates/web/base/waste/services.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
[% IF cobrand.moniker != 'sutton' %]
77
A [% unit.service_name FILTER lower %] container request has been made
88
[% ELSE %]
9-
A [% unit.service_name FILTER lower %] container request was made on [% date.format(unit.requests_open.values.0.date, '%A, %-d %B') %].
9+
[% SET request = unit.requests_open.values.0; %]
10+
A [% unit.service_name FILTER lower %] container request was made on [% date.format(request.date, '%A, %-d %B') %].
1011
We aim to deliver the container within [% cobrand.wasteworks_config.request_timeframe %] (Monday to Friday).
1112

1213
[% IF unit.escalations.container AND NOT unit.escalations.container_open %]
@@ -15,6 +16,12 @@
1516
<a href="[% c.uri_for_action('waste/enquiry', [ property.id ]) %]?service_id=[% unit.service_id %]&amp;event_id=[% unit.escalations.container.id %]:[% unit.escalations.container.guid %]:[% unit.escalations.container.ref %]&amp;category=Failure+to+Deliver+Bags%2FContainers"
1617
class="waste-service-link">please report the problem here</a>
1718
[% END %]
19+
[% IF request.report AND c.cobrand.waste_show_cancel_request(request.report) %]
20+
<br>
21+
You can
22+
<a href="[% c.uri_for_action('waste/cancel_request', [ property.id, request.report.id ]) %]" class="waste-service-link">cancel your container order</a>
23+
if you would like. A refund will not be issued.
24+
[% END %]
1825
[% END %]
1926
</span>
2027

0 commit comments

Comments
 (0)