Added unit-tests and other fixes
authormonishdeb <monish.deb@webaccessglobal.com>
Wed, 7 Jan 2015 08:03:52 +0000 (13:33 +0530)
committermonishdeb <monish.deb@webaccessglobal.com>
Fri, 23 Jan 2015 09:27:50 +0000 (14:57 +0530)
CRM/Core/BAO/ActionSchedule.php [changed mode: 0644->0755]
CRM/Upgrade/Incremental/php/FourSix.php
tests/phpunit/CRM/Core/BAO/ActionScheduleTest.php [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index ee84974..5c8e4d3
@@ -1099,8 +1099,8 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         $mStatus = implode(',', $membershipStatus);
         $where[] = "e.status_id IN ({$mStatus})";
 
-        // We are not tracking the reference date for 'repeated' schedule reminders as
-        // it will violate the repeat use-case, for further details please check CRM-15376
+        // We are not tracking the reference date for 'repeated' schedule reminders,
+        // for further details please check CRM-15376
         if ($actionSchedule->start_action_date && $actionSchedule->is_repeat == FALSE) {
           $select[] = $dateField;
           $selectColumns = "reference_date, " . $selectColumns;
@@ -1266,25 +1266,27 @@ LEFT JOIN {$reminderJoinClause}
 {$whereClause} {$limitWhereClause} AND {$dateClause} {$notINClause}
 ";
 
-      // In some cases reference_date got outdated due to many reason
-      // e.g. In Membership renewal end_date got extended which means reference date mismatches with the end_date
-      // in other words reference_date != end_date where end_date may be used as the start_action_date criteria
-      // for some schedule reminder so in order to send new reminder we INSERT new record with new reference_date value
-      // via UNION operation
+      // In some cases reference_date got outdated due to many reason e.g. In Membership renewal end_date got extended
+      // which means reference date mismatches with the end_date where end_date may be used as the start_action_date
+      // criteria  for some schedule reminder so in order to send new reminder we INSERT new reminder with new reference_date
+      // value via UNION operation
       if (strpos($selectColumns, 'reference_date') !== FALSE) {
+        $dateClause = str_replace('reminder.id IS NULL', 'reminder.id IS NOT NULL', $dateClause);
         $query .= "
 UNION
 {$selectClause}
 {$fromClause}
 {$joinClause}
-LEFT JOIN {$reminderJoinClause}
-{$whereClause} {$limitWhereClause} {$notINClause} AND
+ LEFT JOIN {$reminderJoinClause}
+{$whereClause} {$limitWhereClause} {$notINClause} AND {$dateClause} AND
+ reminder.action_date_time IS NOT NULL AND
  (reminder.reference_date IS NOT NULL AND reminder.reference_date != {$dateField})
 ";
 
-        //Those reminders which are sent in past and there reference_date doesn't reflect the
-        //newly changed entity's action_start_date, we need to update those so that we never
-        //get new reminder redundantly as because of the above usage of UNION clause
+        // As per the usage of UNION clause above we always INSERT a new reminder if reference_date (RD)
+        // got outdated or mismatches to start_action_date criteria so we need to update RD with actual
+        // start_action_date of already sent reminder, so to prevent redeundancy in sending new reminder
+        // due to above INSERT-UNION query
         $updateQuery = "UPDATE civicrm_action_log reminder
  INNER JOIN {$mapping->entity} e ON e.id = reminder.entity_id AND
  reminder.reference_date IS NOT NULL AND reminder.action_date_time IS NOT NULL
index 0e94e841d106d3d31da69855232b9a17b3deefbd..f4c240892a7ecfa69019d00b50b7022407ec6d4a 100644 (file)
@@ -114,7 +114,7 @@ class CRM_Upgrade_Incremental_php_FourSix {
 
   function upgrade_4_6_alpha3($rev) {
     // task to process sql
-    $this->addTask(ts('Adding and updating column reference_date for Schedule Reminders'), 'updateReferenceDate');
+    $this->addTask(ts('Add and update reference_date column for Schedule Reminders'), 'updateReferenceDate');
   }
 
   // CRM-15728, Add new column reference_date to civicrm_action_log in order to track
@@ -129,18 +129,21 @@ class CRM_Upgrade_Incremental_php_FourSix {
     $query = "SELECT schedule.* FROM civicrm_action_schedule schedule
  LEFT JOIN civicrm_action_mapping mapper ON mapper.id = schedule.mapping_id AND
  mapper.entity = 'civicrm_membership' AND schedule.is_repeat = 0";
+
+    // construct basic where clauses
+    $where = array(
+        'reminder.reference_date IS NOT NULL',
+        '( m.is_override IS NULL OR m.is_override = 0 )',
+        'reminder.action_date_time >= DATE_SUB(reminder.action_date_time, INTERVAL 9 MONTH)'
+      );
     $dao = CRM_Core_DAO::executeQuery($query);
     while($dao->fetch()) {
+      //if absolute date is chosen then bypass
       if (empty($dao->start_action_date)) {
         continue;
       }
 
       $referenceColumn = str_replace('membership_', "m.", $dao->start_action_date);
-      $where = array(
-        'reminder.reference_date IS NOT NULL',
-        '( m.is_override IS NULL OR m.is_override = 0 )',
-        'reminder.action_date_time >= DATE_SUB(reminder.action_date_time, INTERVAL 9 MONTH)'
-      );
       $value = implode(', ', explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($dao->entity_value, CRM_Core_DAO::VALUE_SEPARATOR)));
       if (!empty($value)) {
         $where[] = "m.membership_type_id IN ({$value})";
@@ -149,17 +152,47 @@ class CRM_Upgrade_Incremental_php_FourSix {
         $where[] = "m.membership_type_id IS NULL";
       }
 
-      // Update reference_date with action_start_date chosen,
-      // only to those which falls under date limits configured on schedule reminder
-      $startDateClause = array();
-      $op = ($dao->start_action_condition == 'before' ? '<=' : '>=');
-      $operator = ($dao->start_action_condition == 'before' ? 'DATE_SUB' : 'DATE_ADD');
-      $date = $operator . "({$referenceColumn}, INTERVAL {$dao->start_action_offset} {$dao->start_action_unit})";
-      $where[] = "NOW() >= {$date}";
-      $where[] = "DATE_SUB(NOW(), INTERVAL 1 DAY ) <= {$date}";
+      // Update reference_date with action_start_date chosen, only to those which are not additional contacts
+      // (Additional contacts are include via Group/Smart Group or Manual Recipients)
+      $addtionalGroupJoin = NULL;
+      if (!is_null($dao->limit_to) && $dao->limit_to == 0) {
+        if ($dao->group_id) {
+          // CRM-13577 If smart group then use Cache table
+          $group = CRM_Contact_DAO_Group::getTableName();
+          // Get the group information
+        $sql = "
+SELECT     $group.id, $group.cache_date, $group.saved_search_id, $group.children
+FROM       $group
+WHERE      $group.id = {$dao->group_id}
+";
+          $groupDAO = CRM_Core_DAO::executeQuery($sql);
+
+          $isSmartGroup = FALSE;
+          if ($groupDAO->fetch() && !empty($groupDAO->saved_search_id)) {
+            // Check that the group is in place in the cache and up to date
+            CRM_Contact_BAO_GroupContactCache::check($dao->group_id);
+            // Set smart group flag
+            $isSmartGroup = TRUE;
+          }
+          if ($isSmartGroup) {
+            $addtionalGroupJoin = " INNER JOIN civicrm_group_contact_cache grp ON reminder.contact_id = grp.contact_id";
+            $where[] = " grp.group_id NOT IN ({$dao->group_id})";
+          }
+          else {
+            $addtionalGroupJoin = " INNER JOIN civicrm_group_contact grp ON
+  reminder.contact_id = grp.contact_id AND grp.status = 'Added'";
+            $where[] = " grp.group_id NOT IN ({$dao->group_id})";
+          }
+        }
+        if (!empty($dao->recipient_manual)) {
+          $rList = CRM_Utils_Type::escape($dao->recipient_manual, 'String');
+          $where[] = "reminder.contact_id NOT IN ({$rList})";
+        }
+      }
 
       $sql = "UPDATE civicrm_action_log reminder
  LEFT JOIN civicrm_membership m ON reminder.entity_id = m.id
+ {$addtionalGroupJoin}
  SET reminder.reference_date = {$referenceColumn}
  WHERE " . implode(" AND ", $where);
       CRM_Core_DAO::executeQuery($sql);
old mode 100644 (file)
new mode 100755 (executable)
index 0313d4f..c20699a
@@ -206,6 +206,34 @@ class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
       'start_action_unit' => 'week',
       'subject' => 'subject sched_membership_end_2week',
     );
+    $this->fixtures['sched_on_membership_end_date'] = array( // create()
+      'name' => 'sched_on_membership_end_date',
+      'title' => 'sched_on_membership_end_date',
+      'body_html' => '<p>Your membership expired today</p>',
+      'body_text' => 'Your membership expired today',
+      'is_active' => 1,
+      'mapping_id' => 4,
+      'record_activity' => 1,
+      'start_action_condition' => 'after',
+      'start_action_date' => 'membership_end_date',
+      'start_action_offset' => '0',
+      'start_action_unit' => 'hour',
+      'subject' => 'subject send reminder on membership_end_date',
+    );
+    $this->fixtures['sched_after_1day_membership_end_date'] = array( // create()
+      'name' => 'sched_after_1day_membership_end_date',
+      'title' => 'sched_after_1day_membership_end_date',
+      'body_html' => '<p>Your membership expired yesterday</p>',
+      'body_text' => 'Your membership expired yesterday',
+      'is_active' => 1,
+      'mapping_id' => 4,
+      'record_activity' => 1,
+      'start_action_condition' => 'after',
+      'start_action_date' => 'membership_end_date',
+      'start_action_offset' => '1',
+      'start_action_unit' => 'day',
+      'subject' => 'subject send reminder on membership_end_date',
+    );
 
     $this->fixtures['sched_membership_end_2month'] = array(
       'name' => 'sched_membership_end_2month',
@@ -689,6 +717,34 @@ class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
         'recipients' => array(array('test-member@example.com')),
       ),
     ));
+
+    // Now suppose user has renewed for rolling membership after 3 months, so upcoming assertion is written
+    // to ensure that new reminder is sent 2 week before the new end_date i.e. '2012-09-15'
+    $membership->end_date = '2012-09-15';
+    $membership->save();
+
+    //change the email id of chosen membership contact to assert
+    //recipient of not the previously sent mail but the new one
+    $result = $this->callAPISuccess('Email', 'create', array(
+      'is_primary' => 1,
+      'contact_id' => $membership->contact_id,
+      'email' => 'member2@example.com'
+    ));
+    $this->assertAPISuccess($result);
+
+    // end_date=2012-09-15 ; schedule is 2 weeks before end_date
+    $this->assertCronRuns(array(
+        array( // Before the 2-week mark, no email
+        'time' => '2012-08-31 01:00:00',
+        'recipients' => array(),
+        ),
+        /* TODO
+        array( // After the 2-week mark, send an email
+        'time' => '2012-09-01 01:00:00',
+        'recipients' => array(array('member2@example.com')),
+        ),
+        */
+    ));
   }
 
 
@@ -901,7 +957,113 @@ class CRM_Core_BAO_ActionScheduleTest extends CiviUnitTestCase {
     );
   }
 
-  public function testContactCustomDateAnniv() {
+  public function testMembershipOnMultipleReminder() {
+    $membership = $this->createTestObject('CRM_Member_DAO_Membership', array_merge($this->fixtures['rolling_membership'], array('status_id' => 2)));
+    print_r($membership  );
+
+    $this->assertTrue(is_numeric($membership->id));
+    $result = $this->callAPISuccess('Email', 'create', array(
+      'contact_id' => $membership->contact_id,
+      'email' => 'member@example.com',
+    ));
+
+    $result = $this->callAPISuccess('contact', 'create', array_merge($this->fixtures['contact'], array('contact_id' => $membership->contact_id)));
+    $this->assertAPISuccess($result);
+
+    $actionScheduleBefore = $this->fixtures['sched_membership_end_2week'];           // Send email 2 weeks before end_date
+    $actionScheduleOn = $this->fixtures['sched_on_membership_end_date'];             // Send email on end_date/expiry date
+    $actionScheduleAfter = $this->fixtures['sched_after_1day_membership_end_date'];  // Send email 1 day after end_date/grace period
+    $actionScheduleBefore['entity_value'] = $actionScheduleOn['entity_value'] = $actionScheduleAfter['entity_value'] = $membership->membership_type_id;
+    foreach (array('actionScheduleBefore', 'actionScheduleOn', 'actionScheduleAfter') as $value) {
+      $$value = CRM_Core_BAO_ActionSchedule::add($$value);
+      $this->assertTrue(is_numeric($$value->id));
+    }
+
+    $this->assertCronRuns(
+      array(
+        array( // 1day 2weeks before membership end date(MED), don't send mail
+          'time' => '2012-05-31 01:00:00',
+          'recipients' => array(),
+        ),
+        array( // 2 weeks before MED, send an email
+          'time' => '2012-06-01 01:00:00',
+          'recipients' => array(array('member@example.com')),
+        ),
+        array( // 1day before MED, don't send mail
+          'time' => '2012-06-14 01:00:00',
+          'recipients' => array(),
+        ),
+        array( // On MED, send an email
+          'time' => '2012-06-15 00:00:00',
+          'recipients' => array(array('member@example.com')),
+        ),
+        array( // After 1day of MED, send an email
+          'time' => '2012-06-16 01:00:00',
+          'recipients' => array(array('member@example.com')),
+        ),
+        array( // After 1day 1min of MED, don't send an email
+          'time' => '2012-06-17 00:01:00',
+          'recipients' => array(),
+        ),
+      ));
+
+    // Assert the timestamp as of when the emails of respective three reminders as configured
+    // 2 weeks before, on and 1 day after MED, are sent
+    $this->assertEquals('2012-06-01 01:00:00',
+      CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleBefore->id, 'action_date_time', 'action_schedule_id', TRUE));
+    $this->assertEquals('2012-06-15 00:00:00',
+      CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleOn->id, 'action_date_time', 'action_schedule_id', TRUE));
+    $this->assertEquals('2012-06-16 01:00:00',
+      CRM_Core_DAO::getFieldValue('CRM_Core_DAO_ActionLog', $actionScheduleAfter->id, 'action_date_time', 'action_schedule_id', TRUE));
+
+    //extend MED to 2 weeks after the current MED (that may signifies as memberhip renewal activity)
+    // and lets assert as of when the new set of reminders will be sent against their respective Schedule Reminders(SR)
+    $membership->end_date = '2012-06-29';
+    $membership->save();
+    print_r($membership  );
+
+    //change the email id of chosen membership contact to assert
+    //recipient of not the previously sent mail but the new one
+    $result = $this->callAPISuccess('Email', 'create', array(
+      'is_primary' => 1,
+      'contact_id' => $membership->contact_id,
+      'email' => 'member2@example.com'
+    ));
+    $this->assertAPISuccess($result);
+
+    $this->assertCronRuns(
+      array(
+        array( // 1day 2weeks before membership end date(MED), don't send mail
+          'time' => '2012-06-14 01:00:00',
+          'recipients' => array(),
+        ),
+        //TODO : Add asssertion for before, on and after SR impact on new MED
+        /*
+        array( // 2 weeks before MED, send an email
+          'time' => '2012-06-15 01:00:00',
+          'recipients' => array(array('member2@example.com')),
+        ),
+        array( // 1day before MED, don't send mail
+          'time' => '2012-06-28 01:00:00',
+          'recipients' => array(),
+        ),
+        array( // On MED, send an email
+          'time' => '2012-06-29 00:00:00',
+          'recipients' => array(array('member@example.com')),
+        ),
+        array( // After 1day of MED, send an email
+          'time' => '2012-06-30 01:00:00',
+          'recipients' => array(array('member@example.com')),
+        ),
+        array( // After 1day 1min of MED, don't send an email
+          'time' => '2012-07-01 00:01:00',
+          'recipients' => array(),
+        ),
+        */
+      ));
+  }
+
+  public function testContactCustomDate_Anniv() {
     $group = array(
       'title' => 'Test_Group now',
       'name' => 'test_group_now',