move statusBounce out of BAO layer; don't allow self-service when disabled; support...
authorJon Goldberg <jon@megaphonetech.com>
Sat, 1 Aug 2020 21:25:23 +0000 (17:25 -0400)
committerJon Goldberg <jon@megaphonetech.com>
Mon, 3 Aug 2020 23:10:04 +0000 (19:10 -0400)
CRM/Event/BAO/Participant.php
CRM/Event/Form/SelfSvcTransfer.php
CRM/Event/Form/SelfSvcUpdate.php
tests/phpunit/CRM/Event/BAO/ParticipantTest.php

index aa0aaf61706a2a030c5bee788b305d9f75aa730c..1f19c32f74b48ffa0907baad6f8910d97578b107 100644 (file)
@@ -1877,19 +1877,15 @@ WHERE    civicrm_participant.contact_id = {$contactID} AND
    * Evaluate whether a participant record is eligible for self-service transfer/cancellation.  If so,
    * return additional participant/event details.
    *
-   * TODO: BAO-level functions shouldn't set a redirect, and it should be possible to return "false" to the
-   * calling function.  The next refactor will add a fourth param $errors, which can be passed by reference
-   * from the calling function.  Instead of redirecting, we will return the error.
-   * TODO: This function should always return FALSE when self-service has been disabled on an event.
-   * TODO: This function fails when the "hours until self-service" is greater than 24 or less than zero.
+   * TODO: This function fails when the "hours until self-service" is less than zero.
    * @param int $participantId
    * @param string $url
    * @param bool $isBackOffice
    */
-  public static function getSelfServiceEligibility($participantId, $url, $isBackOffice) {
+  public static function getSelfServiceEligibility(int $participantId, string $url, bool $isBackOffice) : array {
     $optionGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'participant_role', 'id', 'name');
     $query = "
-      SELECT cpst.name as status, cov.name as role, cp.fee_level, cp.fee_amount, cp.register_date, cp.status_id, ce.start_date, ce.title, cp.event_id
+      SELECT cpst.name as status, cov.name as role, cp.fee_level, cp.fee_amount, cp.register_date, cp.status_id, ce.start_date, ce.title, cp.event_id, ce.allow_selfcancelxfer
       FROM civicrm_participant cp
       LEFT JOIN civicrm_participant_status_type cpst ON cpst.id = cp.status_id
       LEFT JOIN civicrm_option_value cov ON cov.value = cp.role_id and cov.option_group_id = {$optionGroupId}
@@ -1897,40 +1893,44 @@ WHERE    civicrm_participant.contact_id = {$contactID} AND
       WHERE cp.id = {$participantId}";
     $dao = CRM_Core_DAO::executeQuery($query);
     while ($dao->fetch()) {
+      $details['eligible'] = TRUE;
       $details['status']  = $dao->status;
       $details['role'] = $dao->role;
       $details['fee_level'] = trim($dao->fee_level, CRM_Core_DAO::VALUE_SEPARATOR);
       $details['fee_amount'] = $dao->fee_amount;
       $details['register_date'] = $dao->register_date;
       $details['event_start_date'] = $dao->start_date;
+      $details['allow_selfcancelxfer'] = $dao->allow_selfcancelxfer;
       $eventTitle = $dao->title;
       $eventId = $dao->event_id;
     }
+    if (!$details['allow_selfcancelxfer']) {
+      $details['eligible'] = FALSE;
+      $details['ineligible_message'] = ts('This event registration can not be transferred or cancelled. Contact the event organizer if you have questions.');
+      return $details;
+    }
     //verify participant status is still Registered
     if ($details['status'] != 'Registered') {
-      $status = "You cannot transfer or cancel your registration for " . $eventTitle . ' as you are not currently registered for this event.';
-      CRM_Core_Session::setStatus($status, ts('Sorry'), 'alert');
-      CRM_Utils_System::redirect($url);
+      $details['eligible'] = FALSE;
+      $details['ineligible_message'] = "You cannot transfer or cancel your registration for " . $eventTitle . ' as you are not currently registered for this event.';
+      return $details;
     }
+    // Determine if it's too late to self-service cancel/transfer.
     $query = "select start_date as start, selfcancelxfer_time as time from civicrm_event where id = " . $eventId;
     $dao = CRM_Core_DAO::executeQuery($query);
     while ($dao->fetch()) {
       $time_limit  = $dao->time;
       $start_date = $dao->start;
     }
-    $start_time = new Datetime($start_date);
     $timenow = new Datetime();
-    if (!$isBackOffice && !empty($start_time) && $start_time < $timenow) {
-      $status = ts('Registration for this event cannot be cancelled or transferred once the event has begun. Contact the event organizer if you have questions.');
-      CRM_Core_Error::statusBounce($status, $url, ts('Sorry'));
-    }
-    if (!$isBackOffice && !empty($time_limit) && $time_limit > 0) {
-      $interval = $timenow->diff($start_time);
-      $days = $interval->format('%d');
-      $hours   = $interval->format('%h');
-      if ($hours <= $time_limit && $days < 1) {
-        $status = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]);
-        CRM_Core_Error::statusBounce($status, $url, ts('Sorry'));
+    if (!$isBackOffice && !empty($time_limit)) {
+      $cancelHours = abs($time_limit);
+      $cancelInterval = new DateInterval("PT${cancelHours}H");
+      $cancelInterval->invert = $time_limit < 0 ? 1 : 0;
+      $cancelDeadline = (new Datetime($start_date))->sub($cancelInterval);
+      if ($timenow > $cancelDeadline) {
+        $details['eligible'] = FALSE;
+        $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]);
       }
     }
     return $details;
