From 6a0b06160131085453fbbb70a38ce287e3083efd Mon Sep 17 00:00:00 2001 From: Rich Lott / Artful Robot Date: Fri, 1 Jul 2022 16:51:28 +0100 Subject: [PATCH] Initial go at cleaning up repeattransaction --- api/v3/Contribution.php | 100 ++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index 6000cd96ae..c456d44f5b 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -616,84 +616,76 @@ function _civicrm_api3_contribution_completetransaction_spec(&$params) { * @throws API_Exception */ function civicrm_api3_contribution_repeattransaction($params) { + civicrm_api3_verify_one_mandatory($params, NULL, ['contribution_recur_id', 'original_contribution_id']); + + // We need a contribution to copy. if (empty($params['original_contribution_id'])) { + // Find one from the given recur. A template contribution is preferred, otherwise use the latest one added. $templateContribution = CRM_Contribute_BAO_ContributionRecur::getTemplateContribution($params['contribution_recur_id']); if (empty($templateContribution)) { throw new CiviCRM_API3_Exception('Contribution.repeattransaction failed to get original_contribution_id for recur with ID: ' . $params['contribution_recur_id']); } - $params['original_contribution_id'] = $templateContribution['id']; - } - $contribution = new CRM_Contribute_BAO_Contribution(); - $contribution->id = $params['original_contribution_id']; - if (!$contribution->find(TRUE)) { - throw new API_Exception( - 'A valid original contribution ID is required', 'invalid_data'); } - // We don't support repeattransaction without a related recurring contribution. - if (empty($contribution->contribution_recur_id)) { - throw new API_Exception( - 'Repeattransaction API can only be used in the context of contributions that have a contribution_recur_id.', - 'invalid_data' - ); + else { + // A template/original contribution was specified by the params. Load it. + $templateContribution = Contribution::get(FALSE) + ->addWhere('id', '=', $params['original_contribution_id']) + ->addWhere('is_test', 'IN', [0, 1]) + ->addWhere('contribution_recur_id', 'IS NOT EMPTY') + ->execute()->first(); + if (empty($templateContribution)) { + throw new CiviCRM_API3_Exception("Contribution.repeattransaction failed to load the given original_contribution_id ($params[original_contribution_id]) because it does not exist, or because it does not belong to a recurring contribution"); + } } - $params['payment_processor_id'] = civicrm_api3('contributionRecur', 'getvalue', [ - 'return' => 'payment_processor_id', - 'id' => $contribution->contribution_recur_id, - ]); - - $passThroughParams = [ + // Collect inputs for CRM_Contribute_BAO_Contribution::completeOrder in $input. + $paramsToCopy = [ 'trxn_id', - 'total_amount', 'campaign_id', 'fee_amount', 'financial_type_id', 'contribution_status_id', 'payment_processor_id', - ]; - $input = array_intersect_key($params, array_fill_keys($passThroughParams, NULL)); + 'is_email_receipt', + 'trxn_date', + 'receive_date', + 'card_type_id', + 'pan_truncation', + 'payment_instrument_id', + // Q. not contribution_page_id ? + ]; + $input = array_intersect_key($params, array_fill_keys($paramsToCopy, NULL)); + // Ensure certain keys exist with NULL values if they don't already (not sure if this is ACTUALLY necessary?) + $input += array_fill_keys(['card_type_id', 'pan_truncation'], NULL); + + // Set amount, use templateContribution if not in params. + $input['total_amount'] = $params['total_amount'] ?? $templateContribution['total_amount']; + // Why do we need this extra 'amount' key? It's used in CRM_Contribute_BAO_Contribution::completeOrder to pass on to CRM_Contribute_BAO_ContributionRecur::updateRecurLinkedPledge + // but it could possibly be removed if that code was adjusted unless anything else uses 'amount' + $input['amount'] = $input['total_amount']; + + $input['payment_processor_id'] = civicrm_api3('contributionRecur', 'getvalue', [ + 'return' => 'payment_processor_id', + 'id' => $templateContribution['contribution_recur_id'], + ]); - $ids = []; - if (!$contribution->loadRelatedObjects(['payment_processor_id' => $input['payment_processor_id']], $ids, TRUE)) { - throw new API_Exception('failed to load related objects'); - } - unset($contribution->id, $contribution->receive_date, $contribution->invoice_id); - $contribution->receive_date = $params['receive_date']; + $input['is_test'] = $templateContribution['is_test']; - // @todo Copied from _ipn_process_transaction - needs cleanup/refactor - $objects = $contribution->_relatedObjects; - $objects['contribution'] = &$contribution; - $input['component'] = $contribution->_component; - $input['is_test'] = $contribution->is_test; - $input['amount'] = empty($input['total_amount']) ? $contribution->total_amount : $input['total_amount']; - - if (isset($params['is_email_receipt'])) { - $input['is_email_receipt'] = $params['is_email_receipt']; - } - if (!empty($params['trxn_date'])) { - $input['trxn_date'] = $params['trxn_date']; - } - if (!empty($params['receive_date'])) { - $input['receive_date'] = $params['receive_date']; - } - if (empty($contribution->contribution_page_id)) { + if (empty($templateContribution['contribution_page_id'])) { + // Q. why do we need statics here? static $domainFromName; static $domainFromEmail; if (empty($domainFromEmail) && (empty($params['receipt_from_name']) || empty($params['receipt_from_email']))) { [$domainFromName, $domainFromEmail] = CRM_Core_BAO_Domain::getNameAndEmail(TRUE); } - $input['receipt_from_name'] = CRM_Utils_Array::value('receipt_from_name', $params, $domainFromName); - $input['receipt_from_email'] = CRM_Utils_Array::value('receipt_from_email', $params, $domainFromEmail); - } - $input['card_type_id'] = $params['card_type_id'] ?? NULL; - $input['pan_truncation'] = $params['pan_truncation'] ?? NULL; - if (!empty($params['payment_instrument_id'])) { - $input['payment_instrument_id'] = $params['payment_instrument_id']; + $input['receipt_from_name'] = ($params['receipt_from_name'] ?? NULL) ?: $domainFromName; + $input['receipt_from_email'] = ($params['receipt_from_email'] ?? NULL) ?: $domainFromEmail; } + return CRM_Contribute_BAO_Contribution::completeOrder($input, - !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, - $objects['contribution']->id ?? NULL, + $templateContribution['contribution_recur_id'], + NULL, $params['is_post_payment_create'] ?? NULL); } -- 2.25.1