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