CRM-16737 4.7 version, only payment_status_id accepted as return parameter from payme...
[civicrm-core.git] / CRM / Member / BAO / Membership.php
index 13c2ca8c83516cb3b9bf80849513868c1cc4213a..bc4b216bdaee5d068c9ef4e720dc8bd454d37c88 100644 (file)
@@ -29,8 +29,6 @@
  *
  * @package CRM
  * @copyright CiviCRM LLC (c) 2004-2015
- * $Id$
- *
  */
 class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
 
@@ -47,10 +45,6 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
 
   /**
    * Class constructor.
-   *
-   * @return \CRM_Member_DAO_Membership
-   */
-  /**
    */
   public function __construct() {
     parent::__construct();
@@ -184,8 +178,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
   }
 
   /**
-   * Given the list of params in the params array, fetch the object
-   * and store the values in the values array
+   * Fetch the object and store the values in the values array.
    *
    * @param array $params
    *   Input parameters to find object.
@@ -195,7 +188,9 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
    *   Do you want only active memberships to.
    *                        be returned
    * @param bool $relatedMemberships
-   * @return CRM_Member_BAO_Membership|null the found object or null
+   *
+   * @return CRM_Member_BAO_Membership|null
+   *   The found object or null
    */
   public static function &getValues(&$params, &$values, $active = FALSE, $relatedMemberships = FALSE) {
     if (empty($params)) {
@@ -249,7 +244,8 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
 
     if (empty($params['is_override']) && empty($params['skipStatusCal'])) {
       $dates = array('start_date', 'end_date', 'join_date');
-      $start_date = $end_date = $join_date = NULL; // declare these out of courtesy as IDEs don't pick up the setting of them below
+      // Declare these out of courtesy as IDEs don't pick up the setting of them below.
+      $start_date = $end_date = $join_date = NULL;
       foreach ($dates as $date) {
         $$date = $params[$date] = CRM_Utils_Date::processDate(CRM_Utils_Array::value($date, $params), NULL, TRUE, 'Ymd');
       }
@@ -262,17 +258,9 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
         $excludeIsAdmin = TRUE;
       }
 
-      //CRM-15829 UPDATES
-      // When adding memberships to a contact and If a status is pending then there is no need to perform these calculations. Otherwise it will errernously not realise the pending state and set ot to NEW or GRACE depending on the date ranges.
-      if (isset($params['status_id']) && $params['status_id'] == 5) {
-        $calcStatus['id'] = $params['status_id'];
-      }
-      else {
-        $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($start_date, $end_date, $join_date,
-          'today', $excludeIsAdmin, CRM_Utils_Array::value('membership_type_id', $params), $params
-        );
-      }
-
+      $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($start_date, $end_date, $join_date,
+        'today', $excludeIsAdmin, CRM_Utils_Array::value('membership_type_id', $params), $params
+      );
       if (empty($calcStatus)) {
         // Redirect the form in case of error
         // @todo this redirect in the BAO layer is really bad & should be moved to the form layer
@@ -290,8 +278,8 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
     }
 
     // data cleanup only: all verifications on number of related memberships are done upstream in:
-    //    CRM_Member_BAO_Membership::createRelatedMemberships()
-    //    CRM_Contact_BAO_Relationship::relatedMemberships()
+    // CRM_Member_BAO_Membership::createRelatedMemberships()
+    // CRM_Contact_BAO_Relationship::relatedMemberships()
     if (isset($params['owner_membership_id'])) {
       unset($params['max_related']);
     }
@@ -323,7 +311,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
     $params['membership_id'] = $membership->id;
     if (isset($ids['membership'])) {
       $ids['contribution'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment',
-        $ids['membership'],
+        $membership->id,
         'contribution_id',
         'membership_id'
       );
@@ -415,7 +403,8 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
       $url = CRM_Utils_System::url('civicrm/contact/view/membership',
         "action=view&reset=1&id={$membership->id}&cid={$membership->contact_id}&context=home"
       );
-      if (empty($membership->membership_type_id)) {// ie in an update situation
+      if (empty($membership->membership_type_id)) {
+        // ie in an update situation.
         $membership->find(TRUE);
       }
       $membershipTypes = CRM_Member_PseudoConstant::membershipType();
@@ -555,8 +544,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
   }
 
   /**
-   *
-   * get membership status and membership type values
+   * Get membership status and membership type values.
    *
    * @param int $membershipId
    *   Membership id of values to return.
@@ -601,13 +589,14 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
 
   /**
    * Delete membership.
+   *
    * Wrapper for most delete calls. Use this unless you JUST want to delete related memberships w/o deleting the parent.
    *
    * @param int $membershipId
    *   Membership id that needs to be deleted.
    *
-   *
-   * @return int $results  id of deleted Membership on success, false otherwise
+   * @return int
+   *   Id of deleted Membership on success, false otherwise.
    */
   public static function del($membershipId) {
     //delete related first and then delete parent.
@@ -621,8 +610,8 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
    * @param int $membershipId
    *   Membership id that needs to be deleted.
    *
-   *
-   * @return int $results id of deleted Membership on success, false otherwise
+   * @return int
+   *   Id of deleted Membership on success, false otherwise.
    */
   public static function deleteMembership($membershipId) {
     // CRM-12147, retrieve membership data before we delete it for hooks
@@ -738,211 +727,6 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
     return NULL;
   }
 
-  /**
-   * Build Membership  Block in Contribution Pages.
-   *
-   * @param CRM_Core_Form $form
-   *   Form object.
-   * @param int $pageID
-   *   Unused?.
-   * @param int $cid
-   *   Contact checked for having a current membership for a particular membership.
-   * @param bool $formItems
-   * @param int $selectedMembershipTypeID
-   *   Selected membership id.
-   * @param bool $thankPage
-   *   Thank you page.
-   * @param null $isTest
-   *
-   * @return bool
-   *   Is this a separate membership payment
-   *
-   */
-  public static function buildMembershipBlock(
-    &$form,
-    $pageID,
-    $cid,
-    $formItems = FALSE,
-    $selectedMembershipTypeID = NULL,
-    $thankPage = FALSE,
-    $isTest = NULL
-  ) {
-
-    $separateMembershipPayment = FALSE;
-    if ($form->_membershipBlock) {
-      $form->_currentMemberships = array();
-
-      $membershipBlock = $form->_membershipBlock;
-      $membershipTypeIds = $membershipTypes = $radio = array();
-      $membershipPriceset = (!empty($form->_priceSetId) && $form->_useForMember) ? TRUE : FALSE;
-
-      $allowAutoRenewMembership = $autoRenewOption = FALSE;
-      $autoRenewMembershipTypeOptions = array();
-
-      $paymentProcessor = CRM_Core_PseudoConstant::paymentProcessor(FALSE, FALSE, 'is_recur = 1');
-
-      $separateMembershipPayment = CRM_Utils_Array::value('is_separate_payment', $membershipBlock);
-
-      if ($membershipPriceset) {
-        foreach ($form->_priceSet['fields'] as $pField) {
-          if (empty($pField['options'])) {
-            continue;
-          }
-          foreach ($pField['options'] as $opId => $opValues) {
-            if (empty($opValues['membership_type_id'])) {
-              continue;
-            }
-            $membershipTypeIds[$opValues['membership_type_id']] = $opValues['membership_type_id'];
-          }
-        }
-      }
-      elseif (!empty($membershipBlock['membership_types'])) {
-        $membershipTypeIds = explode(',', $membershipBlock['membership_types']);
-      }
-
-      if (!empty($membershipTypeIds)) {
-        //set status message if wrong membershipType is included in membershipBlock
-        if (isset($form->_mid) && !$membershipPriceset) {
-          $membershipTypeID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
-            $form->_mid,
-            'membership_type_id'
-          );
-          if (!in_array($membershipTypeID, $membershipTypeIds)) {
-            CRM_Core_Session::setStatus(ts("Oops. The membership you're trying to renew appears to be invalid. Contact your site administrator if you need assistance. If you continue, you will be issued a new membership."), ts('Invalid Membership'), 'error');
-          }
-        }
-
-        $membershipTypeValues = self::buildMembershipTypeValues($form, $membershipTypeIds);
-        $form->_membershipTypeValues = $membershipTypeValues;
-        $endDate = NULL;
-        foreach ($membershipTypeIds as $value) {
-          $memType = $membershipTypeValues[$value];
-          if ($selectedMembershipTypeID != NULL) {
-            if ($memType['id'] == $selectedMembershipTypeID) {
-              $form->assign('minimum_fee',
-                CRM_Utils_Array::value('minimum_fee', $memType)
-              );
-              $form->assign('membership_name', $memType['name']);
-              if (!$thankPage && $cid) {
-                $membership = new CRM_Member_DAO_Membership();
-                $membership->contact_id = $cid;
-                $membership->membership_type_id = $memType['id'];
-                if ($membership->find(TRUE)) {
-                  $form->assign('renewal_mode', TRUE);
-                  $memType['current_membership'] = $membership->end_date;
-                  $form->_currentMemberships[$membership->membership_type_id] = $membership->membership_type_id;
-                }
-              }
-              $membershipTypes[] = $memType;
-            }
-          }
-          elseif ($memType['is_active']) {
-            $javascriptMethod = NULL;
-            $allowAutoRenewOpt = 1;
-            if (is_array($form->_paymentProcessors)) {
-              foreach ($form->_paymentProcessors as $id => $val) {
-                if (!$val['is_recur']) {
-                  $allowAutoRenewOpt = 0;
-                  continue;
-                }
-              }
-            }
-
-            $javascriptMethod = array('onclick' => "return showHideAutoRenew( this.value );");
-            $autoRenewMembershipTypeOptions["autoRenewMembershipType_{$value}"] = (int) $allowAutoRenewOpt * CRM_Utils_Array::value($value, CRM_Utils_Array::value('auto_renew', $form->_membershipBlock));;
-
-            if ($allowAutoRenewOpt) {
-              $allowAutoRenewMembership = TRUE;
-            }
-
-            //add membership type.
-            $radio[$memType['id']] = $form->createElement('radio', NULL, NULL, NULL,
-              $memType['id'], $javascriptMethod
-            );
-            if ($cid) {
-              $membership = new CRM_Member_DAO_Membership();
-              $membership->contact_id = $cid;
-              $membership->membership_type_id = $memType['id'];
-
-              //show current membership, skip pending and cancelled membership records,
-              //because we take first membership record id for renewal
-              $membership->whereAdd('status_id != 5 AND status_id !=6');
-
-              if (!is_null($isTest)) {
-                $membership->is_test = $isTest;
-              }
-
-              //CRM-4297
-              $membership->orderBy('end_date DESC');
-
-              if ($membership->find(TRUE)) {
-                if (!$membership->end_date) {
-                  unset($radio[$memType['id']]);
-                  $form->assign('islifetime', TRUE);
-                  continue;
-                }
-                $form->assign('renewal_mode', TRUE);
-                $form->_currentMemberships[$membership->membership_type_id] = $membership->membership_type_id;
-                $memType['current_membership'] = $membership->end_date;
-                if (!$endDate) {
-                  $endDate = $memType['current_membership'];
-                  $form->_defaultMemTypeId = $memType['id'];
-                }
-                if ($memType['current_membership'] < $endDate) {
-                  $endDate = $memType['current_membership'];
-                  $form->_defaultMemTypeId = $memType['id'];
-                }
-              }
-            }
-            $membershipTypes[] = $memType;
-          }
-        }
-      }
-
-      $form->assign('showRadio', $formItems);
-      if ($formItems) {
-        if (!$membershipPriceset) {
-          if (!$membershipBlock['is_required']) {
-            $form->assign('showRadioNoThanks', TRUE);
-            $radio[''] = $form->createElement('radio', NULL, NULL, NULL, 'no_thanks', NULL);
-            $form->addGroup($radio, 'selectMembership', NULL);
-          }
-          elseif ($membershipBlock['is_required'] && count($radio) == 1) {
-            $temp = array_keys($radio);
-            $form->add('hidden', 'selectMembership', $temp[0], array('id' => 'selectMembership'));
-            $form->assign('singleMembership', TRUE);
-            $form->assign('showRadio', FALSE);
-          }
-          else {
-            $form->addGroup($radio, 'selectMembership', NULL);
-          }
-
-          $form->addRule('selectMembership', ts('Please select one of the memberships.'), 'required');
-        }
-        else {
-          $autoRenewOption = CRM_Price_BAO_PriceSet::checkAutoRenewForPriceSet($form->_priceSetId);
-          $form->assign('autoRenewOption', $autoRenewOption);
-        }
-
-        if (!$form->_values['is_pay_later'] && is_array($form->_paymentProcessors) && ($allowAutoRenewMembership || $autoRenewOption)) {
-          $form->addElement('checkbox', 'auto_renew', ts('Please renew my membership automatically.'));
-        }
-
-      }
-
-      $form->assign('membershipBlock', $membershipBlock);
-      $form->assign('membershipTypes', $membershipTypes);
-      $form->assign('allowAutoRenewMembership', $allowAutoRenewMembership);
-      $form->assign('autoRenewMembershipTypeOptions', json_encode($autoRenewMembershipTypeOptions));
-
-      //give preference to user submitted auto_renew value.
-      $takeUserSubmittedAutoRenew = (!empty($_POST) || $form->isSubmitted()) ? TRUE : FALSE;
-      $form->assign('takeUserSubmittedAutoRenew', $takeUserSubmittedAutoRenew);
-    }
-
-    return $separateMembershipPayment;
-  }
-
   /**
    * Return Membership Block info in Contribution Pages.
    *
@@ -950,7 +734,6 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
    *   Contribution page id.
    *
    * @return array|null
-   *
    */
   public static function getMembershipBlock($pageID) {
     $membershipBlock = array();
@@ -983,6 +766,7 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
 
   /**
    * Return a current membership of given contact.
+   *
    * NB: if more than one membership meets criteria, a randomly selected one is returned.
    *
    * @param int $contactID
@@ -998,6 +782,21 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
    * @return array|bool
    */
   public static function getContactMembership($contactID, $memType, $isTest, $membershipId = NULL, $onlySameParentOrg = FALSE) {
+    //check for owner membership id, if it exists update that membership instead: CRM-15992
+    if ($membershipId) {
+      $ownerMemberId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
+        $membershipId,
+        'owner_membership_id', 'id'
+      );
+      if ($ownerMemberId) {
+        $membershipId = $ownerMemberId;
+        $contactID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
+          $membershipId,
+          'contact_id', 'id'
+        );
+      }
+    }
+
     $dao = new CRM_Member_DAO_Membership();
     if ($membershipId) {
       $dao->id = $membershipId;
@@ -1052,6 +851,17 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
         $membership['status_id'],
         'is_current_member', 'id'
       );
+      $ownerMemberId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
+        $membership['id'],
+        'owner_membership_id', 'id'
+      );
+      if ($ownerMemberId) {
+        $membership['id'] = $membership['membership_id'] = $ownerMemberId;
+        $membership['membership_contact_id'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership',
+          $membership['id'],
+          'contact_id', 'id'
+        );
+      }
       return $membership;
     }
 
@@ -1148,7 +958,7 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
   /**
    * Get all exportable fields.
    *
-   * @retun array return array of all exportable fields
+   * @return array return array of all exportable fields
    */
   public static function &exportableFields() {
     $expFieldMembership = CRM_Member_DAO_Membership::export();
@@ -1170,8 +980,9 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
   }
 
   /**
-   * Get membership joins/renewals for a specified membership
-   * type.  Specifically, retrieves a count of memberships whose "Membership
+   * Get membership joins/renewals for a specified membership type.
+   *
+   * Specifically, retrieves a count of memberships whose "Membership
    * Signup" or "Membership Renewal" activity falls in the given date range.
    * Dates match the pattern "yyyy-mm-dd".
    *
@@ -1226,8 +1037,9 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
   }
 
   /**
-   * Get a count of membership for a specified membership type,
-   * optionally for a specified date.  The date must have the form yyyy-mm-dd.
+   * Get a count of membership for a specified membership type, optionally for a specified date.
+   *
+   * The date must have the form yyyy-mm-dd.
    *
    * If $date is omitted, this function counts as a member anyone whose
    * membership status_id indicates they're a current member.
@@ -1309,37 +1121,31 @@ AND civicrm_membership.is_test = %2";
    * @param bool $isPaidMembership
    * @param array $membershipID
    *
-   * @param $isProcessSeparateMembershipTransaction
+   * @param bool $isProcessSeparateMembershipTransaction
    *
-   * @param int $defaultContributionTypeID
+   * @param int $financialTypeID
    * @param array $membershipLineItems
    *   Line items specific to membership payment that is separate to contribution.
-   * @param $isPayLater
+   * @param bool $isPayLater
+   * @param bool $isPending
    *
    * @throws \CRM_Core_Exception
    */
   public static function postProcessMembership(
     $membershipParams, $contactID, &$form, $premiumParams,
     $customFieldsFormatted = NULL, $includeFieldTypes = NULL, $membershipDetails, $membershipTypeIDs, $isPaidMembership, $membershipID,
-    $isProcessSeparateMembershipTransaction, $defaultContributionTypeID, $membershipLineItems, $isPayLater) {
+    $isProcessSeparateMembershipTransaction, $financialTypeID, $membershipLineItems, $isPayLater, $isPending) {
     $result = $membershipContribution = NULL;
     $isTest = CRM_Utils_Array::value('is_test', $membershipParams, FALSE);
-    $errors = $createdMemberships = array();
-
-    //@todo move this into the calling function & pass in the correct financialTypeID
-    if (isset($paymentParams['financial_type'])) {
-      $financialTypeID = $paymentParams['financial_type'];
-    }
-    else {
-      $financialTypeID = $defaultContributionTypeID;
-    }
-
-    if (CRM_Utils_Array::value('membership_source', $form->_params)) {
-      $membershipParams['contribution_source'] = $form->_params['membership_source'];
-    }
+    $errors = $createdMemberships = $paymentResult = array();
 
     if ($isPaidMembership) {
-      $result = CRM_Contribute_BAO_Contribution_Utils::processConfirm($form, $membershipParams,
+      if ($isProcessSeparateMembershipTransaction) {
+        // If we have 2 transactions only one can use the invoice id.
+        $membershipParams['invoiceID'] .= '-2';
+      }
+
+      $paymentResult = CRM_Contribute_BAO_Contribution_Utils::processConfirm($form, $membershipParams,
         $premiumParams, $contactID,
         $financialTypeID,
         'membership',
@@ -1348,20 +1154,24 @@ AND civicrm_membership.is_test = %2";
         $isPayLater
       );
       if (is_a($result[1], 'CRM_Core_Error')) {
-        $errors[1] = CRM_Core_Error::getMessages($result[1]);
+        $errors[1] = CRM_Core_Error::getMessages($paymentResult[1]);
+      }
+
+      if (is_a($paymentResult, 'CRM_Core_Error')) {
+        $errors[1] = CRM_Core_Error::getMessages($paymentResult);
       }
-      elseif (!empty($result[1])) {
+      elseif (!empty($paymentResult['contribution'])) {
+        //note that this will be over-written if we are using a separate membership transaction. Otherwise there is only one
+        $membershipContribution = $paymentResult['contribution'];
         // Save the contribution ID so that I can be used in email receipts
         // For example, if you need to generate a tax receipt for the donation only.
-        $form->_values['contribution_other_id'] = $result[1]->id;
-        //note that this will be over-written if we are using a separate membership transaction. Otherwise there is only one
-        $membershipContribution = $result[1];
+        $form->_values['contribution_other_id'] = $membershipContribution->id;
       }
     }
 
     if ($isProcessSeparateMembershipTransaction) {
       try {
-        $lineItems = $form->_lineItem = $membershipLineItems;
+        $form->_lineItem = $membershipLineItems;
         if (empty($form->_params['auto_renew']) && !empty($membershipParams['is_recur'])) {
           unset($membershipParams['is_recur']);
         }
@@ -1387,7 +1197,56 @@ AND civicrm_membership.is_test = %2";
       $typesTerms = CRM_Utils_Array::value('types_terms', $membershipParams, array());
       foreach ($membershipTypeIDs as $memType) {
         $numTerms = CRM_Utils_Array::value($memType, $typesTerms, 1);
-        $createdMemberships[$memType] = self::createOrRenewMembership($membershipParams, $contactID, $customFieldsFormatted, $membershipID, $memType, $isTest, $numTerms, $membershipContribution, $form);
+        if (!empty($membershipContribution)) {
+          $pendingStatus = CRM_Core_OptionGroup::getValue('contribution_status', 'Pending', 'name');
+          $pending = ($membershipContribution->contribution_status_id == $pendingStatus) ? TRUE : FALSE;
+        }
+        else {
+          $pending = $isPending;
+        }
+        $contributionRecurID = isset($form->_params['contributionRecurID']) ? $form->_params['contributionRecurID'] : NULL;
+
+        $membershipSource = NULL;
+        if (!empty($form->_params['membership_source'])) {
+          $membershipSource = $form->_params['membership_source'];
+        }
+        elseif (isset($form->_values['title']) && !empty($form->_values['title'])) {
+          $membershipSource = ts('Online Contribution:') . ' ' . $form->_values['title'];
+        }
+        $isPayLater = NULL;
+        if (isset($form->_params)) {
+          $isPayLater = CRM_Utils_Array::value('is_pay_later', $form->_params);
+        }
+        $campaignId = NULL;
+        if (isset($form->_values) && is_array($form->_values) && !empty($form->_values)) {
+          $campaignId = CRM_Utils_Array::value('campaign_id', $form->_params);
+          if (!array_key_exists('campaign_id', $form->_params)) {
+            $campaignId = CRM_Utils_Array::value('campaign_id', $form->_values);
+          }
+        }
+
+        list($membership, $renewalMode, $dates) = self::renewMembership(
+          $contactID, $memType, $isTest,
+          date('YmdHis'), CRM_Utils_Array::value('cms_contactID', $membershipParams),
+          $customFieldsFormatted,
+          $numTerms, $membershipID, $pending,
+          $contributionRecurID, $membershipSource, $isPayLater, $campaignId
+        );
+        $form->set('renewal_mode', $renewalMode);
+        if (!empty($dates)) {
+          $form->assign('mem_start_date',
+            CRM_Utils_Date::customFormat($dates['start_date'], '%Y%m%d')
+          );
+          $form->assign('mem_end_date',
+            CRM_Utils_Date::customFormat($dates['end_date'], '%Y%m%d')
+          );
+        }
+
+        if (!empty($membershipContribution)) {
+          // update recurring id for membership record
+          self::updateRecurMembership($membership, $membershipContribution);
+          self::linkMembershipPayment($membership, $membershipContribution);
+        }
       }
       if ($form->_priceSetId && !empty($form->_useForMember) && !empty($form->_lineItem)) {
         foreach ($form->_lineItem[$form->_priceSetId] as & $priceFieldOp) {
@@ -1418,7 +1277,6 @@ AND civicrm_membership.is_test = %2";
     foreach ($createdMemberships as $createdMembership) {
       CRM_Core_BAO_CustomValueTable::postProcess(
         $form->_params,
-        CRM_Core_DAO::$_nullArray,
         'civicrm_membership',
         $createdMembership->id,
         'Membership'
@@ -1431,7 +1289,7 @@ AND civicrm_membership.is_test = %2";
     }
 
     //CRM-15232: Check if membership is created and on the basis of it use
-    //membership reciept template to send payment reciept
+    //membership receipt template to send payment receipt
     if (count($createdMemberships)) {
       $form->_values['isMembership'] = TRUE;
     }
@@ -1449,14 +1307,15 @@ AND civicrm_membership.is_test = %2";
       $form->_values['contribution_id'] = $membershipContributionID;
     }
 
-    // Do not send an email if Recurring transaction is done via Direct Mode
-    // Email will we sent when the IPN is received.
-    if (!empty($form->_params['is_recur']) && $form->_contributeMode == 'direct') {
-      if (!empty($membershipContribution->trxn_id)) {
+    if ($form->_contributeMode == 'direct') {
+      if (CRM_Utils_Array::value('payment_status_id', $paymentResult) == 1) {
+        // Refer to CRM-16737. Payment processors 'should' return payment_status_id
+        // to denote the outcome of the transaction.
         try {
           civicrm_api3('contribution', 'completetransaction', array(
-            'id' => $membershipContribution->id,
-            'trxn_id' => $membershipContribution->trxn_id,
+            'id' => $paymentResult['contribution']->id,
+            'trxn_id' => $paymentResult['contribution']->trxn_id,
+            'is_transactional' => FALSE,
           ));
         }
         catch (CiviCRM_API3_Exception $e) {
@@ -1465,6 +1324,8 @@ AND civicrm_membership.is_test = %2";
           CRM_Core_Error::debug_log_message('contribution ' . $membershipContribution->id . ' not completed with trxn_id ' . $membershipContribution->trxn_id . ' and message ' . $e->getMessage());
         }
       }
+      // Do not send an email if Recurring transaction is done via Direct Mode
+      // Email will we sent when the IPN is received.
       return;
     }
 
@@ -1477,12 +1338,10 @@ AND civicrm_membership.is_test = %2";
   }
 
   /**
-   * Function for updating a membership record's contribution_recur_id
+   * Function for updating a membership record's contribution_recur_id.
    *
    * @param CRM_Member_DAO_Membership $membership
    * @param \CRM_Contribute_BAO_Contribution|\CRM_Contribute_DAO_Contribution $contribution
-   *
-   * @return void
    */
   static public function updateRecurMembership(CRM_Member_DAO_Membership $membership, CRM_Contribute_BAO_Contribution $contribution) {
 
@@ -1499,80 +1358,6 @@ AND civicrm_membership.is_test = %2";
     CRM_Core_DAO::executeQuery($sql, $params);
   }
 
-  /**
-   * @deprecated
-   * A wrapper for renewing memberships from a form - including the form in the membership processing adds complexity
-   * as the forms are being forced to pretend similarity
-   * Try to call the renewMembership directly
-   * @todo - this form method needs to have the interaction with the form layer removed from it
-   * as a BAO function. Note that the api now supports membership renewals & it is not clear this function does anything
-   * not done by the membership.create api (with a lot less unit tests)
-   *
-   * This method will renew / create the membership depending on
-   * whether the given contact has a membership or not. And will add
-   * the modified dates for membership and in the log table.
-   *
-   * @param int $contactID
-   *   Id of the contact.
-   * @param int $membershipTypeID
-   *   Id of the new membership type.
-   * @param bool $is_test
-   *   If this is test contribution or live contribution.
-   * @param CRM_Core_Form $form
-   *   Form object.
-   * @param null $changeToday
-   * @param int $modifiedID
-   *   Individual contact id in case of On Behalf signup (CRM-4027 ).
-   * @param null $customFieldsFormatted
-   * @param int $numRenewTerms
-   *   How many membership terms are being added to end date (default is 1).
-   * @param int $membershipID
-   *   Membership ID, this should always be passed in & optionality should be removed.
-   *
-   * @throws CRM_Core_Exception
-   *
-   */
-  public static function renewMembershipFormWrapper(
-    $contactID,
-    $membershipTypeID,
-    $is_test,
-    &$form,
-    $changeToday = NULL,
-    $modifiedID = NULL,
-    $customFieldsFormatted = NULL,
-    $numRenewTerms = 1,
-    $membershipID = NULL,
-    $pending = FALSE
-  ) {
-    $statusFormat = '%Y-%m-%d';
-    $format = '%Y%m%d';
-    $ids = array();
-    //@todo would be better to make $membershipID a compulsory function param & make form layer responsible for extracting it
-    if (!$membershipID && isset($form->_membershipId)) {
-      $membershipID = $form->_membershipId;
-    }
-
-    //get all active statuses of membership.
-    $allStatus = CRM_Member_PseudoConstant::membershipStatus();
-
-    $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID);
-
-    // check is it pending. - CRM-4555
-    list($pending, $contributionRecurID, $changeToday, $membershipSource, $isPayLater, $campaignId) = self::extractFormValues($form, $changeToday, $membershipTypeDetails, $pending);
-    list($membership, $renewalMode, $dates) = self::renewMembership($contactID, $membershipTypeID, $is_test, $changeToday, $modifiedID, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $allStatus, $membershipTypeDetails, $contributionRecurID, $format, $membershipSource, $ids, $statusFormat, $isPayLater, $campaignId);
-    $form->set('renewal_mode', $renewalMode);
-    if (!empty($dates)) {
-      $form->assign('mem_start_date',
-        CRM_Utils_Date::customFormat($dates['start_date'], $format)
-      );
-      $form->assign('mem_end_date',
-        CRM_Utils_Date::customFormat($dates['end_date'], $format)
-      );
-    }
-    return $membership;
-
-  }
-
   /**
    * Method to fix membership status of stale membership.
    *
@@ -1588,8 +1373,6 @@ AND civicrm_membership.is_test = %2";
    *   Array of month, day, year.
    *   values in case today needs
    *   to be customised, null otherwise
-   *
-   * @return void
    */
   public static function fixMembershipStatusBeforeRenew(&$currentMembership, $changeToday) {
     $today = NULL;
@@ -1714,6 +1497,7 @@ SELECT c.contribution_page_id as pageID
 
   /**
    * Get list of membership fields for profile.
+   *
    * For now we only allow custom membership fields to be in
    * profile
    *
@@ -1760,16 +1544,19 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
   }
 
   /**
-   * Create memberships for related contacts.
-   * takes into account the maximum related memberships
+   * Create memberships for related contacts, taking into account the maximum related memberships.
    *
    * @param array $params
    *   Array of key - value pairs.
    * @param CRM_Core_DAO $dao
    *   Membership object.
    *
-   * @return null|array
-   *   array of memberships if created
+   * @param bool $reset
+   *
+   * @return array|null
+   *   Membership details, if created.
+   *
+   * @throws \CRM_Core_Exception
    */
   public static function createRelatedMemberships(&$params, &$dao, $reset = FALSE) {
     static $relatedContactIds = array();
@@ -1861,7 +1648,8 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
       unset($params['max_related']);
       // Number of inherited memberships available - NULL is interpreted as unlimited, '0' as none
       $available = ($membership->max_related == NULL ? PHP_INT_MAX : $membership->max_related);
-      $queue = array(); // will be used to queue potential memberships to be created
+      // will be used to queue potential memberships to be created.
+      $queue = array();
 
       foreach ($relatedContacts as $contactId => $relationshipStatus) {
         //use existing membership record.
@@ -1956,14 +1744,23 @@ WHERE  civicrm_membership.contact_id = civicrm_contact.id
   }
 
   /**
+   * Build an array of available membership types.
+   *
    * @param CRM_Core_Form $form
    * @param int $membershipTypeID
+   * @param bool $activeOnly
+   *   Do we only want active ones?
+   *   (probably this should default to TRUE but as a newly added parameter we are leaving default b
+   *   behaviour unchanged).
    *
    * @return array
    */
-  public static function &buildMembershipTypeValues(&$form, $membershipTypeID = NULL) {
+  public static function buildMembershipTypeValues(&$form, $membershipTypeID = NULL, $activeOnly = FALSE) {
     $whereClause = " WHERE domain_id = " . CRM_Core_Config::domainID();
 
+    if ($activeOnly) {
+      $whereClause .= " AND is_active = 1 ";
+    }
     if (is_array($membershipTypeID)) {
       $allIDs = implode(',', $membershipTypeID);
       $whereClause .= " AND id IN ( $allIDs )";
@@ -1994,6 +1791,8 @@ FROM   civicrm_membership_type
       'relationship_type_id',
       'relationship_direction',
       'max_related',
+      'duration_unit',
+      'duration_interval',
     );
 
     while ($dao->fetch()) {
@@ -2039,8 +1838,7 @@ FROM   civicrm_membership_type
   }
 
   /**
-   * Check whether payment processor supports
-   * cancellation of membership subscription
+   * Check whether payment processor supports cancellation of membership subscription.
    *
    * @param int $mid
    *   Membership id.
@@ -2096,8 +1894,9 @@ LEFT JOIN civicrm_membership mem ON ( cr.id = mem.contribution_recur_id )
   }
 
   /**
-   * Get membership joins for a specified membership
-   * type.  Specifically, retrieves a count of still current memberships whose
+   * Get membership joins for a specified membership type.
+   *
+   * Specifically, retrieves a count of still current memberships whose
    * join_date and start_date are within a specified date range.  Dates match
    * the pattern "yyyy-mm-dd".
    *
@@ -2148,8 +1947,9 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
   }
 
   /**
-   * Get membership renewals for a specified membership
-   * type.  Specifically, retrieves a count of still current memberships
+   * Get membership renewals for a specified membership type.
+   *
+   * Specifically, retrieves a count of still current memberships
    * whose join_date is before and start_date is within a specified date
    * range.  Dates match the pattern "yyyy-mm-dd".
    *
@@ -2204,7 +2004,7 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
    * @param int $contactID
    * @param CRM_Contribute_Form_Contribution_Confirm $form
    * @param array $tempParams
-   * @param $isTest
+   * @param bool $isTest
    * @param array $lineItems
    * @param $minimumFee
    * @param int $financialTypeID
@@ -2216,9 +2016,7 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
   public static function processSecondaryFinancialTransaction($contactID, &$form, $tempParams, $isTest, $lineItems, $minimumFee, $financialTypeID) {
     $financialType = new CRM_Financial_DAO_FinancialType();
     $financialType->id = $financialTypeID;
-    if (!$financialType->find(TRUE)) {
-      CRM_Core_Error::fatal(ts("Could not find a system table"));
-    }
+    $financialType->find(TRUE);
     $tempParams['amount'] = $minimumFee;
     $tempParams['invoiceID'] = md5(uniqid(rand(), TRUE));
 
@@ -2268,7 +2066,7 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
     //so for differentiating membership contribution from
     //main contribution.
     $form->_params['separate_membership_payment'] = 1;
-    $membershipContribution = CRM_Contribute_Form_Contribution_Confirm::processContribution($form,
+    $membershipContribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($form,
       $tempParams,
       $result,
       $contactID,
@@ -2276,7 +2074,8 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
       $pending,
       TRUE,
       $isTest,
-      $lineItems
+      $lineItems,
+      $form->_bltID
     );
     return $membershipContribution;
   }
@@ -2294,39 +2093,6 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
     ));
   }
 
-  /**
-   * @param array $membershipParams
-   * @param int $contactID
-   * @param $customFieldsFormatted
-   * @param int $membershipID
-   * @param $memType
-   * @param $isTest
-   * @param $numTerms
-   * @param $membershipContribution
-   * @param CRM_Core_Form $form
-   *
-   * @return array
-   */
-  public static function createOrRenewMembership($membershipParams, $contactID, $customFieldsFormatted, $membershipID, $memType, $isTest, $numTerms, $membershipContribution, &$form) {
-    if (!empty($membershipContribution)) {
-      $pending = ($membershipContribution->contribution_status_id == 2) ? TRUE : FALSE;
-    }
-    $membership = self::renewMembershipFormWrapper($contactID, $memType,
-      $isTest, $form, NULL,
-      CRM_Utils_Array::value('cms_contactID', $membershipParams),
-      $customFieldsFormatted, $numTerms,
-      $membershipID, $pending
-    );
-
-    if (!empty($membershipContribution)) {
-      // update recurring id for membership record
-      self::updateRecurMembership($membership, $membershipContribution);
-
-      self::linkMembershipPayment($membership, $membershipContribution);
-    }
-    return $membership;
-  }
-
   /**
    * Turn array of errors into message string.
    *
@@ -2343,61 +2109,6 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
     return ts('Payment Processor Error message') . ': ' . implode('<br/>', $message);
   }
 
-  /**
-   * Extract relevant values from the form so we can separate form logic from BAO logcis.
-   *
-   * @param CRM_Core_Form $form
-   * @param $changeToday
-   * @param $membershipTypeDetails
-   *
-   * @return array
-   */
-  public static function extractFormValues($form, $changeToday, $membershipTypeDetails, $pending = FALSE) {
-    //@todo this is a BAO function & should not inspect the form - the form should do this
-    // & pass required params to the BAO
-    if (CRM_Utils_Array::value('minimum_fee', $membershipTypeDetails) > 0.0) {
-      if (((isset($form->_contributeMode) && $form->_contributeMode == 'notify') || !empty($form->_params['is_pay_later'])
-        ) &&
-        (($form->_values['is_monetary'] && $form->_amount > 0.0) ||
-          CRM_Utils_Array::value('record_contribution', $form->_params)
-        )
-      ) {
-        $pending = TRUE;
-      }
-    }
-    $contributionRecurID = isset($form->_params['contributionRecurID']) ? $form->_params['contributionRecurID'] : NULL;
-
-    //we renew expired membership, CRM-6277
-    if (!$changeToday) {
-      if ($form->get('renewalDate')) {
-        $changeToday = $form->get('renewalDate');
-      }
-      elseif (get_class($form) == 'CRM_Contribute_Form_Contribution_Confirm') {
-        $changeToday = date('YmdHis');
-      }
-    }
-
-    $membershipSource = NULL;
-    if (!empty($form->_params['membership_source'])) {
-      $membershipSource = $form->_params['membership_source'];
-    }
-    elseif (isset($form->_values['title']) && !empty($form->_values['title'])) {
-      $membershipSource = ts('Online Contribution:') . ' ' . $form->_values['title'];
-    }
-    $isPayLater = NULL;
-    if (isset($form->_params)) {
-      $isPayLater = CRM_Utils_Array::value('is_pay_later', $form->_params);
-    }
-    $campaignId = NULL;
-    if (isset($form->_values) && is_array($form->_values) && !empty($form->_values)) {
-      $campaignId = CRM_Utils_Array::value('campaign_id', $form->_params);
-      if (!array_key_exists('campaign_id', $form->_params)) {
-        $campaignId = CRM_Utils_Array::value('campaign_id', $form->_values);
-      }
-    }
-    return array($pending, $contributionRecurID, $changeToday, $membershipSource, $isPayLater, $campaignId);
-  }
-
   /**
    * @param int $contactID
    * @param int $membershipTypeID
@@ -2408,21 +2119,20 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
    * @param $numRenewTerms
    * @param int $membershipID
    * @param $pending
-   * @param $allStatus
-   * @param array $membershipTypeDetails
    * @param int $contributionRecurID
-   * @param $format
    * @param $membershipSource
-   * @param $ids
-   * @param $statusFormat
    * @param $isPayLater
    * @param int $campaignId
    *
    * @throws CRM_Core_Exception
    * @return array
    */
-  public static function renewMembership($contactID, $membershipTypeID, $is_test, $changeToday, $modifiedID, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $allStatus, $membershipTypeDetails, $contributionRecurID, $format, $membershipSource, $ids, $statusFormat, $isPayLater, $campaignId) {
+  public static function renewMembership($contactID, $membershipTypeID, $is_test, $changeToday, $modifiedID, $customFieldsFormatted, $numRenewTerms, $membershipID, $pending, $contributionRecurID, $membershipSource, $isPayLater, $campaignId) {
     $renewalMode = $updateStatusId = FALSE;
+    $allStatus = CRM_Member_PseudoConstant::membershipStatus();
+    $format = '%Y%m%d';
+    $statusFormat = '%Y-%m-%d';
+    $membershipTypeDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($membershipTypeID);
     $dates = array();
     // CRM-7297 - allow membership type to be be changed during renewal so long as the parent org of new membershipType
     // is the same as the parent org of an existing membership of the contact
@@ -2476,7 +2186,7 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
         else {
           $logParams['modified_id'] = $membership->contact_id;
         }
-        CRM_Member_BAO_MembershipLog::add($logParams, CRM_Core_DAO::$_nullArray);
+        CRM_Member_BAO_MembershipLog::add($logParams);
 
         if (!empty($contributionRecurID)) {
           CRM_Core_DAO::setFieldValue('CRM_Member_DAO_Membership', $membership->id,
@@ -2654,9 +2364,7 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
    * Process price set and line items.
    *
    * @param int $membershipId
-   * @param $lineItem
-   *
-   * @return void
+   * @param array $lineItem
    */
   public function processPriceSet($membershipId, $lineItem) {
     //FIXME : need to move this too
@@ -3006,14 +2714,14 @@ WHERE      civicrm_membership.is_test = 0";
    * @param CRM_Core_Form $qf
    * @param array $membershipType
    *   Array with membership type and organization.
-   * @param int $priceSetId
    *
+   * @return int $priceSetId
    */
-  public static function createLineItems(&$qf, $membershipType, &$priceSetId) {
+  public static function createLineItems(&$qf, $membershipType) {
     $qf->_priceSetId = $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', 'default_membership_type_amount', 'id', 'name');
-    if ($priceSetId) {
-      $qf->_priceSet = $priceSets = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetId));
-    }
+    $qf->_priceSet = $priceSets = current(CRM_Price_BAO_PriceSet::getSetDetail($priceSetId));
+
+    // The name of the price field corresponds to the membership_type organization contact.
     $editedFieldParams = array(
       'price_set_id' => $priceSetId,
       'name' => $membershipType[0],
@@ -3040,6 +2748,7 @@ WHERE      civicrm_membership.is_test = 0";
 
     $fieldID = key($qf->_priceSet['fields']);
     $qf->_params['price_' . $fieldID] = CRM_Utils_Array::value('id', $editedResults);
+    return $priceSetId;
   }
 
   /**