Skip to content

Commit

Permalink
Merge pull request #489 from compucorp/CIWEMB-347-pp-cancel-updates
Browse files Browse the repository at this point in the history
CIWEMB-347: Support for payment cancellation workflow for external (non offline) payment processors
  • Loading branch information
erawat authored Aug 7, 2023
2 parents 2ce2d51 + 9a2a0d1 commit a0b7239
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 162 deletions.
320 changes: 192 additions & 128 deletions CRM/MembershipExtras/Form/RecurringContribution/Cancel.php
Original file line number Diff line number Diff line change
@@ -1,128 +1,192 @@
<?php

/**
* QuickForm used to cancel memberships.
*/
class CRM_MembershipExtras_Form_RecurringContribution_Cancel extends CRM_Core_Form {

/**
* ID of recurring contribution to be cancelled.
*
* @var int
*/
private $id;

/**
* ID of contact from where contributions is being cancellsd.
*
* @var int
*/
private $contactID;

/**
* @inheritdoc
*/
public function preProcess() {
$this->id = CRM_Utils_Request::retrieve('crid', 'Positive', $this);
$this->contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
}

/**
* @inheritdoc
*/
public function buildQuickForm() {
CRM_Utils_System::setTitle(ts('Payment Plan Settings'));

$this->add(
'checkbox',
'cancel_pending_installments',
ts('Do you wish to cancel any pending instalment contribution?'),
'',
FALSE
);

$this->add(
'checkbox',
'cancel_memberships',
ts('Do you wish to cancel any linked membership?'),
'',
FALSE
);

$this->addButtons([
[
'type' => 'submit',
'name' => ts('Yes'),
'isDefault' => TRUE,
],
[
'type' => 'cancel',
'name' => ts('No'),
],
]);
}

/**
* @@inheritdoc
*/
public function postProcess() {
$submittedValues = $this->controller->exportValues($this->_name);

if ($submittedValues['cancel_memberships']) {
$this->cancelMemberships();
}

if ($submittedValues['cancel_pending_installments']) {
$this->cancelPendingInstallments();
}

$this->cancelRecurringContribution();
}

/**
* Cancels memberships being payed for with the current recurring
* contribution.
*/
private function cancelMemberships() {
civicrm_api3('MembershipPayment', 'get', [
'sequential' => 1,
'contribution_id.contribution_recur_id.id' => $this->id,
'options' => ['limit' => 0],
'api.Membership.create' => [
'id' => '$value.membership_id',
'is_override' => 1,
'status_override_end_date' => '',
'status_id' => 'Cancelled',
],
]);
}

/**
* Cancels pending contributions associated to current recurring contribution.
*/
private function cancelPendingInstallments() {
civicrm_api3('Contribution', 'get', [
'sequential' => 1,
'contribution_recur_id' => $this->id,
'contact_id' => $this->contactId,
'contribution_status_id' => 'Pending',
'options' => ['limit' => 0],
'api.Contribution.create' => array(
'id' => '$value.id',
'contribution_status_id' => 'Cancelled',
'cancel_date' => date('Y-m-d H:i:s'),
'cancel_reason' => 'Cancelled because related recurring contribution was cancelled.',
),
]);
}

/**
* Cancels current recurring contribution.
*/
private function cancelRecurringContribution() {
civicrm_api3('ContributionRecur', 'cancel', array(
'id' => $this->id,
));
}

}
<?php

/**
* QuickForm used to cancel memberships.
*/
class CRM_MembershipExtras_Form_RecurringContribution_Cancel extends CRM_Core_Form {

/**
* ID of recurring contribution to be cancelled.
*
* @var int
*/
private $id;

/**
* The recurring contribution to be cancelled data.
* @var array
*/
private $recurContribution;

/**
* ID of contact from where contributions is being cancelled.
*
* @var int
*/
private $contactID;

/**
* @var bool
*/
private $isOfflinePaymentProcessor;

/**
* @var bool
*/
private $isMembershipextrasPaymentPlan;

/**
* @inheritdoc
*/
public function preProcess() {
$this->id = CRM_Utils_Request::retrieve('crid', 'Positive', $this);
$this->contactID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);

$this->recurContribution = \Civi\Api4\ContributionRecur::get()
->addSelect('payment_processor_id', 'payment_processor_id:name', 'custom.*')
->addWhere('id', '=', $this->id)
->addOrderBy('id', 'DESC')
->execute()
->first();

// These two payment processors are special case given they are both are not external payment processors.
$this->isOfflinePaymentProcessor = in_array($this->recurContribution['payment_processor_id:name'], ['Offline Recurring Contribution', 'Direct Debit']);
$this->assign('isOfflinePaymentProcessor', $this->isOfflinePaymentProcessor);

// If the is_active field is not set, it means this recurring contribution was not created using Membershipextras
// and might have been created using different method such as using Contribution Pages.
$this->isMembershipextrasPaymentPlan = $this->recurContribution['payment_plan_extra_attributes.is_active'] !== NULL;
$this->assign('isMembershipextrasPaymentPlan', $this->isMembershipextrasPaymentPlan);
}

