Merge pull request #4997 from monishdeb/CRM-15536
[civicrm-core.git] / CRM / Core / BAO / ActionSchedule.php
index a2edbdb03111b51210340ff405d89b87b9ba6678..c8d76595d7bfc2a952eb06e5fa1b4885f9968feb 100755 (executable)
@@ -1,7 +1,7 @@
 <?php
 /*
   +--------------------------------------------------------------------+
-  | CiviCRM version 4.5                                                |
+  | CiviCRM version 4.6                                                |
   +--------------------------------------------------------------------+
   | Copyright (C) 2011 Marty Wright                                    |
   | Licensed to CiviCRM under the Academic Free License version 3.0.   |
@@ -24,7 +24,7 @@
   | GNU Affero General Public License or the licensing of CiviCRM,     |
   | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
   +--------------------------------------------------------------------+
-*/
+ */
 
 /**
  *
 class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
 
   /**
-   * @param null $id
+   * @param int $id
    *
    * @return array
    */
-  static function getMapping($id = NULL) {
+  public static function getMapping($id = NULL) {
     static $_action_mapping;
 
     if ($id && !is_null($_action_mapping) && isset($_action_mapping[$id])) {
@@ -71,8 +71,7 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
   /**
    * Get all fields of the type Date
    */
-
-  static function getDateFields() {
+  public static function getDateFields() {
     $allFields = CRM_Core_BAO_CustomField::getFields('');
     $dateFields = array('birth_date' => ts('Birth Date'));
     foreach ($allFields as $fieldID => $field) {
@@ -86,16 +85,16 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
   /**
    * Retrieve list of selections/drop downs for Scheduled Reminder form
    *
-   * @param bool    $id    mapping id
+   * @param bool $id
+   *   Mapping id.
    *
-   * @return array  associated array of all the drop downs in the form
-   * @static
-   * @access public
+   * @return array
+   *   associated array of all the drop downs in the form
    */
-  static function getSelection($id = NULL) {
-    $mapping = self::getMapping($id);
+  public static function getSelection($id = NULL) {
+    $mapping = self::getMapping();
     $activityStatus = CRM_Core_PseudoConstant::activityStatus();
-    $activityType   = CRM_Core_PseudoConstant::activityType(TRUE, TRUE);
+    $activityType = CRM_Core_PseudoConstant::activityType(TRUE, TRUE);
 
     $participantStatus = CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label');
     $event = CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )");
@@ -112,7 +111,7 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
     $sel1 = $sel2 = $sel3 = $sel4 = $sel5 = array();
     $options = array(
       'manual' => ts('Choose Recipient(s)'),
-      'group' => ts('Select Group'),
+      'group' => ts('Select Group'),
     );
 
     $entityMapping = array();
@@ -192,9 +191,9 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
             break;
 
           case 'event_contacts':
-            $eventContacts = CRM_Core_OptionGroup::values('event_contacts');
+            $eventContacts = CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name');
             $sel5[$entityRecipient] = $eventContacts + $options;
-            $recipientMapping += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+            $recipientMapping += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name');
             break;
 
           case NULL:
@@ -229,7 +228,7 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
             if ($kkey) {
               $auto = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $kkey, 'auto_renew');
             }
-            if ( $auto ) {
+            if ($auto) {
               $vval = $statusLabel + $autoRenew;
             }
             else {
@@ -262,16 +261,16 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
   }
 
   /**
-   * @param null $id
+   * @param int $id
    *
    * @return array
    */
-  static function getSelection1($id = NULL) {
+  public static function getSelection1($id = NULL) {
     $mapping = self::getMapping($id);
     $sel4 = $sel5 = array();
     $options = array(
       'manual' => ts('Choose Recipient(s)'),
-      'group' => ts('Select Group'),
+      'group' => ts('Select Group'),
     );
 
     $recipientMapping = array_combine(array_keys($options), array_keys($options));
@@ -295,9 +294,9 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
           break;
 
         case 'event_contacts':
-          $eventContacts = CRM_Core_OptionGroup::values('event_contacts');
+          $eventContacts = CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name');
           $sel5[$id] = $eventContacts + $options;
-          $recipientMapping += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+          $recipientMapping += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name');
           break;
 
         case NULL:
@@ -316,16 +315,16 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule {
   /**
    * Retrieve list of Scheduled Reminders
    *
-   * @param bool $namesOnly return simple list of names
+   * @param bool $namesOnly
+   *   Return simple list of names.
    *
    * @param null $entityValue
-   * @param null $id
+   * @param int $id
    *
-   * @return array  (reference)   reminder list
-   * @static
-   * @access public
+   * @return array
+   *   (reference)   reminder list
    */
-  static function &getList($namesOnly = FALSE, $entityValue = NULL, $id = NULL) {
+  public static function &getList($namesOnly = FALSE, $entityValue = NULL, $id = NULL) {
     $activity_type = CRM_Core_PseudoConstant::activityType(TRUE, TRUE);
     $activity_status = CRM_Core_PseudoConstant::activityStatus();
 
@@ -414,28 +413,28 @@ AND   cas.entity_value = $id AND
   }
 
   /**
-   * @param $contactId
+   * @param int $contactId
    * @param $to
-   * @param $scheduleID
+   * @param int $scheduleID
    * @param $from
-   * @param $tokenParams
+   * @param array $tokenParams
    *
    * @return bool|null
    * @throws CRM_Core_Exception
    */
-  static function sendReminder($contactId, $to, $scheduleID, $from, $tokenParams) {
+  public static function sendReminder($contactId, $to, $scheduleID, $from, $tokenParams) {
     $email = $to['email'];
     $phoneNumber = $to['phone'];
     $schedule = new CRM_Core_DAO_ActionSchedule();
     $schedule->id = $scheduleID;
 
-    $domain     = CRM_Core_BAO_Domain::getDomain();
-    $result     = NULL;
+    $domain = CRM_Core_BAO_Domain::getDomain();
+    $result = NULL;
     $hookTokens = array();
 
     if ($schedule->find(TRUE)) {
-      $body_text    = $schedule->body_text;
-      $body_html    = $schedule->body_html;
+      $body_text = $schedule->body_text;
+      $body_html = $schedule->body_html;
       $sms_body_text = $schedule->sms_body_text;
       $body_subject = $schedule->subject;
       if (!$body_text) {
@@ -487,10 +486,14 @@ AND   cas.entity_value = $id AND
 
       $smarty = CRM_Core_Smarty::singleton();
       foreach (array(
-          'text', 'html', 'sms_text') as $elem) {
+                 'text',
+                 'html',
+                 'sms_text',
+               ) as $elem) {
         $$elem = $smarty->fetch("string:{$$elem}");
       }
 
+      //@todo - this next section is a duplicate of function CRM_Utils_Token::getTokens
       $matches = array();
       preg_match_all('/(?<!\{|\\\\)\{(\w+\.\w+)\}(?!\})/',
         $body_subject,
@@ -511,6 +514,7 @@ AND   cas.entity_value = $id AND
         }
       }
 
+      // @todo this (along with the copy-&-paste chunk above is a commonly repeated chunk of code & should be in a re-usable function
       $messageSubject = CRM_Utils_Token::replaceContactTokens($body_subject, $contact, FALSE, $subjectToken);
       $messageSubject = CRM_Utils_Token::replaceDomainTokens($messageSubject, $domain, TRUE, $subjectToken);
       $messageSubject = CRM_Utils_Token::replaceComponentTokens($messageSubject, $contact, $subjectToken, TRUE);
@@ -521,7 +525,11 @@ AND   cas.entity_value = $id AND
       if ($schedule->mode == 'SMS' or $schedule->mode == 'User_Preference') {
         $session = CRM_Core_Session::singleton();
         $userID = $session->get('userID') ? $session->get('userID') : $contactId;
-        $smsParams = array('To' => $phoneNumber, 'provider_id' => $schedule->sms_provider_id, 'activity_subject' => $messageSubject);
+        $smsParams = array(
+          'To' => $phoneNumber,
+          'provider_id' => $schedule->sms_provider_id,
+          'activity_subject' => $messageSubject,
+        );
         $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type',
           'SMS',
           'name'
@@ -539,7 +547,6 @@ AND   cas.entity_value = $id AND
 
         CRM_Activity_BAO_Activity::sendSMSMessage($contactId,
           $sms_text,
-          $html,
           $smsParams,
           $activity->id,
           $userID
@@ -579,17 +586,16 @@ AND   cas.entity_value = $id AND
   }
 
   /**
-   * Function to add the schedules reminders in the db
-   *
-   * @param array $params (reference ) an assoc array of name/value pairs
-   * @param array $ids    the array that holds all the db ids
+   * Add the schedules reminders in the db
    *
-   * @return object CRM_Core_DAO_ActionSchedule
-   * @access public
-   * @static
+   * @param array $params
+   *   (reference ) an assoc array of name/value pairs.
+   * @param array $ids
+   *   The array that holds all the db ids.
    *
+   * @return CRM_Core_DAO_ActionSchedule
    */
-  static function add(&$params, $ids = array()) {
+  public static function add(&$params, $ids = array()) {
     $actionSchedule = new CRM_Core_DAO_ActionSchedule();
     $actionSchedule->copyValues($params);
 
@@ -601,14 +607,15 @@ AND   cas.entity_value = $id AND
    * retrieves the relevant objects. It also stores all the retrieved
    * values in the default array
    *
-   * @param array $params   (reference ) an assoc array of name/value pairs
-   * @param array $values (reference ) an assoc array to hold the flattened values
+   * @param array $params
+   *   (reference ) an assoc array of name/value pairs.
+   * @param array $values
+   *   (reference ) an assoc array to hold the flattened values.
    *
-   * @return object CRM_Core_DAO_ActionSchedule object on success, null otherwise
-   * @access public
-   * @static
+   * @return CRM_Core_DAO_ActionSchedule|null
+   *   object on success, null otherwise
    */
-  static function retrieve(&$params, &$values) {
+  public static function retrieve(&$params, &$values) {
     if (empty($params)) {
       return NULL;
     }
@@ -627,14 +634,13 @@ AND   cas.entity_value = $id AND
   }
 
   /**
-   * Function to delete a Reminder
+   * Delete a Reminder
    *
-   * @param  int  $id     ID of the Reminder to be deleted.
+   * @param int $id
+   *   ID of the Reminder to be deleted.
    *
-   * @access public
-   * @static
    */
-  static function del($id) {
+  public static function del($id) {
     if ($id) {
       $dao = new CRM_Core_DAO_ActionSchedule();
       $dao->id = $id;
@@ -647,25 +653,27 @@ AND   cas.entity_value = $id AND
   }
 
   /**
-   * update the is_active flag in the db
+   * Update the is_active flag in the db
    *
-   * @param int      $id        id of the database record
-   * @param boolean  $is_active value we want to set the is_active field
+   * @param int $id
+   *   Id of the database record.
+   * @param bool $is_active
+   *   Value we want to set the is_active field.
    *
-   * @return Object             DAO object on success, null otherwise
-   * @static
+   * @return Object
+   *   DAO object on success, null otherwise
    */
-  static function setIsActive($id, $is_active) {
+  public static function setIsActive($id, $is_active) {
     return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_ActionSchedule', $id, 'is_active', $is_active);
   }
 
   /**
-   * @param $mappingID
+   * @param int $mappingID
    * @param $now
    *
    * @throws CRM_Core_Exception
    */
-  static function sendMailings($mappingID, $now) {
+  public static function sendMailings($mappingID, $now) {
     $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
     $fromEmailAddress = "$domainValues[0] <$domainValues[1]>";
 
@@ -684,45 +692,71 @@ AND   cas.entity_value = $id AND
     while ($actionSchedule->fetch()) {
       $extraSelect = $extraJoin = $extraWhere = $extraOn = '';
 
-    if ($actionSchedule->from_email)
-            $fromEmailAddress = "$actionSchedule->from_name <$actionSchedule->from_email>";
-
+      if ($actionSchedule->from_email) {
+        $fromEmailAddress = "$actionSchedule->from_name <$actionSchedule->from_email>";
+      }
 
+      $activityTypeID = FALSE;
+      $activityStatusID = FALSE;
       if ($actionSchedule->record_activity) {
         if ($mapping->entity == 'civicrm_membership') {
-          $activityTypeID =
-            CRM_Core_OptionGroup::getValue('activity_type', 'Membership Renewal Reminder', 'name');
+          $activityTypeID
+            CRM_Core_OptionGroup::getValue('activity_type', 'Membership Renewal Reminder', 'name');
         }
         else {
-          $activityTypeID =
-            CRM_Core_OptionGroup::getValue('activity_type', 'Reminder Sent', 'name');
+          $activityTypeID
+            CRM_Core_OptionGroup::getValue('activity_type', 'Reminder Sent', 'name');
         }
 
-        $activityStatusID =
-          CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name');
+        $activityStatusID
+          CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name');
       }
 
       if ($mapping->entity == 'civicrm_activity') {
+        $compInfo = CRM_Core_Component::getEnabledComponents();
         $tokenEntity = 'activity';
         $tokenFields = array('activity_id', 'activity_type', 'subject', 'details', 'activity_date_time');
         $extraSelect = ', ov.label as activity_type, e.id as activity_id';
-        $extraJoin   = "
+        $extraJoin = "
 INNER JOIN civicrm_option_group og ON og.name = 'activity_type'
 INNER JOIN civicrm_option_value ov ON e.activity_type_id = ov.value AND ov.option_group_id = og.id";
         $extraOn = ' AND e.is_current_revision = 1 AND e.is_deleted = 0 ';
         if ($actionSchedule->limit_to == 0) {
-          $extraJoin   = "
+          $extraJoin = "
 LEFT JOIN civicrm_option_group og ON og.name = 'activity_type'
 LEFT JOIN civicrm_option_value ov ON e.activity_type_id = ov.value AND ov.option_group_id = og.id";
         }
+
+        //join for caseId
+        // if CiviCase component is enabled
+        if (array_key_exists('CiviCase', $compInfo)) {
+          $extraSelect .= ", civicrm_case_activity.case_id as case_id";
+          $extraJoin .= "
+            LEFT JOIN `civicrm_case_activity` ON `e`.`id` = `civicrm_case_activity`.`activity_id`";
+        }
       }
 
       if ($mapping->entity == 'civicrm_participant') {
         $tokenEntity = 'event';
-        $tokenFields = array('event_type', 'title', 'event_id', 'start_date', 'end_date', 'summary', 'description', 'location', 'info_url', 'registration_url', 'fee_amount', 'contact_email', 'contact_phone', 'balance');
+        $tokenFields = array(
+          'event_type',
+          'title',
+          'event_id',
+          'start_date',
+          'end_date',
+          'summary',
+          'description',
+          'location',
+          'info_url',
+          'registration_url',
+          'fee_amount',
+          'contact_email',
+          'contact_phone',
+          'balance',
+        );
         $extraSelect = ', ov.label as event_type, ev.title, ev.id as event_id, ev.start_date, ev.end_date, ev.summary, ev.description, address.street_address, address.city, address.state_province_id, address.postal_code, email.email as contact_email, phone.phone as contact_phone ';
 
-        $extraJoin   = "
+        $extraJoin = "
 INNER JOIN civicrm_event ev ON e.event_id = ev.id
 INNER JOIN civicrm_option_group og ON og.name = 'event_type'
 INNER JOIN civicrm_option_value ov ON ev.event_type_id = ov.value AND ov.option_group_id = og.id
@@ -732,7 +766,7 @@ LEFT  JOIN civicrm_email email ON email.id = lb.email_id
 LEFT  JOIN civicrm_phone phone ON phone.id = lb.phone_id
 ";
         if ($actionSchedule->limit_to == 0) {
-          $extraJoin   = "
+          $extraJoin = "
 LEFT JOIN civicrm_event ev ON e.event_id = ev.id
 LEFT JOIN civicrm_option_group og ON og.name = 'event_type'
 LEFT JOIN civicrm_option_value ov ON ev.event_type_id = ov.value AND ov.option_group_id = og.id
@@ -748,12 +782,12 @@ LEFT JOIN civicrm_phone phone ON phone.id = lb.phone_id
         $tokenEntity = 'membership';
         $tokenFields = array('fee', 'id', 'join_date', 'start_date', 'end_date', 'status', 'type');
         $extraSelect = ', mt.minimum_fee as fee, e.id as id , e.join_date, e.start_date, e.end_date, ms.name as status, mt.name as type';
-        $extraJoin   = '
+        $extraJoin = '
  INNER JOIN civicrm_membership_type mt ON e.membership_type_id = mt.id
  INNER JOIN civicrm_membership_status ms ON e.status_id = ms.id';
 
         if ($actionSchedule->limit_to == 0) {
-          $extraJoin   = '
+          $extraJoin = '
  LEFT JOIN civicrm_membership_type mt ON e.membership_type_id = mt.id
  LEFT JOIN civicrm_membership_status ms ON e.status_id = ms.id';
         }
@@ -774,7 +808,7 @@ LEFT JOIN civicrm_phone phone ON phone.id = lb.phone_id
       $entityJoinClause .= $extraOn;
 
       $query = "
-SELECT reminder.id as reminderID, reminder.contact_id as contactID, reminder.*, e.id as entityID, e.* {$extraSelect}
+SELECT reminder.id as reminderID, reminder.contact_id as contactID, reminder.entity_table as entityTable, reminder.*, e.id as entityID, e.* {$extraSelect}
 FROM  civicrm_action_log reminder
 {$entityJoinClause}
 {$extraJoin}
@@ -803,13 +837,18 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
           elseif ($field == 'registration_url') {
             $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_System::url('civicrm/event/register', 'reset=1&id=' . $dao->event_id, TRUE, NULL, FALSE);
           }
-          elseif (in_array($field, array('start_date','end_date','join_date','activity_date_time'))) {
+          elseif (in_array($field, array('start_date', 'end_date', 'join_date', 'activity_date_time'))) {
             $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_Date::customFormat($dao->$field);
           }
           elseif ($field == 'balance') {
-            $info = CRM_Contribute_BAO_Contribution::getPaymentInfo($dao->entityID, 'event');
-            $balancePay = CRM_Utils_Array::value('balance', $info);
-            $balancePay = CRM_Utils_Money::format($balancePay);
+            if ($dao->entityTable == 'civicrm_contact') {
+              $balancePay = 'N/A';
+            }
+            elseif (!empty($dao->entityID)) {
+              $info = CRM_Contribute_BAO_Contribution::getPaymentInfo($dao->entityID, 'event');
+              $balancePay = CRM_Utils_Array::value('balance', $info);
+              $balancePay = CRM_Utils_Money::format($balancePay);
+            }
             $entityTokenParams["{$tokenEntity}." . $field] = $balancePay;
           }
           elseif ($field == 'fee_amount') {
@@ -820,7 +859,7 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
           }
         }
 
-        $isError  = 0;
+        $isError = 0;
         $errorMsg = $toEmail = $toPhoneNumber = '';
 
         if ($actionSchedule->mode == 'SMS' or $actionSchedule->mode == 'User_Preference') {
@@ -840,8 +879,8 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         if ($toEmail || !(empty($toPhoneNumber) or $toDoNotSms)) {
           $to['email'] = $toEmail;
           $to['phone'] = $toPhoneNumber;
-          $result =
-            CRM_Core_BAO_ActionSchedule::sendReminder(
+          $result
+            CRM_Core_BAO_ActionSchedule::sendReminder(
               $dao->contactID,
               $to,
               $actionSchedule->id,
@@ -869,12 +908,11 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         CRM_Core_BAO_ActionLog::create($logParams);
 
         // insert activity log record if needed
-        if ($actionSchedule->record_activity) {
+        if ($actionSchedule->record_activity && !$isError) {
           $activityParams = array(
             'subject' => $actionSchedule->title,
             'details' => $actionSchedule->body_html,
-            'source_contact_id' =>
-            $session->get('userID') ? $session->get('userID') : $dao->contactID,
+            'source_contact_id' => $session->get('userID') ? $session->get('userID') : $dao->contactID,
             'target_contact_id' => $dao->contactID,
             'activity_date_time' => date('YmdHis'),
             'status_id' => $activityStatusID,
@@ -882,6 +920,14 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
             'source_record_id' => $dao->entityID,
           );
           $activity = CRM_Activity_BAO_Activity::create($activityParams);
+
+          //file reminder on case if source activity is a case activity
+          if (!empty($dao->case_id)) {
+            $caseActivityParams = array();
+            $caseActivityParams['case_id'] = $dao->case_id;
+            $caseActivityParams['activity_id'] = $activity->id;
+            CRM_Case_BAO_Case::processCaseActivity($caseActivityParams);
+          }
         }
       }
 
@@ -890,17 +936,17 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
   }
 
   /**
-   * @param $mappingID
+   * @param int $mappingID
    * @param $now
    * @param array $params
    *
    * @throws API_Exception
    */
-  static function buildRecipientContacts($mappingID, $now, $params = array()) {
+  public static function buildRecipientContacts($mappingID, $now, $params = array()) {
     $actionSchedule = new CRM_Core_DAO_ActionSchedule();
     $actionSchedule->mapping_id = $mappingID;
     $actionSchedule->is_active = 1;
-    if(!empty($params)) {
+    if (!empty($params)) {
       _civicrm_api3_dao_set_filter($actionSchedule, $params, FALSE, 'ActionSchedule');
     }
     $actionSchedule->find();
@@ -915,6 +961,7 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
       // $limitWhere - this filtering applies only for
       // 'limit to' option
       $select = $join = $where = $limitWhere = array();
+      $selectColumns = "contact_id, entity_id, entity_table, action_schedule_id";
       $limitTo = $actionSchedule->limit_to;
       $value = explode(CRM_Core_DAO::VALUE_SEPARATOR,
         trim($actionSchedule->entity_value, CRM_Core_DAO::VALUE_SEPARATOR)
@@ -926,10 +973,15 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
       );
       $status = implode(',', $status);
 
-      $anniversary = false;
+      $anniversary = FALSE;
 
       if (!CRM_Utils_System::isNull($mapping->entity_recipient)) {
-        $recipientOptions = CRM_Core_OptionGroup::values($mapping->entity_recipient, FALSE, FALSE, FALSE, NULL, 'name');
+        if ($mapping->entity_recipient == 'event_contacts') {
+          $recipientOptions = CRM_Core_OptionGroup::values($mapping->entity_recipient, FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name');
+        }
+        else {
+          $recipientOptions = CRM_Core_OptionGroup::values($mapping->entity_recipient, FALSE, FALSE, FALSE, NULL, 'name');
+        }
       }
       $from = "{$mapping->entity} e";
 
@@ -941,24 +993,26 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
         $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
 
-        if ($limitTo == 0) {
-          // including the activity target contacts if 'in addition' is defined
-          $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$targetID}";
-        }
-        else {
-          switch (CRM_Utils_Array::value($actionSchedule->recipient, $recipientOptions)) {
-            case 'Activity Assignees':
-              $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$assigneeID}";
-              break;
-
-            case 'Activity Source':
-              $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$sourceID}";
-              break;
-
-            default:
-            case 'Activity Targets':
-              $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$targetID}";
-              break;
+        if (!is_null($limitTo)) {
+          if ($limitTo == 0) {
+            // including the activity target contacts if 'in addition' is defined
+            $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$targetID}";
+          }
+          else {
+            switch (CRM_Utils_Array::value($actionSchedule->recipient, $recipientOptions)) {
+              case 'Activity Assignees':
+                $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$assigneeID}";
+                break;
+
+              case 'Activity Source':
+                $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$sourceID}";
+                break;
+
+              default:
+              case 'Activity Targets':
+                $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$targetID}";
+                break;
+            }
           }
         }
         // build where clause
@@ -1021,11 +1075,11 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         $contactField = 'e.contact_id';
         $table = 'civicrm_membership e';
         // build where clause
-        if ( $status == 2 ) {
+        if ($status == 2) {
           //auto-renew memberships
           $where[] = "e.contribution_recur_id IS NOT NULL ";
         }
-        elseif ( $status == 1 ) {
+        elseif ($status == 1) {
           $where[] = "e.contribution_recur_id IS NULL ";
         }
 
@@ -1042,8 +1096,15 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         $notINClause = self::permissionedRelationships($contactField);
 
         $membershipStatus = CRM_Member_PseudoConstant::membershipStatus(NULL, "(is_current_member = 1 OR name = 'Expired')", 'id');
-        $mStatus = implode (',', $membershipStatus);
+        $mStatus = implode(',', $membershipStatus);
         $where[] = "e.status_id IN ({$mStatus})";
+
+        // 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;
+        }
       }
 
       if ($mapping->entity == 'civicrm_contact') {
@@ -1071,7 +1132,7 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL
         if (in_array(2, $status_)) {
           // anniversary mode:
           $dateField = 'DATE_ADD(e.' . $dateDBField . ', INTERVAL ROUND(DATEDIFF(DATE(' . $now . '), e.' . $dateDBField . ') / 365) YEAR)';
-          $anniversary = true;
+          $anniversary = TRUE;
         }
         else {
           // regular mode:
@@ -1116,7 +1177,8 @@ WHERE      $group.id = {$actionSchedule->group_id}
           if ($isSmartGroup) {
             $join[] = "INNER JOIN civicrm_group_contact_cache grp ON {$contactField} = grp.contact_id";
             $where[] = "grp.group_id IN ({$actionSchedule->group_id})";
-          } else {
+          }
+          else {
             $join[] = "INNER JOIN civicrm_group_contact grp ON {$contactField} = grp.contact_id AND grp.status = 'Added'";
             $where[] = "grp.group_id IN ({$actionSchedule->group_id})";
           }
@@ -1126,14 +1188,15 @@ WHERE      $group.id = {$actionSchedule->group_id}
           $where[] = "{$contactField} IN ({$rList})";
         }
       }
-      else {
+      elseif (!is_null($limitTo)) {
         $addGroup = $addWhere = '';
         if ($actionSchedule->group_id) {
           // CRM-13577 If smart group then use Cache table
           if ($isSmartGroup) {
             $addGroup = " INNER JOIN civicrm_group_contact_cache grp ON c.id = grp.contact_id";
             $addWhere = " grp.group_id IN ({$actionSchedule->group_id})";
-          } else {
+          }
+          else {
             $addGroup = " INNER JOIN civicrm_group_contact grp ON c.id = grp.contact_id AND grp.status = 'Added'";
             $addWhere = " grp.group_id IN ({$actionSchedule->group_id})";
           }
@@ -1169,7 +1232,7 @@ reminder.action_schedule_id = %1";
         $date = $operator . "({$dateField}, INTERVAL {$actionSchedule->start_action_offset} {$actionSchedule->start_action_unit})";
         $startDateClause[] = "'{$now}' >= {$date}";
         if ($mapping->entity == 'civicrm_participant') {
-          $startDateClause[] = $operator. "({$now}, INTERVAL 1 DAY ) {$op} " . $dateField;
+          $startDateClause[] = $operator . "({$now}, INTERVAL 1 DAY ) {$op} " . $dateField;
         }
         else {
           $startDateClause[] = "DATE_SUB({$now}, INTERVAL 1 DAY ) <= {$date}";
@@ -1195,22 +1258,59 @@ reminder.action_schedule_id = %1";
       }
 
       $query = "
-INSERT INTO civicrm_action_log (contact_id, entity_id, entity_table, action_schedule_id)
+INSERT INTO civicrm_action_log ({$selectColumns})
 {$selectClause}
 {$fromClause}
 {$joinClause}
 LEFT JOIN {$reminderJoinClause}
 {$whereClause} {$limitWhereClause} AND {$dateClause} {$notINClause}
 ";
-      CRM_Core_DAO::executeQuery($query, array(1 => array($actionSchedule->id, 'Integer')));
 
-      if ($limitTo == 0) {
-        $additionWhere = ' WHERE ';
-        if ($actionSchedule->start_action_date) {
-          $additionWhere = $whereClause . ' AND ';
+      // 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);
+        $referenceQuery = "
+INSERT INTO civicrm_action_log ({$selectColumns})
+{$selectClause}
+{$fromClause}
+{$joinClause}
+ 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})
+LIMIT 0,1
+";
+
+        // 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
+ INNER JOIN civicrm_action_log new_reminder ON
+   new_reminder.action_schedule_id = reminder.action_schedule_id AND
+   new_reminder.reference_date = {$dateField} AND
+   new_reminder.action_date_time IS NULL
+ SET reminder.reference_date = {$dateField}
+ WHERE reminder.action_schedule_id = %1 AND reminder.reference_date IS NOT NULL AND reminder.reference_date != {$dateField}
+";
         }
+
+      CRM_Core_DAO::executeQuery($query, array(1 => array($actionSchedule->id, 'Integer')));
+
+      if (!empty($updateQuery)) {
+        CRM_Core_DAO::executeQuery($referenceQuery, array(1 => array($actionSchedule->id, 'Integer')));
+        CRM_Core_DAO::executeQuery($updateQuery, array(1 => array($actionSchedule->id, 'Integer')));
+      }
+
+      $isSendToAdditionalContacts = (!is_null($limitTo) && $limitTo == 0 && (!empty($addGroup) || !empty($addWhere))) ? TRUE : FALSE;
+      if ($isSendToAdditionalContacts) {
         $contactTable = "civicrm_contact c";
-        $addSelect  = "SELECT c.id as contact_id, c.id as entity_id, 'civicrm_contact' as entity_table, {$actionSchedule->id} as action_schedule_id";
+        $addSelect = "SELECT c.id as contact_id, c.id as entity_id, 'civicrm_contact' as entity_table, {$actionSchedule->id} as action_schedule_id";
         $additionReminderClause = "civicrm_action_log reminder ON reminder.contact_id = c.id AND
           reminder.entity_id          = c.id AND
           reminder.entity_table       = 'civicrm_contact' AND
@@ -1219,7 +1319,7 @@ LEFT JOIN {$reminderJoinClause}
         if ($addWhere) {
           $addWhereClause = "AND {$addWhere}";
         }
-        $insertAdditionalSql ="
+        $insertAdditionalSql = "
 INSERT INTO civicrm_action_log (contact_id, entity_id, entity_table, action_schedule_id)
 {$addSelect}
 FROM ({$contactTable})
@@ -1277,7 +1377,13 @@ INNER JOIN {$reminderJoinClause}
 {$groupByClause}
 {$havingClause}";
 
-        $valsqlInsertValues = CRM_Core_DAO::executeQuery($sqlInsertValues, array(1 => array($actionSchedule->id, 'Integer')));
+        $valsqlInsertValues = CRM_Core_DAO::executeQuery($sqlInsertValues, array(
+            1 => array(
+              $actionSchedule->id,
+              'Integer',
+            ),
+          )
+        );
 
         $arrValues = array();
         while ($valsqlInsertValues->fetch()) {
@@ -1292,7 +1398,7 @@ INNER JOIN {$reminderJoinClause}
           CRM_Core_DAO::executeQuery($query, array(1 => array($actionSchedule->id, 'Integer')));
         }
 
-        if ($limitTo == 0) {
+        if ($isSendToAdditionalContacts) {
           $addSelect .= ', MAX(reminder.action_date_time) as latest_log_time';
           $sqlEndEventCheck = "
 SELECT * FROM {$table}
@@ -1332,7 +1438,7 @@ GROUP BY reminder.contact_id
    *
    * @return null|string
    */
-  static function permissionedRelationships($field) {
+  public static function permissionedRelationships($field) {
     $query = '
 SELECT    cm.id AS owner_id, cm.contact_id AS owner_contact, m.id AS slave_id, m.contact_id AS slave_contact, cmt.relationship_type_id AS relation_type, rel.contact_id_a, rel.contact_id_b, rel.is_permission_a_b, rel.is_permission_b_a
 FROM      civicrm_membership m
@@ -1356,8 +1462,8 @@ WHERE     m.owner_membership_id IS NOT NULL AND
     }
 
     if (!empty($excludeIds)) {
-      $clause = "AND {$field} NOT IN ( " .implode(', ', $excludeIds) . ' ) ';
-      return  $clause;
+      $clause = "AND {$field} NOT IN ( " . implode(', ', $excludeIds) . ' ) ';
+      return $clause;
     }
     return NULL;
   }
@@ -1368,7 +1474,7 @@ WHERE     m.owner_membership_id IS NOT NULL AND
    *
    * @return array
    */
-  static function processQueue($now = NULL, $params = array()) {
+  public static function processQueue($now = NULL, $params = array()) {
     $now = $now ? CRM_Utils_Time::setTime($now) : CRM_Utils_Time::getTime();
 
     $mappings = self::getMapping();
@@ -1385,12 +1491,12 @@ WHERE     m.owner_membership_id IS NOT NULL AND
   }
 
   /**
-   * @param $id
-   * @param $mappingID
+   * @param int $id
+   * @param int $mappingID
    *
    * @return null|string
    */
-  static function isConfigured($id, $mappingID) {
+  public static function isConfigured($id, $mappingID) {
     $queryString = "SELECT count(id) FROM civicrm_action_schedule
                         WHERE  mapping_id = %1 AND
                                entity_value = %2";
@@ -1403,12 +1509,12 @@ WHERE     m.owner_membership_id IS NOT NULL AND
   }
 
   /**
-   * @param $mappingID
+   * @param int $mappingID
    * @param $recipientType
    *
    * @return array
    */
-  static function getRecipientListing($mappingID, $recipientType) {
+  public static function getRecipientListing($mappingID, $recipientType) {
     $options = array();
     if (!$mappingID || !$recipientType) {
       return $options;
@@ -1418,7 +1524,7 @@ WHERE     m.owner_membership_id IS NOT NULL AND
 
     switch ($mapping['entity']) {
       case 'civicrm_participant':
-        $eventContacts = CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name');
+        $eventContacts = CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name');
         if (empty($eventContacts[$recipientType])) {
           return $options;
         }
@@ -1430,4 +1536,5 @@ WHERE     m.owner_membership_id IS NOT NULL AND
 
     return $options;
   }
+
 }