From eea5db8195ea093b86d2c4c077308550044866de Mon Sep 17 00:00:00 2001 From: francescbassas Date: Thu, 11 May 2017 13:29:31 +0200 Subject: [PATCH 1/3] CRM-18081 Allow search of active relationships during a custom range of dates https://issues.civicrm.org/jira/browse/CRM-18081 --- CRM/Contact/BAO/Query.php | 59 +++++++++++++++++++ CRM/Contact/Form/Search/Criteria.php | 2 + CRM/Report/Form/Contact/Relationship.php | 55 ++++++++++++++++- .../Form/Search/Criteria/Relationship.tpl | 6 ++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index b72e793f3985..9aa331029893 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -1972,6 +1972,8 @@ public function whereClauseSingle(&$values, $apiEntity = NULL) { case 'relation_start_date_low': case 'relation_end_date_high': case 'relation_end_date_low': + case 'relation_active_period_date_high': + case 'relation_active_period_date_low': case 'relation_target_name': case 'relation_status': case 'relation_date_low': @@ -4130,6 +4132,7 @@ public function relationship(&$values) { } $this->addRelationshipDateClauses($grouping, $where); + $this->addRelationshipActivePeriodClauses($grouping, $where); if (!empty($relationType) && !empty($rType) && isset($rType->id)) { $where[$grouping][] = 'civicrm_relationship.relationship_type_id = ' . $rType->id; } @@ -4194,6 +4197,62 @@ public function addRelationshipDateClauses($grouping, &$where) { } } + /** + * Add start & end active period criteria in + * @param string $grouping + * @param array $where + * = array to add where clauses to, in case you are generating a temp table. + * not the main query. + */ + public function addRelationshipActivePeriodClauses($grouping, &$where) { + $dateValues = array(); + $dateField = 'active_period_date'; + + $dateValueLow = $this->getWhereValues('relation_active_period_date_low', $grouping); + $dateValueHigh = $this->getWhereValues('relation_active_period_date_high', $grouping); + if (!empty($dateValueLow) && !empty($dateValueHigh)) { + $dateValueLowFormated = date('Ymd', strtotime($dateValueLow[2])); + $dateValueHighFormated = date('Ymd', strtotime($dateValueHigh[2])); + $where[$grouping][] = self::getRelationshipActivePeriodClauses($dateValueLowFormated, $dateValueHighFormated, TRUE); + $this->_qill[$grouping][] = (ts('Relationship was active between')) . " " . CRM_Utils_Date::customFormat($dateValueLowFormated) . " and " . CRM_Utils_Date::customFormat($dateValueHighFormated); + } + elseif (!empty($dateValueLow)) { + $dateValueLowFormated = date('Ymd', strtotime($dateValueLow[2])); + $where[$grouping][] = self::getRelationshipActivePeriodClauses($dateValueLowFormated, NULL, TRUE); + $this->_qill[$grouping][] = (ts('Relationship was active after')) . " " . CRM_Utils_Date::customFormat($dateValueLowFormated); + } + elseif (!empty($dateValueHigh)) { + $dateValueHighFormated = date('Ymd', strtotime($dateValueHigh[2])); + $where[$grouping][] = self::getRelationshipActivePeriodClauses(NULL, $dateValueHighFormated, TRUE); + $this->_qill[$grouping][] = (ts('Relationship was active before')) . " " . CRM_Utils_Date::customFormat($dateValueHighFormated); + } + } + + /** + * Get start & end active period criteria + */ + public static function getRelationshipActivePeriodClauses($from, $to, $forceTableName) { + $tableName = $forceTableName ? 'civicrm_relationship.' : ''; + if (!is_null($from) && !is_null($to)) { + return '(((' . $tableName . 'start_date >= ' . $from . ' AND ' . $tableName . 'start_date <= ' . $to . ') OR + (' . $tableName . 'end_date >= ' . $from . ' AND ' . $tableName . 'end_date <= ' . $to . ') OR + (' . $tableName . 'start_date <= ' . $from . ' AND ' . $tableName . 'end_date >= ' . $to . ' )) OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date IS NULL) OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date >= ' . $from . ') OR + (' . $tableName . 'end_date IS NULL AND ' . $tableName . 'start_date <= ' . $to . '))'; + } + elseif (!is_null($from)) { + return '((' . $tableName . 'start_date >= ' . $from . ') OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date IS NULL) OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date >= ' . $from . '))'; + } + elseif (!is_null($to)) { + return '((' . $tableName . 'start_date <= ' . $to . ') OR + (' . $tableName . 'start_date IS NULL AND ' . $tableName . 'end_date IS NULL) OR + (' . $tableName . 'end_date IS NULL AND ' . $tableName . 'start_date <= ' . $to . '))'; + } + } + /** * Default set of return properties. * diff --git a/CRM/Contact/Form/Search/Criteria.php b/CRM/Contact/Form/Search/Criteria.php index b6280d270255..f876e40e8129 100644 --- a/CRM/Contact/Form/Search/Criteria.php +++ b/CRM/Contact/Form/Search/Criteria.php @@ -436,6 +436,8 @@ public static function relationship(&$form) { CRM_Core_Form_Date::buildDateRange($form, 'relation_start_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); CRM_Core_Form_Date::buildDateRange($form, 'relation_end_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); + CRM_Core_Form_Date::buildDateRange($form, 'relation_active_period_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); + // Add reltionship dates CRM_Core_Form_Date::buildDateRange($form, 'relation_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE); diff --git a/CRM/Report/Form/Contact/Relationship.php b/CRM/Report/Form/Contact/Relationship.php index eb7579c755ce..46aa82cd1274 100644 --- a/CRM/Report/Form/Contact/Relationship.php +++ b/CRM/Report/Form/Contact/Relationship.php @@ -298,6 +298,10 @@ public function __construct() { 'title' => ts('End Date'), 'type' => CRM_Utils_Type::T_DATE, ), + 'active_period_date' => array( + 'title' => ts('Active Period'), + 'type' => CRM_Utils_Type::T_DATE, + ), ), 'grouping' => 'relation-fields', ), @@ -438,7 +442,12 @@ public function where() { $from = CRM_Utils_Array::value("{$fieldName}_from", $this->_params); $to = CRM_Utils_Array::value("{$fieldName}_to", $this->_params); - $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']); + if ($fieldName == 'active_period_date') { + $clause = $this->activeClause($field['name'], $relative, $from, $to, $field['type']); + } + else { + $clause = $this->dateClause($field['name'], $relative, $from, $to, $field['type']); + } } else { $op = CRM_Utils_Array::value("{$fieldName}_op", $this->_params); @@ -774,4 +783,48 @@ public function buildValidityQuery($valid) { return $clause; } + /** + * Get SQL where clause for a active period field. + * + * @param string $fieldName + * @param string $relative + * @param string $from + * @param string $to + * @param string $type + * @param string $fromTime + * @param string $toTime + * + * @return null|string + */ + public function activeClause( + $fieldName, + $relative, $from, $to, $type = NULL, $fromTime = NULL, $toTime = NULL + ) { + $clauses = array(); + if (in_array($relative, array_keys($this->getOperationPair(CRM_Report_Form::OP_DATE)))) { + return NULL; + } + + list($from, $to) = $this->getFromTo($relative, $from, $to, $fromTime, $toTime); + + if ($from) { + $from = ($type == CRM_Utils_Type::T_DATE) ? substr($from, 0, 8) : $from; + } + + if ($to) { + $to = ($type == CRM_Utils_Type::T_DATE) ? substr($to, 0, 8) : $to; + } + + if ($from && $to) { + return CRM_Contact_BAO_Query::getRelationshipActivePeriodClauses($from, $to, FALSE); + } + elseif ($from) { + return CRM_Contact_BAO_Query::getRelationshipActivePeriodClauses($from, NULL, FALSE); + } + elseif ($to) { + return CRM_Contact_BAO_Query::getRelationshipActivePeriodClauses(NULL, $to, FALSE); + } + return NULL; + } + } diff --git a/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl b/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl index a7de91157bbf..ae47f810a815 100644 --- a/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl +++ b/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl @@ -65,6 +65,12 @@ {include file="CRM/Core/DateRange.tpl" fieldName="relation_end_date" from='_low' to='_high'} + + + + + {include file="CRM/Core/DateRange.tpl" fieldName="relation_active_period_date" from='_low' to='_high'} + {if $relationshipGroupTree} From 02633eb09e38eb74dde0e6e0009e727317e76797 Mon Sep 17 00:00:00 2001 From: Francesc Bassas i Bullich Date: Mon, 22 May 2017 17:15:49 +0200 Subject: [PATCH 2/3] Optimizing code https://github.com/civicrm/civicrm-core/pull/10333#discussion_r117754757 --- CRM/Contact/BAO/Query.php | 8 +++++--- CRM/Report/Form/Contact/Relationship.php | 8 +------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index 9aa331029893..6060fa1c83ff 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -4210,22 +4210,24 @@ public function addRelationshipActivePeriodClauses($grouping, &$where) { $dateValueLow = $this->getWhereValues('relation_active_period_date_low', $grouping); $dateValueHigh = $this->getWhereValues('relation_active_period_date_high', $grouping); + $dateValueLowFormated = $dateValueHighFormated = NULL; if (!empty($dateValueLow) && !empty($dateValueHigh)) { $dateValueLowFormated = date('Ymd', strtotime($dateValueLow[2])); $dateValueHighFormated = date('Ymd', strtotime($dateValueHigh[2])); - $where[$grouping][] = self::getRelationshipActivePeriodClauses($dateValueLowFormated, $dateValueHighFormated, TRUE); $this->_qill[$grouping][] = (ts('Relationship was active between')) . " " . CRM_Utils_Date::customFormat($dateValueLowFormated) . " and " . CRM_Utils_Date::customFormat($dateValueHighFormated); } elseif (!empty($dateValueLow)) { $dateValueLowFormated = date('Ymd', strtotime($dateValueLow[2])); - $where[$grouping][] = self::getRelationshipActivePeriodClauses($dateValueLowFormated, NULL, TRUE); $this->_qill[$grouping][] = (ts('Relationship was active after')) . " " . CRM_Utils_Date::customFormat($dateValueLowFormated); } elseif (!empty($dateValueHigh)) { $dateValueHighFormated = date('Ymd', strtotime($dateValueHigh[2])); - $where[$grouping][] = self::getRelationshipActivePeriodClauses(NULL, $dateValueHighFormated, TRUE); $this->_qill[$grouping][] = (ts('Relationship was active before')) . " " . CRM_Utils_Date::customFormat($dateValueHighFormated); } + + if ($activePeriodClauses = self::getRelationshipActivePeriodClauses($dateValueLowFormated, $dateValueHighFormated, TRUE)) { + $where[$grouping][] = $activePeriodClauses; + } } /** diff --git a/CRM/Report/Form/Contact/Relationship.php b/CRM/Report/Form/Contact/Relationship.php index 46aa82cd1274..64ef965811d4 100644 --- a/CRM/Report/Form/Contact/Relationship.php +++ b/CRM/Report/Form/Contact/Relationship.php @@ -815,15 +815,9 @@ public function activeClause( $to = ($type == CRM_Utils_Type::T_DATE) ? substr($to, 0, 8) : $to; } - if ($from && $to) { + if ($from || $to) { return CRM_Contact_BAO_Query::getRelationshipActivePeriodClauses($from, $to, FALSE); } - elseif ($from) { - return CRM_Contact_BAO_Query::getRelationshipActivePeriodClauses($from, NULL, FALSE); - } - elseif ($to) { - return CRM_Contact_BAO_Query::getRelationshipActivePeriodClauses(NULL, $to, FALSE); - } return NULL; } From ed29f65bee5e2f5c595c83c150f5caa4187d0450 Mon Sep 17 00:00:00 2001 From: Francesc Bassas i Bullich Date: Thu, 25 May 2017 10:11:12 +0200 Subject: [PATCH 3/3] Add inline help on advanced search form --- templates/CRM/Contact/Form/Search/Advanced.hlp | 8 ++++++++ .../CRM/Contact/Form/Search/Criteria/Relationship.tpl | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/templates/CRM/Contact/Form/Search/Advanced.hlp b/templates/CRM/Contact/Form/Search/Advanced.hlp index 77a0649f6e78..d9e25dd5f959 100644 --- a/templates/CRM/Contact/Form/Search/Advanced.hlp +++ b/templates/CRM/Contact/Form/Search/Advanced.hlp @@ -82,6 +82,14 @@

