CRM-38: Show Recurring Contributions on Membership Modal View
authorCamilo Rodriguez <camilo@compucorp.co.uk>
Fri, 30 Mar 2018 15:24:11 +0000 (15:24 +0000)
committerCamilo Rodriguez <camilo@compucorp.co.uk>
Thu, 10 May 2018 17:40:14 +0000 (17:40 +0000)
Currently, when viewing a membership from contact's detailed view (on
memberships tab), it is hard to tell if a membership has any recurring
contributions associated to it, even though you can see all payments done for
the membership.

Added a recurring contributions section below the contributions table
currently being shown, by loading recurring contributions using ajax call to
load list of recurring contributions.

Added recurring contributionslist to membership edit view using ajax call.

CRM/Contribute/xml/Menu/Contribute.xml
CRM/Member/Form/MembershipView.php
CRM/Member/Page/RecurringContributions.php [new file with mode: 0644]
Civi/Test/Api3TestTrait.php
templates/CRM/Member/Form/Membership.tpl
templates/CRM/Member/Form/MembershipView.tpl
templates/CRM/Member/Page/RecurringContributions.tpl [new file with mode: 0644]

index 3b3d94a7b3b4dd2814babbe0426b1e2c05aa0d8e..548902dbc07c9f1f342db5c4f497bf73436047c2 100644 (file)
     <page_callback>CRM_Contribute_Page_ContributionRecurPayments</page_callback>
     <access_arguments>access CiviContribute</access_arguments>
   </item>
+  <item>
+    <path>civicrm/membership/recurring-contributions</path>
+    <title>Membership Recurring Contributions</title>
+    <page_callback>CRM_Member_Page_RecurringContributions</page_callback>
+    <access_arguments>access CiviContribute</access_arguments>
+  </item>
 </menu>
index ac54c6a506c5af4d899ca4504cb73e3988a52b58..75eec53687cf532c5770f4212c72461b35f20a79 100644 (file)
@@ -35,7 +35,6 @@
 
 /**
  * This class generates form components for Payment-Instrument
- *
  */
 class CRM_Member_Form_MembershipView extends CRM_Core_Form {
 
@@ -46,6 +45,20 @@ class CRM_Member_Form_MembershipView extends CRM_Core_Form {
    */
   static $_links = NULL;
 
+  /**
+   * The id of the membership being viewed.
+   *
+   * @var int
+   */
+  private $membershipID;
+
+  /**
+   * Contact's ID.
+   *
+   * @var int
+   */
+  private $contactID;
+
   /**
    * Add context information at the end of a link.
    *
@@ -148,16 +161,16 @@ class CRM_Member_Form_MembershipView extends CRM_Core_Form {
    * @return void
    */
   public function preProcess() {
-
     $values = array();
-    $id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
+    $this->membershipID = CRM_Utils_Request::retrieve('id', 'Positive', $this);
+    $this->contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
 
     // Make sure context is assigned to template for condition where we come here view civicrm/membership/view
     $context = CRM_Utils_Request::retrieve('context', 'String', $this);
     $this->assign('context', $context);
 
-    if ($id) {
-      $params = array('id' => $id);
+    if ($this->membershipID) {
+      $params = array('id' => $this->membershipID);
       CRM_Member_BAO_Membership::retrieve($params, $values);
       if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()) {
         $finTypeId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $values['membership_type_id'], 'financial_type_id');
@@ -181,12 +194,12 @@ class CRM_Member_Form_MembershipView extends CRM_Core_Form {
       $this->assign('accessContribution', FALSE);
       if (CRM_Core_Permission::access('CiviContribute')) {
         $this->assign('accessContribution', TRUE);
-        CRM_Member_Page_Tab::associatedContribution($values['contact_id'], $id);
+        CRM_Member_Page_Tab::associatedContribution($values['contact_id'], $this->membershipID);
       }
 
       //Provide information about membership source when it is the result of a relationship (CRM-1901)
       $values['owner_membership_id'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
-        $id,
+        $this->membershipID,
         'owner_membership_id'
       );
 
@@ -375,12 +388,12 @@ SELECT r.id, c.id as cid, c.display_name as name, c.job_title as comment,
 
       CRM_Member_Page_Tab::setContext($this, $values['contact_id']);
 
-      $memType = CRM_Core_DAO::getFieldValue("CRM_Member_DAO_Membership", $id, "membership_type_id");
+      $memType = CRM_Core_DAO::getFieldValue("CRM_Member_DAO_Membership", $this->membershipID, "membership_type_id");
 
-      $groupTree = CRM_Core_BAO_CustomGroup::getTree('Membership', NULL, $id, 0, $memType);
-      CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, NULL, $id);
+      $groupTree = CRM_Core_BAO_CustomGroup::getTree('Membership', NULL, $this->membershipID, 0, $memType);
+      CRM_Core_BAO_CustomGroup::buildCustomDataView($this, $groupTree, FALSE, NULL, NULL, NULL, $this->membershipID);
 
-      $isRecur = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $id, 'contribution_recur_id');
+      $isRecur = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $this->membershipID, 'contribution_recur_id');
 
       $autoRenew = $isRecur ? TRUE : FALSE;
     }
@@ -389,7 +402,7 @@ SELECT r.id, c.id as cid, c.display_name as name, c.job_title as comment,
       $values['membership_type'] .= ' (test) ';
     }
 
