CRM-16160: Changes to add search by age to Advanced Search
authoraydun <aidan.saunders@gmail.com>
Tue, 24 Mar 2015 10:16:12 +0000 (10:16 +0000)
committeraydun <aidan.saunders@gmail.com>
Tue, 24 Mar 2015 10:16:12 +0000 (10:16 +0000)
CRM/Contact/BAO/Query.php
CRM/Contact/Form/Search/Criteria.php
templates/CRM/Contact/Form/Search/Criteria/Demographics.tpl
templates/CRM/Core/AgeRange.tpl [new file with mode: 0644]

index 1cd5f00faf030879778b298fd032424f99246699..ff7f82ddddc3e91dfc0289e76f189faac245cbef 100644 (file)
@@ -1767,6 +1767,8 @@ class CRM_Contact_BAO_Query {
         CRM_Activity_BAO_Query::whereClauseSingle($values, $this);
         return;
 
+      case 'age_low':
+      case 'age_high':
       case 'birth_date_low':
       case 'birth_date_high':
       case 'deceased_date_low':
@@ -1774,6 +1776,10 @@ class CRM_Contact_BAO_Query {
         $this->demographics($values);
         return;
 
+      case 'age_asof_date':
+        // handled by demographics
+        return;
+
       case 'log_date_low':
       case 'log_date_high':
         $this->modifiedDates($values);
@@ -3869,7 +3875,12 @@ WHERE  id IN ( $groupIDs )
   public function demographics(&$values) {
     list($name, $op, $value, $grouping, $wildcard) = $values;
 
-    if (($name == 'birth_date_low') || ($name == 'birth_date_high')) {
+    if (($name == 'age_low') || ($name == 'age_high')) {
+      $this->ageRangeQueryBuilder($values,
+        'contact_a', 'age', 'birth_date', ts('Age')
+      );
+    }
+    elseif (($name == 'birth_date_low') || ($name == 'birth_date_high')) {
 
       $this->dateQueryBuilder($values,
         'contact_a', 'birth_date', 'birth_date', ts('Birth Date')
@@ -5137,6 +5148,104 @@ SELECT COUNT( conts.total_amount ) as cancel_count,
     }
   }
 
+
+  /**
+   * @param $values
+   * @param string $tableName
+   * @param string $fieldName
+   * @param string $dbFieldName
+   * @param $fieldTitle
+   * @param null $options
+   */
+  public function ageRangeQueryBuilder(
+    &$values,
+    $tableName, $fieldName,
+    $dbFieldName, $fieldTitle,
+    $options = NULL
+  ) {
+    list($name, $op, $value, $grouping, $wildcard) = $values;
+
+    $asofDateValues = $this->getWhereValues("{$fieldName}_asof_date", $grouping);
+    $asofDate = NULL;  // will be treated as current day
+    if ($asofDateValues) {
+      $asofDate = CRM_Utils_Date::processDate($asofDateValues[2]);
+      $asofDateFormat = CRM_Utils_Date::customFormat(substr($asofDate, 0, 8));
+      $fieldTitle .= ' ' . ts('as of') . ' ' . $asofDateFormat;
+    }
+
+    if ($name == "{$fieldName}_low" ||
+      $name == "{$fieldName}_high"
+    ) {
+      if (isset($this->_rangeCache[$fieldName])) {
+        return;
+      }
+      $this->_rangeCache[$fieldName] = 1;
+
+      $secondOP = $secondPhrase = $secondValue = NULL;
+
+      if ($name == "{$fieldName}_low") {
+        $firstPhrase = ts('greater than or equal to');
+        // NB: age > X means date of birth < Y
+        $firstOP = '<=';
+        $firstDate = self::calcDateFromAge($asofDate, $value, 'min');
+
+        $secondValues = $this->getWhereValues("{$fieldName}_high", $grouping);
+        if (!empty($secondValues)) {
+          $secondOP = '>=';
+          $secondPhrase = ts('less than or equal to');
+          $secondValue = $secondValues[2];
+          $secondDate = self::calcDateFromAge($asofDate, $secondValue, 'max');
+        }
+      }
+      else {
+        $firstOP = '>=';
+        $firstPhrase = ts('less than or equal to');
+        $firstDate = self::calcDateFromAge($asofDate, $value, 'max');
+
+        $secondValues = $this->getWhereValues("{$fieldName}_low", $grouping);
+        if (!empty($secondValues)) {
+          $secondOP = '<=';
+          $secondPhrase = ts('greater than or equal to');
+          $secondValue = $secondValues[2];
+          $secondDate = self::calcDateFromAge($asofDate, $secondValue, 'min');
+        }
+      }
+
+
+      if ($secondOP) {
+        $this->_where[$grouping][] = "
+( {$tableName}.{$dbFieldName} $firstOP '$firstDate' ) AND
+( {$tableName}.{$dbFieldName} $secondOP '$secondDate' )
+";
+        $displayValue = $options ? $options[$value] : $value;
+        $secondDisplayValue = $options ? $options[$secondValue] : $secondValue;
+
+        $this->_qill[$grouping][]
+          = "$fieldTitle - $firstPhrase \"$displayValue\" " . ts('AND') . " $secondPhrase \"$secondDisplayValue\"";
+      }
+      else {
+        $this->_where[$grouping][] = "{$tableName}.{$dbFieldName} $firstOP '$firstDate'";
+        $displayValue = $options ? $options[$value] : $value;
+        $this->_qill[$grouping][] = "$fieldTitle - $firstPhrase \"$displayValue\"";
+      }
+      $this->_tables[$tableName] = $this->_whereTables[$tableName] = 1;
+      return;
+    }
+  }
+
+  public static function calcDateFromAge($asofDate, $age, $type) {
+    $date = new DateTime($asofDate);
+    if ($type == "min") {
+      // minimum age is $age: dob <= date - age "235959"
+      $date->sub(new DateInterval("P" . $age . "Y"));
+      return $date->format('Ymd') . "235959";
+    } else {
+      // max age is $age: dob >= date - (age + 1y) + 1d "000000"
+      $date->sub(new DateInterval("P" . ($age + 1) . "Y"))->add(new DateInterval("P1D"));
+      return $date->format('Ymd') . "000000";
+    }
+  }
+
   /**
    * Given the field name, operator, value & its data type
    * builds the where Clause for the query
index 995b4158925a74546d3a3e2e3794805ae8fa8a13..86b2dc286a5dd3aa492fc61b065aa23dedb9ca6b 100644 (file)
@@ -489,6 +489,12 @@ class CRM_Contact_Form_Search_Criteria {
     }
     $form->addGroup($genderOptions, 'gender_id', ts('Gender'))->setAttribute('allowClear', TRUE);
 
+    $form->add('text', 'age_low', ts('Min Age'));
+    $form->addRule('age_low', ts('Please enter a positive integer'), 'positiveInteger');
+    $form->add('text', 'age_high', ts('Max Age'));
+    $form->addRule('age_high', ts('Please enter a positive integer'), 'positiveInteger');
+    $form->addDate('age_asof_date', ts('Age as of Date'), FALSE, array('formatType' => 'searchDate'));
+
     CRM_Core_Form_Date::buildDateRange($form, 'birth_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth');
 
     CRM_Core_Form_Date::buildDateRange($form, 'deceased_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth');
index b4fe33a430b39d0176b1ca7d97da477b00abec15..9548d5acf273dc2e8768a8a770b556fdd18e6e12 100644 (file)
     {include file="CRM/Core/DateRange.tpl" fieldName="birth_date" from='_low' to='_high'}
        </tr>
        <tr>
-   <td>
-            {$form.is_deceased.label}<br />
+           <tr><td><label>{ts}Age{/ts}</label></td></tr>
+    {include file="CRM/Core/AgeRange.tpl" fieldName="age" from='_low' to='_high' date='_asof_date'}
+       </tr>
+       <tr>
+         <td>
+           {$form.is_deceased.label}<br />
            {$form.is_deceased.html}
          </td>
       </tr>
diff --git a/templates/CRM/Core/AgeRange.tpl b/templates/CRM/Core/AgeRange.tpl
new file mode 100644 (file)
index 0000000..a56f297
--- /dev/null
@@ -0,0 +1,58 @@
+{*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2014                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+*}
+{*this is included inside a table row*}
+<td>
+  <span class="crm-age-range">
+    <span class="crm-age-range-min">
+      {assign var=minName   value=$fieldName|cat:$from}
+      {$form.$minName.label}
+      {$form.$minName.html}
+    </span>
+    <span class="crm-age-range-max">
+      {assign var=maxName   value=$fieldName|cat:$to}
+      {$form.$maxName.label}
+      {$form.$maxName.html}
+    </span>
+  </span>
+</td>
+<td>
+  <span class="crm-age-range-asofdate">
+      {assign var=dateName   value=$fieldName|cat:$date}
+      {$form.$dateName.label}
+      {include file="CRM/common/jcalendar.tpl" elementName=$dateName}
+  </span>
+  {literal}
+    <script type="text/javascript">
+      cj(".crm-age-range").change(function() {
+        if (cj('.crm-age-range-min :text').val() || cj('.crm-age-range-max :text').val()) {
+          cj(".crm-age-range-asofdate").show();
+        } else {
+          cj(".crm-age-range-asofdate").hide();
+        }
+      }).change();
+    </script>
+  {/literal}
+</td>