index 5d72f2aaa34e02be3e7fc568179372c36fdb8f42..1d5ac36d72101f2f6ed0a37b0db4ec2f93f52984 100644 (file)
@@ -137,7 +137,7 @@ class CRM_Event_Form_SelfSvcTransfer extends CRM_Core_Form {
     $this->_userContext = $session->readUserContext();
     $this->_from_participant_id = CRM_Utils_Request::retrieve('pid', 'Positive', $this, FALSE, NULL, 'REQUEST');
     $this->_userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE, NULL, 'REQUEST');
-    $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST');
+    $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST') ?? FALSE;
     $params = ['id' => $this->_from_participant_id];
     $participant = $values = [];
     $this->_participant = CRM_Event_BAO_Participant::getValues($params, $values, $participant);
@@ -165,6 +165,9 @@ class CRM_Event_Form_SelfSvcTransfer extends CRM_Core_Form {
 
     $details = CRM_Event_BAO_Participant::participantDetails($this->_from_participant_id);
     $selfServiceDetails = CRM_Event_BAO_Participant::getSelfServiceEligibility($this->_from_participant_id, $url, $this->isBackoffice);
+    if (!$selfServiceDetails['eligible']) {
+      CRM_Core_Error::statusBounce($selfServiceDetails['ineligible_message'], $url, ts('Sorry'));
+    }
     $details = array_merge($details, $selfServiceDetails);
     $this->assign('details', $details);
     //This participant row will be cancelled.  Get line item(s) to cancel
index 33b9f7f3f87853a0893000e80b1d61ed736e7d97..a87d7aff27c305acd112bbd447e431a5f34a8112 100644 (file)
@@ -110,7 +110,7 @@ class CRM_Event_Form_SelfSvcUpdate extends CRM_Core_Form {
     $participant = $values = [];
     $this->_participant_id = CRM_Utils_Request::retrieve('pid', 'Positive', $this, FALSE, NULL, 'REQUEST');
     $this->_userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE, NULL, 'REQUEST');
-    $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST');
+    $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, FALSE, 'REQUEST') ?? FALSE;
     $params = ['id' => $this->_participant_id];
     $this->_participant = CRM_Event_BAO_Participant::getValues($params, $values, $participant);
     $this->_part_values = $values[$this->_participant_id];
