Merge pull request #8762 from eileenmcnaughton/xml
[civicrm-core.git] / CRM / Contribute / Form / UpdateSubscription.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
fa938177 6 | Copyright CiviCRM LLC (c) 2004-2016 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
6a488035 29 * @package CRM
fa938177 30 * @copyright CiviCRM LLC (c) 2004-2016
6a488035
TO
31 */
32
33/**
c0f62075 34 * This class generates form components generic to recurring contributions.
6a488035
TO
35 *
36 * It delegates the work to lower level subclasses and integrates the changes
37 * back in. It also uses a lot of functionality with the CRM API's, so any change
38 * made here could potentially affect the API etc. Be careful, be aware, use unit tests.
6a488035
TO
39 */
40class CRM_Contribute_Form_UpdateSubscription extends CRM_Core_Form {
41
42 /**
0c6c47a5 43 * The recurring contribution id, used when editing the recurring contribution.
6a488035
TO
44 *
45 * @var int
46 */
0c6c47a5 47 protected $contributionRecurID = NULL;
6a488035
TO
48
49 protected $_coid = NULL;
50
51 protected $_subscriptionDetails = NULL;
52
53 protected $_selfService = FALSE;
54
55 public $_paymentProcessor = NULL;
56
57 public $_paymentProcessorObj = NULL;
58
0c6c47a5 59 /**
60 * Fields that affect the schedule and are defined as editable by the processor.
61 *
62 * @var array
63 */
64 protected $editableScheduleFields = array();
65
6a488035 66 /**
fe482240 67 * The id of the contact associated with this recurring contribution.
6a488035
TO
68 *
69 * @var int
6a488035 70 */
430ae6dd
TO
71 public $_contactID;
72
c0f62075 73 /**
74 * Pre-processing for the form.
75 *
76 * @throws \Exception
77 */
00be9182 78 public function preProcess() {
6a488035 79
a0d322b1
KW
80 $this->setAction(CRM_Core_Action::UPDATE);
81
0c6c47a5 82 $this->contributionRecurID = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE);
83 if ($this->contributionRecurID) {
84 $this->_paymentProcessor = CRM_Contribute_BAO_ContributionRecur::getPaymentProcessor($this->contributionRecurID);
85 if (!$this->_paymentProcessor) {
86 CRM_Core_Error::statusBounce(ts('There is no valid processor for this subscription so it cannot be edited.'));
87 }
c0f62075 88 $this->_paymentProcessorObj = $this->_paymentProcessor['object'];
0c6c47a5 89 $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->contributionRecurID);
6a488035
TO
90 }
91
92 $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE);
93 if ($this->_coid) {
94 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info');
95 $this->_paymentProcessorObj = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
96 $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
0c6c47a5 97 $this->contributionRecurID = $this->_subscriptionDetails->recur_id;
6a488035 98 }
0c6c47a5 99 elseif ($this->contributionRecurID) {
100 $this->_coid = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $this->contributionRecurID, 'id', 'contribution_recur_id');
8de2c073 101 }
6a488035 102
0c6c47a5 103 if ((!$this->contributionRecurID) ||
6a488035
TO
104 ($this->_subscriptionDetails == CRM_Core_DAO::$_nullObject)
105 ) {
106 CRM_Core_Error::fatal('Required information missing.');
107 }
108
109 if ($this->_subscriptionDetails->membership_id && $this->_subscriptionDetails->auto_renew) {
110 CRM_Core_Error::fatal(ts('You cannot update the subscription.'));
111 }
112
113 if (!CRM_Core_Permission::check('edit contributions')) {
114 $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE);
115 if (!CRM_Contact_BAO_Contact_Utils::validChecksum($this->_subscriptionDetails->contact_id, $userChecksum)) {
116 CRM_Core_Error::fatal(ts('You do not have permission to update subscription.'));
117 }
118 $this->_selfService = TRUE;
119 }
120 $this->assign('self_service', $this->_selfService);
121
0c6c47a5 122 $this->editableScheduleFields = $this->_paymentProcessorObj->getEditableRecurringScheduleFields();
123
124 $changeHelpText = $this->_paymentProcessorObj->getRecurringScheduleUpdateHelpText();
125 if (!in_array('amount', $this->editableScheduleFields)) {
126 // Not sure if this is good behaviour - maintaining this existing behaviour for now.
127 CRM_Core_Session::setStatus($changeHelpText, ts('Warning'), 'alert');
128 }
129 else {
130 $this->assign('changeHelpText', $changeHelpText);
131 }
132 $alreadyHardCodedFields = array('amount', 'installments');
133 foreach ($this->editableScheduleFields as $editableScheduleField) {
134 if (!in_array($editableScheduleField, $alreadyHardCodedFields)) {
95d72a5f 135 $this->addField($editableScheduleField, array('entity' => 'ContributionRecur'));
0c6c47a5 136 }
6a488035
TO
137 }
138
0c6c47a5 139 $this->assign('editableScheduleFields', array_diff($this->editableScheduleFields, $alreadyHardCodedFields));
6a488035
TO
140 $this->assign('paymentProcessor', $this->_paymentProcessor);
141 $this->assign('frequency_unit', $this->_subscriptionDetails->frequency_unit);
142 $this->assign('frequency_interval', $this->_subscriptionDetails->frequency_interval);
143
144 if ($this->_subscriptionDetails->contact_id) {
145 list($this->_donorDisplayName, $this->_donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($this->_subscriptionDetails->contact_id);
146 }
147
148 CRM_Utils_System::setTitle(ts('Update Recurring Contribution'));
149
0c6c47a5 150 // Handle context redirection.
6a488035
TO
151 CRM_Contribute_BAO_ContributionRecur::setSubscriptionContext();
152 }
153
154 /**
347e061b 155 * Set default values for the form.
6a488035 156 *
347e061b 157 * Note that in edit/view mode the default values are retrieved from the database.
6a488035 158 */
00be9182 159 public function setDefaultValues() {
6a488035
TO
160 $this->_defaults = array();
161 $this->_defaults['amount'] = $this->_subscriptionDetails->amount;
162 $this->_defaults['installments'] = $this->_subscriptionDetails->installments;
7083dc2a 163 $this->_defaults['campaign_id'] = $this->_subscriptionDetails->campaign_id;
467fe956 164 $this->_defaults['financial_type_id'] = $this->_subscriptionDetails->financial_type_id;
6a488035 165 $this->_defaults['is_notify'] = 1;
0c6c47a5 166 foreach ($this->editableScheduleFields as $field) {
167 $this->_defaults[$field] = $this->_subscriptionDetails->$field;
168 }
6a488035
TO
169
170 return $this->_defaults;
171 }
172
173 /**
fe482240 174 * Actually build the components of the form.
6a488035
TO
175 */
176 public function buildQuickForm() {
8de2c073 177 // CRM-16398: If current recurring contribution got > 1 lineitems then make amount field readonly
178 $amtAttr = array('size' => 20);
179 $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($this->_coid);
180 if (count($lineItems) > 1) {
181 $amtAttr += array('readonly' => TRUE);
182 }
183 $this->addMoney('amount', ts('Recurring Contribution Amount'), TRUE, $amtAttr,
184 TRUE, 'currency', $this->_subscriptionDetails->currency, TRUE
6a488035
TO
185 );
186
467fe956 187 $this->add('text', 'installments', ts('Number of Installments'), array('size' => 20), FALSE);
6a488035
TO
188
189 if ($this->_donorEmail) {
190 $this->add('checkbox', 'is_notify', ts('Notify Contributor?'));
191 }
192
7083dc2a 193 if (CRM_Core_Permission::check('edit contributions')) {
edb227cd 194 CRM_Campaign_BAO_Campaign::addCampaign($this, $this->_subscriptionDetails->campaign_id);
467fe956 195 }
196
0c6c47a5 197 if (CRM_Contribute_BAO_ContributionRecur::supportsFinancialTypeChange($this->contributionRecurID)) {
0d93f90b 198 $this->addEntityRef('financial_type_id', ts('Financial Type'), array('entity' => 'FinancialType'), !$this->_selfService);
7083dc2a 199 }
200
6a488035 201 $type = 'next';
481a74f4 202 if ($this->_selfService) {
6a488035
TO
203 $type = 'submit';
204 }
205
206 // define the buttons
207 $this->addButtons(array(
208 array(
209 'type' => $type,
210 'name' => ts('Save'),
211 'isDefault' => TRUE,
212 ),
213 array(
214 'type' => 'cancel',
215 'name' => ts('Cancel'),
216 ),
217 )
218 );
219 }
220
221 /**
347e061b 222 * Called after the user submits the form.
6a488035
TO
223 */
224 public function postProcess() {
225 // store the submitted values in an array
226 $params = $this->exportValues();
227
228 if ($this->_selfService && $this->_donorEmail) {
229 // for self service force notify
230 $params['is_notify'] = 1;
231 }
232
233 // if this is an update of an existing recurring contribution, pass the ID
234 $params['id'] = $this->_subscriptionDetails->recur_id;
235 $message = '';
236
237 $params['subscriptionId'] = $this->_subscriptionDetails->subscription_id;
4eeb9a5b 238 $updateSubscription = TRUE;
6a488035 239 if ($this->_paymentProcessorObj->isSupported('changeSubscriptionAmount')) {
353ffa53 240 $updateSubscription = $this->_paymentProcessorObj->changeSubscriptionAmount($message, $params);
6a488035
TO
241 }
242 if (is_a($updateSubscription, 'CRM_Core_Error')) {
353ffa53
TO
243 CRM_Core_Error::displaySessionError($updateSubscription);
244 $status = ts('Could not update the Recurring contribution details');
245 $msgTitle = ts('Update Error');
246 $msgType = 'error';
6a488035
TO
247 }
248 elseif ($updateSubscription) {
353ffa53
TO
249 // save the changes
250 $result = CRM_Contribute_BAO_ContributionRecur::add($params);
251 $status = ts('Recurring contribution has been updated to: %1, every %2 %3(s) for %4 installments.',
252 array(
253 1 => CRM_Utils_Money::format($params['amount'], $this->_subscriptionDetails->currency),
254 2 => $this->_subscriptionDetails->frequency_interval,
255 3 => $this->_subscriptionDetails->frequency_unit,
256 4 => $params['installments'],
257 )
258 );
259
260 $msgTitle = ts('Update Success');
261 $msgType = 'success';
c6c91efc 262 $msg = ts('Recurring Contribution Updated');
353ffa53
TO
263 $contactID = $this->_subscriptionDetails->contact_id;
264
265 if ($this->_subscriptionDetails->amount != $params['amount']) {
266 $message .= "<br /> " . ts("Recurring contribution amount has been updated from %1 to %2 for this subscription.",
267 array(
268 1 => CRM_Utils_Money::format($this->_subscriptionDetails->amount, $this->_subscriptionDetails->currency),
269 2 => CRM_Utils_Money::format($params['amount'], $this->_subscriptionDetails->currency),
270 )) . ' ';
c6c91efc 271 if ($this->_subscriptionDetails->amount < $params['amount']) {
272 $msg = ts('Recurring Contribution Updated - increased installment amount');
273 }
274 else {
275 $msg = ts('Recurring Contribution Updated - decreased installment amount');
276 }
353ffa53 277 }
6a488035 278
353ffa53
TO
279 if ($this->_subscriptionDetails->installments != $params['installments']) {
280 $message .= "<br /> " . ts("Recurring contribution installments have been updated from %1 to %2 for this subscription.", array(
281 1 => $this->_subscriptionDetails->installments,
317fceb4 282 2 => $params['installments'],
353ffa53
TO
283 )) . ' ';
284 }
6a488035 285
353ffa53
TO
286 $activityParams = array(
287 'source_contact_id' => $contactID,
288 'activity_type_id' => CRM_Core_OptionGroup::getValue('activity_type',
289 'Update Recurring Contribution',
290 'name'
291 ),
c6c91efc 292 'subject' => $msg,
353ffa53
TO
293 'details' => $message,
294 'activity_date_time' => date('YmdHis'),
295 'status_id' => CRM_Core_OptionGroup::getValue('activity_status',
296 'Completed',
297 'name'
298 ),
299 );
300 $session = CRM_Core_Session::singleton();
301 $cid = $session->get('userID');
6a488035 302
353ffa53
TO
303 if ($cid) {
304 $activityParams['target_contact_id'][] = $activityParams['source_contact_id'];
305 $activityParams['source_contact_id'] = $cid;
306 }
307 CRM_Activity_BAO_Activity::create($activityParams);
308
309 if (!empty($params['is_notify'])) {
310 // send notification
311 if ($this->_subscriptionDetails->contribution_page_id) {
312 CRM_Core_DAO::commonRetrieveAll('CRM_Contribute_DAO_ContributionPage', 'id',
313 $this->_subscriptionDetails->contribution_page_id, $value, array(
314 'title',
315 'receipt_from_name',
316 'receipt_from_email',
317 )
6a488035 318 );
353ffa53
TO
319 $receiptFrom = '"' . CRM_Utils_Array::value('receipt_from_name', $value[$this->_subscriptionDetails->contribution_page_id]) . '" <' . $value[$this->_subscriptionDetails->contribution_page_id]['receipt_from_email'] . '>';
320 }
321 else {
322 $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
323 $receiptFrom = "$domainValues[0] <$domainValues[1]>";
6a488035 324 }
353ffa53
TO
325
326 list($donorDisplayName, $donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($contactID);
327
328 $tplParams = array(
329 'recur_frequency_interval' => $this->_subscriptionDetails->frequency_interval,
330 'recur_frequency_unit' => $this->_subscriptionDetails->frequency_unit,
331 'amount' => CRM_Utils_Money::format($params['amount']),
332 'installments' => $params['installments'],
333 );
334
335 $tplParams['contact'] = array('display_name' => $donorDisplayName);
336 $tplParams['receipt_from_email'] = $receiptFrom;
337
338 $sendTemplateParams = array(
339 'groupName' => 'msg_tpl_workflow_contribution',
340 'valueName' => 'contribution_recurring_edit',
341 'contactId' => $contactID,
342 'tplParams' => $tplParams,
343 'isTest' => $this->_subscriptionDetails->is_test,
344 'PDFFilename' => 'receipt.pdf',
345 'from' => $receiptFrom,
346 'toName' => $donorDisplayName,
347 'toEmail' => $donorEmail,
348 );
349 list($sent) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams);
6a488035 350 }
353ffa53 351 }
6a488035
TO
352
353 $session = CRM_Core_Session::singleton();
353ffa53 354 $userID = $session->get('userID');
481a74f4 355 if ($userID && $status) {
6a488035 356 CRM_Core_Session::setStatus($status, $msgTitle, $msgType);
0db6c3e1 357 }
4c9b6178 358 elseif (!$userID) {
317fceb4 359 if ($status) {
6a488035 360 CRM_Utils_System::setUFMessage($status);
317fceb4 361 }
6a488035
TO
362 // keep result as 1, since we not displaying anything on the redirected page anyway
363 return CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/subscriptionstatus',
353ffa53 364 "reset=1&task=update&result=1"));
6a488035
TO
365 }
366 }
96025800 367
0c6c47a5 368 /**
369 * Explicitly declare the form context.
370 */
371 public function getDefaultContext() {
372 return 'create';
373 }
374
6a488035 375}