{ts}Use this filter to limit search results to relationships where the related contacts are part of a specific group or groups. For example, you might want to find all individuals who are employees ('Employee of' relationship type) of organizations which are in your 'Corporate Sponsors' group.{/ts}

{/htxt} +{htxt id="id-relationship-active-period-title"} +{ts}Active Period{/ts} +{/htxt} +{htxt id="id-relationship-active-period"} +

{ts}Use this filter to limit search results to relationships which were active during the specified time period (e.g. this is useful if you need to search all volunteer relationships that were active during 2014).{/ts}

+

{ts}By definition a relationship is active if any of these conditions occurs: (1) Start Date <= Current Date <= End Date (2) Start Date <= Current Date and End Date is not defined (3) Start Date is not defined and Current Date <= End Date{/ts}

+{/htxt} + {htxt id="id-all-tags-title"} {ts}Tags{/ts} {/htxt} diff --git a/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl b/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl index ae47f810a815..f0c3aef3c23f 100644 --- a/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl +++ b/templates/CRM/Contact/Form/Search/Criteria/Relationship.tpl @@ -66,7 +66,7 @@ {include file="CRM/Core/DateRange.tpl" fieldName="relation_end_date" from='_low' to='_high'} - + {help id="id-relationship-active-period" file="CRM/Contact/Form/Search/Advanced.hlp"}
{include file="CRM/Core/DateRange.tpl" fieldName="relation_active_period_date" from='_low' to='_high'}