dev/financial#87 make it possible to add a payment to a fully paid contribution
[civicrm-core.git] / CRM / Contribute / Form / UpdateSubscription.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
6a488035 13 * @package CRM
ca5cec67 14 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
15 */
16
4ae44cc6 17
6a488035 18/**
c0f62075 19 * This class generates form components generic to recurring contributions.
6a488035
TO
20 *
21 * It delegates the work to lower level subclasses and integrates the changes
22 * back in. It also uses a lot of functionality with the CRM API's, so any change
23 * made here could potentially affect the API etc. Be careful, be aware, use unit tests.
6a488035 24 */
ca809bb6 25class CRM_Contribute_Form_UpdateSubscription extends CRM_Contribute_Form_ContributionRecur {
6a488035 26
6a488035
TO
27 protected $_subscriptionDetails = NULL;
28
6a488035
TO
29 public $_paymentProcessor = NULL;
30
31 public $_paymentProcessorObj = NULL;
32
0c6c47a5 33 /**
34 * Fields that affect the schedule and are defined as editable by the processor.
35 *
36 * @var array
37 */
be2fb01f 38 protected $editableScheduleFields = [];
0c6c47a5 39
6a488035 40 /**
fe482240 41 * The id of the contact associated with this recurring contribution.
6a488035
TO
42 *
43 * @var int
6a488035 44 */
430ae6dd
TO
45 public $_contactID;
46
c0f62075 47 /**
48 * Pre-processing for the form.
49 *
50 * @throws \Exception
51 */
00be9182 52 public function preProcess() {
6a488035 53
1f17c8ef 54 parent::preProcess();
a0d322b1
KW
55 $this->setAction(CRM_Core_Action::UPDATE);
56
6a488035
TO
57 if ($this->_coid) {
58 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info');
cd3ad80e 59 // @todo test & replace with $this->_paymentProcessorObj = Civi\Payment\System::singleton()->getById($this->_paymentProcessor['id']);
6a488035 60 $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
0c6c47a5 61 $this->contributionRecurID = $this->_subscriptionDetails->recur_id;
6a488035 62 }
0c6c47a5 63 elseif ($this->contributionRecurID) {
64 $this->_coid = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->contributionRecurID, 'id', 'contribution_recur_id');
8de2c073 65 }
6a488035 66
1273d77c 67 if (!$this->contributionRecurID || !$this->_subscriptionDetails) {
7bcd892a 68 CRM_Core_Error::statusBounce(ts('Required information missing.'));
6a488035
TO
69 }
70
71 if ($this->_subscriptionDetails->membership_id && $this->_subscriptionDetails->auto_renew) {
14b61354 72 // Add Membership details to form
be2fb01f 73 $membership = civicrm_api3('Membership', 'get', [
14b61354 74 'contribution_recur_id' => $this->contributionRecurID,
be2fb01f 75 ]);
14b61354
MW
76 if (!empty($membership['count'])) {
77 $membershipDetails = reset($membership['values']);
78 $values['membership_id'] = $membershipDetails['id'];
79 $values['membership_name'] = $membershipDetails['membership_name'];
80 }
81 $this->assign('recurMembership', $values);
82 $this->assign('contactId', $this->_subscriptionDetails->contact_id);
6a488035
TO
83 }
84
e0e281fc 85 $this->assign('self_service', $this->isSelfService());
6a488035 86
0c6c47a5 87 $this->editableScheduleFields = $this->_paymentProcessorObj->getEditableRecurringScheduleFields();
88
89 $changeHelpText = $this->_paymentProcessorObj->getRecurringScheduleUpdateHelpText();
90 if (!in_array('amount', $this->editableScheduleFields)) {
91 // Not sure if this is good behaviour - maintaining this existing behaviour for now.
92 CRM_Core_Session::setStatus($changeHelpText, ts('Warning'), 'alert');
93 }
94 else {
95 $this->assign('changeHelpText', $changeHelpText);
96 }
be2fb01f 97 $alreadyHardCodedFields = ['amount', 'installments'];
0c6c47a5 98 foreach ($this->editableScheduleFields as $editableScheduleField) {
99 if (!in_array($editableScheduleField, $alreadyHardCodedFields)) {
be2fb01f 100 $this->addField($editableScheduleField, ['entity' => 'ContributionRecur'], FALSE, FALSE);
0c6c47a5 101 }
6a488035
TO
102 }
103
0699333e 104 // when custom data is included in this page
e0e281fc 105 if (!empty($_POST['hidden_custom']) && !$this->isSelfService()) {
0699333e
MW
106 CRM_Custom_Form_CustomData::preProcess($this, NULL, NULL, 1, 'ContributionRecur', $this->contributionRecurID);
107 CRM_Custom_Form_CustomData::buildQuickForm($this);
108 CRM_Custom_Form_CustomData::setDefaultValues($this);
109 }
110
0c6c47a5 111 $this->assign('editableScheduleFields', array_diff($this->editableScheduleFields, $alreadyHardCodedFields));
6a488035
TO
112
113 if ($this->_subscriptionDetails->contact_id) {
114 list($this->_donorDisplayName, $this->_donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($this->_subscriptionDetails->contact_id);
115 }
116
117 CRM_Utils_System::setTitle(ts('Update Recurring Contribution'));
118
0c6c47a5 119 // Handle context redirection.
6a488035
TO
120 CRM_Contribute_BAO_ContributionRecur::setSubscriptionContext();
121 }
122
123 /**
347e061b 124 * Set default values for the form.
6a488035 125 *
347e061b 126 * Note that in edit/view mode the default values are retrieved from the database.
6a488035 127 */
00be9182 128 public function setDefaultValues() {
be2fb01f 129 $this->_defaults = [];
6a488035
TO
130 $this->_defaults['amount'] = $this->_subscriptionDetails->amount;
131 $this->_defaults['installments'] = $this->_subscriptionDetails->installments;
7083dc2a 132 $this->_defaults['campaign_id'] = $this->_subscriptionDetails->campaign_id;
467fe956 133 $this->_defaults['financial_type_id'] = $this->_subscriptionDetails->financial_type_id;
6a488035 134 $this->_defaults['is_notify'] = 1;
0c6c47a5 135 foreach ($this->editableScheduleFields as $field) {
0699333e 136 $this->_defaults[$field] = isset($this->_subscriptionDetails->$field) ? $this->_subscriptionDetails->$field : NULL;
0c6c47a5 137 }
6a488035
TO
138
139 return $this->_defaults;
140 }
141
142 /**
fe482240 143 * Actually build the components of the form.
6a488035
TO
144 */
145 public function buildQuickForm() {
8de2c073 146 // CRM-16398: If current recurring contribution got > 1 lineitems then make amount field readonly
be2fb01f 147 $amtAttr = ['size' => 20];
8de2c073 148 $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->_coid);
149 if (count($lineItems) > 1) {
be2fb01f 150 $amtAttr += ['readonly' => TRUE];
8de2c073 151 }
152 $this->addMoney('amount', ts('Recurring Contribution Amount'), TRUE, $amtAttr,
153 TRUE, 'currency', $this->_subscriptionDetails->currency, TRUE
6a488035
TO
154 );
155
be2fb01f 156 $this->add('text', 'installments', ts('Number of Installments'), ['size' => 20], FALSE);
6a488035
TO
157
158 if ($this->_donorEmail) {
159 $this->add('checkbox', 'is_notify', ts('Notify Contributor?'));
160 }
161
7083dc2a 162 if (CRM_Core_Permission::check('edit contributions')) {
edb227cd 163 CRM_Campaign_BAO_Campaign::addCampaign($this, $this->_subscriptionDetails->campaign_id);
467fe956 164 }
165
0c6c47a5 166 if (CRM_Contribute_BAO_ContributionRecur::supportsFinancialTypeChange($this->contributionRecurID)) {
e0e281fc 167 $this->addEntityRef('financial_type_id', ts('Financial Type'), ['entity' => 'FinancialType'], !$this->isSelfService());
7083dc2a 168 }
169
0699333e
MW
170 // Add custom data
171 $this->assign('customDataType', 'ContributionRecur');
172 $this->assign('entityID', $this->contributionRecurID);
173
6a488035 174 $type = 'next';
e0e281fc 175 if ($this->isSelfService()) {
6a488035
TO
176 $type = 'submit';
177 }
178
179 // define the buttons
be2fb01f 180 $this->addButtons([
1330f57a
SL
181 [
182 'type' => $type,
183 'name' => ts('Save'),
184 'isDefault' => TRUE,
185 ],
186 [
187 'type' => 'cancel',
188 'name' => ts('Cancel'),
189 ],
190 ]);
6a488035
TO
191 }
192
193 /**
347e061b 194 * Called after the user submits the form.
6a488035
TO
195 */
196 public function postProcess() {
197 // store the submitted values in an array
198 $params = $this->exportValues();
199
e0e281fc 200 if ($this->isSelfService() && $this->_donorEmail) {
6a488035
TO
201 // for self service force notify
202 $params['is_notify'] = 1;
203 }
204
205 // if this is an update of an existing recurring contribution, pass the ID
206 $params['id'] = $this->_subscriptionDetails->recur_id;
207 $message = '';
208
209 $params['subscriptionId'] = $this->_subscriptionDetails->subscription_id;
4eeb9a5b 210 $updateSubscription = TRUE;
b690491e 211 if ($this->_paymentProcessorObj->supports('changeSubscriptionAmount')) {
4ae44cc6
RLAR
212 try {
213 $updateSubscription = $this->_paymentProcessorObj->changeSubscriptionAmount($message, $params);
214 }
215 catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
216 CRM_Core_Error::statusBounce($e->getMessage());
217 }
6a488035
TO
218 }
219 if (is_a($updateSubscription, 'CRM_Core_Error')) {
353ffa53
TO
220 CRM_Core_Error::displaySessionError($updateSubscription);
221 $status = ts('Could not update the Recurring contribution details');
222 $msgTitle = ts('Update Error');
223 $msgType = 'error';
6a488035
TO
224 }
225 elseif ($updateSubscription) {
0699333e
MW
226 // Handle custom data
227 $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params, $this->contributionRecurID, 'ContributionRecur');
353ffa53 228 // save the changes
0699333e 229 CRM_Contribute_BAO_ContributionRecur::add($params);
353ffa53 230 $status = ts('Recurring contribution has been updated to: %1, every %2 %3(s) for %4 installments.',
be2fb01f 231 [
353ffa53
TO
232 1 => CRM_Utils_Money::format($params['amount'], $this->_subscriptionDetails->currency),
233 2 => $this->_subscriptionDetails->frequency_interval,
234 3 => $this->_subscriptionDetails->frequency_unit,
235 4 => $params['installments'],
be2fb01f 236 ]
353ffa53
TO
237 );
238
239 $msgTitle = ts('Update Success');
240 $msgType = 'success';
c6c91efc 241 $msg = ts('Recurring Contribution Updated');
353ffa53
TO
242 $contactID = $this->_subscriptionDetails->contact_id;
243
244 if ($this->_subscriptionDetails->amount != $params['amount']) {
245 $message .= "<br /> " . ts("Recurring contribution amount has been updated from %1 to %2 for this subscription.",
be2fb01f 246 [
353ffa53
TO
247 1 => CRM_Utils_Money::format($this->_subscriptionDetails->amount, $this->_subscriptionDetails->currency),
248 2 => CRM_Utils_Money::format($params['amount'], $this->_subscriptionDetails->currency),
be2fb01f 249 ]) . ' ';
c6c91efc 250 if ($this->_subscriptionDetails->amount < $params['amount']) {
251 $msg = ts('Recurring Contribution Updated - increased installment amount');
252 }
253 else {
254 $msg = ts('Recurring Contribution Updated - decreased installment amount');
255 }
353ffa53 256 }
6a488035 257
353ffa53 258 if ($this->_subscriptionDetails->installments != $params['installments']) {
be2fb01f 259 $message .= "<br /> " . ts("Recurring contribution installments have been updated from %1 to %2 for this subscription.", [
1330f57a
SL
260 1 => $this->_subscriptionDetails->installments,
261 2 => $params['installments'],
262 ]) . ' ';
353ffa53 263 }
6a488035 264
be2fb01f 265 $activityParams = [
353ffa53 266 'source_contact_id' => $contactID,
12853dec 267 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Update Recurring Contribution'),
c6c91efc 268 'subject' => $msg,
353ffa53
TO
269 'details' => $message,
270 'activity_date_time' => date('YmdHis'),
12853dec 271 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Completed'),
be2fb01f 272 ];
12853dec 273
353ffa53
TO
274 $session = CRM_Core_Session::singleton();
275 $cid = $session->get('userID');
6a488035 276
353ffa53
TO
277 if ($cid) {
278 $activityParams['target_contact_id'][] = $activityParams['source_contact_id'];
279 $activityParams['source_contact_id'] = $cid;
280 }
281 CRM_Activity_BAO_Activity::create($activityParams);
282
283 if (!empty($params['is_notify'])) {
284 // send notification
285 if ($this->_subscriptionDetails->contribution_page_id) {
286 CRM_Core_DAO::commonRetrieveAll('CRM_Contribute_DAO_ContributionPage', 'id',
be2fb01f 287 $this->_subscriptionDetails->contribution_page_id, $value, [
353ffa53
TO
288 'title',
289 'receipt_from_name',
290 'receipt_from_email',
be2fb01f 291 ]
6a488035 292 );
353ffa53
TO
293 $receiptFrom = '"' . CRM_Utils_Array::value('receipt_from_name', $value[$this->_subscriptionDetails->contribution_page_id]) . '" <' . $value[$this->_subscriptionDetails->contribution_page_id]['receipt_from_email'] . '>';
294 }
295 else {
296 $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
297 $receiptFrom = "$domainValues[0] <$domainValues[1]>";
6a488035 298 }
353ffa53
TO
299
300 list($donorDisplayName, $donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($contactID);
301
be2fb01f 302 $tplParams = [
353ffa53
TO
303 'recur_frequency_interval' => $this->_subscriptionDetails->frequency_interval,
304 'recur_frequency_unit' => $this->_subscriptionDetails->frequency_unit,
305 'amount' => CRM_Utils_Money::format($params['amount']),
306 'installments' => $params['installments'],
be2fb01f 307 ];
353ffa53 308
be2fb01f 309 $tplParams['contact'] = ['display_name' => $donorDisplayName];
353ffa53
TO
310 $tplParams['receipt_from_email'] = $receiptFrom;
311
be2fb01f 312 $sendTemplateParams = [
353ffa53
TO
313 'groupName' => 'msg_tpl_workflow_contribution',
314 'valueName' => 'contribution_recurring_edit',
315 'contactId' => $contactID,
316 'tplParams' => $tplParams,
317 'isTest' => $this->_subscriptionDetails->is_test,
318 'PDFFilename' => 'receipt.pdf',
319 'from' => $receiptFrom,
320 'toName' => $donorDisplayName,
321 'toEmail' => $donorEmail,
be2fb01f 322 ];
353ffa53 323 list($sent) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams);
6a488035 324 }
353ffa53 325 }
6a488035
TO
326
327 $session = CRM_Core_Session::singleton();
353ffa53 328 $userID = $session->get('userID');
481a74f4 329 if ($userID && $status) {
6a488035 330 CRM_Core_Session::setStatus($status, $msgTitle, $msgType);
0db6c3e1 331 }
4c9b6178 332 elseif (!$userID) {
317fceb4 333 if ($status) {
6a488035 334 CRM_Utils_System::setUFMessage($status);
317fceb4 335 }
6a488035
TO
336 // keep result as 1, since we not displaying anything on the redirected page anyway
337 return CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/subscriptionstatus',
353ffa53 338 "reset=1&task=update&result=1"));
6a488035
TO
339 }
340 }
96025800 341
6a488035 342}