Skip to content

Commit

Permalink
Add new Payment.sendconfirmation api
Browse files Browse the repository at this point in the history
This adds a new api Payment.sendconfirmation intended to be an equivalent of
Contribution.sendconfirmation.

The key thing this needs to be able to do is to load up all
related information to assign to the template - this is not all covered in this
PR & until it is functionally equivalent to AdditionalPayment::sendEmail
we can't switch over but since it's new functionality we should be able to
merge & keep working on it.

Note that there is discussion on the PR this relates to (civicrm#13330) about what should
happen if 'Payment.create' api accepts 'is_email_receipt'. I think the most logical
outcome is that receipts would go out if it caused the contribution to be completed
(ie. we pass is_email_receipt into completetransaction api).

However, I don't feel like that is 'settled' yet - separating into a second api
(Payment.sendnotification) means we will have 2 generic function which can be called
from multiple places in the code rather than 2 functions tightly tied to the form layer.
I think it's OK if it is 2 not one
  • Loading branch information
eileenmcnaughton committed Feb 11, 2019
1 parent bc116c0 commit 00c434a
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CRM/Contribute/BAO/Contribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -4000,7 +4000,8 @@ public static function addActivityForPayment($entityObj, $trxnObj, $activityType
*
* @return mixed
*/
public static function getPaymentInfo($id, $component, $getTrxnInfo = FALSE, $usingLineTotal = FALSE) {
public static function getPaymentInfo($id, $component = 'contribution', $getTrxnInfo = FALSE, $usingLineTotal = FALSE) {
// @todo deprecate passing in component - always call with contribution.
if ($component == 'event') {
$entity = 'participant';
$entityTable = 'civicrm_participant';
Expand Down
1 change: 1 addition & 0 deletions CRM/Core/BAO/FinancialTrxn.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ public static function getPartialPaymentWithType($entityId, $entityName = 'parti
return $value;
}

// @todo - deprecate passing in entity & type - just figure out contribution id FIRST
if ($entityName == 'participant') {
$contributionId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $entityId, 'contribution_id', 'participant_id');
}
Expand Down
2 changes: 1 addition & 1 deletion CRM/Core/BAO/MessageTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ public static function sendTemplate($params) {
}
}

return array($sent, $mailContent['subject'], $mailContent['text'], $mailContent['html']);
return ['is_sent' => $sent, 'subject' => $mailContent['subject'], 'message_txt' => $mailContent['text'], 'message_html' => $mailContent['html']];
}

}
153 changes: 153 additions & 0 deletions CRM/Financial/BAO/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,157 @@ public static function create($params) {
return $trxn;
}

/**
* Send an email confirming a payment that has been received.
*
* @param array $params
*
* @return array
*/
public static function sendConfirmation($params) {

$entities = self::loadRelatedEntities($params['id']);
$sendTemplateParams = array(
'groupName' => 'msg_tpl_workflow_contribution',
'valueName' => 'payment_or_refund_notification',
'PDFFilename' => ts('notification') . '.pdf',
'contactId' => $entities['contact']['id'],
'toName' => $entities['contact']['display_name'],
'email' => $entities['contact']['email'],
'tplParams' => self::getConfirmationTemplateParameters($entities),
);
return CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams);
}

/**
* Load entities related to the current payment id.
*
* This gives us all the data we need to send an email confirmation but avoiding
* getting anything not tested for the confirmations. We retrieve the 'full' event as
* it has been traditionally assigned in full.
*
* @param int $id
*
* @return array
* - contact = ['id' => x, 'display_name' => y, 'email' => z]
* - event = [.... full event details......]
* - contribution = ['id' => x],
* - payment = [payment info + payment summary info]
*/
protected static function loadRelatedEntities($id) {
$entities = [];
$contributionID = (int) civicrm_api3('EntityFinancialTrxn', 'getvalue', [
'financial_trxn_id' => $id,
'entity_table' => 'civicrm_contribution',
'return' => 'entity_id',
]);
$entities['contribution'] = ['id' => $contributionID];
$entities['payment'] = array_merge(civicrm_api3('FinancialTrxn', 'getsingle', ['id' => $id]),
CRM_Contribute_BAO_Contribution::getPaymentInfo($contributionID)
);

$contactID = self::getPaymentContactID($contributionID);
list($displayName, $email) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactID);
$entities['contact'] = ['id' => $contactID, 'display_name' => $displayName, 'email' => $email];

$participantRecords = civicrm_api3('ParticipantPayment', 'get', [
'contribution_id' => $contributionID,
'api.Participant.get' => ['return' => 'event_id'],
'sequential' => 1,
])['values'];
if (!empty($participantRecords)) {
$entities['event'] = civicrm_api3('Event', 'getsingle', ['id' => $participantRecords[0]['api.Participant.get']['values'][0]['event_id']]);
}

return $entities;
}