/**
* @inheritdoc
*/
public function buildQuickForm() {
CRM_Utils_System::setTitle(ts('Payment Plan Settings'));

if ($this->isMembershipextrasPaymentPlan) {
$this->add(
'checkbox',
'cancel_pending_installments',
ts('Do you wish to cancel any pending instalment contribution?'),
'',
FALSE
);

$this->add(
'checkbox',
'cancel_memberships',
ts('Do you wish to cancel any linked membership?'),
'',
FALSE
);
}

$this->addButtons([
[
'type' => 'submit',
'name' => ts('Yes'),
'isDefault' => TRUE,
],
[
'type' => 'cancel',
'name' => ts('No'),
],
]);
}

/**
* @@inheritdoc
*/
public function postProcess() {
$submittedValues = $this->controller->exportValues($this->_name);

if (!$this->isOfflinePaymentProcessor) {
$isProcessedExternallySuccessfully = $this->invokePreRecurContributionCancellationHook();
if ($isProcessedExternallySuccessfully === FALSE) {
CRM_Core_Session::setStatus(ts('An error occurred while trying to cancel this recurring contribution.'), ts('Cancellation Failed'), 'error');
return;
}
}

$transaction = new CRM_Core_Transaction();
try {
if (!empty($submittedValues['cancel_memberships'])) {
$this->cancelMemberships();
}

if (!empty($submittedValues['cancel_pending_installments'])) {
$this->cancelPendingInstallments();
}

$this->cancelRecurringContribution();

$transaction->commit();
}
catch (Exception $e) {
$transaction->rollback();
CRM_Core_Session::setStatus(ts('An error occurred while trying to cancel this recurring contribution: ') . ':' . $e->getMessage(), ts('Cancellation Failed'), 'error');
}
}

private function invokePreRecurContributionCancellationHook() {
$nullObject = CRM_Utils_Hook::$_nullObject;
$isProcessedExternallySuccessfully = FALSE;
CRM_Utils_Hook::singleton()->invoke(
['recurContribution', 'isMembershipextrasPaymentPlan', 'isProcessedExternallySuccessfully'],
$this->recurContribution, $this->isMembershipextrasPaymentPlan, $isProcessedExternallySuccessfully,
$nullObject, $nullObject, $nullObject,
'membershipextras_preRecurContributionCancellation'
);

return $isProcessedExternallySuccessfully;
}

/**
* Cancels memberships being payed for with the current recurring
* contribution.
*/
private function cancelMemberships() {
civicrm_api3('MembershipPayment', 'get', [
'sequential' => 1,
'contribution_id.contribution_recur_id.id' => $this->id,
'options' => ['limit' => 0],
'api.Membership.create' => [
'id' => '$value.membership_id',
'is_override' => 1,
'status_override_end_date' => '',
'status_id' => 'Cancelled',
],
]);
}

/**
* Cancels pending contributions associated to current recurring contribution.
*/
private function cancelPendingInstallments() {
civicrm_api3('Contribution', 'get', [
'sequential' => 1,
'contribution_recur_id' => $this->id,
'contact_id' => $this->contactId,
'contribution_status_id' => 'Pending',
'options' => ['limit' => 0],
'api.Contribution.create' => array(
'id' => '$value.id',
'contribution_status_id' => 'Cancelled',
'cancel_date' => date('Y-m-d H:i:s'),
'cancel_reason' => 'Cancelled because related recurring contribution was cancelled.',
),
]);
}

/**
* Cancels current recurring contribution.
*/
private function cancelRecurringContribution() {
civicrm_api3('ContributionRecur', 'cancel', array(
'id' => $this->id,
));
}

}
5 changes: 3 additions & 2 deletions CRM/MembershipExtras/Hook/Links/RecurringContribution.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,14 @@ private function getRecurringContribution() {
*/
public function alterLinks() {
foreach ($this->links as &$actionLink) {
if ($actionLink['name'] == 'Cancel' && $this->isSupportedPaymentPlan()) {
$isSupportedPaymentPlan = $this->isSupportedPaymentPlan();
if ($actionLink['name'] == 'Cancel' && $isSupportedPaymentPlan) {
unset($actionLink['ref']);
$actionLink['url'] = 'civicrm/recurring-contribution/cancel';
$actionLink['qs'] = 'reset=1&crid=%%crid%%&cid=%%cid%%&context=contribution';
}

if ($actionLink['name'] == 'Edit' && $this->isSupportedPaymentPlan()) {
if ($actionLink['name'] == 'Edit' && $isSupportedPaymentPlan) {
$this->mask |= CRM_Core_Action::UPDATE;
}
}
Expand Down
Loading

0 comments on commit a0b7239

Please sign in to comment.