From 02b5be41fa1384ce0dc0bfde93094d2b7a084509 Mon Sep 17 00:00:00 2001 From: Victoria Mihell-Hale Date: Thu, 16 Jan 2025 22:21:59 +0000 Subject: [PATCH] [Bexley][WW] Cancel GGW subscription --- perllib/FixMyStreet/App/Controller/Waste.pm | 4 +- .../App/Form/Waste/Garden/Cancel.pm | 1 + .../App/Form/Waste/Garden/Cancel/Bexley.pm | 38 ++++++++ perllib/FixMyStreet/Cobrand/Bexley/Garden.pm | 93 ++++++++++++++++++- perllib/FixMyStreet/Cobrand/Bexley/Waste.pm | 5 +- perllib/Integrations/Agile.pm | 78 ++++++++++++++++ t/app/controller/waste_bexley_garden.t | 2 + 7 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 perllib/FixMyStreet/App/Form/Waste/Garden/Cancel/Bexley.pm create mode 100644 perllib/Integrations/Agile.pm diff --git a/perllib/FixMyStreet/App/Controller/Waste.pm b/perllib/FixMyStreet/App/Controller/Waste.pm index aeb2a4a0ebf..fa97724bc69 100644 --- a/perllib/FixMyStreet/App/Controller/Waste.pm +++ b/perllib/FixMyStreet/App/Controller/Waste.pm @@ -1280,7 +1280,9 @@ sub garden_cancel : Chained('garden_setup') : Args(0) { $c->forward('check_if_staff_can_pay', [ $payment_method ]); $c->stash->{first_page} = 'intro'; - $c->stash->{form_class} = 'FixMyStreet::App::Form::Waste::Garden::Cancel'; + $c->stash->{form_class} + = $c->cobrand->call_hook('waste_cancel_form_class') + || 'FixMyStreet::App::Form::Waste::Garden::Cancel'; $c->forward('form'); } diff --git a/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel.pm b/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel.pm index 65447b80e25..71912143877 100644 --- a/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel.pm +++ b/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel.pm @@ -34,6 +34,7 @@ has_field confirm => ( option_label => 'I confirm I wish to cancel my subscription', required => 1, label => "Confirm", + order => 998, ); has_field submit => ( diff --git a/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel/Bexley.pm b/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel/Bexley.pm new file mode 100644 index 00000000000..faab311ded6 --- /dev/null +++ b/perllib/FixMyStreet/App/Form/Waste/Garden/Cancel/Bexley.pm @@ -0,0 +1,38 @@ +package FixMyStreet::App::Form::Waste::Garden::Cancel::Bexley; + +use utf8; +use HTML::FormHandler::Moose; +extends 'FixMyStreet::App::Form::Waste::Garden::Cancel'; + +has_field reason => ( + type => 'Select', + widget => 'RadioGroup', + required => 1, + label => 'Reason for cancellation', + messages => { required => 'Please select a reason' }, +); + +sub options_reason { + my $form = shift; + + my @options = ( + 'Price', + 'Service Issues', + 'Moving Out of Borough', + 'Other', + ); + return map { { label => $_, value => $_ } } @options; +} + +has_field reason_further_details => ( + required => 1, + type => 'Text', + widget => 'Textarea', + label => + "If you selected 'Other', please provide further details (up to 250 characters)", + required_when => { reason => 'Other' }, + maxlength => 250, + messages => { required => 'Please provide further details' }, +); + +1; diff --git a/perllib/FixMyStreet/Cobrand/Bexley/Garden.pm b/perllib/FixMyStreet/Cobrand/Bexley/Garden.pm index 46a623ece76..c766fa8bd24 100644 --- a/perllib/FixMyStreet/Cobrand/Bexley/Garden.pm +++ b/perllib/FixMyStreet/Cobrand/Bexley/Garden.pm @@ -6,15 +6,102 @@ FixMyStreet::Cobrand::Bexley::Garden - code specific to Bexley WasteWorks GGW package FixMyStreet::Cobrand::Bexley::Garden; +use DateTime::Format::Strptime; +use Integrations::Agile; +use FixMyStreet::App::Form::Waste::Garden::Cancel::Bexley; + use Moo::Role; with 'FixMyStreet::Roles::Cobrand::SCP', 'FixMyStreet::Roles::Cobrand::Paye'; +has agile => ( + is => 'lazy', + default => sub { + my $self = shift; + my $cfg = $self->feature('agile'); + return Integrations::Agile->new(%$cfg); + }, +); + sub garden_service_name { 'garden waste collection service' } -# TODO No current subscription look up here -# -sub garden_current_subscription { undef } +sub garden_service_ids { + return [ 'GA-140', 'GA-240' ]; +} + +sub garden_current_subscription { + my $self = shift; + + my $current = $self->{c}->stash->{property}{garden_current_subscription}; + return $current if $current; + + my $uprn = $self->{c}->stash->{property}{uprn}; + return undef unless $uprn; + +# TODO Fetch active subscription from DB for UPRN +# (get_original_sub() in Controller/Waste.pm needs to handle Bexley UPRN). +# Could be more than one customer, so match against email. +# Could be more than one contract, so match against reference. + + my $results = $self->agile->CustomerSearch($uprn); + return undef unless $results && $results->{Customers}; + my $customer = $results->{Customers}[0]; + return undef unless $customer && $customer->{ServiceContracts}; + my $contract = $customer->{ServiceContracts}[0]; + return unless $contract; + + my $parser + = DateTime::Format::Strptime->new( pattern => '%d/%m/%Y %H:%M' ); + my $end_date = $parser->parse_datetime( $contract->{EndDate} ); + + # Agile says there is a subscription; now get service data from + # Whitespace + my $services = $self->{c}->stash->{services}; + for ( @{ $self->garden_service_ids } ) { + if ( my $srv = $services->{$_} ) { + $srv->{customer_external_ref} + = $customer->{CustomerExternalReference}; + $srv->{end_date} = $end_date; + return $srv; + } + } + + return { + agile_only => 1, + customer_external_ref => $customer->{CustomerExternalReference}, + end_date => $end_date, + }; +} + +# TODO This is a placeholder +sub get_current_garden_bins { 1 } + +sub waste_cancel_asks_staff_for_user_details { 1 } + +sub waste_cancel_form_class { + 'FixMyStreet::App::Form::Waste::Garden::Cancel::Bexley'; +} + +sub waste_garden_sub_params { + my ( $self, $data, $type ) = @_; + + my $c = $self->{c}; + + if ( $data->{category} eq 'Cancel Garden Subscription' ) { + my $srv = $self->garden_current_subscription; + + my $parser = DateTime::Format::Strptime->new( pattern => '%d/%m/%Y' ); + my $due_date_str = $parser->format_datetime( $srv->{end_date} ); + + my $reason = $data->{reason}; + $reason .= ': ' . $data->{reason_further_details} + if $data->{reason_further_details}; + + $c->set_param( 'customer_external_ref', $srv->{customer_external_ref} ); + $c->set_param( 'due_date', $due_date_str ); + $c->set_param( 'reason', $reason ); + } +} =item * You can order a maximum of five bins diff --git a/perllib/FixMyStreet/Cobrand/Bexley/Waste.pm b/perllib/FixMyStreet/Cobrand/Bexley/Waste.pm index 44b9cee4afd..5e91492b2d9 100644 --- a/perllib/FixMyStreet/Cobrand/Bexley/Waste.pm +++ b/perllib/FixMyStreet/Cobrand/Bexley/Waste.pm @@ -459,6 +459,9 @@ sub bin_services_for_address { ]; } + $property->{garden_current_subscription} + = $self->garden_current_subscription; + @site_services_filtered = $self->_remove_service_if_assisted_exists(@site_services_filtered); @site_services_filtered = $self->service_sort(@site_services_filtered); @@ -1246,7 +1249,7 @@ sub in_cab_logs_reason_prefixes { 'Clear Sacks' => ['MDR-SACK', 'CW-SACK'], 'Paper & Card' => ['PA-1100', 'PA-1280', 'PA-140', 'PA-240', 'PA-55', 'PA-660', 'PA-940', 'PC-180', 'PC-55'], 'Food' => ['FO-140', 'FO-23'], - 'Garden' => ['GA-140', 'GA-240'], + 'Garden' => ['GA-140', 'GA-240'], # TODO Call Garden.pm->garden_service_ids to make sure these IDs are consistent 'Plastics & Glass' => ['PG-1100', 'PG-1280', 'PG-240', 'PG-360', 'PG-55', 'PG-660', 'PG-940', 'PL-1100', 'PL-1280', 'PL-140', 'PL-55', 'PL-660', 'PL-940'], 'Glass' => ['GL-1100', 'GL-1280', 'GL-55', 'GL-660'], 'Refuse' => ['RES-1100', 'RES-1280', 'RES-140', 'RES-180', 'RES-240', 'RES-660', 'RES-720', 'RES-940', 'RES-CHAM', 'RES-DBIN', 'RES-SACK'], diff --git a/perllib/Integrations/Agile.pm b/perllib/Integrations/Agile.pm new file mode 100644 index 00000000000..ff76321dfab --- /dev/null +++ b/perllib/Integrations/Agile.pm @@ -0,0 +1,78 @@ +=head1 NAME + +Integrations::Agile - Agile Applications API integration + +=head1 DESCRIPTION + +This module provides an interface to the Agile Applications API + +=cut + +package Integrations::Agile; + +use strict; +use warnings; + +use HTTP::Request; +use JSON::MaybeXS; +use LWP::UserAgent; +use Moo; +use URI; + +has url => ( is => 'ro' ); + +# TODO Logging + +sub call { + my ( $self, %args ) = @_; + + my $action = $args{action}; + my $controller = $args{controller}; + my $data = $args{data}; + my $method = 'POST'; + + my $body = { + Method => $method, + Controller => $controller, + Action => $action, + Data => $data, + }; + my $body_json = encode_json($body); + + my $uri = URI->new( $self->{url} ); + + my $req = HTTP::Request->new( $method, $uri ); + $req->content_type('application/json; charset=UTF-8'); + $req->content($body_json); + + my $ua = LWP::UserAgent->new; + my $res = $ua->request($req); + + if ( $res->is_success ) { + return decode_json( $res->content ); + } else { + die $res->content; + } +} + +sub IsAddressFree { + my ( $self, $uprn ) = @_; + + return $self->call( + action => 'isaddressfree', + controller => 'customer', + data => { UPRN => $uprn }, + ); +} + +sub CustomerSearch { + my ( $self, $uprn ) = @_; + + return $self->call( + action => 'search', + controller => 'customer', + data => { ServiceContractUPRN => $uprn }, + ); +} + +1; diff --git a/t/app/controller/waste_bexley_garden.t b/t/app/controller/waste_bexley_garden.t index ebadd0c9d30..bf621764b0f 100644 --- a/t/app/controller/waste_bexley_garden.t +++ b/t/app/controller/waste_bexley_garden.t @@ -18,6 +18,8 @@ $dbi_mock->mock( 'connect', sub { return $dbh; } ); +my $agile_mock = Test::MockModule->new('Integrations::Agile'); + my $mech = FixMyStreet::TestMech->new; my $body = $mech->create_body_ok(2494, 'Bexley', { cobrand => 'bexley' });