Merge pull request #6600 from colemanw/CRM-17038
authorcolemanw <coleman@civicrm.org>
Thu, 27 Aug 2015 15:08:02 +0000 (11:08 -0400)
committercolemanw <coleman@civicrm.org>
Thu, 27 Aug 2015 15:08:02 +0000 (11:08 -0400)
CRM-17038 - Fix custom tokens in greetings

28 files changed:
CRM/Contact/Form/ProfileContact.php
CRM/Contact/Page/AJAX.php
CRM/Contact/Page/DedupeFind.php
CRM/Contact/Page/DedupeMerge.php [new file with mode: 0644]
CRM/Contribute/Form/AbstractEditPayment.php
CRM/Contribute/Form/Contribution.php
CRM/Contribute/Form/Contribution/Confirm.php
CRM/Contribute/Form/Contribution/Main.php
CRM/Core/BAO/CustomValueTable.php
CRM/Core/BAO/PrevNextCache.php
CRM/Core/Form.php
CRM/Core/Payment.php
CRM/Core/Payment/PayPalImpl.php
CRM/Core/Permission.php
CRM/Core/xml/Menu/Contact.xml
CRM/Custom/Form/CustomDataByType.php
CRM/Dedupe/Merger.php
CRM/Event/Form/Registration/Confirm.php
CRM/Event/Form/Registration/Register.php
CRM/Member/BAO/Membership.php
CRM/Member/Form.php
CRM/Member/Form/Membership.php
CRM/Member/Form/MembershipRenewal.php
templates/CRM/Contact/Page/DedupeFind.tpl
templates/CRM/Contact/Page/DedupeMerge.tpl [new file with mode: 0644]
tests/phpunit/CRM/Member/Form/MembershipRenewalTest.php
tests/phpunit/CRM/Member/Form/MembershipTest.php
tests/phpunit/api/v3/CustomValueTest.php

