From ece567afe98d0f780552568cc86d68e926ff3a31 Mon Sep 17 00:00:00 2001 From: smarcet Date: Mon, 9 Nov 2020 10:59:05 -0300 Subject: [PATCH] Added admin endpoints to cancel order/tickets refunds DELETE api/v1/summits/all/orders/{order_id}/refund/cancel DELETE api/v1/summits/all/orders/{order_id}/tickets/{ticket_id}/refund/cancel Change-Id: I93c5c747785a7e97adc1fc30b67ba3ee33e97e8c Signed-off-by: smarcet --- .../OAuth2SummitOrdersApiController.php | 54 +++++++++++++++++++ app/Http/routes.php | 12 ++++- .../Attendees/SummitAttendeeTicket.php | 17 +++++- .../Summit/Registration/SummitOrder.php | 21 ++++++-- app/Services/Model/ISummitOrderService.php | 16 ++++++ app/Services/Model/Imp/SummitOrderService.php | 38 +++++++++++++ database/seeds/ApiEndpointsSeeder.php | 30 +++++++++++ 7 files changed, 181 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php index c1f045b6..248aba62 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitOrdersApiController.php @@ -497,6 +497,60 @@ final class OAuth2SummitOrdersApiController } } + /** + * @param $order_id + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function cancelRefundRequestOrder($order_id){ + try { + + $order = $this->service->cancelRequestRefundOrder(intval($order_id)); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($order)->serialize( Request::input('expand', ''))); + + } + catch (ValidationException $ex) { + Log::warning($ex); + return $this->error412($ex->getMessages()); + } + catch(EntityNotFoundException $ex) + { + Log::warning($ex); + return $this->error404(array('message'=> $ex->getMessage())); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $order_id + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function cancelRefundRequestTicket($ticket_id){ + try { + + $ticket = $this->service->cancelRequestRefundTicket(intval($ticket_id)); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($ticket)->serialize( Request::input('expand', ''))); + + } + catch (ValidationException $ex) { + Log::warning($ex); + return $this->error412($ex->getMessages()); + } + catch(EntityNotFoundException $ex) + { + Log::warning($ex); + return $this->error404(array('message'=> $ex->getMessage())); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + /** * @param $order_id * @param $ticket_id diff --git a/app/Http/routes.php b/app/Http/routes.php index 908efb26..337e08d5 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -147,13 +147,21 @@ Route::group([ }); Route::group(['prefix' => '{order_id}'], function () { - Route::delete('refund', 'OAuth2SummitOrdersApiController@requestRefundMyOrder'); + Route::group(['prefix' => 'refund'], function () { + Route::delete('', 'OAuth2SummitOrdersApiController@requestRefundMyOrder'); + Route::delete('cancel', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitOrdersApiController@cancelRefundRequestOrder']); + }); + Route::put('resend', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitOrdersApiController@reSendOrderEmail']); Route::put('', 'OAuth2SummitOrdersApiController@updateMyOrder'); Route::group(['prefix' => 'tickets'], function () { Route::group(['prefix' => '{ticket_id}'], function () { Route::get('pdf', 'OAuth2SummitOrdersApiController@getTicketPDFByOrderId'); - Route::delete('refund', 'OAuth2SummitOrdersApiController@requestRefundMyTicket'); + Route::group(['prefix' => 'refund'], function () { + Route::delete('', 'OAuth2SummitOrdersApiController@requestRefundMyTicket'); + Route::delete('cancel', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitOrdersApiController@cancelRefundRequestTicket']); + }); + Route::group(['prefix' => 'attendee'], function () { Route::put('', 'OAuth2SummitOrdersApiController@assignAttendee'); Route::put('reinvite', 'OAuth2SummitOrdersApiController@reInviteAttendee'); diff --git a/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendeeTicket.php b/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendeeTicket.php index f9feb4a7..89af5732 100644 --- a/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendeeTicket.php +++ b/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendeeTicket.php @@ -580,23 +580,36 @@ class SummitAttendeeTicket extends SilverstripeBaseModel } } - public function setPaid() + public function setPaid($set_bought_date = true) { $this->status = IOrderConstants::PaidStatus; - $this->bought_date = new \DateTime('now', new \DateTimeZone('UTC')); + if($set_bought_date) + $this->bought_date = new \DateTime('now', new \DateTimeZone('UTC')); } + public function setCancelled() { if ($this->status == IOrderConstants::PaidStatus) return; $this->status = IOrderConstants::CancelledStatus; } + function cancelRefundRequest():void { + if(!$this->isRefundRequested()) + throw new ValidationException(sprintf("You can not cancel any refund on this ticket")); + $this->status = IOrderConstants::PaidStatus; + } + public function setRefunded() { $this->status = IOrderConstants::RefundedStatus; } + public function setRefundRequests() + { + $this->status = IOrderConstants::RefundRequestedStatus; + } + /** * @return bool */ diff --git a/app/Models/Foundation/Summit/Registration/SummitOrder.php b/app/Models/Foundation/Summit/Registration/SummitOrder.php index 1f5926db..4e6c42c0 100644 --- a/app/Models/Foundation/Summit/Registration/SummitOrder.php +++ b/app/Models/Foundation/Summit/Registration/SummitOrder.php @@ -385,7 +385,7 @@ class SummitOrder extends SilverstripeBaseModel implements IQREntity foreach ($this->tickets as $ticket){ if($ticket->isBadgePrinted()){ - throw new ValidationException(sprintf( "you can not request a refund for this ticket %s ( badge already printed)", $ticket->getNumber())); + throw new ValidationException(sprintf( "You can not request a refund for this ticket %s (badge already printed).", $ticket->getNumber())); } } @@ -393,7 +393,7 @@ class SummitOrder extends SilverstripeBaseModel implements IQREntity if($now > $begin_date){ Log::debug("SummitOrder::requestRefund: now is greater than Summit.BeginDate"); - throw new ValidationException("you can not request a refund after summit started"); + throw new ValidationException("You can not request a refund after summit started."); } $interval = $begin_date->diff($now); @@ -403,13 +403,28 @@ class SummitOrder extends SilverstripeBaseModel implements IQREntity Log::debug(sprintf("SummitOrder::requestRefund: days_before_event_starts %s", $days_before_event_starts)); if($this->status != IOrderConstants::PaidStatus){ - throw new ValidationException("you can not request a refund on this order"); + throw new ValidationException("You can not request a refund on this order."); } $this->status = IOrderConstants::RefundRequestedStatus; + foreach ($this->tickets as $ticket){ + $ticket->setRefundRequests(); + } + Event::fire(new RequestedSummitOrderRefund($this->getId(), $days_before_event_starts)); } + + function cancelRefundRequest():void { + if(!$this->isRefundRequested()) + throw new ValidationException(sprintf("You can not cancel any refund on this order")); + + $this->status = IOrderConstants::PaidStatus; + + foreach ($this->tickets as $ticket){ + $ticket->setPaid(false); + } + } /** * @return string */ diff --git a/app/Services/Model/ISummitOrderService.php b/app/Services/Model/ISummitOrderService.php index d7401cf8..354b4868 100644 --- a/app/Services/Model/ISummitOrderService.php +++ b/app/Services/Model/ISummitOrderService.php @@ -370,4 +370,20 @@ interface ISummitOrderService extends IProcessPaymentService */ public function reSendOrderEmail(int $order_id):SummitOrder; + /** + * @param int $order_id + * @return SummitOrder + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function cancelRequestRefundOrder(int $order_id): SummitOrder; + + /** + * @param int $ticket_id + * @return SummitAttendeeTicket + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function cancelRequestRefundTicket(int $ticket_id): SummitAttendeeTicket; + } \ No newline at end of file diff --git a/app/Services/Model/Imp/SummitOrderService.php b/app/Services/Model/Imp/SummitOrderService.php index 02d2bb2f..cac215a4 100644 --- a/app/Services/Model/Imp/SummitOrderService.php +++ b/app/Services/Model/Imp/SummitOrderService.php @@ -1351,6 +1351,44 @@ final class SummitOrderService }); } + /** + * @param int $order_id + * @return SummitOrder + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function cancelRequestRefundOrder(int $order_id): SummitOrder + { + return $this->tx_service->transaction(function () use ($order_id) { + $order = $this->order_repository->getById($order_id); + if (is_null($order) || !$order instanceof SummitOrder) + throw new EntityNotFoundException('order not found'); + + $order->cancelRefundRequest(); + + return $order; + }); + } + + /** + * @param int $ticket_id + * @return SummitAttendeeTicket + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function cancelRequestRefundTicket(int $ticket_id): SummitAttendeeTicket + { + return $this->tx_service->transaction(function () use ($ticket_id) { + $ticket = $this->ticket_repository->getById($ticket_id); + if (is_null($ticket) || !$ticket instanceof SummitAttendeeTicket) + throw new EntityNotFoundException('ticket not found'); + + $ticket->cancelRefundRequest(); + + return $ticket; + }); + } + /** * @param Member $current_user , * @param int $order_id diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index 89904757..28326514 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -496,6 +496,21 @@ class ApiEndpointsSeeder extends Seeder sprintf(SummitScopes::UpdateMyRegistrationOrders, $current_realm), ], ], + [ + 'name' => 'cancel-order-refund', + 'route' => '/api/v1/summits/all/orders/{order_id}/refund/cancel', + 'http_method' => 'DELETE', + 'scopes' => [ + sprintf(SummitScopes::WriteSummitData, $current_realm), + sprintf(SummitScopes::UpdateRegistrationOrders, $current_realm), + ], + 'authz_groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins + ] + ], [ 'name' => 'resend-order', 'route' => '/api/v1/summits/all/orders/{order_id}/resend', @@ -545,6 +560,21 @@ class ApiEndpointsSeeder extends Seeder sprintf(SummitScopes::UpdateMyRegistrationOrders, $current_realm), ], ], + [ + 'name' => 'cancel-refund-ticket-request', + 'route' => '/api/v1/summits/all/orders/{order_id}/tickets/{ticket_id}/refund/cancel', + 'http_method' => 'DELETE', + 'scopes' => [ + sprintf(SummitScopes::WriteSummitData, $current_realm), + sprintf(SummitScopes::UpdateRegistrationOrders, $current_realm), + ], + 'authz_groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins + ] + ], [ 'name' => 'get-ticket-pdf', 'route' => '/api/v1/summits/all/orders/{order_id}/tickets/{ticket_id}/pdf',