-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #489 from compucorp/CIWEMB-347-pp-cancel-updates
CIWEMB-347: Support for payment cancellation workflow for external (non offline) payment processors
- Loading branch information
Showing
3 changed files
with
235 additions
and
162 deletions.
There are no files selected for viewing
320 changes: 192 additions & 128 deletions
320
CRM/MembershipExtras/Form/RecurringContribution/Cancel.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.