index e96af55b07e299f90c3b7b5485d5afb3f61d882f..79bd68b52bdd178653acc6f3b4de0e40de17c9fd 100644 (file)
@@ -108,9 +108,9 @@ class CRM_Contact_Form_ProfileContact {
 
   /**
    * @param $form
+   * @param array $params Parameters from the form.
    */
-  public static function postProcess($form) {
-    $params = $form->_params;
+  public static function postProcess($form, $params) {
     if (!empty($form->_honor_block_is_active) && !empty($params['soft_credit_type_id'])) {
       $honorId = NULL;
 
index 814986e0292b52843c888b21df69a932a7fd6f8a..9bcbc2f932dca57bd5e5e25e06da8a68f6d50257 100644 (file)
@@ -669,55 +669,211 @@ LIMIT {$offset}, {$rowCount}
     CRM_Utils_JSON::output(array('status' => ($status) ? $oper : $status));
   }
 
+  /**
+   * Retrieve list of duplicate pairs from cache table.
+   */
   public static function getDedupes() {
-
-    $sEcho = CRM_Utils_Type::escape($_REQUEST['sEcho'], 'Integer');
-    $offset = isset($_REQUEST['iDisplayStart']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayStart'], 'Integer') : 0;
-    $rowCount = isset($_REQUEST['iDisplayLength']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayLength'], 'Integer') : 25;
-    $sort = 'sort_name';
-    $sortOrder = isset($_REQUEST['sSortDir_0']) ? CRM_Utils_Type::escape($_REQUEST['sSortDir_0'], 'String') : 'asc';
-
-    $gid = isset($_REQUEST['gid']) ? CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer') : 0;
-    $rgid = isset($_REQUEST['rgid']) ? CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer') : 0;
+    $offset    = isset($_REQUEST['start']) ? CRM_Utils_Type::escape($_REQUEST['start'], 'Integer') : 0;
+    $rowCount  = isset($_REQUEST['length']) ? CRM_Utils_Type::escape($_REQUEST['length'], 'Integer') : 25;
+
+    $gid         = isset($_REQUEST['gid']) ? CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer') : 0;
+    $rgid        = isset($_REQUEST['rgid']) ? CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer') : 0;
+    $selected    = isset($_REQUEST['selected']) ? CRM_Utils_Type::escape($_REQUEST['selected'], 'Integer') : 0;
+    if ($rowCount < 0) {
+      $rowCount = 0;
+    }
     $contactType = '';
     if ($rgid) {
       $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
     }
 
-    $cacheKeyString = "merge {$contactType}_{$rgid}_{$gid}";
-    $searchRows = array();
-    $selectorElements = array('src', 'dst', 'weight', 'actions');
+    $cacheKeyString   = "merge {$contactType}_{$rgid}_{$gid}";
+    $searchRows       = array();
+    $selectorElements = array('is_selected', 'is_selected_input', 'src_image', 'src', 'src_email', 'src_street', 'src_postcode', 'dst_image', 'dst', 'dst_email', 'dst_street', 'dst_postcode', 'conflicts', 'weight', 'actions');
 
-    $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
-                                                             pn.entity_id2 = de.contact_id2 )";
-    $where = "de.id IS NULL";
+    foreach ($_REQUEST['columns'] as $columnInfo) {
+      if (!empty($columnInfo['search']['value'])) {
+        ${$columnInfo['data']} = CRM_Utils_Type::escape($columnInfo['search']['value'], 'String');
+      }
+    }
+    $join  = '';
+    $where = array();
+    $searchData = CRM_Utils_Array::value('search', $_REQUEST);
+    $searchData['value'] = CRM_Utils_Type::escape($searchData['value'], 'String');
+
+    if ($src || !empty($searchData['value'])) {
+      $src = $src ? $src : $searchData['value'];
+      $where[] = " cc1.display_name LIKE '%{$src}%'";
+    }
+    if ($dst || !empty($searchData['value'])) {
+      $dst = $dst ? $dst : $searchData['value'];
+      $where[] = " cc2.display_name LIKE '%{$dst}%'";
+    }
+    if ($src_email || !empty($searchData['value'])) {
+      $src_email = $src_email ? $src_email : $searchData['value'];
+      $where[] = " (ce1.is_primary = 1 AND ce1.email LIKE '%{$src_email}%')";
+    }
+    if ($dst_email || !empty($searchData['value'])) {
+      $dst_email = $dst_email ? $dst_email : $searchData['value'];
+      $where[] = " (ce2.is_primary = 1 AND ce2.email LIKE '%{$dst_email}%')";
+    }
+    if ($src_postcode || !empty($searchData['value'])) {
+      $src_postcode = $src_postcode ? $src_postcode : $searchData['value'];
+      $where[] = " (ca1.is_primary = 1 AND ca1.postal_code LIKE '%{$src_postcode}%')";
+    }
+    if ($dst_postcode || !empty($searchData['value'])) {
+      $dst_postcode = $dst_postcode ? $dst_postcode : $searchData['value'];
+      $where[] = " (ca2.is_primary = 1 AND ca2.postal_code LIKE '%{$dst_postcode}%')";
+    }
+    if ($src_street || !empty($searchData['value'])) {
+      $src_street = $src_street ? $src_street : $searchData['value'];
+      $where[] = " (ca1.is_primary = 1 AND ca1.street_address LIKE '%{$src_street}%')";
+    }
+    if ($dst_street || !empty($searchData['value'])) {
+      $dst_street = $dst_street ? $dst_street : $searchData['value'];
+      $where[] = " (ca2.is_primary = 1 AND ca2.street_address LIKE '%{$dst_street}%')";
+    }
+    if (!empty($searchData['value'])) {
+      $whereClause   = ' ( ' . implode(' OR ', $where) . ' ) ';
+    }
+    else {
+      if (!empty($where)) {
+        $whereClause  = implode(' AND ', $where);
+      }
+    }
+    $whereClause .= $whereClause ? ' AND de.id IS NULL' : ' de.id IS NULL';
 
-    $iFilteredTotal = $iTotal = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, $join, $where);
-    $mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where, $offset, $rowCount);
+    if ($selected) {
+      $whereClause .= ' AND pn.is_selected = 1';
+    }
+    $join .= " LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND pn.entity_id2 = de.contact_id2 )";
+
+    $select = array(
+      'cc1.contact_type'     => 'src_contact_type',
+      'cc1.display_name'     => 'src_display_name',
+      'cc1.contact_sub_type' => 'src_contact_sub_type',
+      'cc2.contact_type'     => 'dst_contact_type',
+      'cc2.display_name'     => 'dst_display_name',
+      'cc2.contact_sub_type' => 'dst_contact_sub_type',
+      'ce1.email'            => 'src_email',
+      'ce2.email'            => 'dst_email',
+      'ca1.postal_code'      => 'src_postcode',
+      'ca2.postal_code'      => 'dst_postcode',
+      'ca1.street_address'   => 'src_street',
+      'ca2.street_address'   => 'dst_street',
+    );
+
+    if ($select) {
+      $join .= " INNER JOIN civicrm_contact cc1 ON cc1.id = pn.entity_id1";
+      $join .= " INNER JOIN civicrm_contact cc2 ON cc2.id = pn.entity_id2";
+      $join .= " LEFT JOIN civicrm_email ce1 ON (ce1.contact_id = pn.entity_id1 AND ce1.is_primary = 1 )";
+      $join .= " LEFT JOIN civicrm_email ce2 ON (ce2.contact_id = pn.entity_id2 AND ce2.is_primary = 1 )";
+      $join .= " LEFT JOIN civicrm_address ca1 ON (ca1.contact_id = pn.entity_id1 AND ca1.is_primary = 1 )";
+      $join .= " LEFT JOIN civicrm_address ca2 ON (ca2.contact_id = pn.entity_id2 AND ca2.is_primary = 1 )";
+    }
+    $iTotal = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, $join, $whereClause);
+    foreach ($_REQUEST['order'] as $orderInfo) {
+      if (!empty($orderInfo['column'])) {
+        $orderColumnNumber = $orderInfo['column'];
+        $dir               = $orderInfo['dir'];
+      }
+    }
+    $columnDetails = CRM_Utils_Array::value($orderColumnNumber, $_REQUEST['columns']);
+    if (!empty($columnDetails)) {
+      switch ($columnDetails['data']) {
+        case 'src':
+          $whereClause .= " ORDER BY cc1.display_name {$dir}";
+          break;
+
+        case 'src_email':
+          $whereClause .= " ORDER BY ce1.email {$dir}";
+          break;
+
+        case 'src_street':
+          $whereClause .= " ORDER BY ca1.street_address {$dir}";
+          break;
+
+        case 'src_postcode':
+          $whereClause .= " ORDER BY ca1.postal_code {$dir}";
+          break;
+
+        case 'dst':
+          $whereClause .= " ORDER BY cc2.display_name {$dir}";
+          break;
+
+        case 'dst_email':
+          $whereClause .= " ORDER BY ce2.email {$dir}";
+          break;
+
+        case 'dst_street':
+          $whereClause .= " ORDER BY ca2.street_address {$dir}";
+          break;
+
+        case 'dst_postcode':
+          $whereClause .= " ORDER BY ca2.postal_code {$dir}";
+          break;
+
+        default:
+          break;
+      }
+    }
 
-    foreach ($mainContacts as $mainId => $main) {
-      $searchRows[$mainId]['src'] = CRM_Utils_System::href($main['srcName'], 'civicrm/contact/view', "reset=1&cid={$main['srcID']}");
-      $searchRows[$mainId]['dst'] = CRM_Utils_System::href($main['dstName'], 'civicrm/contact/view', "reset=1&cid={$main['dstID']}");
-      $searchRows[$mainId]['weight'] = CRM_Utils_Array::value('weight', $main);
+    $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $whereClause, $offset, $rowCount, $select);
+    $iFilteredTotal = CRM_Core_DAO::singleValueQuery("SELECT FOUND_ROWS()");
+
+    $count = 0;
+    foreach ($dupePairs as $key => $pairInfo) {
+      $pair =& $pairInfo['data'];
+      $srcContactSubType  = CRM_Utils_Array::value('src_contact_sub_type', $pairInfo);
+      $dstContactSubType  = CRM_Utils_Array::value('dst_contact_sub_type', $pairInfo);
+      $srcTypeImage = CRM_Contact_BAO_Contact_Utils::getImage($srcContactSubType ?
+        $srcContactSubType : $pairInfo['src_contact_type'],
+        FALSE,
+        $pairInfo['entity_id1']
+      );
+      $dstTypeImage = CRM_Contact_BAO_Contact_Utils::getImage($dstContactSubType ?
+        $dstContactSubType : $pairInfo['dst_contact_type'],
+        FALSE,
+        $pairInfo['entity_id2']
+      );
 
-      if (!empty($main['canMerge'])) {
-        $mergeParams = "reset=1&cid={$main['srcID']}&oid={$main['dstID']}&action=update&rgid={$rgid}";
+      $searchRows[$count]['is_selected'] = $pairInfo['is_selected'];
+      $searchRows[$count]['is_selected_input'] = "<input type='checkbox' class='crm-dedupe-select' name='pnid_{$pairInfo['prevnext_id']}' value='{$pairInfo['is_selected']}' onclick='toggleDedupeSelect(this)'>";
+      $searchRows[$count]['src_image'] = $srcTypeImage;
+      $searchRows[$count]['src'] = CRM_Utils_System::href($pair['srcName'], 'civicrm/contact/view', "reset=1&cid={$pairInfo['entity_id1']}");
+      $searchRows[$count]['src_email'] = CRM_Utils_Array::value('src_email', $pairInfo);
+      $searchRows[$count]['src_street'] = CRM_Utils_Array::value('src_street', $pairInfo);
+      $searchRows[$count]['src_postcode'] = CRM_Utils_Array::value('src_postcode', $pairInfo);
+      $searchRows[$count]['dst_image'] = $dstTypeImage;
+      $searchRows[$count]['dst'] = CRM_Utils_System::href($pair['dstName'], 'civicrm/contact/view', "reset=1&cid={$pairInfo['entity_id2']}");
+      $searchRows[$count]['dst_email'] = CRM_Utils_Array::value('dst_email', $pairInfo);
+      $searchRows[$count]['dst_street'] = CRM_Utils_Array::value('dst_street', $pairInfo);
+      $searchRows[$count]['dst_postcode'] = CRM_Utils_Array::value('dst_postcode', $pairInfo);
+      $searchRows[$count]['conflicts'] = str_replace("',", "',<br/>", CRM_Utils_Array::value('conflicts', $pair));
+      $searchRows[$count]['weight'] = CRM_Utils_Array::value('weight', $pair);
+
+      if (!empty($pairInfo['data']['canMerge'])) {
+        $mergeParams = "reset=1&cid={$pairInfo['entity_id1']}&oid={$pairInfo['entity_id2']}&action=update&rgid={$rgid}";
         if ($gid) {
           $mergeParams .= "&gid={$gid}";
         }
 
-        $searchRows[$mainId]['actions'] = '<a class="action-item crm-hover-button" href="' . CRM_Utils_System::url('civicrm/contact/merge', $mergeParams) . '">' . ts('merge') . '</a>';
-        $searchRows[$mainId]['actions'] .= "<a class='action-item crm-hover-button crm-notDuplicate' href='#' onClick=\"processDupes( {$main['srcID']}, {$main['dstID']}, 'dupe-nondupe', 'dupe-listing'); return false;\">" . ts('not a duplicate') . "</a>";
+        $searchRows[$count]['actions']  = "<a class='crm-dedupe-flip' href='#' data-pnid={$pairInfo['prevnext_id']}>" . ts('flip') . "</a>&nbsp;|&nbsp;";
+        $searchRows[$count]['actions'] .= CRM_Utils_System::href(ts('merge'), 'civicrm/contact/merge', $mergeParams);
+        $searchRows[$count]['actions'] .= "&nbsp;|&nbsp;<a id='notDuplicate' href='#' onClick=\"processDupes( {$pairInfo['entity_id1']}, {$pairInfo['entity_id2']}, 'dupe-nondupe', 'dupe-listing'); return false;\">" . ts('not a duplicate') . "</a>";
       }
       else {
-        $searchRows[$mainId]['actions'] = '<em>' . ts('Insufficient access rights - cannot merge') . '</em>';
+        $searchRows[$count]['actions'] = '<em>' . ts('Insufficient access rights - cannot merge') . '</em>';
       }
+      $count++;
     }
 
-    CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
-    echo CRM_Utils_JSON::encodeDataTableSelector($searchRows, $sEcho, $iTotal, $iFilteredTotal, $selectorElements);
-
-    CRM_Utils_System::civiExit();
+    $dupePairs = array(
+      'data'            => $searchRows,
+      'recordsTotal'    => $iTotal,
+      'recordsFiltered' => $iFilteredTotal,
+    );
+    CRM_Utils_JSON::output($dupePairs);
   }
 
   /**
@@ -742,6 +898,30 @@ LIMIT {$offset}, {$rowCount}
     CRM_Utils_JSON::output($paperSize);
   }
 
+  /**
+   * Swap contacts in a dupe pair i.e main with duplicate contact.
+   */
+  public static function flipDupePairs($prevNextId = NULL) {
+    if (!$prevNextId) {
+      $prevNextId = $_REQUEST['pnid'];
+    }
+    $query = "
+      UPDATE civicrm_prevnext_cache cpc
+      INNER JOIN civicrm_prevnext_cache old on cpc.id = old.id 
+      SET cpc.entity_id1 = cpc.entity_id2, cpc.entity_id2 = old.entity_id1 ";
+    if (is_array($prevNextId) && !CRM_Utils_Array::crmIsEmptyArray($prevNextId)) {
+      $prevNextId = implode(', ', $prevNextId);
+      $prevNextId = CRM_Utils_Type::escape($prevNextId, 'String');
+      $query     .= "WHERE cpc.id IN ({$prevNextId}) AND cpc.is_selected = 1";
+    }
+    else {
+      $prevNextId = CRM_Utils_Type::escape($prevNextId, 'Positive');
+      $query     .= "WHERE cpc.id = $prevNextId";
+    }
+    CRM_Core_DAO::executeQuery($query);
+    CRM_Utils_JSON::output();
+  }
+
   /**
    * Used to store selected contacts across multiple pages in advanced search.
    */
@@ -807,6 +987,44 @@ LIMIT {$offset}, {$rowCount}
     CRM_Utils_JSON::output($addressVal);
   }
 
+  /**
+   * Mark dupe pairs as selected from un-selected state or vice-versa, in dupe cache table.
+   */
+  public static function toggleDedupeSelect() {
+    $rgid = CRM_Utils_Type::escape($_REQUEST['rgid'], 'Integer');
+    $gid  = CRM_Utils_Type::escape($_REQUEST['gid'], 'Integer');
+    $pnid = $_REQUEST['pnid'];
+    $isSelected = CRM_Utils_Type::escape($_REQUEST['is_selected'], 'Boolean');
+
+    $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
+    $cacheKeyString  = "merge $contactType";
+    $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
+    $cacheKeyString .= $gid ? "_{$gid}" : '_0';
+
+    $params = array(
+      1 => array($isSelected, 'Boolean'),
+      3 => array("$cacheKeyString%", 'String'), // using % to address rows with conflicts as well
+    );
+
+    //check pnid is_array or integer
+    $whereClause = NULL;
+    if (is_array($pnid) && !CRM_Utils_Array::crmIsEmptyArray($pnid)) {
+      $pnid = implode(', ', $pnid);
+      $pnid = CRM_Utils_Type::escape($pnid, 'String');
+      $whereClause = " id IN ( {$pnid} ) ";
+    }
+    else {
+      $pnid = CRM_Utils_Type::escape($pnid, 'Integer');
+      $whereClause = " id = %2";
+      $params[2]   = array($pnid, 'Integer');
+    }
+
+    $sql = "UPDATE civicrm_prevnext_cache SET is_selected = %1 WHERE {$whereClause} AND cacheKey LIKE %3";
+    CRM_Core_DAO::executeQuery($sql, $params);
+
+    CRM_Utils_System::civiExit();
+  }
+
   /**
    * Retrieve contact relationships.
    */
index e02e9d26471e0db700f5145d3f2ef56554f109c5..9c2ea680e7bfe19244c4c2cf5a3e002b7ee3ad18 100644 (file)
@@ -94,7 +94,7 @@ class CRM_Contact_Page_DedupeFind extends CRM_Core_Page_Basic {
     elseif ($action & CRM_Core_Action::MAP) {
       // do a batch merge if requested
       $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0);
-      $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', TRUE, TRUE);
+      $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', TRUE, 75);
 
       $skippedCount = CRM_Utils_Request::retrieve('skipped', 'Positive', $this, FALSE, 0);
       $skippedCount = $skippedCount + count($result['skipped']);
@@ -157,6 +157,9 @@ class CRM_Contact_Page_DedupeFind extends CRM_Core_Page_Basic {
       if ($rgid) {
         $sourceParams .= "&rgid={$rgid}";
       }
+      if ($context == 'conflicts') {
+        $sourceParams .= "&selected=1";
+      }
 
       $this->assign('sourceUrl', CRM_Utils_System::url('civicrm/ajax/dedupefind', $sourceParams, FALSE, NULL, FALSE));
 
@@ -165,11 +168,31 @@ class CRM_Contact_Page_DedupeFind extends CRM_Core_Page_Basic {
       $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
       $cacheKeyString .= $gid ? "_{$gid}" : '_0';
 
+      $stats = CRM_Dedupe_Merger::getMergeStatsMsg($cacheKeyString);
+      if ($stats) {
+        CRM_Core_Session::setStatus($stats);
+        // reset so we not displaying same message again
+        CRM_Dedupe_Merger::resetMergeStats($cacheKeyString);
+      }
       $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
                                                                  pn.entity_id2 = de.contact_id2 )";
       $where = "de.id IS NULL";
+      if ($context == 'conflicts') {
+        $where .= " AND pn.is_selected = 1";
+      }
       $this->_mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where);
       if (empty($this->_mainContacts)) {
+        if ($context == 'conflicts') {
+          // if the current screen was intended to list only selected contacts, move back to full dupe list
+          $sourceParams = 'reset=1&action=update';
+          if ($gid) {
+            $sourceParams .= "&gid={$gid}";
+          }
+          if ($rgid) {
+            $sourceParams .= "&rgid={$rgid}";
+          }
+          CRM_Utils_System::redirect(CRM_Utils_System::url(CRM_Utils_System::currentPath(), $sourceParams));
+        }
         if ($gid) {
           $foundDupes = $this->get("dedupe_dupes_$gid");
           if (!$foundDupes) {
diff --git a/CRM/Contact/Page/DedupeMerge.php b/CRM/Contact/Page/DedupeMerge.php
new file mode 100644 (file)
index 0000000..4e1f17b
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.5                                                |
+ +--------------------------------------------------------------------+
+ | 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        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2014
+ * $Id$
+ *
+ */
+class CRM_Contact_Page_DedupeMerge extends CRM_Core_Page {
+
+  const BATCHLIMIT = 2;
+
+  /**
+   * Browse batch merges.
+   *
+   * @return void
+   * @access public
+   */
+  public function run() {
+    $runner = self::getRunner();
+    if ($runner) {
+      // Run Everything in the Queue via the Web.
+      $runner->runAllViaWeb();
+    }
+    else {
+      CRM_Core_Session::setStatus(ts('Nothing to merge.'));
+    }
+
+    // parent run
+    return parent::run();
+  }
+
+  /**
+   * Build a queue of tasks by dividing dupe pairs in batches.
+   */
+  public static function getRunner() {
+    $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0);
+    $gid  = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0);
+    $action = CRM_Utils_Request::retrieve('action', 'String', CRM_Core_DAO::$_nullObject);
+    $mode   = CRM_Utils_Request::retrieve('mode', 'String', CRM_Core_DAO::$_nullObject, FALSE, 'safe');
+
+    $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
+    $cacheKeyString = "merge {$contactType}";
+    $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
+    $cacheKeyString .= $gid ? "_{$gid}" : '_0';
+
+    $urlQry = "reset=1&action=update&rgid={$rgid}";
+    $urlQry = $gid ? ($urlQry . "&gid={$gid}") : $urlQry;
+
+    if ($mode == 'aggressive' && !CRM_Core_Permission::check('force merge duplicate contacts')) {
+      CRM_Core_Session::setStatus(ts('You do not have permission to force merge duplicate contact records'), ts('Permission Denied'), 'error');
+      CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry));
+    }
+    // Setup the Queue
+    $queue = CRM_Queue_Service::singleton()->create(array(
+      'name'  => $cacheKeyString,
+      'type'  => 'Sql',
+      'reset' => TRUE,
+    ));
+
+    $where = NULL;
+    if ($action == CRM_Core_Action::MAP) {
+      $where = "pn.is_selected = 1";
+      $isSelected = 1;
+    }
+    else {
+      // else merge all (2)
+      $isSelected = 2;
+    }
+
+    $total  = CRM_Core_BAO_PrevNextCache::getCount($cacheKeyString, NULL, $where);
+    if ($total <= 0) {
+      // Nothing to do.
+      CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry));
+    }
+
+    // reset merge stats, so we compute new stats
+    CRM_Dedupe_Merger::resetMergeStats($cacheKeyString);
+
+    for ($i = 1; $i <= ceil($total / self::BATCHLIMIT); $i++) {
+      $task  = new CRM_Queue_Task(
+        array('CRM_Contact_Page_DedupeMerge', 'callBatchMerge'),
+        array($rgid, $gid, $mode, TRUE, self::BATCHLIMIT, $isSelected),
+        "Processed " . $i * self::BATCHLIMIT . " pair of duplicates out of " . $total
+      );
+
+      // Add the Task to the Queue
+      $queue->createItem($task);
+    }
+
+    // Setup the Runner
+    $urlQry .= "&context=conflicts";
+    $runner = new CRM_Queue_Runner(array(
+      'title'     => ts('Merging Duplicates..'),
+      'queue'     => $queue,
+      'errorMode' => CRM_Queue_Runner::ERROR_ABORT,
+      'onEndUrl'  => CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry, TRUE, NULL, FALSE),
+    ));
+
+    return $runner;
+  }
+
+  /**
+   * Carry out batch merges.
+   */
+  public static function callBatchMerge(CRM_Queue_TaskContext $ctx, $rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $batchLimit = 1, $isSelected = 2) {
+    $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, $mode, $autoFlip, $batchLimit, $isSelected);
+
+    return CRM_Queue_Task::TASK_SUCCESS;
+  }
+
+}
index 096c07da8325552a776ee0aef15e69fc4469c28a..912f2eaebea1ca9b3915f7435d7186e22503e2da 100644 (file)
@@ -633,4 +633,79 @@ LEFT JOIN  civicrm_contribution on (civicrm_contribution.contact_id = civicrm_co
     }
   }
 
+  /**
+   * Begin post processing.
+   *
+   * This function aims to start to bring together common postProcessing functions.
+   *
+   * Eventually these are also shared with the front end forms & may need to be moved to where they can also
+   * access this function.
+   */
+  protected function beginPostProcess() {
+    if (in_array('credit_card_exp_date', array_keys($this->_params))) {
+      $this->_params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($this->_params);
+      $this->_params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($this->_params);
+    }
+
+    $this->_params['ip_address'] = CRM_Utils_System::ipAddress();
+  }
+
+
+  /**
+   * Add the billing address to the contact who paid.
+   *
+   * Note that this function works based on the presence or otherwise of billing fields & can be called regardless of
+   * whether they are 'expected' (due to assumptions about the payment processor type or the setting to collect billing
+   * for pay later.
+   */
+  protected function processBillingAddress() {
+    $fields = array();
+
+    $fields['email-Primary'] = 1;
+    $this->_params['email-5'] = $this->_params['email-Primary'] = $this->_contributorEmail;
+    // now set the values for the billing location.
+    foreach (array_keys($this->_fields) as $name) {
+      $fields[$name] = 1;
+    }
+
+    // also add location name to the array
+    $this->_params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $this->_params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $this->_params) . ' ' . CRM_Utils_Array::value('billing_last_name', $this->_params);
+    $this->_params["address_name-{$this->_bltID}"] = trim($this->_params["address_name-{$this->_bltID}"]);
+
+    $fields["address_name-{$this->_bltID}"] = 1;
+
+    //ensure we don't over-write the payer's email with the member's email
+    if ($this->_contributorContactID == $this->_contactID) {
+      $fields["email-{$this->_bltID}"] = 1;
+    }
+
+    list($hasBillingField, $addressParams) = CRM_Contribute_BAO_Contribution::getPaymentProcessorReadyAddressParams($this->_params, $this->_bltID);
+    $nameFields = array('first_name', 'middle_name', 'last_name');
+
+    foreach ($nameFields as $name) {
+      $fields[$name] = 1;
+      if (array_key_exists("billing_$name", $this->_params)) {
+        $this->_params[$name] = $this->_params["billing_{$name}"];
+        $this->_params['preserveDBName'] = TRUE;
+      }
+    }
+
+    if ($hasBillingField) {
+      $addressParams = array_merge($this->_params, $addressParams);
+      //here we are setting up the billing contact - if different from the member they are already created
+      // but they will get billing details assigned
+      CRM_Contact_BAO_Contact::createProfileContact($addressParams, $fields,
+        $this->_contributorContactID, NULL, NULL,
+        CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactID, 'contact_type')
+      );
+    }
+    // Add additional parameters that the payment processors are used to receiving.
+    if (!empty($this->_params["billing_state_province_id-{$this->_bltID}"])) {
+      $this->_params["state_province-{$this->_bltID}"] = $this->_params["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($this->_params["billing_state_province_id-{$this->_bltID}"]);
+    }
+    if (!empty($this->_params["billing_country_id-{$this->_bltID}"])) {
+      $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]);
+    }
+  }
+
 }
