--CRM-16188, added function to check the total amount and sum of line total
[civicrm-core.git] / CRM / Contribute / BAO / Contribution.php
index 8adabd06d9abfa31bf03c0cb188e31d6c5dc4495..0f30b667d3658f040e4215f9918d938ee78c72ab 100644 (file)
@@ -129,23 +129,29 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
       }
     }
 
+    //set defaults in create mode
+    if (!$contributionID) {
+      CRM_Core_DAO::setCreateDefaults($params, self::getDefaults());
+    }
+
     //if contribution is created with cancelled or refunded status, add credit note id
     if (!empty($params['contribution_status_id'])) {
       $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
 
       if (($params['contribution_status_id'] == array_search('Refunded', $contributionStatus)
-          || $params['contribution_status_id'] == array_search('Cancelled', $contributionStatus))
+        || $params['contribution_status_id'] == array_search('Cancelled', $contributionStatus))
       ) {
         if (empty($params['creditnote_id']) || $params['creditnote_id'] == "null") {
           $params['creditnote_id'] = self::createCreditNoteId();
         }
       }
     }
-
-    //set defaults in create mode
-    if (!$contributionID) {
-      CRM_Core_DAO::setCreateDefaults($params, self::getDefaults());
+    else {
+      // Since the fee amount is expecting this (later on) ensure it is always set.
+      // It would only not be set for an update where it is unchanged.
+      $params['contribution_status_id'] = civicrm_api3('Contribution', 'getvalue', array('id' => $contributionID, 'return' => 'contribution_status_id'));
     }
+
     self::calculateMissingAmountParams($params, $contributionID);
 
     if (!empty($params['payment_instrument_id'])) {
@@ -201,7 +207,7 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
 
     if (self::isUpdateToRecurringContribution($params)) {
       CRM_Contribute_BAO_ContributionRecur::updateOnNewPayment(
-        $params['contribution_recur_id'],
+        (!empty($params['contribution_recur_id']) ? $params['contribution_recur_id'] : $params['prevContribution']->contribution_recur_id),
         $contributionStatus[$params['contribution_status_id']]
       );
     }
@@ -291,6 +297,32 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
     return $null;
   }
 
+  /**
+   * Get the values and resolve the most common mappings.
+   *
+   * Since contribution status is resolved in almost every function that calls getValues it makes
+   * sense to have an extra function to resolve it rather than repeat the code.
+   *
+   * Think carefully before adding more mappings to be resolved as there could be performance implications
+   * if this function starts to be called from more iterative functions.
+   *
+   * @param array $params
+   *   Input parameters to find object.
+   *
+   * @return array
+   *   Array of the found contribution.
+   * @throws CRM_Core_Exception
+   */
+  public static function getValuesWithMappings($params) {
+    $values = $ids = array();
+    $contribution = self::getValues($params, $values, $ids);
+    if (is_null($contribution)) {
+      throw new CRM_Core_Exception('No contribution found');
+    }
+    $values['contribution_status'] = CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $values['contribution_status_id']);
+    return $values;
+  }
+
   /**
    * Calculate net_amount & fee_amount if they are not set.
    *
@@ -778,6 +810,15 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
         ),
       );
 
+      // CRM-17787
+      $campaignTitle = array(
+        'contribution_campaign_title' => array(
+          'title' => ts('Campaign Title'),
+          'name' => 'campaign_title',
+          'where' => 'civicrm_campaign.title',
+          'data_type' => CRM_Utils_Type::T_STRING,
+        ),
+      );
       $softCreditFields = array(
         'contribution_soft_credit_name' => array(
           'name' => 'contribution_soft_credit_name',
@@ -822,7 +863,7 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
       );
 
       $fields = array_merge($impFields, $typeField, $contributionStatus, $contributionPage, $optionField, $expFieldProduct,
-        $expFieldsContrib, $contributionNote, $contributionRecurId, $extraFields, $softCreditFields, $financialAccount, $premiums,
+        $expFieldsContrib, $contributionNote, $contributionRecurId, $extraFields, $softCreditFields, $financialAccount, $premiums, $campaignTitle,
         CRM_Core_BAO_CustomField::getFieldsForImport('Contribution', FALSE, FALSE, FALSE, $checkPermission)
       );
 
@@ -2036,13 +2077,29 @@ LEFT JOIN  civicrm_contribution contribution ON ( componentPayment.contribution_
       if (!empty($input['amount'])) {
         $contribution->total_amount = $contributionParams['total_amount'] = $input['amount'];
       }
-      $templateContribution = civicrm_api3('Contribution', 'getsingle', array(
-        'contribution_recur_id' => $contributionParams['contribution_recur_id'],
-        'options' => array('limit' => 1),
-      ));
+      $templateContribution = CRM_Contribute_BAO_ContributionRecur::getTemplateContribution($contributionParams['contribution_recur_id']);
+      if (!empty($contributionParams['contribution_recur_id'])) {
+        $recurringContribution = civicrm_api3('ContributionRecur', 'getsingle', array(
+          'id' => $contributionParams['contribution_recur_id'],
+        ));
+        if (!empty($recurringContribution['campaign_id'])) {
+          // CRM-17718 the campaign id on the contribution recur record should get precedence.
+          $contributionParams['campaign_id'] = $recurringContribution['campaign_id'];
+        }
+        if (!empty($recurringContribution['financial_type_id'])) {
+          // CRM-17718 the campaign id on the contribution recur record should get precedence.
+          $contributionParams['financial_type_id'] = $recurringContribution['financial_type_id'];
+        }
+      }
       $contributionParams['skipLineItem'] = TRUE;
       $contributionParams['status_id'] = 'Pending';
-      $contributionParams['financial_type_id'] = $templateContribution['financial_type_id'];
+      if (isset($contributionParams['financial_type_id'])) {
+        // Give precedence to passed in type.
+        $contribution->financial_type_id = $contributionParams['financial_type_id'];
+      }
+      else {
+        $contributionParams['financial_type_id'] = $templateContribution['financial_type_id'];
+      }
       $contributionParams['contact_id'] = $templateContribution['contact_id'];
       $contributionParams['source'] = empty($templateContribution['source']) ? ts('Recurring contribution') : $templateContribution['source'];
       $createContribution = civicrm_api3('Contribution', 'create', $contributionParams);
@@ -2352,7 +2409,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
         $entityID = $ids['membership'][0];
       }
 
-      $template->assign('cancelSubscriptionUrl', $paymentObject->subscriptionURL($entityID, $entity));
+      $template->assign('cancelSubscriptionUrl', $paymentObject->subscriptionURL($entityID, $entity, 'cancel'));
       $template->assign('updateSubscriptionBillingUrl', $paymentObject->subscriptionURL($entityID, $entity, 'billing'));
       $template->assign('updateSubscriptionUrl', $paymentObject->subscriptionURL($entityID, $entity, 'update'));
 
@@ -2445,7 +2502,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
             $template->assign('is_separate_payment', 0);
 
             if ($recur && $paymentObject) {
-              $url = $paymentObject->subscriptionURL($membership->id, 'membership');
+              $url = $paymentObject->subscriptionURL($membership->id, 'membership', 'cancel');
               $template->assign('cancelSubscriptionUrl', $url);
               $url = $paymentObject->subscriptionURL($membership->id, 'membership', 'billing');
               $template->assign('updateSubscriptionBillingUrl', $url);
@@ -2888,7 +2945,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
 
       $paymentObject = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($contributionId, 'contribute', 'obj');
       if (!empty($paymentObject)) {
-        $supportsCancel[$cacheKeyString] = $paymentObject->isSupported('cancelSubscription') && !$isCancelled;
+        $supportsCancel[$cacheKeyString] = $paymentObject->supports('cancelRecurring') && !$isCancelled;
       }
     }
     return $supportsCancel[$cacheKeyString];
@@ -3060,9 +3117,13 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
       );
       if ($contributionStatus == 'Refunded') {
         $trxnParams['trxn_date'] = !empty($params['contribution']->cancel_date) ? $params['contribution']->cancel_date : date('YmdHis');
+        if (isset($params['refund_trxn_id'])) {
+          // CRM-17751 allow a separate trxn_id for the refund to be passed in via api & form.
+          $trxnParams['trxn_id'] = $params['refund_trxn_id'];
+        }
       }
       //CRM-16259, set is_payment flag for non pending status
-      if (!in_array(CRM_Utils_Array::value('contribution_status_id', $params), $pendingStatus)) {
+      if (!in_array($contributionStatus, $pendingStatus)) {
         $trxnParams['is_payment'] = 1;
       }
       if (!empty($params['payment_processor'])) {
@@ -3081,10 +3142,17 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
       $params['trxnParams'] = $trxnParams;
 
       if (!empty($params['prevContribution'])) {
+        $updated = FALSE;
         $params['trxnParams']['total_amount'] = $trxnParams['total_amount'] = $params['total_amount'] = $params['prevContribution']->total_amount;
         $params['trxnParams']['fee_amount'] = $params['prevContribution']->fee_amount;
         $params['trxnParams']['net_amount'] = $params['prevContribution']->net_amount;
-        $params['trxnParams']['trxn_id'] = $params['prevContribution']->trxn_id;
+        if (!isset($params['trxnParams']['trxn_id'])) {
+          // Actually I have no idea why we are overwriting any values from the previous contribution.
+          // (filling makes sense to me). However, only protecting this value as I really really know we
+          // don't want this one overwritten.
+          // CRM-17751.
+          $params['trxnParams']['trxn_id'] = $params['prevContribution']->trxn_id;
+        }
         $params['trxnParams']['status_id'] = $params['prevContribution']->contribution_status_id;
 
         if (!(($params['prevContribution']->contribution_status_id == array_search('Pending', $contributionStatuses)
@@ -3120,17 +3188,24 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
             $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount'];
             self::updateFinancialAccounts($params);
             $params['trxnParams']['to_financial_account_id'] = $trxnParams['to_financial_account_id'];
+            $updated = TRUE;
           }
         }
 
         //Update contribution status
         $params['trxnParams']['status_id'] = $params['contribution']->contribution_status_id;
-        $params['trxnParams']['trxn_id'] = $params['contribution']->trxn_id;
+        if (!isset($params['refund_trxn_id'])) {
+          // CRM-17751 This has previously been deliberately set. No explanation as to why one variant
+          // gets preference over another so I am only 'protecting' a very specific tested flow
+          // and letting natural justice take care of the rest.
+          $params['trxnParams']['trxn_id'] = $params['contribution']->trxn_id;
+        }
         if (!empty($params['contribution_status_id']) &&
           $params['prevContribution']->contribution_status_id != $params['contribution']->contribution_status_id
         ) {
           //Update Financial Records
           self::updateFinancialAccounts($params, 'changedStatus');
+          $updated = TRUE;
         }
 
         // change Payment Instrument for a Completed contribution
@@ -3152,6 +3227,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
               self::updateFinancialAccounts($params, 'changePaymentInstrument');
               $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount'];
               self::updateFinancialAccounts($params, 'changePaymentInstrument');
+              $updated = TRUE;
             }
           }
           elseif ((!CRM_Utils_System::isNull($params['contribution']->payment_instrument_id) ||
@@ -3162,6 +3238,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
             self::updateFinancialAccounts($params, 'changePaymentInstrument');
             $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount'];
             self::updateFinancialAccounts($params, 'changePaymentInstrument');
+            $updated = TRUE;
           }
           elseif (!CRM_Utils_System::isNull($params['contribution']->check_number) &&
             $params['contribution']->check_number != $params['prevContribution']->check_number
@@ -3174,6 +3251,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
             $params['trxnParams']['check_number'] = $params['contribution']->check_number;
             $params['total_amount'] = $params['trxnParams']['total_amount'] = $trxnParams['total_amount'];
             self::updateFinancialAccounts($params, 'changePaymentInstrument');
+            $updated = TRUE;
           }
         }
 
@@ -3188,6 +3266,22 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
           //Update Financial Records
           $params['trxnParams']['from_financial_account_id'] = NULL;
           self::updateFinancialAccounts($params, 'changedAmount');
+          $updated = TRUE;
+        }
+
+        if (!$updated) {
+          // Looks like we might have a data correction update.
+          // This would be a case where a transaction id has been entered but it is incorrect &
+          // the person goes back in & fixes it, as opposed to a new transaction.
+          // Currently the UI doesn't support multiple refunds against a single transaction & we are only supporting
+          // the data fix scenario.
+          // CRM-17751.
+          if (isset($params['refund_trxn_id'])) {
+            $refundIDs = CRM_Core_BAO_FinancialTrxn::getRefundTransactionIDs($params['id']);
+            if ($refundIDs['trxn_id'] != $params['refund_trxn_id']) {
+              civicrm_api3('FinancialTrxn', 'create', array('id' => $refundIDs['financialTrxnId'], 'trxn_id' => $params['refund_trxn_id']));
+            }
+          }
         }
       }
 
@@ -3562,11 +3656,14 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac
    *
    * @return null|object
    */