-    $subscriptionCancelled = CRM_Member_BAO_Membership::isSubscriptionCancelled($id);
+    $subscriptionCancelled = CRM_Member_BAO_Membership::isSubscriptionCancelled($this->membershipID);
     $values['auto_renew'] = ($autoRenew && !$subscriptionCancelled) ? 'Yes' : 'No';
 
     //do check for campaigns
diff --git a/CRM/Member/Page/RecurringContributions.php b/CRM/Member/Page/RecurringContributions.php
new file mode 100644 (file)
index 0000000..a91d224
--- /dev/null
@@ -0,0 +1,155 @@
+<?php\r
+\r
+/**\r
+ * Shows list of recurring contributions related to membership.\r
+ */\r
+class CRM_Member_Page_RecurringContributions extends CRM_Core_Page {\r
+\r
+  /**\r
+   * ID of the membership for which we need to see related recurring contributions.\r
+   *\r
+   * @var int\r
+   */\r
+  private $membershipID = NULL;\r
+\r
+  /**\r
+   * ID of the contact owner of the membership.\r
+   *\r
+   * @var int\r
+   */\r
+  public $contactID = NULL;\r
+\r
+  /**\r
+   * Builds list of recurring contributions associated to membership.\r
+   *\r
+   * @return null\r
+   */\r
+  public function run() {\r
+    $this->membershipID = CRM_Utils_Request::retrieve('membershipID', 'Positive', $this);\r
+    $this->contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE);\r
+\r
+    $this->loadRecurringContributions();\r
+\r
+    return parent::run();\r
+  }\r
+\r
+  /**\r
+   * Loads recurring contributions and assigns them to the form, to be used on\r
+   * the template.\r
+   */\r
+  private function loadRecurringContributions() {\r
+    $recurringContributions = $this->getRecurContributions($this->membershipID);\r
+\r
+    if (!empty($recurringContributions)) {\r
+      $this->assign('recurRows', $recurringContributions);\r
+      $this->assign('recur', TRUE);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Obtains list of recurring contributions associated to a membership.\r
+   *\r
+   * @param int $membershipID\r
+   *\r
+   * @return array\r
+   */\r
+  private function getRecurContributions($membershipID) {\r
+    $result = civicrm_api3('MembershipPayment', 'get', array(\r
+      'sequential' => 1,\r
+      'contribution_id.contribution_recur_id.id' => ['IS NOT NULL' => TRUE],\r
+      'options' => ['limit' => 0],\r
+      'return' => array(\r
+        'contribution_id.contribution_recur_id.id',\r
+        'contribution_id.contribution_recur_id.contact_id',\r
+        'contribution_id.contribution_recur_id.start_date',\r
+        'contribution_id.contribution_recur_id.end_date',\r
+        'contribution_id.contribution_recur_id.next_sched_contribution_date',\r
+        'contribution_id.contribution_recur_id.amount',\r
+        'contribution_id.contribution_recur_id.currency',\r
+        'contribution_id.contribution_recur_id.frequency_unit',\r
+        'contribution_id.contribution_recur_id.frequency_interval',\r
+        'contribution_id.contribution_recur_id.installments',\r
+        'contribution_id.contribution_recur_id.contribution_status_id',\r
+        'contribution_id.contribution_recur_id.is_test',\r
+        'contribution_id.contribution_recur_id.payment_processor_id',\r
+      ),\r
+      'membership_id' => $membershipID,\r
+    ));\r
+    $recurringContributions = array();\r
+    $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus();\r
+\r
+    foreach ($result['values'] as $payment) {\r
+      $recurringContributionID = $payment['contribution_id.contribution_recur_id.id'];\r
+      $alreadyProcessed = isset($recurringContributions[$recurringContributionID]);\r
+\r
+      if ($alreadyProcessed) {\r
+        continue;\r
+      }\r
+\r
+      foreach ($payment as $field => $value) {\r
+        $key = strtr($field, array('contribution_id.contribution_recur_id.' => ''));\r
+        $recurringContributions[$recurringContributionID][$key] = $value;\r
+      }\r
+\r
+      $contactID = $recurringContributions[$recurringContributionID]['contact_id'];\r
+      $contributionStatusID = $recurringContributions[$recurringContributionID]['contribution_status_id'];\r
+\r
+      $recurringContributions[$recurringContributionID]['id'] = $recurringContributionID;\r
+      $recurringContributions[$recurringContributionID]['contactId'] = $contactID;\r
+      $recurringContributions[$recurringContributionID]['contribution_status'] = CRM_Utils_Array::value($contributionStatusID, $contributionStatuses);\r
+\r
+      $this->setActionsForRecurringContribution($recurringContributionID, $recurringContributions[$recurringContributionID]);\r
+    }\r
+    return $recurringContributions;\r
+  }\r
+\r
+  /**\r
+   * Calculates and assigns the actions available for given recurring\r
+   * contribution.\r
+   *\r
+   * @param int $recurID\r
+   * @param array $recurringContribution\r
+   */\r
+  private function setActionsForRecurringContribution($recurID, &$recurringContribution) {\r
+    $action = array_sum(array_keys($this->recurLinks($recurID)));\r
+    // no action allowed if it's not active\r
+    $recurringContribution['is_active'] = ($recurringContribution['contribution_status_id'] != 3);\r
+    if ($recurringContribution['is_active']) {\r
+      $details = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($recurringContribution['id'], 'recur');\r
+      $hideUpdate = $details->membership_id & $details->auto_renew;\r
+      if ($hideUpdate || empty($details->processor_id)) {\r
+        $action -= CRM_Core_Action::UPDATE;\r
+      }\r
+      $recurringContribution['action'] = CRM_Core_Action::formLink(\r
+        $this->recurLinks($recurID),\r
+        $action,\r
+        array(\r
+          'cid' => $this->contactID,\r
+          'crid' => $recurID,\r
+          'cxt' => 'contribution',\r
+        ),\r
+        ts('more'),\r
+        FALSE,\r
+        'contribution.selector.recurring',\r
+        'Contribution',\r
+        $recurID\r
+      );\r
+    }\r
+  }\r
+\r
+  /**\r
+   * This method returns the links that are given for recur search row.\r
+   * currently the links added for each row are:\r
+   * - View\r
+   * - Edit\r
+   * - Cancel\r
+   *\r
+   * @param bool $id\r
+   *\r
+   * @return array\r
+   */\r
+  private function recurLinks($id) {\r
+    return CRM_Contribute_Page_Tab::recurLinks($id, 'contribution');\r
+  }\r
+\r
+}\r
index 46880df32bf573f261aa8d116b5ba1d7145034a9..1efcd13ce693246853128e091e7ee156058b8b78 100644 (file)
@@ -160,7 +160,7 @@ trait Api3TestTrait {
    * @param string $entity
    * @param array $params
    * @param null $count
-   * @throws Exception
+   * @throws \Exception
    * @return array|int
    */
   public function callAPISuccessGetCount($entity, $params, $count = NULL) {
@@ -170,7 +170,7 @@ trait Api3TestTrait {
     );
     $result = $this->civicrm_api($entity, 'getcount', $params);
     if (!is_int($result) || !empty($result['is_error']) || isset($result['values'])) {
-      throw new Exception('Invalid getcount result : ' . print_r($result, TRUE) . " type :" . gettype($result));
+      throw new \Exception('Invalid getcount result : ' . print_r($result, TRUE) . " type :" . gettype($result));
     }
     if (is_int($count)) {
       $this->assertEquals($count, $result, "incorrect count returned from $entity getcount");
@@ -193,7 +193,7 @@ trait Api3TestTrait {
    *   - array
    *   - object
    *
-   * @throws Exception
+   * @throws \Exception
    * @return array|int
    */
   public function callAPISuccessGetSingle($entity, $params, $checkAgainst = NULL) {
index 16b3f090123ac7729ec1ec6d11826a31de1ce097..683c543ea41db0cc4e7fe6dd9fd8e587137a5d22 100644 (file)
       {if $accessContribution and $action eq 2 and $rows.0.contribution_id}
         <div class="crm-accordion-wrapper">
           <div class="crm-accordion-header">{ts}Related Contributions{/ts}</div>
-          <div class="crm-accordion-body">{include file="CRM/Contribute/Form/Selector.tpl" context="Search"}</div>
+          <div class="crm-accordion-body">
+            {include file="CRM/Contribute/Form/Selector.tpl" context="Search"}
+            <script type="text/javascript">
+              var membershipID = {$entityID};
+              var contactID = {$contactId};
+              {literal}
+              CRM.$(function($) {
+                CRM.loadPage(
+                  CRM.url(
+                    'civicrm/membership/recurring-contributions',
+                    {
+                      reset: 1,
+                      membershipID: membershipID,
+                      cid: contactID
+                    },
+                    'back'
+                  ),
+                  {
+                    target : '#membership-recurring-contributions',
+                    dialog : false
+                  }
+                );
+              });
+              {/literal}
+            </script>
+            <div id="membership-recurring-contributions"></div>
+          </div>
         </div>
       {/if}
       {if $softCredit}
index a5d4ddaa405e5efc51ff4edcf818db2123a07732..87405f107a2f0bbbad718bff53de03dc32484d62 100644 (file)
 
     {include file="CRM/Custom/Page/CustomDataView.tpl"}
 
-    {if $accessContribution and $rows.0.contribution_id}
-        <div class="crm-accordion-wrapper">
-              <div class="crm-accordion-header">{ts}Related Contributions{/ts}</div>
-              <div class="crm-accordion-body">{include file="CRM/Contribute/Form/Selector.tpl" context="Search"}</div>
+    {if $accessContribution}
+      <div class="crm-accordion-wrapper">
+        <div class="crm-accordion-header">
+          {ts}Related Contributions and Recurring Contributions{/ts}
+        </div>
+        <div class="crm-accordion-body">
+          {if $rows.0.contribution_id}
+            {include file="CRM/Contribute/Form/Selector.tpl" context="Search"}
+          {/if}
+          <script type="text/javascript">
+            var membershipID = {$id};
+            var contactID = {$contactId};
+            {literal}
+            CRM.$(function($) {
+              CRM.loadPage(
+                CRM.url(
+                  'civicrm/membership/recurring-contributions',
+                  {
+                    reset: 1,
+                    membershipID: membershipID,
+                    cid: contactID
+                  },
+                  'back'
+                ),
+                {
+                  target : '#membership-recurring-contributions',
+                  dialog : false
+                }
+              );
+            });
+            {/literal}
+          </script>
+          <div id="membership-recurring-contributions"></div>
         </div>
+      </div>
     {/if}
 
     {if $softCredit}
diff --git a/templates/CRM/Member/Page/RecurringContributions.tpl b/templates/CRM/Member/Page/RecurringContributions.tpl
new file mode 100644 (file)
index 0000000..37dcdc2
--- /dev/null
@@ -0,0 +1,6 @@
+{if $recur}\r
+  <div class="solid-border-top">\r
+    <br /><label>{ts 1=$displayName}Recurring Contributions{/ts}</label>\r
+  </div>\r
+  {include file="CRM/Contribute/Page/ContributionRecur.tpl" action=16}\r
+{/if}\r