@@ -140,6 +140,9 @@ class CRM_Event_Form_SelfSvcUpdate extends CRM_Core_Form {
     $contributionId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $this->_participant_id, 'contribution_id', 'participant_id');
     $this->assign('contributionId', $contributionId);
     $selfServiceDetails = CRM_Event_BAO_Participant::getSelfServiceEligibility($this->_participant_id, $url, $this->isBackoffice);
+    if (!$selfServiceDetails['eligible']) {
+      CRM_Core_Error::statusBounce($selfServiceDetails['ineligible_message'], $url, ts('Sorry'));
+    }
     $details = array_merge($details, $selfServiceDetails);
     $this->assign('details', $details);
     $this->selfsvcupdateUrl = CRM_Utils_System::url('civicrm/event/selfsvcupdate', "reset=1&id={$this->_participant_id}&id=0");
index 0a3cba0fd9c5de0e1c6f7b92e562707446cee40c..eb8dc5700842b0a681e17e3baeeedf799c3716ce 100644 (file)
@@ -434,27 +434,14 @@ class CRM_Event_BAO_ParticipantTest extends CiviUnitTestCase {
       'start_date' => $startDate,
     ]);
     $url = CRM_Utils_System::url('civicrm/event/info', "reset=1&id={$this->_eventId}");
-    // If we return without an error, then success.  But we don't always expect success.
-    try {
-      CRM_Event_BAO_Participant::getSelfServiceEligibility($participantId, $url, $isBackOffice);
-    }
-    catch (\Exception $e) {
-      if ($successExpected === FALSE) {
-        return;
-      }
-      else {
-        $this->fail();
-      }
-    }
-    if ($successExpected === FALSE) {
-      $this->fail();
-    }
+    $details = CRM_Event_BAO_Participant::getSelfServiceEligibility($participantId, $url, $isBackOffice);
+    $this->assertEquals($details['eligible'], $successExpected);
   }
 
   public function selfServiceScenarios() {
     // Standard pass scenario
     $scenarios[] = [
-      'selfSvcEnabled' => TRUE,
+      'selfSvcEnabled' => 1,
       'selfSvcHours' => 12,
       'hoursToEvent' => 16,
       'participantStatusId' => 1,
@@ -463,7 +450,7 @@ class CRM_Event_BAO_ParticipantTest extends CiviUnitTestCase {
     ];
     // Too late to self-service
     $scenarios[] = [
-      'selfSvcEnabled' => TRUE,
+      'selfSvcEnabled' => 1,
       'selfSvcHours' => 12,
       'hoursToEvent' => 8,
       'participantStatusId' => 1,
@@ -472,13 +459,40 @@ class CRM_Event_BAO_ParticipantTest extends CiviUnitTestCase {
     ];
     // Participant status is other than "Registered".
     $scenarios[] = [
-      'selfSvcEnabled' => TRUE,
+      'selfSvcEnabled' => 1,
       'selfSvcHours' => 12,
       'hoursToEvent' => 16,
       'participantStatusId' => 2,
       'isBackOffice' => FALSE,
       'successExpected' => FALSE,
     ];
+    // Event doesn't allow self-service
+    $scenarios[] = [
+      'selfSvcEnabled' => 0,
+      'selfSvcHours' => 12,
+      'hoursToEvent' => 16,
+      'participantStatusId' => 1,
+      'isBackOffice' => FALSE,
+      'successExpected' => FALSE,
+    ];
+    // Cancellation deadline is > 24 hours, still ok to cancel
+    $scenarios[] = [
+      'selfSvcEnabled' => 1,
+      'selfSvcHours' => 36,
+      'hoursToEvent' => 46,
+      'participantStatusId' => 1,
+      'isBackOffice' => FALSE,
+      'successExpected' => TRUE,
+    ];
+    // Cancellation deadline is > 24 hours, too late to cancel
+    $scenarios[] = [
+      'selfSvcEnabled' => 1,
+      'selfSvcHours' => 36,
+      'hoursToEvent' => 25,
+      'participantStatusId' => 1,
+      'isBackOffice' => FALSE,
+      'successExpected' => FALSE,
+    ];
     return $scenarios;
   }