-  public static function recordAdditionalPayment($contributionId, $trxnsData, $paymentType = 'owed', $participantId = NULL) {
+  public static function recordAdditionalPayment($contributionId, $trxnsData, $paymentType = 'owed', $participantId = NULL, $updateStatus = TRUE) {
     $statusId = CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name');
     $getInfoOf['id'] = $contributionId;
     $defaults = array();
     $contributionDAO = CRM_Contribute_BAO_Contribution::retrieve($getInfoOf, $defaults, CRM_Core_DAO::$_nullArray);
+    if (!$participantId) {
+      $participantId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $contributionId, 'participant_id');
+    }
 
     if ($paymentType == 'owed') {
       // build params for recording financial trxn entry
@@ -3676,8 +3773,9 @@ WHERE eft.financial_trxn_id IN ({$trxnId}, {$baseTrxnId['financialTrxnId']})
       // the reason because it performs 'status change' related code execution for financial records
       // which in 'Pending Refund' => 'Completed' is not useful, instead specific financial record updates
       // are coded below i.e. just updating financial_item status to 'Paid'
-      $contributionDetails = CRM_Core_DAO::setFieldValue('CRM_Contribute_BAO_Contribution', $contributionId, 'contribution_status_id', $statusId);
-
+      if ($updateStatus) {
+        $contributionDetails = CRM_Core_DAO::setFieldValue('CRM_Contribute_BAO_Contribution', $contributionId, 'contribution_status_id', $statusId);
+      }
       // add financial item entry
       $financialItemStatus = CRM_Core_PseudoConstant::get('CRM_Financial_DAO_FinancialItem', 'status_id');
       $getLine['entity_id'] = $contributionDAO->id;
@@ -3952,7 +4050,7 @@ WHERE con.id = {$contributionId}
       empty($params['skipLineItem']) && !$isLineItem
     ) {
       $taxRateParams = $taxRates[$params['financial_type_id']];
-      $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($params['total_amount'], $taxRateParams);
+      $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount(CRM_Utils_Array::value('total_amount', $params), $taxRateParams);
       $params['tax_amount'] = round($taxAmount['tax_amount'], 2);
 
       // Get Line Item on update of contribution
@@ -3967,7 +4065,7 @@ WHERE con.id = {$contributionId}
           $params['line_item'][$setID][$priceFieldID]['tax_amount'] = $params['tax_amount'];
         }
       }