/**
* @param int $contributionID
*
* @return int
*/
public static function getPaymentContactID($contributionID) {
$contribution = civicrm_api3('Contribution', 'getsingle', [
'id' => $contributionID ,
'return' => ['contact_id'],
]);
return (int) $contribution['contact_id'];
}
/**
* @param array $entities
* Related entities as an array keyed by the various entities.
*
* @return array
* Values required for the notification
* - contact_id
* - template_variables
* - event (DAO of event if relevant)
*/
public static function getConfirmationTemplateParameters($entities) {
$templateVariables = [
'contactDisplayName' => $entities['contact']['display_name'],
'totalAmount' => $entities['payment']['total'],
'amountOwed' => $entities['payment']['balance'],
'paymentAmount' => $entities['payment']['total_amount'],
'event' => NULL,
'component' => 'contribution',
];
if (!empty($entities['event'])) {
$templateVariables['component'] = 'event';
$templateVariables['event'] = $entities['event'];
}

return self::filterUntestedTemplateVariables($templateVariables);
}

/**
* Filter out any untested variables.
*
* This just serves to highlight if any variables are added without a unit test also being added.
*
* (if hit then add a unit test for the param & add to this array).
*
* @param array $params
*
* @return array
*/
public static function filterUntestedTemplateVariables($params) {
$testedTemplateVariables = [
'contactDisplayName',
'totalAmount',
'amountOwed',
'paymentAmount',
'event',
'component',
/**
'isRefund',
'totalPaid',
'refundAmount',
'paymentsComplete',
'receive_date',
'paidBy',
'checkNumber',
'contributeMode',
'isAmountzero',
'billingName',
'address',
'credit_card_type',
'credit_card_number',
'credit_card_exp_date',
'isShowLocation',
'location',
'eventEmail',
* $event.participant_role
*/
];
$filteredParams = [];
foreach ($testedTemplateVariables as $templateVariable) {
// This will cause an a-notice if any are NOT set - by design. Ensuring
// they are set prevents leakage.
$filteredParams[$templateVariable] = $params[$templateVariable];
}
return $filteredParams;
}

}
40 changes: 40 additions & 0 deletions api/v3/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,43 @@ function _civicrm_api3_payment_cancel_spec(&$params) {
),
);
}

/**
* Send a payment confirmation.
*
* @param array $params
* Input parameters.
*
* @return array
* @throws Exception
*/
function civicrm_api3_payment_sendconfirmation($params) {
$allowedParams = [
'receipt_from_email',
'receipt_from_name',
'cc_receipt',
'bcc_receipt',
'receipt_text',
'id',
];
$input = array_intersect_key($params, array_flip($allowedParams));
// use either the contribution or membership receipt, based on whether it’s a membership-related contrib or not
$result = CRM_Financial_BAO_Payment::sendConfirmation($input);
return civicrm_api3_create_success([$params['id'] => $result]);
}

/**
* Adjust Metadata for sendconfirmation action.
*
* The metadata is used for setting defaults, documentation & validation.
*
* @param array $params
* Array of parameters determined by getfields.
*/
function _civicrm_api3_payment_sendconfirmation_spec(&$params) {
$params['id'] = array(
'api.required' => 1,
'title' => ts('Payment ID'),
'type' => CRM_Utils_Type::T_INT,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public function testAddPaymentUsingCreditCardForPartialyPaidContribution() {
$this->checkResults(array(30, 70), 2);
$mut->assertSubjects(['Payment Receipt -']);
$mut->checkMailLog([
'Dear Anthony Anderson',
'Dear ' . CRM_Contact_BAO_Contact::displayName($this->_individualId),
'Payment Details',
'Total Fees: $ 100.00',
'This Payment Amount: $ 70.00',
Expand Down
33 changes: 33 additions & 0 deletions tests/phpunit/api/v3/PaymentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,39 @@ public function testGetPayment() {
));
}

/**
* Test email receipt for partial payment.
*/
public function testPaymentEmailReceipt() {
$mut = new CiviMailUtils($this, TRUE);
list($lineItems, $contribution) = $this->createParticipantWithContribution();
$params = array(
'contribution_id' => $contribution['id'],
'total_amount' => 50,
);
$payment = $this->callAPISuccess('payment', 'create', $params);
$this->checkPaymentResult($payment, [
$payment['id'] => [
'from_financial_account_id' => 7,
'to_financial_account_id' => 6,
'total_amount' => 50,
'status_id' => 1,
'is_payment' => 1,
],
]);

$this->callAPISuccess('Payment', 'sendconfirmation', ['id' => $payment['id']]);
$mut->assertSubjects(array('Payment Receipt - Annual CiviCRM meet'));
$mut->checkMailLog(array(
'Dear Mr. Anthony Anderson II',
'Total Fees: $ 300.00',
'This Payment Amount: $ 50.00',
'Balance Owed: $ 100.00', //150 was paid in the 1st payment.
'Event Information and Location',
));
$mut->stop();
}

/**
* Test create payment api with no line item in params
*/
Expand Down

0 comments on commit 00c434a

Please sign in to comment.