Merge pull request #12594 from eileenmcnaughton/sales_tax_trait
[civicrm-core.git] / CRM / Contribute / Form / UpdateBilling.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
8c9251b3 6 | Copyright CiviCRM LLC (c) 2004-2018 |
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/**
29 *
30 * @package CRM
8c9251b3 31 * @copyright CiviCRM LLC (c) 2004-2018
6a488035
TO
32 */
33
34/**
347e061b 35 * This class generates form components for processing a contribution.
6a488035
TO
36 */
37class CRM_Contribute_Form_UpdateBilling extends CRM_Core_Form {
38 protected $_crid = NULL;
39 protected $_coid = NULL;
40 protected $_mode = NULL;
41
42 protected $_subscriptionDetails = NULL;
43
44 protected $_selfService = FALSE;
45
46 public $_bltID = NULL;
a6513ad5
EM
47
48 /**
49 * @var array current payment processor including a copy of the object in 'object' key
50 */
51 public $_paymentProcessor = array();
6a488035 52
6a488035 53 /**
fe482240 54 * Set variables up before form is built.
6a488035
TO
55 */
56 public function preProcess() {
57 $this->_mid = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE);
58 $this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE);
59 if ($this->_crid) {
60 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_crid, 'recur', 'info');
f1e72c05 61 $this->_paymentProcessor['object'] = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_crid, 'recur', 'obj');
6a488035 62 $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_crid);
eea16664 63
6a488035
TO
64 // Are we cancelling a recurring contribution that is linked to an auto-renew membership?
65 if ($this->_subscriptionDetails->membership_id) {
66 $this->_mid = $this->_subscriptionDetails->membership_id;
67 }
68 }
69
70 $this->_coid = CRM_Utils_Request::retrieve('coid', 'Integer', $this, FALSE);
71 if ($this->_coid) {
72 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'info');
a6513ad5 73 $this->_paymentProcessor['object'] = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_coid, 'contribute', 'obj');
6a488035
TO
74 $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_coid, 'contribution');
75 }
76
77 if ($this->_mid) {
78 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_mid, 'membership', 'info');
a6513ad5 79 $this->_paymentProcessor['object'] = CRM_Financial_BAO_PaymentProcessor::getProcessorForEntity($this->_mid, 'membership', 'obj');
6a488035
TO
80 $this->_subscriptionDetails = CRM_Contribute_BAO_ContributionRecur::getSubscriptionDetails($this->_mid, 'membership');
81 $membershipTypes = CRM_Member_PseudoConstant::membershipType();
82 $membershipTypeId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $this->_mid, 'membership_type_id');
83 $this->assign('membershipType', CRM_Utils_Array::value($membershipTypeId, $membershipTypes));
84 $this->_mode = 'auto_renew';
85 }
86
1273d77c 87 if ((!$this->_crid && !$this->_coid && !$this->_mid) || (!$this->_subscriptionDetails)) {
6a488035
TO
88 CRM_Core_Error::fatal('Required information missing.');
89 }
90 if (!CRM_Core_Permission::check('edit contributions')) {
91 $userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE);
92 if (!CRM_Contact_BAO_Contact_Utils::validChecksum($this->_subscriptionDetails->contact_id, $userChecksum)) {
93 CRM_Core_Error::fatal(ts('You do not have permission to cancel subscription.'));
94 }
95 $this->_selfService = TRUE;
96 }
97
a6513ad5 98 if (!$this->_paymentProcessor['object']->isSupported('updateSubscriptionBillingInfo')) {
6a488035 99 CRM_Core_Error::fatal(ts("%1 processor doesn't support updating subscription billing details.",
353ffa53
TO
100 array(1 => $this->_paymentProcessor['object']->_processorName)
101 ));
6a488035
TO
102 }
103 $this->assign('paymentProcessor', $this->_paymentProcessor);
104
8345c9d3 105 $this->assignBillingType();
6a488035
TO
106
107 $this->assign('frequency_unit', $this->_subscriptionDetails->frequency_unit);
108 $this->assign('frequency_interval', $this->_subscriptionDetails->frequency_interval);
109 $this->assign('amount', $this->_subscriptionDetails->amount);
110 $this->assign('installments', $this->_subscriptionDetails->installments);
111 $this->assign('mode', $this->_mode);
112
113 // handle context redirection
114 CRM_Contribute_BAO_ContributionRecur::setSubscriptionContext();
115 }
116
186c9c17 117 /**
0c6c47a5 118 * Set the default values of various form elements.
186c9c17 119 *
186c9c17 120 * @return array
0c6c47a5 121 * Default values
186c9c17 122 */
00be9182 123 public function setDefaultValues() {
6a488035
TO
124 $this->_defaults = array();
125
126 if ($this->_subscriptionDetails->contact_id) {
353ffa53
TO
127 $fields = array();
128 $names = array(
129 'first_name',
130 'middle_name',
131 'last_name',
132 "street_address-{$this->_bltID}",
133 "city-{$this->_bltID}",
134 "postal_code-{$this->_bltID}",
135 "country_id-{$this->_bltID}",
136 "state_province_id-{$this->_bltID}",
6a488035
TO
137 );
138 foreach ($names as $name) {
139 $fields[$name] = 1;
140 }
141 $fields["state_province-{$this->_bltID}"] = 1;
142 $fields["country-{$this->_bltID}"] = 1;
143 $fields["email-{$this->_bltID}"] = 1;
144 $fields['email-Primary'] = 1;
145
146 CRM_Core_BAO_UFGroup::setProfileDefaults($this->_subscriptionDetails->contact_id, $fields, $this->_defaults);
147
148 // use primary email address if billing email address is empty
149 if (empty($this->_defaults["email-{$this->_bltID}"]) &&
150 !empty($this->_defaults['email-Primary'])
151 ) {
152 $this->_defaults["email-{$this->_bltID}"] = $this->_defaults['email-Primary'];
153 }
154
155 foreach ($names as $name) {
156 if (!empty($this->_defaults[$name])) {
157 $this->_defaults['billing_' . $name] = $this->_defaults[$name];
158 }
159 }
160 }
161
6a488035
TO
162 $config = CRM_Core_Config::singleton();
163 // set default country from config if no country set
a7488080 164 if (empty($this->_defaults["billing_country_id-{$this->_bltID}"])) {
6a488035
TO
165 $this->_defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry;
166 }
167
6a488035
TO
168 return $this->_defaults;
169 }
170
171 /**
fe482240 172 * Build the form object.
6a488035
TO
173 */
174 public function buildQuickForm() {
175 $type = 'next';
481a74f4 176 if ($this->_selfService) {
6a488035
TO
177 $type = 'submit';
178 }
179
180 $this->addButtons(array(
181 array(
182 'type' => $type,
183 'name' => ts('Save'),
184 'isDefault' => TRUE,
185 ),
186 array(
187 'type' => 'cancel',
188 'name' => ts('Cancel'),
189 ),
190 )
191 );
192
3c3aac0b 193 CRM_Core_Payment_Form::buildPaymentForm($this, $this->_paymentProcessor, TRUE, TRUE);
6a488035
TO
194 $this->addFormRule(array('CRM_Contribute_Form_UpdateBilling', 'formRule'), $this);
195 }
196
197 /**
fe482240 198 * Global form rule.
6a488035 199 *
014c4014
TO
200 * @param array $fields
201 * The input form values.
202 * @param array $files
203 * The uploaded files if any.
0c6c47a5 204 * @param CRM_Core_Form $self
da6b46f4 205 *
6a488035 206 *
72b3a70c
CW
207 * @return bool|array
208 * true if no errors, else array of errors
6a488035 209 */
00be9182 210 public static function formRule($fields, $files, $self) {
7cb3d4f0
CW
211 $errors = array();
212 CRM_Core_Form::validateMandatoryFields($self->_fields, $fields, $errors);
6a488035 213
a479fe60 214 // validate the payment instrument values (e.g. credit card number)
1d1fee72 215 CRM_Core_Payment_Form::validatePaymentInstrument($self->_paymentProcessor['id'], $fields, $errors, NULL);
6a488035 216
6a488035
TO
217 return empty($errors) ? TRUE : $errors;
218 }
219
220 /**
fe482240 221 * Process the form.
6a488035
TO
222 */
223 public function postProcess() {
224 $params = $this->controller->exportValues($this->_name);
225 $status = NULL;
226
227 // now set the values for the billing location.
228 foreach ($this->_fields as $name => $value) {
229 $fields[$name] = 1;
230 }
231 $fields["email-{$this->_bltID}"] = 1;
232
233 $processorParams = array();
234 foreach ($params as $key => $val) {
235 $key = str_replace('billing_', '', $key);
236 list($key) = explode('-', $key);
237 $processorParams[$key] = $val;
238 }
239 $processorParams['state_province'] = CRM_Core_PseudoConstant::stateProvince($params["billing_state_province_id-{$this->_bltID}"], FALSE);
240 $processorParams['country'] = CRM_Core_PseudoConstant::country($params["billing_country_id-{$this->_bltID}"], FALSE);
241 $processorParams['month'] = $processorParams['credit_card_exp_date']['M'];
242 $processorParams['year'] = $processorParams['credit_card_exp_date']['Y'];
243 $processorParams['subscriptionId'] = $this->_subscriptionDetails->subscription_id;
244 $processorParams['amount'] = $this->_subscriptionDetails->amount;
f1e72c05 245 $updateSubscription = $this->_paymentProcessor['object']->updateSubscriptionBillingInfo($message, $processorParams);
6a488035
TO
246 if (is_a($updateSubscription, 'CRM_Core_Error')) {
247 CRM_Core_Error::displaySessionError($updateSubscription);
248 }
249 elseif ($updateSubscription) {
250 $ctype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_subscriptionDetails->contact_id, 'contact_type');
251 $contact = &CRM_Contact_BAO_Contact::createProfileContact($params,
252 $fields,
253 $this->_subscriptionDetails->contact_id,
254 NULL,
255 NULL,
256 $ctype
257 );
258
259 // build tpl params
260 if ($this->_subscriptionDetails->membership_id) {
261 $inputParams = array('id' => $this->_subscriptionDetails->membership_id);
262 CRM_Member_BAO_Membership::getValues($inputParams, $tplParams);
263 $tplParams = $tplParams[$this->_subscriptionDetails->membership_id];
264 $tplParams['membership_status'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $tplParams['status_id']);
265 $tplParams['membershipType'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $tplParams['membership_type_id']);
266 $status = ts('Billing details for your automatically renewed %1 membership have been updated.',
267 array(1 => $tplParams['membershipType'])
268 );
269 $msgTitle = ts('Details Updated');
270 $msgType = 'success';
271 }
272 else {
273 $status = ts('Billing details for the recurring contribution of %1, every %2 %3 have been updated.',
274 array(
275 1 => $this->_subscriptionDetails->amount,
276 2 => $this->_subscriptionDetails->frequency_interval,
21dfd5f5 277 3 => $this->_subscriptionDetails->frequency_unit,
6a488035
TO
278 )
279 );
280 $msgTitle = ts('Details Updated');
281 $msgType = 'success';
eea16664 282
6a488035
TO
283 $tplParams = array(
284 'recur_frequency_interval' => $this->_subscriptionDetails->frequency_interval,
285 'recur_frequency_unit' => $this->_subscriptionDetails->frequency_unit,
286 'amount' => $this->_subscriptionDetails->amount,
287 );
288 }
289
290 // format new address for display
291 $addressParts = array("street_address", "city", "postal_code", "state_province", "country");
292 foreach ($addressParts as $part) {
293 $addressParts[$part] = CRM_Utils_Array::value($part, $processorParams);
294 }
295 $tplParams['address'] = CRM_Utils_Address::format($addressParts);
296
297 // format old address to store in activity details
298 $this->_defaults["state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvince($this->_defaults["state_province-{$this->_bltID}"], FALSE);
299 $this->_defaults["country-{$this->_bltID}"] = CRM_Core_PseudoConstant::country($this->_defaults["country-{$this->_bltID}"], FALSE);
300 $addressParts = array("street_address", "city", "postal_code", "state_province", "country");
301 foreach ($addressParts as $part) {
302 $key = "{$part}-{$this->_bltID}";
303 $addressParts[$part] = CRM_Utils_Array::value($key, $this->_defaults);
304 }
305 $this->_defaults['address'] = CRM_Utils_Address::format($addressParts);
306
307 // format new billing name
308 $name = $processorParams['first_name'];
a7488080 309 if (!empty($processorParams['middle_name'])) {
6a488035
TO
310 $name .= " {$processorParams['middle_name']}";
311 }
312 $name .= ' ' . $processorParams['last_name'];
313 $name = trim($name);
314 $tplParams['billingName'] = $name;
315
316 // format old billing name
317 $name = $this->_defaults['first_name'];
a7488080 318 if (!empty($this->_defaults['middle_name'])) {
6a488035
TO
319 $name .= " {$this->_defaults['middle_name']}";
320 }
321 $name .= ' ' . $this->_defaults['last_name'];
322 $name = trim($name);
323 $this->_defaults['billingName'] = $name;
324
325 $message .= "
326<br/><br/>New Billing Name and Address
327<br/>==============================
328<br/>{$tplParams['billingName']}
329<br/>{$tplParams['address']}
330
331<br/><br/>Previous Billing Name and Address
332<br/>==================================
333<br/>{$this->_defaults['billingName']}
334<br/>{$this->_defaults['address']}";
335
336 $activityParams = array(
337 'source_contact_id' => $this->_subscriptionDetails->contact_id,
593dbb07 338 'activity_type_id' => CRM_Core_PseudoConstant::getKey(
339 'CRM_Activity_BAO_Activity',
340 'activity_type_id',
341 'Update Recurring Contribution Billing Details'
6a488035
TO
342 ),
343 'subject' => ts('Recurring Contribution Billing Details Updated'),
344 'details' => $message,
345 'activity_date_time' => date('YmdHis'),
593dbb07 346 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Completed'),
6a488035
TO
347 );
348 $session = CRM_Core_Session::singleton();
349 $cid = $session->get('userID');
350 if ($cid) {
351 $activityParams['target_contact_id'][] = $activityParams['source_contact_id'];
352 $activityParams['source_contact_id'] = $cid;
353 }
354 CRM_Activity_BAO_Activity::create($activityParams);
355
356 // send notification
357 if ($this->_subscriptionDetails->contribution_page_id) {
358 CRM_Core_DAO::commonRetrieveAll('CRM_Contribute_DAO_ContributionPage', 'id',
359 $this->_subscriptionDetails->contribution_page_id, $value, array(
360 'title',
361 'receipt_from_name',
362 'receipt_from_email',
363 )
364 );
365 $receiptFrom = '"' . CRM_Utils_Array::value('receipt_from_name', $value[$this->_subscriptionDetails->contribution_page_id]) . '" <' . $value[$this->_subscriptionDetails->contribution_page_id]['receipt_from_email'] . '>';
366 }
367 else {
368 $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
369 $receiptFrom = "$domainValues[0] <$domainValues[1]>";
370 }
371 list($donorDisplayName, $donorEmail) = CRM_Contact_BAO_Contact::getContactDetails($this->_subscriptionDetails->contact_id);
372 $tplParams['contact'] = array('display_name' => $donorDisplayName);
373
374 $date = CRM_Utils_Date::format($processorParams['credit_card_exp_date']);
375 $tplParams['credit_card_exp_date'] = CRM_Utils_Date::mysqlToIso($date);
376 $tplParams['credit_card_number'] = CRM_Utils_System::mungeCreditCard($processorParams['credit_card_number']);
377 $tplParams['credit_card_type'] = $processorParams['credit_card_type'];
378
379 $sendTemplateParams = array(
380 'groupName' => $this->_subscriptionDetails->membership_id ? 'msg_tpl_workflow_membership' : 'msg_tpl_workflow_contribution',
381 'valueName' => $this->_subscriptionDetails->membership_id ? 'membership_autorenew_billing' : 'contribution_recurring_billing',
382 'contactId' => $this->_subscriptionDetails->contact_id,
383 'tplParams' => $tplParams,
384 'isTest' => $this->_subscriptionDetails->is_test,
385 'PDFFilename' => 'receipt.pdf',
386 'from' => $receiptFrom,
387 'toName' => $donorDisplayName,
388 'toEmail' => $donorEmail,
389 );
c6327d7d 390 list($sent) = CRM_Core_BAO_MessageTemplate::sendTemplate($sendTemplateParams);
6a488035
TO
391 }
392 else {
393 $status = ts('There was some problem updating the billing details.');
394 $msgTitle = ts('Update Error');
395 $msgType = 'error';
396 }
397
398 $session = CRM_Core_Session::singleton();
353ffa53 399 $userID = $session->get('userID');
481a74f4 400 if ($userID && $status) {
6a488035 401 $session->setStatus($status, $msgTitle, $msgType);
0db6c3e1 402 }
4c9b6178 403 elseif (!$userID) {
389bcebf 404 if ($status) {
6a488035 405 CRM_Utils_System::setUFMessage($status);
389bcebf 406 }
6a488035 407 $result = (int) ($updateSubscription && isset($ctype));
389bcebf 408 if (isset($tplParams)) {
6a488035 409 $session->set('resultParams', $tplParams);
389bcebf 410 }
eea16664 411 return CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contribute/subscriptionstatus',
353ffa53 412 "reset=1&task=billing&result={$result}"));
6a488035
TO
413 }
414 }
96025800 415
6a488035 416}