-      $params['total_amount'] = $params['total_amount'] + $params['tax_amount'];
+      $params['total_amount'] = CRM_Utils_Array::value('total_amount', $params) + $params['tax_amount'];
     }
     elseif (isset($params['api.line_item.create'])) {
       // Update total amount of contribution using lineItem
@@ -4159,6 +4257,20 @@ WHERE con.id = {$contributionId}
     }
   }
 
+  /**
+   * Is there only one line item attached to the contribution.
+   *
+   * @param int $id
+   *   Contribution ID.
+   *
+   * @return bool
+   * @throws \CiviCRM_API3_Exception
+   */
+  public static function isSingleLineItem($id) {
+    $lineItemCount = civicrm_api3('LineItem', 'getcount', array('contribution_id' => $id));
+    return ($lineItemCount == 1);
+  }
+
   /**
    * Complete an order.
    *
@@ -4196,27 +4308,37 @@ WHERE con.id = {$contributionId}
       'campaign_id',
       'receive_date',
     );
+    if (self::isSingleLineItem($primaryContributionID)) {
+      $inputContributionWhiteList[] = 'financial_type_id';
+    }
+
+    $participant = CRM_Utils_Array::value('participant', $objects);
+    $memberships = CRM_Utils_Array::value('membership', $objects);
+    $recurContrib = CRM_Utils_Array::value('contributionRecur', $objects);
+    $event = CRM_Utils_Array::value('event', $objects);
 
     $contributionParams = array_merge(array(
       'contribution_status_id' => 'Completed',
-      'financial_type_id' => $contribution->financial_type_id,
+      'source' => self::getRecurringContributionDescription($contribution, $event),
     ), array_intersect_key($input, array_fill_keys($inputContributionWhiteList, 1)
     ));
 
-    $participant = CRM_Utils_Array::value('participant', $objects);
-    $memberships = CRM_Utils_Array::value('membership', $objects);
-    $recurContrib = CRM_Utils_Array::value('contributionRecur', $objects);
     if (!empty($recurContrib->id)) {
       $contributionParams['contribution_recur_id'] = $recurContrib->id;
     }
+    $changeDate = CRM_Utils_Array::value('trxn_date', $input, date('YmdHis'));
+
+    if (empty($contributionParams['receive_date']) && $changeDate) {
+      $contributionParams['receive_date'] = $changeDate;
+    }
+
     self::repeatTransaction($contribution, $input, $contributionParams);
+    $contributionParams['financial_type_id'] = $contribution->financial_type_id;
 
     if (is_numeric($memberships)) {
       $memberships = array($objects['membership']);
     }
 
-    $changeDate = CRM_Utils_Array::value('trxn_date', $input, date('YmdHis'));
-
     $values = array();
     if (isset($input['is_email_receipt'])) {
       $values['is_email_receipt'] = $input['is_email_receipt'];
@@ -4224,11 +4346,10 @@ WHERE con.id = {$contributionId}
 
     if ($input['component'] == 'contribute') {
       if ($contribution->contribution_page_id) {
+        // Figure out what we gain from this.
         CRM_Contribute_BAO_ContributionPage::setValues($contribution->contribution_page_id, $values);
-        $contributionParams['source'] = ts('Online Contribution') . ': ' . $values['title'];
       }
       elseif ($recurContrib && $recurContrib->id) {
-        $contributionParams['contribution_page_id'] = NULL;
         $values['amount'] = $recurContrib->amount;
         $values['financial_type_id'] = $objects['contributionType']->id;
         $values['title'] = $source = ts('Offline Recurring Contribution');
@@ -4237,10 +4358,6 @@ WHERE con.id = {$contributionId}
         $values['receipt_from_email'] = $domainValues[1];
       }
 
-      if (empty($contributionParams['receive_date']) && $changeDate) {
-        $contributionParams['receive_date'] = $changeDate;
-      }
-
       if ($recurContrib && $recurContrib->id && !isset($input['is_email_receipt'])) {
         //CRM-13273 - is_email_receipt setting on recurring contribution should take precedence over contribution page setting
         // but CRM-16124 if $input['is_email_receipt'] is set then that should not be overridden.
@@ -4319,19 +4436,17 @@ LIMIT 1;";
             //we might be renewing membership,
             //so make status override false.
             $membershipParams['is_override'] = FALSE;
+            //CRM-17723 - reset static $relatedContactIds array()
+            $var = TRUE;
+            CRM_Member_BAO_Membership::createRelatedMemberships($var, $var, TRUE);
             civicrm_api3('Membership', 'create', $membershipParams);
-
-            //update related Memberships.
-            CRM_Member_BAO_Membership::updateRelatedMemberships($membership->id, $membershipParams);
           }
         }
       }
     }
     else {
       if (empty($input['IAmAHorribleNastyBeyondExcusableHackInTheCRMEventFORMTaskClassThatNeedsToBERemoved'])) {
-        $eventDetail = civicrm_api3('Event', 'getsingle', array('id' => $objects['event']->id));
-        $contributionParams['source'] = ts('Online Event Registration') . ': ' . $eventDetail['title'];
-        if ($eventDetail['is_email_confirm']) {
+        if ($event->is_email_confirm) {
           // @todo this should be set by the function that sends the mail after sending.
           $contributionParams['receipt_date'] = $changeDate;
         }
@@ -4542,4 +4657,193 @@ LIMIT 1;";
     }
   }
 
+  /**
+   * This function is used to record partial payments for contribution
+   *
+   * @param array $contribution
+   *
+   * @param array $params
+   *
+   * @return object
+   */
+  public static function recordPartialPayment($contribution, $params) {
+    $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
+    $pendingStatus = array(
+      array_search('Pending', $contributionStatuses),
+      array_search('In Progress', $contributionStatuses),
+    );
+    $statusId = array_search('Completed', $contributionStatuses);
+    if (in_array(CRM_Utils_Array::value('contribution_status_id', $contribution), $pendingStatus)) {
+      $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' "));
+      $balanceTrxnParams['to_financial_account_id'] = CRM_Contribute_PseudoConstant::financialAccountType($contribution['financial_type_id'], $relationTypeId);
+    }
+    elseif (!empty($params['payment_processor'])) {
+      $balanceTrxnParams['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getFinancialAccount($contribution['payment_processor'], 'civicrm_payment_processor', 'financial_account_id');
+    }
+    elseif (!empty($params['payment_instrument_id'])) {
+      $balanceTrxnParams['to_financial_account_id'] = CRM_Financial_BAO_FinancialTypeAccount::getInstrumentFinancialAccount($contribution['payment_instrument_id']);
+    }
+    else {
+      $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name LIKE 'Asset' "));
+      $queryParams = array(1 => array($relationTypeId, 'Integer'));
+      $balanceTrxnParams['to_financial_account_id'] = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_financial_account WHERE is_default = 1 AND financial_account_type_id = %1", $queryParams);
+    }
+    $relationTypeId = key(CRM_Core_PseudoConstant::accountOptionValues('account_relationship', NULL, " AND v.name LIKE 'Accounts Receivable Account is' "));
+    $fromFinancialAccountId = CRM_Contribute_PseudoConstant::financialAccountType($contribution['financial_type_id'], $relationTypeId);
+    $balanceTrxnParams['from_financial_account_id'] = $fromFinancialAccountId;
+    $balanceTrxnParams['total_amount'] = $params['total_amount'];
+    $balanceTrxnParams['contribution_id'] = $params['contribution_id'];
+    $balanceTrxnParams['trxn_date'] = !empty($params['contribution_receive_date']) ? $params['contribution_receive_date'] : date('YmdHis');
+    $balanceTrxnParams['fee_amount'] = CRM_Utils_Array::value('fee_amount', $params);
+    $balanceTrxnParams['net_amount'] = CRM_Utils_Array::value('total_amount', $params);
+    $balanceTrxnParams['currency'] = $contribution['currency'];
+    $balanceTrxnParams['trxn_id'] = CRM_Utils_Array::value('contribution_trxn_id', $params, NULL);
+    $balanceTrxnParams['status_id'] = $statusId;
+    $balanceTrxnParams['payment_instrument_id'] = CRM_Utils_Array::value('payment_instrument_id', $params, $contribution['payment_instrument_id']);
+    $balanceTrxnParams['check_number'] = CRM_Utils_Array::value('check_number', $params);
+    if ($fromFinancialAccountId != NULL &&
+      ($statusId == array_search('Completed', $contributionStatuses) || $statusId == array_search('Partially paid', $contributionStatuses))
+    ) {
+      $balanceTrxnParams['is_payment'] = 1;
+    }
+    if (!empty($params['payment_processor'])) {
+      $balanceTrxnParams['payment_processor_id'] = $params['payment_processor'];
+    }
+    return CRM_Core_BAO_FinancialTrxn::create($balanceTrxnParams);
+  }
+
+  /**
+   * Get the description (source field) for the recurring contribution.
+   *
+   * @param CRM_Contribute_BAO_Contribution $contribution
+   * @param CRM_Event_DAO_Event|null $event
+   *
+   * @return array
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected static function getRecurringContributionDescription($contribution, $event) {
+    if (!empty($contribution->contribution_page_id)) {
+      $contributionPageTitle = civicrm_api3('ContributionPage', 'getvalue', array(
+        'id' => $contribution->contribution_page_id,
+        'return' => 'title',
+      ));
+      return ts('Online Contribution') . ': ' . $contributionPageTitle;
+    }
+    elseif ($event) {
+      return ts('Online Event Registration') . ': ' . $event->title;
+    }
+    return 'recurring contribution';
+  }
+
+  /**
+   * Function to add payments for contribution
+   * for Partially Paid status
+   *
+   * @param array $lineItems
+   * @param array $contributions
+   *
+   */
+  public static function addPayments($lineItems, $contributions) {
+    // get financial trxn which is a payment
+    $ftSql = "SELECT ft.id 
+      FROM civicrm_financial_trxn ft 
+      INNER JOIN civicrm_entity_financial_trxn eft ON eft.financial_trxn_id = ft.id AND eft.entity_table = 'civicrm_contribution'
+      WHERE eft.entity_id = %1 AND ft.is_payment = 1";
+    $sql = "SELECT fi.id, li.price_field_value_id
+      FROM civicrm_financial_item fi
+      INNER JOIN civicrm_line_item li ON li.id = fi.entity_id
+      WHERE li.contribution_id = %1";
+
+    foreach ($contributions as $k => $contribution) {
+      if ($contribution->contribution_status_id != CRM_Core_OptionGroup::getValue('contribution_status', 'Partially paid', 'name')) {
+        continue;
+      }
+      $ftId = CRM_Core_DAO::singleValueQuery($ftSql, array(1 => array($contribution->id, 'Integer')));
+      // get financial item
+      $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($contribution->id, 'Integer')));
+      while ($dao->fetch()) {
+        $ftIds[$dao->price_field_value_id] = $dao->id;
+      }
+
+      $params = array(
+        'entity_table' => 'civicrm_financial_item',
+        'financial_trxn_id' => $ftId,
+      );
+      foreach ($lineItems as $key => $value) {
+        $paid = $value['line_total'] * ($contribution->net_amount / $contribution->total_amount);
+        // Record Entity Financial Trxn
+        $params['amount'] = round($paid, 2);
+        $params['entity_id'] = $ftIds[$value['price_field_value_id']];
+
+        civicrm_api3('EntityFinancialTrxn', 'create', $params);
+      }
+    }
+  }
+
+  /**
+   * Function use to store line item proportionaly in
+   * in entity financial trxn table
+   *
+   * @param array $params
+   *  array of contribution params.
+   * @param object $trxn
+   *  CRM_Financial_DAO_FinancialTrxn object
+   * @param array $contribution
+   *
+   */
+  public static function assignProportionalLineItems($params, $trxn, $contribution) {
+    $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($params['contribution_id']);
+    if (!empty($lineItems)) {
+      // get financial item
+      $sql = "SELECT fi.id, li.price_field_value_id
+      FROM civicrm_financial_item fi
+      INNER JOIN civicrm_line_item li ON li.id = fi.entity_id
+      WHERE li.contribution_id = %1";
+      $dao = CRM_Core_DAO::executeQuery($sql, array(1 => array($params['contribution_id'], 'Integer')));
+      while ($dao->fetch()) {
+        $ftIds[$dao->price_field_value_id] = $dao->id;
+      }
+      $eftParams = array(
+        'entity_table' => 'civicrm_financial_item',
+        'financial_trxn_id' => $trxn->id,
+      );
+      foreach ($lineItems as $key => $value) {
+        $paid = $value['line_total'] * ($params['total_amount'] / $contribution['total_amount']);
+        // Record Entity Financial Trxn
+        $eftParams['amount'] = round($paid, 2);
+        $eftParams['entity_id'] = $ftIds[$value['price_field_value_id']];
+
+        civicrm_api3('EntityFinancialTrxn', 'create', $eftParams);
+      }
+    }
+  }
+
+  /**
+   * Function use to check check line items
+   *
+   * @param array $params
+   *  array of order params.
+   *
+   * @return string
+   */
+  public static function checkLineItems(&$params) {
+    $totalAmount = CRM_Utils_Array::value('total_amount', $params);
+    $lineItemAmount = 0;
+    foreach ($params['line_items'] as &$lineItems) {
+      foreach ($lineItems['line_item'] as &$item) {
+        if (empty($item['financial_type_id'])) {
+          $item['financial_type_id'] = $params['financial_type_id'];
+        }
+        $lineItemAmount += $item['line_total'];
+      }
+    }
+    if (!isset($totalAmount)) {
+      $params['total_amount'] = $lineItemAmount;
+    }
+    elseif ($totalAmount != $lineItemAmount) {
+      return "Line item total doesn't match with total amount.";
+    }
+    return NULL;
+  }
+
 }