index 3ef4825c9f064e9a276852c166314fbe48f0afac..a883db6a7c885bd4846318e5965cb21f45c25c73 100644 (file)
@@ -1056,13 +1056,13 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
       $submittedValues['source'] = ts('Submit Credit Card Payment by: %1', array(1 => $userSortName));
     }
 
-    $params = $this->_params = $submittedValues;
+    $params = $submittedValues;
+    $this->_params = array_merge($this->_params, $submittedValues);
 
     // Mapping requiring documentation.
     $this->_params['payment_processor'] = $submittedValues['payment_processor_id'];
 
     $now = date('YmdHis');
-    $fields = array();
 
     // we need to retrieve email address
     if ($this->_context == 'standalone' && !empty($submittedValues['is_email_receipt'])) {
@@ -1072,56 +1072,13 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
       $this->assign('displayName', $this->userDisplayName);
     }
 
-    // Set email for primary location.
-    $fields['email-Primary'] = 1;
-    $params['email-Primary'] = $this->userEmail;
-
-    // now set the values for the billing location.
-    foreach (array_keys($this->_fields) as $name) {
-      $fields[$name] = 1;
-    }
-
-    // also add location name to the array
-    $params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array::value('billing_last_name', $params);
-
-    $params["address_name-{$this->_bltID}"] = trim($params["address_name-{$this->_bltID}"]);
-    $fields["address_name-{$this->_bltID}"] = 1;
-
-    $nameFields = array('first_name', 'middle_name', 'last_name');
-    foreach ($nameFields as $name) {
-      $fields[$name] = 1;
-      if (array_key_exists("billing_$name", $params)) {
-        $params[$name] = $params["billing_{$name}"];
-        $params['preserveDBName'] = TRUE;
-      }
-    }
-
+    $this->_contributorEmail = $this->userEmail;
+    $this->_contributorContactID = $contactID;
+    $this->processBillingAddress();
     if (!empty($params['source'])) {
       unset($params['source']);
     }
