Merge pull request #23075 from civicrm/5.48
[civicrm-core.git] / CRM / Member / BAO / Membership.php
index 8e95311d446b1dfe3a4e6c53f9fcdf9506b565a7..7daf555876735d2be9f89262ec2be52bc86ad9ba 100644 (file)
@@ -30,13 +30,6 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
 
   public static $_signupActType = NULL;
 
-  /**
-   * Class constructor.
-   */
-  public function __construct() {
-    parent::__construct();
-  }
-
   /**
    * Takes an associative array and creates a membership object.
    *
@@ -200,13 +193,11 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
    * @param array $values
    *   Output values of the object.
    * @param bool $active
-   *   Do you want only active memberships to.
-   *                        be returned
+   *   Return only memberships with an 'is_current_member' status.
    *
-   * @return CRM_Member_BAO_Membership|null
-   *   The found object or null
+   * @return CRM_Member_BAO_Membership[]|null
    */
-  public static function &getValues(&$params, &$values, $active = FALSE) {
+  public static function getValues($params, &$values, $active = FALSE) {
     if (empty($params)) {
       return NULL;
     }
@@ -289,9 +280,9 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
       else {
         $calcDates = [];
       }
-      $params['start_date'] = $params['start_date'] ?? ($calcDates['start_date'] ?? 'null');
-      $params['end_date'] = $params['end_date'] ?? ($calcDates['end_date'] ?? 'null');
-      $params['join_date'] = $params['join_date'] ?? ($calcDates['join_date'] ?? 'null');
+      $params['start_date'] = empty($params['start_date']) ? ($calcDates['start_date'] ?? 'null') : $params['start_date'];
+      $params['end_date'] = empty($params['end_date']) ? ($calcDates['end_date'] ?? 'null') : $params['end_date'];
+      $params['join_date'] = empty($params['join_date']) ? ($calcDates['join_date'] ?? 'null') : $params['join_date'];
 
       //fix for CRM-3570, during import exclude the statuses those having is_admin = 1
       $excludeIsAdmin = $params['exclude_is_admin'] ?? FALSE;
@@ -339,70 +330,80 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
     }
 
     $params['membership_id'] = $membership->id;
-    // @todo further cleanup required to remove use of $ids['contribution'] from here
-    if (isset($ids['membership'])) {
-      $contributionID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment',
-        $membership->id,
-        'contribution_id',
-        'membership_id'
-      );
-      // @todo this is a temporary step to removing $ids['contribution'] completely
-      if (empty($params['contribution_id']) && !empty($contributionID)) {
-        $params['contribution_id'] = $contributionID;
+    // For api v4 we skip all of this stuff. There is an expectation that v4 users either use
+    // the order api, or handle any financial / related processing themselves.
+    // Note that the processing below is fairly intertwined with core usage and in some places
+    // problematic or to be removed.
+    // Note the choice of 'version' as a parameter is to make it
+    // unavailable through apiv3.
+    // once we are rid of direct calls to the BAO::create from core
+    // we will deprecate this stuff into the v3 api.
+    if (($params['version'] ?? 0) !== 4) {
+      // @todo further cleanup required to remove use of $ids['contribution'] from here
+      if (isset($ids['membership'])) {
+        $contributionID = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipPayment',
+          $membership->id,
+          'contribution_id',
+          'membership_id'
+        );
+        // @todo this is a temporary step to removing $ids['contribution'] completely
+        if (empty($params['contribution_id']) && !empty($contributionID)) {
+          $params['contribution_id'] = $contributionID;
+        }
       }
-    }
-
-    // This code ensures a line item is created but it is recommended you pass in 'skipLineItem' or 'line_item'
-    if (empty($params['line_item']) && !empty($params['membership_type_id']) && empty($params['skipLineItem'])) {
-      CRM_Price_BAO_LineItem::getLineItemArray($params, NULL, 'membership', $params['membership_type_id']);
-    }
-    $params['skipLineItem'] = TRUE;
 
-    // Record contribution for this membership and create a MembershipPayment
-    // @todo deprecate this.
-    if (!empty($params['contribution_status_id'])) {
-      $memInfo = array_merge($params, ['membership_id' => $membership->id]);
-      $params['contribution'] = self::recordMembershipContribution($memInfo);
-    }
-
-    // If the membership has no associated contribution then we ensure
-    // the line items are 'correct' here. This is a lazy legacy
-    // hack whereby they are deleted and recreated
-    if (empty($contributionID)) {
-      if (!empty($params['lineItems'])) {
-        $params['line_item'] = $params['lineItems'];
+      // This code ensures a line item is created but it is recommended you pass in 'skipLineItem' or 'line_item'
+      if (empty($params['line_item']) && !empty($params['membership_type_id']) && empty($params['skipLineItem'])) {
+        CRM_Price_BAO_LineItem::getLineItemArray($params, NULL, 'membership', $params['membership_type_id']);
       }
-      // do cleanup line items if membership edit the Membership type.
-      if (!empty($ids['membership'])) {
-        CRM_Price_BAO_LineItem::deleteLineItems($ids['membership'], 'civicrm_membership');
+      $params['skipLineItem'] = TRUE;
+
+      // Record contribution for this membership and create a MembershipPayment
+      // @todo deprecate this.
+      if (!empty($params['contribution_status_id'])) {
+        $memInfo = array_merge($params, ['membership_id' => $membership->id]);
+        $params['contribution'] = self::recordMembershipContribution($memInfo);
       }
-      // @todo - we should ONLY do the below if a contribution is created. Let's
-      // get some deprecation notices in here & see where it's hit & work to eliminate.
-      // This could happen if there is no contribution or we are in one of many
-      // weird and wonderful flows. This is scary code. Keep adding tests.
-      if (!empty($params['line_item']) && empty($params['contribution_id'])) {
 
-        foreach ($params['line_item'] as $priceSetId => $lineItems) {
-          foreach ($lineItems as $lineIndex => $lineItem) {
-            $lineMembershipType = $lineItem['membership_type_id'] ?? NULL;
-            if (!empty($params['contribution'])) {
-              $params['line_item'][$priceSetId][$lineIndex]['contribution_id'] = $params['contribution']->id;
-            }
-            if ($lineMembershipType && $lineMembershipType == ($params['membership_type_id'] ?? NULL)) {
-              $params['line_item'][$priceSetId][$lineIndex]['entity_id'] = $membership->id;
-              $params['line_item'][$priceSetId][$lineIndex]['entity_table'] = 'civicrm_membership';
-            }
-            elseif (!$lineMembershipType && !empty($params['contribution'])) {
-              $params['line_item'][$priceSetId][$lineIndex]['entity_id'] = $params['contribution']->id;
-              $params['line_item'][$priceSetId][$lineIndex]['entity_table'] = 'civicrm_contribution';
+      // If the membership has no associated contribution then we ensure
+      // the line items are 'correct' here. This is a lazy legacy
+      // hack whereby they are deleted and recreated
+      if (empty($contributionID)) {
+        if (!empty($params['lineItems'])) {
+          $params['line_item'] = $params['lineItems'];
+        }
+        // do cleanup line items if membership edit the Membership type.
+        if (!empty($ids['membership'])) {
+          CRM_Price_BAO_LineItem::deleteLineItems($ids['membership'], 'civicrm_membership');
+        }
+        // @todo - we should ONLY do the below if a contribution is created. Let's
+        // get some deprecation notices in here & see where it's hit & work to eliminate.
+        // This could happen if there is no contribution or we are in one of many
+        // weird and wonderful flows. This is scary code. Keep adding tests.
+        if (!empty($params['line_item']) && empty($params['contribution_id'])) {
+
+          foreach ($params['line_item'] as $priceSetId => $lineItems) {
+            foreach ($lineItems as $lineIndex => $lineItem) {
+              $lineMembershipType = $lineItem['membership_type_id'] ?? NULL;
+              if (!empty($params['contribution'])) {
+                $params['line_item'][$priceSetId][$lineIndex]['contribution_id'] = $params['contribution']->id;
+              }
+              if ($lineMembershipType && $lineMembershipType == ($params['membership_type_id'] ?? NULL)) {
+                $params['line_item'][$priceSetId][$lineIndex]['entity_id'] = $membership->id;
+                $params['line_item'][$priceSetId][$lineIndex]['entity_table'] = 'civicrm_membership';
+              }
+              elseif (!$lineMembershipType && !empty($params['contribution'])) {
+                $params['line_item'][$priceSetId][$lineIndex]['entity_id'] = $params['contribution']->id;
+                $params['line_item'][$priceSetId][$lineIndex]['entity_table'] = 'civicrm_contribution';
+              }
             }
           }
+          CRM_Price_BAO_LineItem::processPriceSet(
+            $membership->id,
+            $params['line_item'],
+            $params['contribution'] ?? NULL
+          );
         }
-        CRM_Price_BAO_LineItem::processPriceSet(
-          $membership->id,
-          $params['line_item'],
-          $params['contribution'] ?? NULL
-        );
       }
     }
 
@@ -641,7 +642,6 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
 
     $transaction = new CRM_Core_Transaction();
 
-    $results = NULL;
     //delete activity record
     $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, FALSE, FALSE, 'name');
 
@@ -682,11 +682,11 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
    * @param int $ownerMembershipId
    * @param int $contactId
    *
-   * @return null
+   * @return void
    */
   public static function deleteRelatedMemberships($ownerMembershipId, $contactId = NULL) {
     if (!$ownerMembershipId && !$contactId) {
-      return FALSE;
+      return;
     }
 
     $membership = new CRM_Member_DAO_Membership();
@@ -712,7 +712,7 @@ INNER JOIN  civicrm_membership_type type ON ( type.id = membership.membership_ty
    * @param string $status
    *   Active or inactive.
    *
-   * @return array
+   * @return array|null
    *   array of memberships based on status
    */
   public static function activeMembers($memberships, $status = 'active') {
@@ -2185,13 +2185,12 @@ INNER JOIN  civicrm_contact contact ON ( contact.id = membership.contact_id AND
    * @param bool $all
    *   if more than one payment associated with membership id need to be returned.
    *
-   * @return int|int[]
+   * @return int|int[]|null
    *   contribution id
    * @todo we should get this off the line item
    *
    */
   public static function getMembershipContributionId($membershipId, $all = FALSE) {
-
     $membershipPayment = new CRM_Member_DAO_MembershipPayment();
     $membershipPayment->membership_id = $membershipId;
     if ($all && $membershipPayment->find()) {
@@ -2290,8 +2289,6 @@ WHERE {$whereClause}";
      AND {$membershipStatusClause}
      AND civicrm_membership.owner_membership_id IS NULL ";
 
-    $allMembershipTypes = CRM_Member_BAO_MembershipType::getAllMembershipTypes();
-
     $dao2 = CRM_Core_DAO::executeQuery($query, $queryParams);
 
     while ($dao2->fetch()) {
@@ -2299,12 +2296,10 @@ WHERE {$whereClause}";
 
       // CRM-7248: added excludeIsAdmin param to the following fn call to prevent moving to admin statuses
       //get the membership status as per id.
-      $newStatus = civicrm_api3('membership_status', 'calc',
-        [
-          'membership_id' => $dao2->membership_id,
-          'ignore_admin_only' => TRUE,
-        ], TRUE
-      );
+      $newStatus = civicrm_api3('membership_status', 'calc', [
+        'membership_id' => $dao2->membership_id,
+        'ignore_admin_only' => TRUE,
+      ]);
       $statusId = $newStatus['id'] ?? NULL;
 
       //process only when status change.
@@ -2367,8 +2362,7 @@ WHERE {$whereClause}";
   }
 
   /**
-   * Returns the membership types for a particular contact
-   * who has lifetime membership without end date.
+   * Returns the membership types for a contact, optionally filtering to lifetime memberships only.
    *
    * @param int $contactID
    * @param bool $isTest
@@ -2376,33 +2370,26 @@ WHERE {$whereClause}";
    *
    * @return array
    */
-  public static function getAllContactMembership($contactID, $isTest = FALSE, $onlyLifeTime = FALSE) {
+  public static function getAllContactMembership($contactID, $isTest = FALSE, $onlyLifeTime = FALSE) : array {
     $contactMembershipType = [];
     if (!$contactID) {
       return $contactMembershipType;
     }
 
-    $dao = new CRM_Member_DAO_Membership();
-    $dao->contact_id = $contactID;
-    $pendingStatusId = array_search('Pending', CRM_Member_PseudoConstant::membershipStatus());
-    $dao->whereAdd("status_id != $pendingStatusId");
-
-    if ($isTest) {
-      $dao->is_test = $isTest;
-    }
-    else {
-      $dao->whereAdd('is_test IS NULL OR is_test = 0');
-    }
+    $membershipQuery = \Civi\Api4\Membership::get(FALSE)
+      ->addWhere('contact_id', '=', $contactID)
+      ->addWhere('status_id:name', '<>', 'Pending')
+      ->addWhere('is_test', '=', $isTest)
+      //CRM-4297
+      ->addOrderBy('end_date', 'DESC');
 
     if ($onlyLifeTime) {
-      $dao->whereAdd('end_date IS NULL');
+      // membership#14 - use duration_unit for calculating lifetime, not join/end date.
+      $membershipQuery->addWhere('membership_type_id.duration_unit', '=', 'lifetime');
     }
-
-    $dao->find();
-    while ($dao->fetch()) {
-      $membership = [];
-      CRM_Core_DAO::storeValues($dao, $membership);
-      $contactMembershipType[$dao->membership_type_id] = $membership;
+    $memberships = $membershipQuery->execute();
+    foreach ($memberships as $membership) {
+      $contactMembershipType[$membership['membership_type_id']] = $membership;
     }
     return $contactMembershipType;
   }