-    CRM_Contact_BAO_Contact::createProfileContact($params, $fields,
-      $contactID,
-      NULL, NULL,
-      CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
-        $contactID,
-        'contact_type'
-      )
-    );
-
-    // add all the additional payment params we need
-    if (!empty($this->_params["billing_state_province_id-{$this->_bltID}"])) {
-      $this->_params["state_province-{$this->_bltID}"] = $this->_params["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($this->_params["billing_state_province_id-{$this->_bltID}"]);
-    }
-    if (!empty($this->_params["billing_country_id-{$this->_bltID}"])) {
-      $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($this->_params["billing_country_id-{$this->_bltID}"]);
-    }
-
-    if (in_array('credit_card_exp_date', array_keys($this->_params))) {
-      $this->_params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($this->_params);
-      $this->_params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($this->_params);
-    }
 
-    $this->_params['ip_address'] = CRM_Utils_System::ipAddress();
     $this->_params['amount'] = $this->_params['total_amount'];
     $this->_params['amount_level'] = 0;
     $this->_params['description'] = ts("Contribution submitted by a staff person using contributor's credit card");
@@ -1416,6 +1373,8 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP
   protected function submit($submittedValues, $action, $pledgePaymentID) {
     $softParams = $softIDs = array();
     $pId = $contribution = $isRelatedId = FALSE;
+    $this->_params = $submittedValues;
+    $this->beginPostProcess();
 
     if (!empty($submittedValues['price_set_id']) && $action & CRM_Core_Action::UPDATE) {
       $line = CRM_Price_BAO_LineItem::getLineItems($this->_id, 'contribution');
index c705eeb568b0e6cbdb4b05af2317ffeefff4a26e..8f28e5989b2974f0ddd01880ccf45b1d5452706e 100644 (file)
@@ -832,13 +832,10 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     $contactID = $contributionParams['contact_id'];
 
     $isEmailReceipt = !empty($form->_values['is_email_receipt']);
-    // How do these vary from params? These are currently passed to
-    // - custom data function....
-    $formParams = $form->_params;
-    $isSeparateMembershipPayment = empty($formParams['separate_membership_payment']) ? FALSE : TRUE;
-    $pledgeID = empty($formParams['pledge_id']) ? NULL : $formParams['pledge_id'];
+    $isSeparateMembershipPayment = empty($params['separate_membership_payment']) ? FALSE : TRUE;
+    $pledgeID = empty($params['pledge_id']) ? NULL : $params['pledge_id'];
     if (!$isSeparateMembershipPayment && !empty($form->_values['pledge_block_id']) &&
-      (!empty($formParams['is_pledge']) || $pledgeID)) {
+      (!empty($params['is_pledge']) || $pledgeID)) {
       $isPledge = TRUE;
     }
     else {
@@ -927,7 +924,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     }
 
     //CRM-13981, processing honor contact into soft-credit contribution
-    CRM_Contact_Form_ProfileContact::postProcess($form);
+    CRM_Contact_Form_ProfileContact::postProcess($form, $params);
 
     // process soft credit / pcp pages
     CRM_Contribute_Form_Contribution_Confirm::processPcpSoft($params, $contribution);
@@ -937,7 +934,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
       if ($pledgeID) {
         //when user doing pledge payments.
         //update the schedule when payment(s) are made
-        foreach ($form->_params['pledge_amount'] as $paymentId => $dontCare) {
+        foreach ($params['pledge_amount'] as $paymentId => $dontCare) {
           $scheduledAmount = CRM_Core_DAO::getFieldValue(
             'CRM_Pledge_DAO_PledgePayment',
             $paymentId,
@@ -1010,7 +1007,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr
     }
 
     if ($online && $contribution) {
-      CRM_Core_BAO_CustomValueTable::postProcess($form->_params,
+      CRM_Core_BAO_CustomValueTable::postProcess($params,
         'civicrm_contribution',
         $contribution->id,
         'Contribution'
index eeb94c6fcf723106bb68811977e4a321f0dc849f..0832bd9aa47fd006f5fe563461b8d33121ba78f9 100644 (file)
@@ -1294,7 +1294,7 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
     $this->set('invoiceID', $invoiceID);
     $params['invoiceID'] = $invoiceID;
     $params['description'] = ts('Online Contribution') . ': ' . (($this->_pcpInfo['title']) ? $this->_pcpInfo['title'] : $this->_values['title']);
-
+    $params['button'] = $this->controller->getButtonName();
     // required only if is_monetary and valid positive amount
     if ($this->_values['is_monetary'] &&
       is_array($this->_paymentProcessor) &&
@@ -1346,31 +1346,6 @@ class CRM_Contribute_Form_Contribution_Main extends CRM_Contribute_Form_Contribu
 
   }
 
-  /**
-   * Handle pre approval for processors.
-   *
-   * This fits with the flow where a pre-approval is done and then confirmed in the next stage when confirm is hit.
-   *
-   * This applies to processors that
-   * @param array $params
-   */
-  protected function handlePreApproval(&$params) {
-    try {
-      $payment = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
-      $params['component'] = 'contribute';
-      $result = $payment->doPreApproval($params);
-    }
-    catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
-      CRM_Core_Error::displaySessionError($e->getMessage());
-      CRM_Utils_System::redirect($params['cancelURL']);
-    }
-
-    $this->set('pre_approval_parameters', $result['pre_approval_parameters']);
-    if (!empty($result['redirect_url'])) {
-      CRM_Utils_System::redirect($result['redirect_url']);
-    }
-  }
-
   /**
    * Process confirm function and pass browser to the thank you page.
    */
index a765dd8bb3ce7e7332a889e5466f0fca11fcd0eb..901abf8627ef909bd8a072e1fa49536919121dda 100644 (file)
@@ -217,9 +217,16 @@ class CRM_Core_BAO_CustomValueTable {
             default:
               break;
           }
-          $set[$field['column_name']] = "%{$count}";
-          $params[$count] = array($value, $type);
-          $count++;
+          if (strtolower($value) === "null") {
+            // when unsetting a value to null, we don't need to validate the type
+            // https://projectllr.atlassian.net/browse/VGQBMP-20
+            $set[$field['column_name']] = $value;
+          }
+          else {
+            $set[$field['column_name']] = "%{$count}";
+            $params[$count] = array($value, $type);
+            $count++;
+          }
         }
 
         if (!empty($set)) {
index 87eea954ac7da5a023414648795f33722c32525d..2c4077cc6c2d61c422a7cba2da3ab07e8d17a1d8 100644 (file)
@@ -153,12 +153,48 @@ WHERE  cacheKey     = %3 AND
 
     if (isset($cacheKey)) {
       $sql .= " AND cacheKey LIKE %4";
-      $params[4] = array("{$cacheKey}%", 'String');
+      $params[4] = array("{$cacheKey}%", 'String'); // used % to address any row with conflict-cacheKey e.g "merge Individual_8_0_conflicts"
     }
 
     CRM_Core_DAO::executeQuery($sql, $params);
   }
 
+  public static function markConflict($id1, $id2, $cacheKey, $conflicts) {
+    if (empty($cacheKey) || empty($conflicts)) {
+      return FALSE;
+    }
+
+    $sql  = "SELECT pn.* 
+      FROM  civicrm_prevnext_cache pn
+      WHERE 
+      ((pn.entity_id1 = %1 AND pn.entity_id2 = %2) OR (pn.entity_id1 = %2 AND pn.entity_id2 = %1)) AND 
+      (cacheKey = %3 OR cacheKey = %4)";
+    $params = array(
+      1 => array($id1, 'Integer'),
+      2 => array($id2, 'Integer'),
+      3 => array("{$cacheKey}", 'String'),
+      4 => array("{$cacheKey}_conflicts", 'String'),
+    );
+    $pncFind = CRM_Core_DAO::executeQuery($sql, $params);
+
+    while ($pncFind->fetch()) {
+      $data = $pncFind->data;
+      if (!empty($data)) {
+        $data = unserialize($data);
+        $data['conflicts'] = implode(",", array_values($conflicts));
+
+        $pncUp = new CRM_Core_DAO_PrevNextCache();
+        $pncUp->id = $pncFind->id;
+        if ($pncUp->find(TRUE)) {
+          $pncUp->data     = serialize($data);
+          $pncUp->cacheKey = "{$cacheKey}_conflicts";
+          $pncUp->save();
+        }
+      }
+    }
+    return TRUE;
+  }
+
   /**
    * @param $cacheKey
    * @param NULL $join
@@ -168,14 +204,25 @@ WHERE  cacheKey     = %3 AND
    *
    * @return array
    */
-  public static function retrieve($cacheKey, $join = NULL, $where = NULL, $offset = 0, $rowCount = 0) {
+  public static function retrieve($cacheKey, $join = NULL, $where = NULL, $offset = 0, $rowCount = 0, $select = array()) {
+    $selectString = 'pn.*';
+    if (!empty($select)) {
+      $aliasArray = array();
+      foreach ($select as $column => $alias) {
+        $aliasArray[] = $column . ' as ' . $alias;
+      }
+      $selectString .= " , " . implode(' , ', $aliasArray);
+    }
     $query = "
-SELECT data
+SELECT SQL_CALC_FOUND_ROWS {$selectString}
 FROM   civicrm_prevnext_cache pn
        {$join}
-WHERE  cacheKey = %1
+WHERE  (pn.cacheKey = %1 OR pn.cacheKey = %2)
 ";
-    $params = array(1 => array($cacheKey, 'String'));
+    $params = array(
+      1 => array($cacheKey, 'String'),
+      2 => array("{$cacheKey}_conflicts", 'String'),
+    );
 
     if ($where) {
       $query .= " AND {$where}";
@@ -190,14 +237,31 @@ WHERE  cacheKey = %1
 
     $dao = CRM_Core_DAO::executeQuery($query, $params);
 
-    $main = array();
+    $main  = array();
+    $count = 0;
     while ($dao->fetch()) {
       if (self::is_serialized($dao->data)) {
-        $main[] = unserialize($dao->data);
+        $main[$count] = unserialize($dao->data);
       }
       else {
-        $main[] = $dao->data;
+        $main[$count] = $dao->data;
+      }
+
+      if (!empty($select)) {
+        $extraData = array();
+        foreach ($select as $dfield => $sfield) {
+          $extraData[$sfield]  = $dao->$sfield;
+        }
+        $main[$count] = array(
+          'prevnext_id' => $dao->id,
+          'is_selected' => $dao->is_selected,
+          'entity_id1'  => $dao->entity_id1,
+          'entity_id2'  => $dao->entity_id2,
+          'data'        => $main[$count],
+        );
+        $main[$count] = array_merge($main[$count], $extraData);
       }
+      $count++;
     }
 
     return $main;
@@ -235,13 +299,16 @@ WHERE  cacheKey = %1
     $query = "
 SELECT COUNT(*) FROM civicrm_prevnext_cache pn
        {$join}
-WHERE cacheKey $op %1
+WHERE (pn.cacheKey $op %1 OR pn.cacheKey $op %2)
 ";
     if ($where) {
       $query .= " AND {$where}";
     }
 
-    $params = array(1 => array($cacheKey, 'String'));
+    $params = array(
+      1 => array($cacheKey, 'String'),
+      2 => array("{$cacheKey}_conflicts", 'String'),
+    );
     return (int) CRM_Core_DAO::singleValueQuery($query, $params, TRUE, FALSE);
   }
 
index 932c1d81844525f18d6834de7e778193a5a95fe3..f39bdf09c6d3d536abd78ed11764fd60fe4c69cc 100644 (file)
@@ -740,6 +740,39 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     $this->assign('paymentProcessorID', $this->_paymentProcessorID);
   }
 
+  /**
+   * Handle pre approval for processors.
+   *
+   * This fits with the flow where a pre-approval is done and then confirmed in the next stage when confirm is hit.
+   *
+   * This function is shared between contribution & event forms & this is their common class.
+   *
+   * However, this should be seen as an in-progress refactor, the end goal being to also align the
+   * backoffice forms that action payments.
+   *
+   * @param array $params
+   */
+  protected function handlePreApproval(&$params) {
+    try {
+      $payment = Civi\Payment\System::singleton()->getByProcessor($this->_paymentProcessor);
+      $params['component'] = 'contribute';
+      $result = $payment->doPreApproval($params);
+      if (empty($result)) {
+        // This could happen, for example, when paypal looks at the button value & decides it is not paypal express.
+        return;
+      }
+    }
+    catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
+      CRM_Core_Error::displaySessionError($e->getMessage());
+      CRM_Utils_System::redirect($params['cancelURL']);
+    }
+
+    $this->set('pre_approval_parameters', $result['pre_approval_parameters']);
+    if (!empty($result['redirect_url'])) {
+      CRM_Utils_System::redirect($result['redirect_url']);
+    }
+  }
+
   /**
    * Setter function for options.
    *
index 39f5aa34a251b82564fd3e388497c08491c37592..8dde6f3fe3c596b0c4ab5030365766e39b71edaa 100644 (file)
@@ -82,6 +82,25 @@ abstract class CRM_Core_Payment {
 
   protected $_paymentProcessor;
 
+  /**
+   * Base url of the calling form.
+   *
+   * This is used for processors that need to return the browser back to the CiviCRM site.
+   *
+   * @var string
+   */
+  protected $baseReturnUrl;
+
+  /**
+   * Set Base return URL.
+   *
+   * @param string $url
+   *   Url of site to return browser to.
+   */
+  public function setBaseReturnUrl($url) {
+    $this->baseReturnUrl = $url;
+  }
+
   /**
    * Opportunity for the payment processor to override the entire form build.
    *
@@ -539,9 +558,14 @@ abstract class CRM_Core_Payment {
   /**
    * Get base url dependent on component.
    *
-   * @return string|void
+   * (or preferably set it using the setter function).
+   *
+   * @return string
    */
   protected function getBaseReturnUrl() {
+    if ($this->baseReturnUrl) {
+      return $this->baseReturnUrl;
+    }
     if ($this->_component == 'event') {
       $baseURL = 'civicrm/event/register';
     }
index a811b096758ec8723ad2d4ed4bf298c76d2bfe7c..e513bace19376ec2ec0350cca74a26455d4de2a2 100644 (file)
@@ -25,6 +25,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Payment\Exception\PaymentProcessorException;
+
 /**
  *
  * @package CRM
@@ -88,7 +90,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    * @return bool
    */
   protected function supportsPreApproval() {
-    if ($this->_processorName == ts('PayPal Express')) {
+    if ($this->_processorName == ts('PayPal Express') || $this->_processorName == ts('PayPal Pro')) {
       return TRUE;
     }
     return FALSE;
@@ -226,7 +228,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    * @return array
    */
   public function getPreApprovalDetails($storedDetails) {
-    return $this->getExpressCheckoutDetails($storedDetails['token']);
+    return empty($storedDetails['token']) ? array() : $this->getExpressCheckoutDetails($storedDetails['token']);
   }
 
   /**
@@ -427,7 +429,9 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    * @throws \Civi\Payment\Exception\PaymentProcessorException
    */
   public function doPayment(&$params, $component = 'contribute') {
-    if ($this->_paymentProcessor['payment_processor_type'] != 'PayPal_Express') {
+    if ($this->_paymentProcessor['payment_processor_type'] != 'PayPal_Express'
+    && (!empty($params['credit_card_number']) && empty($params['token']))
+    ) {
       return parent::doPayment($params, $component);
     }
     $this->_component = $component;
@@ -741,6 +745,9 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    *   - redirect_url (if set the browser will be redirected to this.
    */
   public function doPreApproval(&$params) {
+    if (!isset($params['button']) || !stristr($params['button'], 'express')) {
+      return array();
+    }
     $this->_component = $params['component'];
     $token = $this->setExpressCheckOut($params);
     return array(
index 37deeed72b3bada7bc87c8cf50bb949845e4a2b1..05f744c0ee4fe30ca84bda53aa533330722710d0 100644 (file)
@@ -771,6 +771,10 @@ class CRM_Core_Permission {
         $prefix . ts('merge duplicate contacts'),
         ts('Delete Contacts must also be granted in order for this to work.'),
       ),
+      'force merge duplicate contacts' => array(
+        $prefix . ts('force merge duplicate contacts'),
+        ts('Delete Contacts must also be granted in order for this to work.'),
+      ),
       'view debug output' => array(
         $prefix . ts('view debug output'),
         ts('View results of debug and backtrace'),
index e729f31ca3b7ad8ee64e08075b5093ce64cbda40..7f1ac88b2a4110640171edbd880689b1a2b25bfc 100644 (file)
    <page_callback>CRM_Contact_Page_AJAX::getDedupes</page_callback>
    <access_arguments>merge duplicate contacts</access_arguments>
 </item>
+<item>
+  <path>civicrm/contact/dedupemerge</path>
+   <title>Batch Merge Duplicate Contacts</title>
+   <page_callback>CRM_Contact_Page_DedupeMerge</page_callback>
+   <access_arguments>merge duplicate contacts</access_arguments>
+</item>
 <item>
    <path>civicrm/dedupe/exception</path>
    <title>Dedupe Exceptions</title>
      <page_callback>CRM_Contact_Page_AJAX::selectUnselectContacts</page_callback>
      <access_arguments>access CiviCRM</access_arguments>
 </item>
+<item>
+     <path>civicrm/ajax/toggleDedupeSelect</path>
+     <page_callback>CRM_Contact_Page_AJAX::toggleDedupeSelect</page_callback>
+     <access_arguments>merge duplicate contacts</access_arguments>
+</item>
+<item>
+     <path>civicrm/ajax/flipDupePairs</path>
+     <page_callback>CRM_Contact_Page_AJAX::flipDupePairs</page_callback>
+     <access_arguments>merge duplicate contacts</access_arguments>
+</item>
 <item>
   <path>civicrm/activity/sms/add</path>
   <path_arguments>action=add</path_arguments>
index da5f6544e640d11ed93631b2477482e3a4e3ae2a..6d8d46214be0410ff5184087f9cce05488fbf548 100644 (file)
@@ -50,14 +50,13 @@ class CRM_Custom_Form_CustomDataByType extends CRM_Core_Form {
     $this->assign('cdType', FALSE);
     $this->assign('cgCount', $this->_groupCount);
 
-    // Carry qf key, since this form is not inheriting core form (unclear is still the case).
-    if ($qfKey = CRM_Utils_Request::retrieve('qfKey', 'String')) {
-      $this->assign('qfKey', $qfKey);
-    }
     if (!is_array($this->_subType) && strstr($this->_subType, CRM_Core_DAO::VALUE_SEPARATOR)) {
       $this->_subType = str_replace(CRM_Core_DAO::VALUE_SEPARATOR, ',', trim($this->_subType, CRM_Core_DAO::VALUE_SEPARATOR));
     }
     CRM_Custom_Form_CustomData::setGroupTree($this, $this->_subType, NULL, $this->_groupID);
+
+    $this->assign('suppressForm', TRUE);
+    $this->controller->_generateQFKey = FALSE;
   }
 
   /**
index 36badd2d6c416602501f19989210763bc70e5d9c..e7fb33915f560884df4c159d370dd49abc0e6934 100644 (file)
@@ -565,11 +565,12 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    *                              Does a force merge otherwise.
    * @param bool $autoFlip to let api decide which contact to retain and which to delete.
    *   Wether to let api decide which contact to retain and which to delete.
-   * @param bool $redirectForPerformance
+   * @param int $batchLimit number of merges to carry out in one batch.
+   * @param int $isSelected if records with is_selected column needs to be processed.
    *
    * @return array|bool
    */
-  public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $redirectForPerformance = FALSE) {
+  public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $batchLimit = 1, $isSelected = 2) {
     $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
     $cacheKeyString = "merge {$contactType}";
     $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
@@ -577,11 +578,16 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
                                                              pn.entity_id2 = de.contact_id2 )";
 
-    $limit = $redirectForPerformance ? 75 : 1;
-    $where = "de.id IS NULL LIMIT {$limit}";
+    $where = "de.id IS NULL";
+    if ($isSelected === 0 || $isSelected === 1) {
+      $where .= " AND pn.is_selected = {$isSelected}";
+    }// else consider all dupe pairs
+    $where .= " LIMIT {$batchLimit}";
+
+    $redirectForPerformance = ($batchLimit > 1) ? TRUE : FALSE;
 
     $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where);
-    if (empty($dupePairs) && !$redirectForPerformance) {
+    if (empty($dupePairs) && !$redirectForPerformance && $isSelected == 2) {
       // If we haven't found any dupes, probably cache is empty.
       // Try filling cache and give another try.
       CRM_Core_BAO_PrevNextCache::refillCache($rgid, $gid, $cacheKeyString);
@@ -596,6 +602,65 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     return CRM_Dedupe_Merger::merge($dupePairs, $cacheParams, $mode, $autoFlip, $redirectForPerformance);
   }
 
+  public static function updateMergeStats($cacheKeyString, $result = array()) {
+    // gather latest stats
+    $merged  = count($result['merged']);
+    $skipped = count($result['skipped']);
+
+    if ($merged <= 0 && $skipped <= 0) {
+      return;
+    }
+
+    // get previous stats
+    $previousStats = CRM_Core_BAO_PrevNextCache::retrieve("{$cacheKeyString}_stats");
+    if (!empty($previousStats)) {
+      if ($previousStats[0]['merged']) {
+        $merged = $merged + $previousStats[0]['merged'];
+      }
+      if ($previousStats[0]['skipped']) {
+        $skipped = $skipped + $previousStats[0]['skipped'];
+      }
+    }
+
+    // delete old stats
+    CRM_Dedupe_Merger::resetMergeStats($cacheKeyString);
+
+    // store the updated stats
+    $data = array(
+      'merged'  => $merged,
+      'skipped' => $skipped,
+    );
+    $data = CRM_Core_DAO::escapeString(serialize($data));
+
+    $values   = array();
+    $values[] = " ( 'civicrm_contact', 0, 0, '{$cacheKeyString}_stats', '$data' ) ";
+    CRM_Core_BAO_PrevNextCache::setItem($values);
+  }
+
+  public static function resetMergeStats($cacheKeyString) {
+    return CRM_Core_BAO_PrevNextCache::deleteItem(NULL, "{$cacheKeyString}_stats");
+  }
+
+  public static function getMergeStats($cacheKeyString) {
+    $stats = CRM_Core_BAO_PrevNextCache::retrieve("{$cacheKeyString}_stats");
+    if (!empty($stats)) {
+      $stats = $stats[0];
+    }
+    return $stats;
+  }
+
+  public static function getMergeStatsMsg($cacheKeyString) {
+    $msg   = '';
+    $stats = CRM_Dedupe_Merger::getMergeStats($cacheKeyString);
+    if (!empty($stats['merged'])) {
+      $msg = "{$stats['merged']} " . ts(' Contact(s) were merged. ');
+    }
+    if (!empty($stats['skipped'])) {
+      $msg .= $stats['skipped'] . ts(' Contact(s) were skipped.');
+    }
+    return $msg;
+  }
+
   /**
    * Merge given set of contacts. Performs core operation.
    *
@@ -656,7 +721,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         $migrationInfo['rows'] = &$rowsElementsAndInfo['rows'];
 
         // go ahead with merge if there is no conflict
-        if (!CRM_Dedupe_Merger::skipMerge($mainId, $otherId, $migrationInfo, $mode)) {
+        $conflicts = array();
+        if (!CRM_Dedupe_Merger::skipMerge($mainId, $otherId, $migrationInfo, $mode, $conflicts)) {
           CRM_Dedupe_Merger::moveAllBelongings($mainId, $otherId, $migrationInfo);
           $resultStats['merged'][] = array('main_id' => $mainId, 'other_id' => $otherId);
         }
@@ -664,10 +730,18 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
           $resultStats['skipped'][] = array('main_id' => $mainId, 'other_id' => $otherId);
         }
 
-        // delete entry from PrevNextCache table so we don't consider the pair next time
-        // pair may have been flipped, so make sure we delete using both orders
-        CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString);
-        CRM_Core_BAO_PrevNextCache::deletePair($otherId, $mainId, $cacheKeyString);
+        // store  any conflicts
+        if (!empty($conflicts)) {
+          foreach ($conflicts as $key => $dnc) {
+            $conflicts[$key] = "{$migrationInfo['rows'][$key]['title']}: '{$migrationInfo['rows'][$key]['main']}' vs. '{$migrationInfo['rows'][$key]['other']}'";
+          }
+          CRM_Core_BAO_PrevNextCache::markConflict($mainId, $otherId, $cacheKeyString, $conflicts);
+        }
+        else {
+          // delete entry from PrevNextCache table so we don't consider the pair next time
+          // pair may have been flipped, so make sure we delete using both orders
+          CRM_Core_BAO_PrevNextCache::deletePair($mainId, $otherId, $cacheKeyString, TRUE);
+        }
 
         CRM_Core_DAO::freeResult();
         unset($rowsElementsAndInfo, $migrationInfo);
@@ -685,6 +759,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         unset($dupePairs);
       }
     }
+
+    CRM_Dedupe_Merger::updateMergeStats($cacheKeyString, $resultStats);
     return $resultStats;
   }
 
@@ -705,8 +781,8 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    *
    * @return bool
    */
-  public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe') {
-    $conflicts = array();
+  public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe', &$conflicts = array()) {
+    //$conflicts = array();
     $migrationData = array(
       'old_migration_info' => $migrationInfo,
       'mode' => $mode,
@@ -727,15 +803,10 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
         // particular field or not
         if (!empty($migrationInfo['rows'][$key]['main'])) {
           // if main also has a value its a conflict
-          if ($mode == 'safe') {
-            // note it down & lets wait for response from the hook.
-            // For no response skip this merge
-            $conflicts[$key] = NULL;
-          }
-          elseif ($mode == 'aggressive') {
-            // let the main-field be overwritten
-            continue;
-          }
+
+          // note it down & lets wait for response from the hook.
+          // For no response $mode will decide if to skip this merge
+          $conflicts[$key] = NULL;
         }
       }
       elseif (substr($key, 0, 14) == 'move_location_' and $val != NULL) {
@@ -760,16 +831,11 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
               // try insert address at new available loc-type
               $migrationInfo['location'][$fieldName][$fieldCount]['locTypeId'] = $newTypeId;
             }
-            elseif ($mode == 'safe') {
+            else {
               // note it down & lets wait for response from the hook.
-              // For no response skip this merge
+              // For no response $mode will decide if to skip this merge
               $conflicts[$key] = NULL;
             }
-            elseif ($mode == 'aggressive') {
-              // let the loc-type-id be same as that of other-contact & go ahead
-              // with merge assuming aggressive mode
-              continue;
-            }
           }
         }
         elseif ($migrationInfo['rows'][$key]['main'] == $migrationInfo['rows'][$key]['other']) {
@@ -785,13 +851,16 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
     // merge happens with new values filled in here. For a particular field / row not to be merged
     // field should be unset from fields_in_conflict.
     $migrationData['fields_in_conflict'] = $conflicts;
+    $migrationData['merge_mode']         = $mode;
     CRM_Utils_Hook::merge('batch', $migrationData, $mainId, $otherId);
     $conflicts = $migrationData['fields_in_conflict'];
+    // allow hook to override / manipulate migrationInfo as well
+    $migrationInfo = $migrationData['old_migration_info'];
 
     if (!empty($conflicts)) {
       foreach ($conflicts as $key => $val) {
         if ($val === NULL and $mode == 'safe') {
-          // un-resolved conflicts still present. Lets skip this merge.
+          // un-resolved conflicts still present. Lets skip this merge after saving the conflict / reason.
           return TRUE;
         }
         else {
@@ -799,6 +868,10 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
           $migrationInfo[$key] = $val;
         }
       }
+      // if there are conflicts and mode is aggressive, allow hooks to decide if to skip merges
+      if (array_key_exists('skip_merge', $migrationData)) {
+        return (bool) $migrationData['skip_merge'];
+      }
     }
     return FALSE;
   }
index eaf024563d9d60e1eceeb8545df4a03f776328d8..bcafb74ea1c2bb590630bd154bdfec239e3afa38 100644 (file)
@@ -582,6 +582,10 @@ class CRM_Event_Form_Registration_Confirm extends CRM_Event_Form_Registration {
         if (is_array($this->_paymentProcessor)) {
           $payment = $this->_paymentProcessor['object'];
         }
+        if (!empty($this->_paymentProcessor) &&  $this->_paymentProcessor['object']->supports('preApproval')) {
+          $preApprovalParams = $this->_paymentProcessor['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
+          $value = array_merge($value, $preApprovalParams);
+        }
         $result = NULL;
 
         if (!empty($value['is_pay_later']) ||
index 7362870b6fb2e596968ca489928f7830cbfff3f5..00367dada86328d6e8abc3724415ebdee9c63d15 100644 (file)
@@ -912,7 +912,11 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
   }
 
   /**
-   * Check if profiles are complete when event registration occurs(CRM-9587)
+   * Check if profiles are complete when event registration occurs(CRM-9587).
+   *
+   * @param array $fields
+   * @param array $errors
+   * @param int $eventId
    */
   public static function checkProfileComplete($fields, &$errors, $eventId) {
     $email = '';
@@ -1066,6 +1070,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
 
       if (is_array($this->_paymentProcessor)) {
         $payment = $this->_paymentProcessor['object'];
+        $payment->setBaseReturnUrl('civicrm/event/register');
       }
       // default mode is direct
       $this->set('contributeMode', 'direct');
@@ -1091,6 +1096,8 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
         $params['invoiceID'] = $invoiceID;
       }
       $this->_params = $this->get('params');
+      // Set the button so we know what
+      $params['button'] = $this->controller->getButtonName();
       if (!empty($this->_params) && is_array($this->_params)) {
         $this->_params[0] = $params;
       }
@@ -1099,77 +1106,66 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
         $this->_params[] = $params;
       }
       $this->set('params', $this->_params);
-
       if ($this->_paymentProcessor &&
-        $this->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON
+        $this->_paymentProcessor['object']->supports('preApproval')
+        && !$this->_allowWaitlist &&
+        !$this->_requireApproval
       ) {
-        //get the button name
-        $buttonName = $this->controller->getButtonName();
-        if (in_array($buttonName,
-            array(
-              $this->_expressButtonName,
-              $this->_expressButtonName . '_x',
-              $this->_expressButtonName . '_y',
-            )
-          ) && empty($params['is_pay_later']) &&
-          !$this->_allowWaitlist &&
-          !$this->_requireApproval
-        ) {
-          $this->set('contributeMode', 'express');
 
-          // Send Event Name & Id in Params
-          $params['eventName'] = $this->_values['event']['title'];
-          $params['eventId'] = $this->_values['event']['id'];
-
-          $params['cancelURL'] = CRM_Utils_System::url('civicrm/event/register',
-            "_qf_Register_display=1&qfKey={$this->controller->_key}",
-            TRUE, NULL, FALSE
-          );
-          if (CRM_Utils_Array::value('additional_participants', $params, FALSE)) {
-            $urlArgs = "_qf_Participant_1_display=1&rfp=1&qfKey={$this->controller->_key}";
-          }
-          else {
-            $urlArgs = "_qf_Confirm_display=1&rfp=1&qfKey={$this->controller->_key}";
-          }
-          $params['returnURL'] = CRM_Utils_System::url('civicrm/event/register',
-            $urlArgs,
-            TRUE, NULL, FALSE
-          );
-          $params['invoiceID'] = $invoiceID;
+        $this->handlePreApproval($params);
 
-          $params['component'] = 'event';
-          $token = $payment->doPreApproval($params);
-          if (is_a($token, 'CRM_Core_Error')) {
-            CRM_Core_Error::displaySessionError($token);
-            CRM_Utils_System::redirect($params['cancelURL']);
-          }
+        $this->set('contributeMode', 'express');
 
-          $this->set('token', $token);
+        // Send Event Name & Id in Params
+        $params['eventName'] = $this->_values['event']['title'];
+        $params['eventId'] = $this->_values['event']['id'];
 
-          $paymentURL = $this->_paymentProcessor['url_site'] . "/cgi-bin/webscr?cmd=_express-checkout&token=$token";
+        $params['cancelURL'] = CRM_Utils_System::url('civicrm/event/register',
+          "_qf_Register_display=1&qfKey={$this->controller->_key}",
+          TRUE, NULL, FALSE
+        );
+        if (CRM_Utils_Array::value('additional_participants', $params, FALSE)) {
+          $urlArgs = "_qf_Participant_1_display=1&rfp=1&qfKey={$this->controller->_key}";
+        }
+        else {
+          $urlArgs = "_qf_Confirm_display=1&rfp=1&qfKey={$this->controller->_key}";
+        }
+        $params['returnURL'] = CRM_Utils_System::url('civicrm/event/register',
+          $urlArgs,
+          TRUE, NULL, FALSE
+        );
+        $params['invoiceID'] = $invoiceID;
 
-          CRM_Utils_System::redirect($paymentURL);
+        $params['component'] = 'event';
+        $token = $payment->doPreApproval($params);
+        if (is_a($token, 'CRM_Core_Error')) {
+          CRM_Core_Error::displaySessionError($token);
+          CRM_Utils_System::redirect($params['cancelURL']);
         }
+
+        $this->set('token', $token);
+
+        $paymentURL = $this->_paymentProcessor['url_site'] . "/cgi-bin/webscr?cmd=_express-checkout&token=$token";
+        CRM_Utils_System::redirect($paymentURL);
       }
       elseif ($this->_paymentProcessor &&
         $this->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_NOTIFY
       ) {
         $this->set('contributeMode', 'notify');
       }
-    }
-    else {
-      $session = CRM_Core_Session::singleton();
-      $params['description'] = ts('Online Event Registration') . ' ' . $this->_values['event']['title'];
+      else {
+        $params['description'] = ts('Online Event Registration') . ' ' . $this->_values['event']['title'];
 
-      $this->_params = array();
-      $this->_params[] = $params;
-      $this->set('params', $this->_params);
+        $this->_params = array();
+        $this->_params[] = $params;
+        $this->set('params', $this->_params);
 
-      if (
-        empty($params['additional_participants'])
-        && !$this->_values['event']['is_confirm_enabled'] // CRM-11182 - Optional confirmation screen
-      ) {
-        self::processRegistration($this->_params);
+        if (
+          empty($params['additional_participants'])
+          && !$this->_values['event']['is_confirm_enabled'] // CRM-11182 - Optional confirmation screen
+        ) {
+          self::processRegistration($this->_params);
+        }
       }
     }
 
@@ -1186,8 +1182,6 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
    * @param array $params
    *   Form values.
    * @param int $contactID
-   *
-   * @return void
    */
   public function processRegistration($params, $contactID = NULL) {
     $session = CRM_Core_Session::singleton();
@@ -1313,7 +1307,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
         $primaryContactId, $isTest, TRUE
       );
 
-      //lets carry all paticipant params w/ values.
+      //lets carry all participant params w/ values.
       foreach ($additionalIDs as $participantID => $contactId) {
         $participantNum = NULL;
         if ($participantID == $registerByID) {
@@ -1366,7 +1360,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
    *
    * @param array $fields
    *   The input form values(anonymous user).
-   * @param array $self
+   * @param CRM_Event_Form_Registration_Register $self
    *   Event data.
    * @param bool $isAdditional
    *   Treat isAdditional participants a bit differently.
index 212f7593af3779c8aa41b33d85f36db1ff6f1e1a..c6f8545be549dcf0dd0a69cab7a759be4bdc4c66 100644 (file)
@@ -2354,6 +2354,7 @@ WHERE      civicrm_membership.is_test = 0";
     $contributionSoftParams = CRM_Utils_Array::value('soft_credit', $params);
     $recordContribution = array(
       'contact_id',
+      'fee_amount',
       'total_amount',
       'receive_date',
       'financial_type_id',
index 627fd5c64cbb17125b173b25d4d17f2edcf95890..8d7a2e1f33d5278ae90f1215399dcb9bf7e69be4 100644 (file)
@@ -84,6 +84,13 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment {
    */
   public $_priceSet;
 
+  /**
+   * Values submitted to the form, processed along the way.
+   *
+   * @var array
+   */
+  protected $_params = array();
+
   public function preProcess() {
     // Check for edit permission.
     if (!CRM_Core_Permission::checkActionPermission('CiviMember', $this->_action)) {
@@ -429,4 +436,34 @@ class CRM_Member_Form extends CRM_Contribute_Form_AbstractEditPayment {
     return $formValues;
   }
 
+  /**
+   * Assign billing name to the template.
+   */
+  protected function assignBillingName() {
+    $name = '';
+    if (!empty($this->_params['billing_first_name'])) {
+      $name = $this->_params['billing_first_name'];
+    }
+
+    if (!empty($this->_params['billing_middle_name'])) {
+      $name .= " {$this->_params['billing_middle_name']}";
+    }
+
+    if (!empty($this->_params['billing_last_name'])) {
+      $name .= " {$this->_params['billing_last_name']}";
+    }
+    $this->assign('billingName', $name);
+  }
+
+  /**
+   * Wrapper function for unit tests.
+   *
+   * @param array $formValues
+   */
+  public function testSubmit($formValues) {
+    $this->_memType = $formValues['membership_type_id'][1];
+    $this->_params = $formValues;
+    $this->submit();
+  }
+
 }
index 65a439deaccb8bc8ea57abf9cc4c11bf7746cb28..20ee9aa2b932db6142d0dcd3768d77e019e618d0 100644 (file)
@@ -370,19 +370,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       $this->assign('is_pay_later', TRUE);
     }
     if ($this->_mode) {
-      // set default country from config if no country set
-      $config = CRM_Core_Config::singleton();
-      if (empty($defaults["billing_country_id-{$this->_bltID}"])) {
-        $defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry;
-      }
-
-      if (empty($defaults["billing_state_province_id-{$this->_bltID}"])) {
-        $defaults["billing_state_province_id-{$this->_bltID}"] = $config->defaultContactStateProvince;
-      }
-
-      $billingDefaults = $this->getProfileDefaults('Billing', $this->_contactID);
-      $defaults = array_merge($defaults, $billingDefaults);
-
+      $defaults = $this->getBillingDefaults($defaults);
       // hack to simplify credit card entry for testing
       // $defaults['credit_card_type']     = 'Visa';
       // $defaults['credit_card_number']   = '4807731747657838';
@@ -951,7 +939,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
     // get the submitted form values.
     $this->_params = $this->controller->exportValues($this->_name);
 
-    $this->submit($this->_params);
+    $this->submit();
 
     $this->setUserContext();
   }
@@ -1003,21 +991,6 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
     $form->assign('customValues', $customValues);
 
     if ($form->_mode) {
-      $name = '';
-      if (!empty($form->_params['billing_first_name'])) {
-        $name = $form->_params['billing_first_name'];
-      }
-
-      if (!empty($form->_params['billing_middle_name'])) {
-        $name .= " {$form->_params['billing_middle_name']}";
-      }
-
-      if (!empty($form->_params['billing_last_name'])) {
-        $name .= " {$form->_params['billing_last_name']}";
-      }
-
-      $form->assign('billingName', $name);
-
       // assign the address formatted up for display
       $addressParts = array(
         "street_address-{$form->_bltID}",
@@ -1130,24 +1103,23 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
    *
    * This is also accessed by unit tests.
    *
-   * @param array $formValues
-   *
    * @return array
    */
-  public function submit($formValues) {
+  public function submit() {
     $isTest = ($this->_mode == 'test') ? 1 : 0;
-
+    $this->storeContactFields($this->_params);
+    $formValues = $this->_params;
     $joinDate = $startDate = $endDate = NULL;
     $membershipTypes = $membership = $calcDate = array();
     $membershipType = NULL;
 
     $mailSend = FALSE;
     $formValues = $this->setPriceSetParameters($formValues);
-
     $params = $softParams = $ids = array();
 
     $allMemberStatus = CRM_Member_PseudoConstant::membershipStatus();
     $allContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus();
+    $this->processBillingAddress();
 
     if ($this->_id) {
       $ids['membership'] = $params['id'] = $this->_id;
@@ -1234,8 +1206,6 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       }
     }
 
-    $this->storeContactFields($formValues);
-
     $params['contact_id'] = $this->_contactID;
 
     $fields = array(
@@ -1389,47 +1359,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
 
       //get the payment processor id as per mode.
       $params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor['id'];
-
-      $now = date('YmdHis');
-      $fields = array();
-
-      // set email for primary location.
-      $fields['email-Primary'] = 1;
-      $formValues['email-5'] = $formValues['email-Primary'] = $this->_memberEmail;
-      $params['register_date'] = $now;
-
-      // now set the values for the billing location.
-      foreach ($this->_fields as $name => $dontCare) {
-        $fields[$name] = 1;
-      }
-
-      // also add location name to the array
-      $formValues["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_middle_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_last_name', $formValues);
-
-      $formValues["address_name-{$this->_bltID}"] = trim($formValues["address_name-{$this->_bltID}"]);
-
-      $fields["address_name-{$this->_bltID}"] = 1;
-      //ensure we don't over-write the payer's email with the member's email
-      if ($this->_contributorContactID == $this->_contactID) {
-        $fields["email-{$this->_bltID}"] = 1;
-      }
-
-      $nameFields = array('first_name', 'middle_name', 'last_name');
-
-      foreach ($nameFields as $name) {
-        $fields[$name] = 1;
-        if (array_key_exists("billing_$name", $formValues)) {
-          $formValues[$name] = $formValues["billing_{$name}"];
-          $formValues['preserveDBName'] = TRUE;
-        }
-      }
-      if ($this->_contributorContactID == $this->_contactID) {
-        //see CRM-12869 for discussion of why we don't do this for separate payee payments
-        CRM_Contact_BAO_Contact::createProfileContact($formValues, $fields,
-          $this->_contributorContactID, NULL, NULL,
-          CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactID, 'contact_type')
-        );
-      }
+      $params['register_date'] = date('YmdHis');
 
       // add all the additional payment params we need
       $formValues["state_province-{$this->_bltID}"] = $formValues["billing_state_province-{$this->_bltID}"]
@@ -1549,6 +1479,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
           }
         }
       }
+      $now = date('YmdHis');
       $params['receive_date'] = $now;
       $params['invoice_id'] = $formValues['invoiceID'];
       $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)',
@@ -1588,6 +1519,9 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
         if (!empty($softParams) && empty($paymentParams['is_recur'])) {
           $membershipParams['soft_credit'] = $softParams;
         }
+        if (isset($result['fee_amount'])) {
+          $membershipParams['fee_amount'] = $result['fee_amount'];
+        }
         // This is required to trigger the recording of the membership contribution in the
         // CRM_Member_BAO_Membership::Create function.
         // @todo stop setting this & 'teach' the create function to respond to something
@@ -1763,6 +1697,7 @@ class CRM_Member_Form_Membership extends CRM_Member_Form {
       // although at some point we should switch in the templates.
       $formValues['receipt_text_signup'] = $formValues['receipt_text'];
       // send email receipt
+      $this->assignBillingName();
       $mailSend = self::emailReceipt($this, $formValues, $membership);
     }
 
index 224a3df3e0293eb5c79f1909957616fd26dc2e4c..40d4172f155c0f1c774083d28853c27ff89c6e7b 100644 (file)
@@ -29,8 +29,6 @@
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2015
- * $Id$
- *
  */
 
 /**
@@ -234,19 +232,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
     $this->assign('member_is_test', CRM_Utils_Array::value('member_is_test', $defaults));
 
     if ($this->_mode) {
-      // set default country from config if no country set
-      $config = CRM_Core_Config::singleton();
-      if (empty($defaults["billing_country_id-{$this->_bltID}"])) {
-        $defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry;
-      }
-
-      if (empty($defaults["billing_state_province_id-{$this->_bltID}"])) {
-        $defaults["billing_state_province_id-{$this->_bltID}"] = $config->defaultContactStateProvince;
-      }
-
-      $billingDefaults = $this->getProfileDefaults('Billing', $this->_contactID);
-      $defaults = array_merge($defaults, $billingDefaults);
-
+      $defaults = $this->getBillingDefaults($defaults);
     }
     return $defaults;
   }
@@ -462,9 +448,6 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
 
   /**
    * Process the renewal form.
-   *
-   *
-   * @return void
    */
   public function postProcess() {
     // get the submitted form values.
@@ -510,7 +493,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
     $now = CRM_Utils_Date::getToday(NULL, 'YmdHis');
     $this->convertDateFieldsToMySQL($this->_params);
     $this->assign('receive_date', $this->_params['receive_date']);
-    $this->processBillingAddress($now);
+    $this->processBillingAddress();
     list($userName) = CRM_Contact_BAO_Contact_Location::getEmailDetails(CRM_Core_Session::singleton()->get('userID'));
     $this->_params['total_amount'] = CRM_Utils_Array::value('total_amount', $this->_params,
       CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $this->_memType, 'minimum_fee')
@@ -642,7 +625,7 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
           $li['financial_type_id'] = $submittedFinancialType;
         }
       }
-      $this->_params['total_amount'] = CRM_Utils_Array::value('amount', $this->_params);
+
       if (!empty($lineItem)) {
         $this->_params['lineItems'] = $lineItem;
         $this->_params['processPriceSet'] = TRUE;
@@ -749,62 +732,23 @@ class CRM_Member_Form_MembershipRenewal extends CRM_Member_Form {
   }
 
   /**
-   * Wrapper function for unit tests.
+   * @param $defaults
    *
-   * @param array $formValues
+   * @return array
    */
-  public function testSubmit($formValues) {
-    $this->_memType = $formValues['membership_type_id'][1];
-    $this->_params = $formValues;
-    $this->submit($formValues);
-  }
-
-  protected function assignBillingName() {
-    $name = '';
-    if (!empty($this->_params['billing_first_name'])) {
-      $name = $this->_params['billing_first_name'];
+  protected function getBillingDefaults($defaults) {
+    // set default country from config if no country set
+    $config = CRM_Core_Config::singleton();
+    if (empty($defaults["billing_country_id-{$this->_bltID}"])) {
+      $defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry;
     }
 
-    if (!empty($this->_params['billing_middle_name'])) {
-      $name .= " {$this->_params['billing_middle_name']}";
+    if (empty($defaults["billing_state_province_id-{$this->_bltID}"])) {
+      $defaults["billing_state_province_id-{$this->_bltID}"] = $config->defaultContactStateProvince;
     }
 
-    if (!empty($this->_params['billing_last_name'])) {
-      $name .= " {$this->_params['billing_last_name']}";
-    }
-    $this->assign('billingName', $name);
-  }
-
-  /**
-   * Add the billing address to the contact who paid.
-   */
-  protected function processBillingAddress() {
-    $fields = array();
-
-    // set email for primary location.
-    $fields['email-Primary'] = 1;
-    $this->_params['email-5'] = $this->_params['email-Primary'] = $this->_contributorEmail;
-
-    // also add location name to the array
-    $this->_params["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $this->_params) . ' ' . CRM_Utils_Array::value('billing_middle_name', $this->_params) . ' ' . CRM_Utils_Array::value('billing_last_name', $this->_params);
-
-    $this->_params["address_name-{$this->_bltID}"] = trim($this->_params["address_name-{$this->_bltID}"]);
-
-    $fields["address_name-{$this->_bltID}"] = 1;
-    $fields["email-{$this->_bltID}"] = 1;
-
-    list($hasBillingField, $addressParams) = CRM_Contribute_BAO_Contribution::getPaymentProcessorReadyAddressParams($this->_params, $this->_bltID);
-
-    $addressParams['preserveDBName'] = TRUE;
-    if ($hasBillingField) {
-      $addressParams = array_merge($this->_params, $addressParams);
-      //here we are setting up the billing contact - if different from the member they are already created
-      // but they will get billing details assigned
-      CRM_Contact_BAO_Contact::createProfileContact($addressParams, $fields,
-        $this->_contributorContactID, NULL, NULL,
-        CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactID, 'contact_type')
-      );
-    }
+    $billingDefaults = $this->getProfileDefaults('Billing', $this->_contactID);
+    return array_merge($defaults, $billingDefaults);
   }
 
 }
index 6a777f5b0521f9717484facc1f3e26173ae08c00..9ed0b08e32a22625347bee34f72adf40b0e876bd 100644 (file)
 *}
 {if $action eq 2 || $action eq 16}
 <div class="form-item">
-  <table class='pagerDisplay'>
+  <div class="crm-accordion-wrapper crm-search_filters-accordion">
+    <div class="crm-accordion-header">
+    {ts}Filter Contacts{/ts}</a>
+    </div><!-- /.crm-accordion-header -->
+    <div class="crm-accordion-body">
+      <table class="no-border form-layout-compressed" id="searchOptions" style="width:100%;">
+        <tr>
+          <td class="crm-contact-form-block-contact1">
+            <label for="contact1">{ts}Contact 1{/ts}</label><br />
+            <input type="text" placeholder="Search Contact1" search-column="2" />
+          </td>
+          <td class="crm-contact-form-block-contact2">
+            <label for="contact2">{ts}Contact 2{/ts}</label><br />
+            <input type="text" placeholder="Search Contact2" search-column="4" />
+          </td>
+          <td class="crm-contact-form-block-email1">
+            <label for="email1">{ts}Email 1{/ts}</label><br />
+            <input type="text" placeholder="Search Email1" search-column="5" />
+          </td>
+          <td class="crm-contact-form-block-email2">
+            <label for="email2">{ts}Email 2{/ts}</label><br />
+            <input type="text" placeholder="Search Email2" search-column="6" />
+          </td>
+        </tr>
+        <tr>
+          <td class="crm-contact-form-block-street-address1">
+            <label for="street-adddress1">{ts}Street Address 1{/ts}</label><br />
+            <input type="text" placeholder="Search Street Address1" search-column="7" />
+          </td>
+          <td class="crm-contact-form-block-street-address2">
+            <label for="street-adddress2">{ts}Street Address 2{/ts}</label><br />
+            <input type="text" placeholder="Search Street Address2" search-column="8" />
+          </td>
+          <td class="crm-contact-form-block-postcode1">
+            <label for="postcode1">{ts}Postcode 1{/ts}</label><br />
+            <input type="text" placeholder="Search Postcode1" search-column="9" />
+          </td>
+          <td class="crm-contact-form-block-postcode2">
+            <label for="postcode2">{ts}Postcode 2{/ts}</label><br />
+            <input type="text" placeholder="Search Postcode2" search-column="10" />
+          </td>
+        </tr>
+      </table>
+    </div><!-- /.crm-accordion-body -->
+  </div><!-- /.crm-accordion-wrapper -->
+  <div>
+    Show / Hide columns:
+    <input type='checkbox' id ='steet-address' class='toggle-vis' data-column-main="7" data-column-dupe="8" >  
+        <label for="steet-address">{ts}Street Address{/ts}&nbsp;</label>
+    <input type='checkbox' id ='post-code' class='toggle-vis' data-column-main="9" data-column-dupe="10" >  
+        <label for="post-code">{ts}Post Code{/ts}&nbsp;</label>
+    <input type='checkbox' id ='conflicts' class='toggle-vis' data-column-main="11"  >  
+        <label for="conflicts">{ts}Conflicts{/ts}&nbsp; </label>
+    <input type='checkbox' id ='threshold' class='toggle-vis' data-column-main="12"  >  
+        <label for="threshold">{ts}Threshold{/ts}&nbsp;</label>
+  </div><br/>
+  <span id="dupePairs_length_selection">
+    <input type='checkbox' id ='crm-dedupe-display-selection' name="display-selection">
+    <label for="display-selection">{ts}Within Selections{/ts}&nbsp;</label>  
+  </span>
+
+  <table id="dupePairs"
+    class="nestedActivitySelector crm-ajax-table"
+    cellspacing="0"
+    width="100%"
+    data-page-length="10",
+    data-searching='true',
+    data-dom='flrtip',
+    data-order='[]',
+    data-column-defs='{literal}[{"targets": [0,1,3,13], "orderable":false}, {"targets": [7,8,9,10,11,12], "visible":false}]{/literal}'>
     <thead>
-    <tr class="columnheader"><th id="nosort">{ts}Contact{/ts} 1</th><th id="nosort">{ts}Contact{/ts} 2 ({ts}Duplicate{/ts})</th><th id="nosort">{ts}Threshold{/ts}</th><th id="nosort">&nbsp;</th></tr>
+      <tr class="columnheader"> 
+        <th data-data="is_selected_input" class="crm-dedupe-selection"><input type="checkbox" value="0" name="pnid_all" class="crm-dedupe-select-all"></th>
+        <th data-data="src_image"    class="crm-empty">&nbsp;</th>
+        <th data-data="src"          class="crm-contact">{ts}Contact{/ts} 1</th>
+        <th data-data="dst_image"    class="crm-empty">&nbsp;</th>
+        <th data-data="dst"          class="crm-contact-duplicate">{ts}Contact{/ts} 2 ({ts}Duplicate{/ts})</th>
+        <th data-data="src_email"    class="crm-contact">{ts}Email{/ts} 1</th>
+        <th data-data="dst_email"    class="crm-contact-duplicate">{ts}Email{/ts} 2 ({ts}Duplicate{/ts})</th>
+        <th data-data="src_street"   class="crm-contact">{ts}Street Address{/ts} 1</th>
+        <th data-data="dst_street"   class="crm-contact-duplicate">{ts}Street Address{/ts} 2 ({ts}Duplicate{/ts})</th>
+        <th data-data="src_postcode" class="crm-contact">{ts}Postcode{/ts} 1</th>
+        <th data-data="dst_postcode" class="crm-contact-duplicate">{ts}Postcode{/ts} 2 ({ts}Duplicate{/ts})</th>
+        <th data-data="conflicts"    class="crm-contact-conflicts">{ts}Conflicts{/ts}</th>
+        <th data-data="weight"       class="crm-threshold">{ts}Threshold{/ts}</th>
+        <th data-data="actions"      class="crm-empty">&nbsp;</th>
+      </tr>
     </thead>
+    <tbody>
+    </tbody>
   </table>
-  {include file="CRM/common/jsortable.tpl" sourceUrl=$sourceUrl useAjax=1 }
   {if $cid}
     <table style="width: 45%; float: left; margin: 10px;">
       <tr class="columnheader"><th colspan="2">{ts 1=$main_contacts[$cid]}Merge %1 with{/ts}</th></tr>
 
 {if $context eq 'search'}
    {crmButton href=$backURL icon="close"}{ts}Done{/ts}{/crmButton}
+{elseif $context eq 'conflicts'}
+   {if call_user_func(array('CRM_Core_Permission','check'), 'force merge duplicate contacts')}
+     {if $gid}
+       {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map&mode=aggressive" a=1}{/capture}
+     {else}
+       {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&action=map&mode=aggressive" a=1}{/capture}
+     {/if}
+     <a href="{$backURL}" title="{ts}Force Merge Selected Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in force merge mode - all selected duplicates will be merged into main contacts even in case of any conflicts. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><div class="icon ui-icon-script"></div>{ts}Force Merge Selected Duplicates{/ts}</span></a>
+
+     {if $gid}
+       {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map" a=1}{/capture}
+     {else}
+       {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&action=map" a=1}{/capture}
+     {/if}
+     <a href="{$backURL}" title="{ts}Safe Merge Selected Duplicates{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><div class="icon ui-icon-script"></div>{ts}Safe Merge Selected Duplicates{/ts}</span></a>
+   {/if}
+
+   {if $gid}
+      {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&action=update&rgid=`$rgid`&gid=`$gid`" a=1}{/capture}
+   {else}
+      {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&action=update&rgid=`$rgid`" a=1}{/capture}
+   {/if}
+   <a href="{$backURL}" title="{ts}List All Duplicates{/ts}" class="button"><span><span class="icon ui-icon-refresh"></span> {ts}List All Duplicates{/ts}</span></a>
 {else}
    {if $gid}
       {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=renew" a=1}{/capture}
    </a>
 
    {if $gid}
-      {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map" a=1}{/capture}
+      {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`&action=map" a=1}{/capture}
    {else}
-      {capture assign=backURL}{crmURL p="civicrm/contact/dedupefind" q="reset=1&rgid=`$rgid`&action=map" a=1}{/capture}
+      {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&action=map" a=1}{/capture}
    {/if}
-   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button">
-     <span><div class="icon ui-icon-script"></div> {ts}Batch Merge Duplicates{/ts}</span>
-   </a>
+   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the selected duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><div class="icon ui-icon-script"></div>{ts}Batch Merge Selected Duplicates{/ts}</span></a>
+
+   {if $gid}
+      {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`&gid=`$gid`" a=1}{/capture}
+   {else}
+      {capture assign=backURL}{crmURL p="civicrm/contact/dedupemerge" q="reset=1&rgid=`$rgid`" a=1}{/capture}
+   {/if}
+   <a href="{$backURL}" title="{ts}Batch Merge Duplicate Contacts{/ts}" onclick="return confirm('{ts escape="js"}This will run the batch merge process on the listed duplicates. The operation will run in safe mode - only records with no direct data conflicts will be merged. Click OK to proceed if you are sure you wish to run this operation.{/ts}');" class="button"><span><div class="icon ui-icon-script"></div>{ts}Batch Merge All Duplicates{/ts}</span></a>
+
+   <a href='#' title="{ts}Flip Selected Duplicates{/ts}" class="crm-dedupe-flip-selections button"><span><span class="icon ui-icon-refresh"></span> {ts}Flip Selected Duplicates{/ts}</span></a>
 
    {capture assign=backURL}{crmURL p="civicrm/contact/deduperules" q="reset=1" a=1}{/capture}
    <a href="{$backURL}" class="button crm-button-type-cancel">
 {include file='CRM/common/dedupe.tpl'}
 {literal}
 <script type="text/javascript">
-var oTable = null;
+  (function($) {
+    CRM.$('table#dupePairs').data({
+      "ajax": {
+        "url": {/literal}'{$sourceUrl}'{literal}
+      },
+      rowCallback: function (row, data) {
+        // Set the checked state of the checkbox in the table
+        $('input.crm-dedupe-select', row).prop('checked', data.is_selected == 1);
+        if (data.is_selected == 1) {
+          $(row).toggleClass('crm-row-selected');
+        }
+        // for action column at the last, set nowrap
+        $('td:last', row).attr('nowrap','nowrap');
+        // for conflcts column
+        var col = CRM.$('table#dupePairs thead th.crm-contact-conflicts').index();
+        $('td:eq(' + col + ')', row).attr('nowrap','nowrap');
+      }
+    });
+    $(function($) {
+      $('.button').click(function() {
+        // no unsaved changes confirmation dialogs
+        $('[data-warn-changes=true]').attr('data-warn-changes', 'false');
+      });
+
+      var sourceUrl = {/literal}'{$sourceUrl}'{literal};
+      var context   = {/literal}'{$context}'{literal};
+
+      // redraw datatable if searching within selected records
+      $('#crm-dedupe-display-selection').on('click', function(){
+        reloadUrl = sourceUrl;
+        if($(this).prop('checked')){
+          reloadUrl = sourceUrl+'&selected=1';
+        }
+        CRM.$('table#dupePairs').DataTable().ajax.url(reloadUrl).draw();
+      });
+    
+      $('#dupePairs_length_selection').appendTo('#dupePairs_length');
+
+      // apply selected class on click of a row
+      $('#dupePairs tbody').on('click', 'tr', function(e) {
+        $(this).toggleClass('crm-row-selected');
+        $('input.crm-dedupe-select', this).prop('checked', $(this).hasClass('crm-row-selected'));
+        var ele = $('input.crm-dedupe-select', this);
+        toggleDedupeSelect(ele, 0);
+      });
+
+      // when select-all checkbox is checked
+      $('#dupePairs thead tr .crm-dedupe-selection').on('click', function() {
+        var checked = $('.crm-dedupe-select-all').prop('checked');
+        if (checked) {
+          $("#dupePairs tbody tr input[type='checkbox']").prop('checked', true);
+          $("#dupePairs tbody tr").addClass('crm-row-selected');
+        }
+        else{
+          $("#dupePairs tbody tr input[type='checkbox']").prop('checked', false);
+          $("#dupePairs tbody tr").removeClass('crm-row-selected');
+        }
+        var ele = $('#dupePairs tbody tr');
+        toggleDedupeSelect(ele, 1);
+      });
+
+      // inline search boxes placed in tfoot
+      $('#dupePairsColFilters thead th').each( function () {
+        var title = $('#dupePairs thead th').eq($(this).index()).text();
+        if (title.length > 1) {
+          $(this).html( '<input type="text" placeholder="Search '+title+'" />' );
+        }
+      });
+
+      // get dataTable
+      var table = CRM.$('table#dupePairs').DataTable();
+
+      // apply the search
+      $('#searchOptions input').on( 'keyup change', function () {
+        table
+          .column($(this).attr('search-column'))
+          .search(this.value)
+          .draw();
+      });
+
+      // show / hide columns
+      $('input.toggle-vis').on('click', function (e) {
+        var column = table.column( $(this).attr('data-column-main') );
+        column.visible( ! column.visible() );
+  
+        // nowrap to conflicts column is applied only during initial rendering
+        // for show / hide clicks we need to set it explicitly
+        var col = CRM.$('table#dupePairs thead th.crm-contact-conflicts').index() + 1;
+        if (col > 0) {
+          CRM.$('table#dupePairs tbody tr td:nth-child(' + col + ')').attr('nowrap','nowrap');
+        }
+
+        if ($(this).attr('data-column-dupe')) {
+          column = table.column( $(this).attr('data-column-dupe') );
+          column.visible( ! column.visible() );
+        }
+      });
+      
+      // keep the conflicts checkbox checked when context is "conflicts"
+      if(context == 'conflicts') {
+        $('#conflicts').attr('checked', true);  
+        var column = table.column( $('#conflicts').attr('data-column-main') );
+        column.visible( ! column.visible() );
+      }
+  
+      // on click of flip link of a row
+      $('#dupePairs tbody').on('click', 'tr .crm-dedupe-flip', function(e) {
+        e.stopPropagation();
+        var $el   = $(this);
+        var $elTr = $(this).closest('tr');
+        var postUrl = {/literal}"{crmURL p='civicrm/ajax/flipDupePairs' h=0 q='snippet=4'}"{literal};
+        var request = $.post(postUrl, {pnid : $el.data('pnid')});
+        request.done(function(dt) {
+          var mapper = {1:3, 2:4, 5:6, 7:8, 9:10}
+          var idx = table.row($elTr).index();
+          $.each(mapper, function(key, val) {
+            var v1  = table.cell(idx, key).data();
+            var v2  = table.cell(idx, val).data();
+            table.cell(idx, key).data(v2);
+            table.cell(idx, val).data(v1);
+          });
+          // keep the checkbox checked if needed
+          $('input.crm-dedupe-select', $elTr).prop('checked', $elTr.hasClass('crm-row-selected'));
+        });
+      });
+  
+      $(".crm-dedupe-flip-selections").on('click', function(e) {
+        var ids = [];
+        $('.crm-row-selected').each(function() {
+          var ele = CRM.$('input.crm-dedupe-select', this);
+          ids.push(CRM.$(ele).attr('name').substr(5));
+        });
+        if (ids.length > 0) {
+          var dataUrl = {/literal}"{crmURL p='civicrm/ajax/flipDupePairs' h=0 q='snippet=4'}"{literal};
+          CRM.$.post(dataUrl, {pnid: ids}, function (response) {
+            var mapper = {1:3, 2:4, 5:6, 7:8, 9:10}
+            $('.crm-row-selected').each(function() {
+              var idx = table.row(this).index();
+              $.each(mapper, function(key, val) {
+                var v1  = table.cell(idx, key).data();
+                var v2  = table.cell(idx, val).data();
+                table.cell(idx, key).data(v2);
+                table.cell(idx, val).data(v1);
+              });
+              // keep the checkbox checked if needed
+              $('input.crm-dedupe-select', this).prop('checked', $(this).hasClass('crm-row-selected'));
+            });
+          }, 'json');
+        }
+      });
+    });
+  })(CRM.$);
+
+  function toggleDedupeSelect(element, isMultiple) {
+    if (!isMultiple) {
+      var is_selected = CRM.$(element).prop('checked') ? 1: 0;
+      var id = CRM.$(element).prop('name').substr(5);
+    }
+    else {
+      var id = [];
+      CRM.$(element).each(function() {
+        var sth = CRM.$('input.crm-dedupe-select', this);
+        id.push(CRM.$(sth).prop('name').substr(5));
+      });
+      var is_selected = CRM.$('.crm-dedupe-select-all').prop('checked') ? 1 : 0;
+    }
+  
+    var dataUrl = {/literal}"{crmURL p='civicrm/ajax/toggleDedupeSelect' h=0 q='snippet=4'}"{literal};
+    var rgid = {/literal}"{$rgid}"{literal};
+    var gid = {/literal}"{$gid}"{literal};
+  
+    rgid = rgid.length > 0 ? rgid : 0;
+    gid  = gid.length > 0 ? gid : 0;
+    
+    CRM.$.post(dataUrl, {pnid: id, rgid: rgid, gid: gid, is_selected: is_selected}, function (data) {
+      // nothing to do for now
+    }, 'json');
+  }
 </script>
 {/literal}
diff --git a/templates/CRM/Contact/Page/DedupeMerge.tpl b/templates/CRM/Contact/Page/DedupeMerge.tpl
new file mode 100644 (file)
index 0000000..a270e4f
--- /dev/null
@@ -0,0 +1,25 @@
+{*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.5                                                |
+ +--------------------------------------------------------------------+
+ | 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        |
+ +--------------------------------------------------------------------+
+*}
index d6ba3dc80be14bc4d3893c4981a47d87b41d22a4..fb95bda61d19587793bd9dfd46f89650a9240b82 100644 (file)
@@ -280,6 +280,53 @@ class CRM_Member_Form_MembershipRenewalTest extends CiviUnitTestCase {
     ));
   }
 
+  /**
+   * Test the submit function of the membership form.
+   */
+  public function testSubmitRecurCompleteInstant() {
+    $form = $this->getForm();
+
+    $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessorID);
+    $processor->setDoDirectPaymentResult(array(
+      'payment_status_id' => 1,
+      'trxn_id' => 'kettles boil water',
+      'fee_amount' => .29,
+    ));
+
+    $this->callAPISuccess('MembershipType', 'create', array(
+      'id' => $this->membershipTypeAnnualFixedID,
+      'duration_unit' => 'month',
+      'duration_interval' => 1,
+      'auto_renew' => TRUE,
+    ));
+    $this->createLoggedInUser();
+    $form->preProcess();
+
+    $form->_contactID = $this->_individualId;
+    $params = $this->getBaseSubmitParams();
+    $form->_mode = 'test';
+
+    $form->testSubmit($params);
+    $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
+    $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 1);
+
+    $contribution = $this->callAPISuccess('Contribution', 'getsingle', array(
+      'contact_id' => $this->_individualId,
+      'is_test' => TRUE,
+    ));
+
+    $this->assertEquals('kettles boil water', $contribution['trxn_id']);
+    $this->assertEquals(.29, $contribution['fee_amount']);
+    $this->assertEquals(78, $contribution['total_amount']);
+    $this->assertEquals(77.71, $contribution['net_amount']);
+
+    $this->callAPISuccessGetCount('LineItem', array(
+      'entity_id' => $membership['id'],
+      'entity_table' => 'civicrm_membership',
+      'contribution_id' => $contribution['id'],
+    ), 1);
+
+  }
   /**
    * Test the submit function of the membership form.
    */
@@ -420,6 +467,7 @@ class CRM_Member_Form_MembershipRenewalTest extends CiviUnitTestCase {
       'record_contribution' => TRUE,
       'trxn_id' => 777,
       'contribution_status_id' => 1,
+      'fee_amount' => .5,
     );
     $form->_contactID = $this->_individualId;
 
@@ -433,6 +481,7 @@ class CRM_Member_Form_MembershipRenewalTest extends CiviUnitTestCase {
     ));
 
     $this->assertEquals($contribution['trxn_id'], 777);
+    $this->assertEquals(.5, $contribution['fee_amount']);
     $this->callAPISuccessGetCount('LineItem', array(
       'entity_id' => $membership['id'],
       'entity_table' => 'civicrm_membership',
@@ -460,4 +509,49 @@ class CRM_Member_Form_MembershipRenewalTest extends CiviUnitTestCase {
     return $form;
   }
 
+  /**
+   * Get some re-usable parameters for the submit function.
+   *
+   * @return array
+   */
+  protected function getBaseSubmitParams() {
+    $params = array(
+      'cid' => $this->_individualId,
+      'price_set_id' => 0,
+      'join_date' => date('m/d/Y', time()),
+      'start_date' => '',
+      'end_date' => '',
+      'campaign_id' => '',
+      // This format reflects the 23 being the organisation & the 25 being the type.
+      'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID),
+      'auto_renew' => '1',
+      'is_recur' => 1,
+      'max_related' => 0,
+      'num_terms' => '1',
+      'source' => '',
+      'total_amount' => '78.00',
+      'financial_type_id' => '2', //Member dues, see data.xml
+      'soft_credit_type_id' => 11,
+      'soft_credit_contact_id' => '',
+      'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
+      'receipt_text' => 'Thank you text',
+      'payment_processor_id' => $this->_paymentProcessorID,
+      'credit_card_number' => '4111111111111111',
+      'cvv2' => '123',
+      'credit_card_exp_date' => array(
+        'M' => '9',
+        'Y' => '2019', // TODO: Future proof
+      ),
+      'credit_card_type' => 'Visa',
+      'billing_first_name' => 'Test',
+      'billing_middlename' => 'Last',
+      'billing_street_address-5' => '10 Test St',
+      'billing_city-5' => 'Test',
+      'billing_state_province_id-5' => '1003',
+      'billing_postal_code-5' => '90210',
+      'billing_country_id-5' => '1228',
+    );
+    return $params;
+  }
+
 }
index 3c88f645154e74b3ed3d6e6f5893cf80971fd652..ae19247929756799f14688ebf5aa490a718489c7 100644 (file)
@@ -468,7 +468,8 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
       'billing_postal_code-5' => '90210',
       'billing_country_id-5' => '1228',
     );
-    $form->submit($params);
+    $form->_contactID = $this->_individualId;
+    $form->testSubmit($params);
     $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
     $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 0);
     $contribution = $this->callAPISuccess('Contribution', 'get', array(
@@ -499,8 +500,8 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
     $this->createLoggedInUser();
     $params = $this->getBaseSubmitParams();
     $form->_mode = 'test';
-
-    $form->submit($params);
+    $form->_contactID = $this->_individualId;
+    $form->testSubmit($params);
     $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
     $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 1);
 
@@ -517,6 +518,65 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
     ), 1);
   }
 
+  /**
+   * Test the submit function of the membership form.
+   */
+  public function testSubmitPayLaterWithBilling() {
+    $form = $this->getForm(NULL);
+    $this->createLoggedInUser();
+    $params = array(
+      'cid' => $this->_individualId,
+      'join_date' => date('m/d/Y', time()),
+      'start_date' => '',
+      'end_date' => '',
+      // This format reflects the 23 being the organisation & the 25 being the type.
+      'membership_type_id' => array(23, $this->membershipTypeAnnualFixedID),
+      'auto_renew' => '0',
+      'max_related' => '',
+      'num_terms' => '2',
+      'source' => '',
+      'total_amount' => '50.00',
+      //Member dues, see data.xml
+      'financial_type_id' => '2',
+      'soft_credit_type_id' => '',
+      'soft_credit_contact_id' => '',
+      'payment_instrument_id' => 4,
+      'from_email_address' => '"Demonstrators Anonymous" <info@example.org>',
+      'receipt_text_signup' => 'Thank you text',
+      'payment_processor_id' => $this->_paymentProcessorID,
+      'record_contribution' => TRUE,
+      'trxn_id' => 777,
+      'contribution_status_id' => 2,
+      'billing_first_name' => 'Test',
+      'billing_middlename' => 'Last',
+      'billing_street_address-5' => '10 Test St',
+      'billing_city-5' => 'Test',
+      'billing_state_province_id-5' => '1003',
+      'billing_postal_code-5' => '90210',
+      'billing_country_id-5' => '1228',
+    );
+    $form->_contactID = $this->_individualId;
+
+    $form->testSubmit($params);
+    $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
+    $contribution = $this->callAPISuccessGetSingle('Contribution', array(
+      'contact_id' => $this->_individualId,
+      'contribution_status_id' => 2,
+    ));
+    $this->assertEquals($contribution['trxn_id'], 777);
+
+    $this->callAPISuccessGetCount('LineItem', array(
+      'entity_id' => $membership['id'],
+      'entity_table' => 'civicrm_membership',
+      'contribution_id' => $contribution['id'],
+    ), 1);
+    $this->callAPISuccessGetSingle('address', array(
+      'contact_id' => $this->_individualId,
+      'street_address' => '10 Test St',
+      'postal_code' => 90210,
+    ));
+  }
+
   /**
    * Test the submit function of the membership form.
    */
@@ -524,7 +584,11 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
     $form = $this->getForm();
 
     $processor = Civi\Payment\System::singleton()->getById($this->_paymentProcessorID);
-    $processor->setDoDirectPaymentResult(array('payment_status_id' => 1, 'trxn_id' => 'kettles boil water'));
+    $processor->setDoDirectPaymentResult(array(
+      'payment_status_id' => 1,
+      'trxn_id' => 'kettles boil water',
+      'fee_amount' => .14,
+    ));
     $this->callAPISuccess('MembershipType', 'create', array(
       'id' => $this->membershipTypeAnnualFixedID,
       'duration_unit' => 'month',
@@ -535,16 +599,19 @@ class CRM_Member_Form_MembershipTest extends CiviUnitTestCase {
     $this->createLoggedInUser();
     $params = $this->getBaseSubmitParams();
     $form->_mode = 'test';
-
-    $form->submit($params);
+    $form->_contactID = $this->_individualId;
+    $form->testSubmit($params);
     $membership = $this->callAPISuccessGetSingle('Membership', array('contact_id' => $this->_individualId));
     $this->callAPISuccessGetCount('ContributionRecur', array('contact_id' => $this->_individualId), 1);
 
-    $contribution = $this->callAPISuccess('Contribution', 'get', array(
+    $contribution = $this->callAPISuccess('Contribution', 'getsingle', array(
       'contact_id' => $this->_individualId,
       'is_test' => TRUE,
     ));
 
+    $this->assertEquals(.14, $contribution['fee_amount']);
+    $this->assertEquals('kettles boil water', $contribution['trxn_id']);
+
     $this->callAPISuccessGetCount('LineItem', array(
       'entity_id' => $membership['id'],
       'entity_table' => 'civicrm_membership',
index be4e17b2710603c4e8b437c4508b392735f1851b..1997b9547e4267b4a7c643b40e30c2e5e26c7ea0 100644 (file)
@@ -39,21 +39,21 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
 
   public function setUp() {
     parent::setUp();
-
-    $this->_populateOptionAndCustomGroup();
   }
 
-  public function _populateOptionAndCustomGroup() {
+  public function _populateOptionAndCustomGroup($type = NULL) {
     $dataValues = array(
       'integer' => array(1, 2, 3),
       'number' => array(10.11, 20.22, 30.33),
       'string' => array(substr(sha1(rand()), 0, 4), substr(sha1(rand()), 0, 3), substr(sha1(rand()), 0, 2)),
-      'country' => array_rand(CRM_Core_PseudoConstant::country(FALSE, FALSE), 3),
+      //'country' => array_rand(CRM_Core_PseudoConstant::country(FALSE, FALSE), 3),
       'state_province' => array_rand(CRM_Core_PseudoConstant::stateProvince(FALSE, FALSE), 3),
       'date' => NULL,
       'contact' => NULL,
     );
 
+    $dataValues = !empty($type) ? array($type => $dataValues[$type]) : $dataValues;
+
     foreach ($dataValues as $dataType => $values) {
       $this->optionGroup[$dataType] = array('values' => $values);
       if (!empty($values)) {
@@ -88,9 +88,20 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
 
     // true tells quickCleanup to drop any tables that might have been created in the test
     $this->quickCleanup($tablesToTruncate, TRUE);
+
+    // cleanup created option group for each custom-set before running next test
+    if (!empty($this->optionGroup)) {
+      foreach ($this->optionGroup as $type => $value) {
+        if (!empty($value['id'])) {
+          $this->callAPISuccess('OptionGroup', 'delete', array('id' => $value['id']));
+        }
+      }
+    }
   }
 
   public function testCreateCustomValue() {
+    $this->_populateOptionAndCustomGroup();
+
     $customFieldDataType = CRM_Core_BAO_CustomField::dataType();
     $dataToHtmlTypes = CRM_Core_BAO_CustomField::dataToHtml();
     $count = 0;
@@ -109,10 +120,7 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
             $type = 'string';
           }
           else {
-            if ($dataType == 'Country') {
-              $type == 'country';
-            }
-            elseif ($dataType == 'StateProvince') {
+            if ($dataType == 'StateProvince') {
               $type = 'state_province';
             }
             elseif ($dataType == 'ContactReference') {
@@ -304,25 +312,23 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
    * @throws \CiviCRM_API3_Exception
    */
   public function testAlterOptionValue() {
+    $this->_populateOptionAndCustomGroup('string');
+
     $selectField = $this->customFieldCreate(array(
-      'custom_group_id' => $this->ids['single']['custom_group_id'],
+      'custom_group_id' => $this->ids['string']['custom_group_id'],
       'label' => 'Custom Select',
       'html_type' => 'Select',
-      'option_values' => array(
-        'one' => 'Option1',
-        'two' => 'Option2',
-        'notone' => 'OptionNotOne',
-      ),
+      'option_group_id' => $this->optionGroup['string']['id'],
     ));
     $selectField = civicrm_api3('customField', 'getsingle', array('id' => $selectField['id']));
     $radioField = $this->customFieldCreate(array(
-      'custom_group_id' => $this->ids['single']['custom_group_id'],
+      'custom_group_id' => $this->ids['string']['custom_group_id'],
       'label' => 'Custom Radio',
       'html_type' => 'Radio',
       'option_group_id' => $selectField['option_group_id'],
     ));
     $multiSelectField = $this->customFieldCreate(array(
-      'custom_group_id' => $this->ids['single']['custom_group_id'],
+      'custom_group_id' => $this->ids['string']['custom_group_id'],
       'label' => 'Custom Multi-Select',
       'html_type' => 'Multi-Select',
       'option_group_id' => $selectField['option_group_id'],
@@ -330,18 +336,18 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
     $selectName = 'custom_' . $selectField['id'];
     $radioName = 'custom_' . $radioField['id'];
     $multiSelectName = 'custom_' . $multiSelectField['id'];
-    $controlFieldName = 'custom_' . $this->ids['single']['custom_field_id'];
+    $controlFieldName = 'custom_' . $this->ids['string']['custom_field_id'];
 
     $params = array(
       'first_name' => 'abc4',
       'last_name' => 'xyz4',
       'contact_type' => 'Individual',
       'email' => 'man4@yahoo.com',
-      $selectName => 'one',
-      $multiSelectName => array('one', 'two', 'notone'),
-      $radioName => 'notone',
+      $selectName => $this->optionGroup['string']['values'][0],
+      $multiSelectName => $this->optionGroup['string']['values'],
+      $radioName => $this->optionGroup['string']['values'][1],
       // The control group in a science experiment should be unaffected
-      $controlFieldName => 'one',
+      $controlFieldName => $this->optionGroup['string']['values'][2],
     );
 
     $contact = $this->callAPISuccess('Contact', 'create', $params);
@@ -350,13 +356,13 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
       'id' => $contact['id'],
       'return' => array($selectName, $multiSelectName),
     ));
-    $this->assertEquals('one', $result[$selectName]);
-    $this->assertEquals(array('one', 'two', 'notone'), $result[$multiSelectName]);
+    $this->assertEquals($params[$selectName], $result[$selectName]);
+    $this->assertEquals($params[$multiSelectName], $result[$multiSelectName]);
 
     $this->callAPISuccess('OptionValue', 'create', array(
       'value' => 'one-modified',
       'option_group_id' => $selectField['option_group_id'],
-      'name' => 'Option1',
+      'name' => 'string 1',
       'options' => array(
         'match-mandatory' => array('option_group_id', 'name'),
       ),
@@ -368,11 +374,11 @@ class api_v3_CustomValueTest extends CiviUnitTestCase {
     ));
     // Ensure the relevant fields have been updated
     $this->assertEquals('one-modified', $result[$selectName]);
-    $this->assertEquals(array('one-modified', 'two', 'notone'), $result[$multiSelectName]);
+    $this->assertEquals(array('one-modified', $params[$radioName], $params[$controlFieldName]), $result[$multiSelectName]);
     // This field should not have changed because we didn't alter this option
-    $this->assertEquals('notone', $result[$radioName]);
+    $this->assertEquals($params[$radioName], $result[$radioName]);
     // This should not have changed because this field doesn't use the affected option group
-    $this->assertEquals('one', $result[$controlFieldName]);
+    $this->assertEquals($params[$controlFieldName], $result[$controlFieldName]);
   }
 
 }