Merge pull request #6293 from JoeMurray/patch-1
[civicrm-core.git] / CRM / Member / Form / Membership.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
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
e7112fa7 31 * @copyright CiviCRM LLC (c) 2004-2015
6a488035
TO
32 */
33
34/**
5e56c7a5 35 * This class generates form components for offline membership form.
6a488035
TO
36 */
37class CRM_Member_Form_Membership extends CRM_Member_Form {
38
39 protected $_memType = NULL;
40
41 protected $_onlinePendingContributionId;
42
43 public $_mode;
44
45 public $_contributeMode = 'direct';
46
47 protected $_recurMembershipTypes;
48
49 protected $_memTypeSelected;
50
d424ffde 51 /**
fe482240 52 * Display name of the member.
d424ffde
CW
53 *
54 * @var string
6a488035 55 */
b11c92be 56 protected $_memberDisplayName = NULL;
6a488035 57
d424ffde
CW
58 /**
59 * email of the person paying for the membership (used for receipts)
60 */
b11c92be 61 protected $_memberEmail = NULL;
6a488035 62
d424ffde 63 /**
fe482240 64 * Contact ID of the member.
d424ffde
CW
65 *
66 * @var int
67 */
cc984198 68 public $_contactID = NULL;
6a488035 69
d424ffde
CW
70 /**
71 * Display name of the person paying for the membership (used for receipts)
72 *
73 * @var string
74 */
b11c92be 75 protected $_contributorDisplayName = NULL;
6a488035 76
d424ffde 77 /**
b11c92be 78 * email of the person paying for the membership (used for receipts)
79 */
80 protected $_contributorEmail = NULL;
6a488035 81
d424ffde
CW
82 /**
83 * email of the person paying for the membership (used for receipts)
84 *
85 * @var int
86 */
b11c92be 87 protected $_contributorContactID = NULL;
6a488035 88
d424ffde 89 /**
fe482240 90 * ID of the person the receipt is to go to.
d424ffde
CW
91 *
92 * @var int
b11c92be 93 */
94 protected $_receiptContactId = NULL;
6a488035 95
d424ffde 96 /**
4aa7d844 97 * Keep a class variable for ALL membership IDs so
6a488035 98 * postProcess hook function can do something with it
d424ffde
CW
99 *
100 * @var array
6a488035 101 */
b11c92be 102 protected $_membershipIDs = array();
6a488035 103
5d86176b 104 /**
4aa7d844 105 * An array to hold a list of date fields on the form
5d86176b 106 * so that they can be converted to ISO in a consistent manner
107 *
108 * @var array
109 */
110 protected $_dateFields = array(
111 'receive_date' => array('default' => 'now'),
112 );
113
18aa3e1e 114 /**
4efc56ef
EM
115 * Get selected membership type from the form values.
116 *
e4a6290d 117 * @param array $priceSet
4efc56ef
EM
118 * @param array $params
119 *
120 * @return array
18aa3e1e 121 */
e4a6290d 122 public static function getSelectedMemberships($priceSet, $params) {
18aa3e1e 123 $memTypeSelected = array();
e4a6290d 124 $priceFieldIDS = self::getPriceFieldIDs($params, $priceSet);
125 if (!empty($priceSet) && is_array($priceFieldIDS)) {
18aa3e1e
EM
126 foreach ($priceFieldIDS as $priceFieldId) {
127 if ($id = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_type_id')) {
128 $memTypeSelected[$id] = $id;
129 }
130 }
131 }
132 else {
9e8b2bb1 133 $memTypeSelected = array($params['membership_type_id'][1] => $params['membership_type_id'][1]);
18aa3e1e
EM
134 }
135 return $memTypeSelected;
136 }
137
138 /**
139 * Extract price set fields and values from $params.
140 *
5e56c7a5 141 * @param array $params
e4a6290d 142 * @param array $priceSet
5e56c7a5 143 *
18aa3e1e
EM
144 * @return array
145 */
e4a6290d 146 public static function getPriceFieldIDs($params, $priceSet) {
147 $priceFieldIDS = $fieldIds = array();
148 if (isset($priceSet['fields']) && is_array($priceSet['fields'])) {
149 $fieldIds = array_keys($priceSet['fields']);
150 foreach ($fieldIds as $fieldId) {
151 if (!empty($params['price_' . $fieldId])) {
152 if (is_array($params['price_' . $fieldId])) {
153 foreach ($params['price_' . $fieldId] as $priceFldVal => $isSet) {
154 if ($isSet) {
155 $priceFieldIDS[] = $priceFldVal;
156 }
18aa3e1e
EM
157 }
158 }
e4a6290d 159 else {
160 $priceFieldIDS[] = $params['price_' . $fieldId];
161 }
18aa3e1e
EM
162 }
163 }
164 }
165 return $priceFieldIDS;
166 }
167
5e56c7a5 168 /**
169 * Form preProcess function.
170 *
171 * @throws \Exception
172 */
6a488035 173 public function preProcess() {
186a737c
EM
174 // This string makes up part of the class names, differentiating them (not sure why) from the membership fields.
175 $this->assign('formClass', 'membership');
a6513ad5 176 parent::preProcess();
6a488035
TO
177 // get price set id.
178 $this->_priceSetId = CRM_Utils_Array::value('priceSetId', $_GET);
179 $this->set('priceSetId', $this->_priceSetId);
180 $this->assign('priceSetId', $this->_priceSetId);
181
6a488035
TO
182 if ($this->_action & CRM_Core_Action::DELETE) {
183 $contributionID = CRM_Member_BAO_Membership::getMembershipContributionId($this->_id);
184 // check delete permission for contribution
185 if ($this->_id && $contributionID && !CRM_Core_Permission::checkActionPermission('CiviContribute', $this->_action)) {
186 CRM_Core_Error::fatal(ts("This Membership is linked to a contribution. You must have 'delete in CiviContribute' permission in order to delete this record."));
187 }
188 }
189
6a488035 190 if ($this->_action & CRM_Core_Action::ADD) {
c905ba98 191 if (!CRM_Member_BAO_Membership::statusAvailabilty($this->_contactID)) {
192 // all possible statuses are disabled - redirect back to contact form
193 CRM_Core_Error::statusBounce(ts('There are no configured membership statuses. You cannot add this membership until your membership statuses are correctly configured'));
194 }
6a488035
TO
195
196 if ($this->_contactID) {
197 //check whether contact has a current membership so we can alert user that they may want to do a renewal instead
4256ea25
AH
198 $contactMemberships = array();
199 $memParams = array('contact_id' => $this->_contactID);
200 CRM_Member_BAO_Membership::getValues($memParams, $contactMemberships, TRUE);
201 $cMemTypes = array();
202 foreach ($contactMemberships as $mem) {
203 $cMemTypes[] = $mem['membership_type_id'];
204 }
205 if (count($cMemTypes) > 0) {
206 $memberorgs = CRM_Member_BAO_MembershipType::getMemberOfContactByMemTypes($cMemTypes);
207 $mems_by_org = array();
2e8b13d1 208 foreach ($contactMemberships as $mem) {
4256ea25 209 $mem['member_of_contact_id'] = CRM_Utils_Array::value($mem['membership_type_id'], $memberorgs);
a7488080 210 if (!empty($mem['membership_end_date'])) {
4256ea25
AH
211 $mem['membership_end_date'] = CRM_Utils_Date::customformat($mem['membership_end_date']);
212 }
213 $mem['membership_type'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
214 $mem['membership_type_id'],
215 'name', 'id'
6a488035 216 );
4256ea25
AH
217 $mem['membership_status'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus',
218 $mem['status_id'],
219 'label', 'id'
6a488035 220 );
74dd0d90
CW
221 $mem['renewUrl'] = CRM_Utils_System::url('civicrm/contact/view/membership',
222 "reset=1&action=renew&cid={$this->_contactID}&id={$mem['id']}&context=membership&selectedChild=member"
223 . ($this->_mode ? '&mode=live' : '')
224 );
4256ea25
AH
225 $mem['membershipTab'] = CRM_Utils_System::url('civicrm/contact/view',
226 "reset=1&force=1&cid={$this->_contactID}&selectedChild=member"
227 );
228 $mems_by_org[$mem['member_of_contact_id']] = $mem;
6a488035 229 }
74dd0d90 230 $this->assign('existingContactMemberships', $mems_by_org);
6a488035
TO
231 }
232 }
1001e556 233 else {
74dd0d90 234 // In standalone mode we don't have a contact id yet so lookup will be done client-side with this script:
d292601b
AH
235 $resources = CRM_Core_Resources::singleton();
236 $resources->addScriptFile('civicrm', 'templates/CRM/Member/Form/MembershipStandalone.js');
d292601b
AH
237 $passthru = array(
238 'typeorgs' => CRM_Member_BAO_MembershipType::getMembershipTypeOrganization(),
74dd0d90
CW
239 'memtypes' => CRM_Core_PseudoConstant::get('CRM_Member_BAO_Membership', 'membership_type_id'),
240 'statuses' => CRM_Core_PseudoConstant::get('CRM_Member_BAO_Membership', 'status_id'),
d292601b
AH
241 );
242 $resources->addSetting(array('existingMems' => $passthru));
243 }
6a488035
TO
244 }
245
246 // when custom data is included in this page
a7488080 247 if (!empty($_POST['hidden_custom'])) {
6a488035
TO
248 CRM_Custom_Form_CustomData::preProcess($this);
249 CRM_Custom_Form_CustomData::buildQuickForm($this);
250 CRM_Custom_Form_CustomData::setDefaultValues($this);
251 }
252
253 // CRM-4395, get the online pending contribution id.
254 $this->_onlinePendingContributionId = NULL;
255 if (!$this->_mode && $this->_id && ($this->_action & CRM_Core_Action::UPDATE)) {
256 $this->_onlinePendingContributionId = CRM_Contribute_BAO_Contribution::checkOnlinePendingContribution($this->_id,
257 'Membership'
258 );
259 }
260 $this->assign('onlinePendingContributionId', $this->_onlinePendingContributionId);
6a488035 261
e2046b33 262 $this->setPageTitle(ts('Membership'));
6a488035
TO
263 }
264
265 /**
5e56c7a5 266 * Set default values for the form.
6a488035
TO
267 */
268 public function setDefaultValues() {
6a488035
TO
269
270 if ($this->_priceSetId) {
9da8dc8c 271 return CRM_Price_BAO_PriceSet::setDefaultPriceSet($this, $defaults);
6a488035
TO
272 }
273
274 $defaults = parent::setDefaultValues();
275
276 //setting default join date and receive date
569fe706 277 list($now, $currentTime) = CRM_Utils_Date::setDateDefaults();
6a488035
TO
278 if ($this->_action == CRM_Core_Action::ADD) {
279 $defaults['receive_date'] = $now;
569fe706 280 $defaults['receive_date_time'] = $currentTime;
6a488035
TO
281 }
282
283 if (is_numeric($this->_memType)) {
284 $defaults['membership_type_id'] = array();
285 $defaults['membership_type_id'][0] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
286 $this->_memType,
287 'member_of_contact_id',
288 'id'
289 );
290 $defaults['membership_type_id'][1] = $this->_memType;
291 }
292 else {
293 $defaults['membership_type_id'] = $this->_memType;
294 }
295
296 $defaults['num_terms'] = 1;
297
a7488080 298 if (!empty($defaults['id'])) {
6a488035
TO
299 if ($this->_onlinePendingContributionId) {
300 $defaults['record_contribution'] = $this->_onlinePendingContributionId;
301 }
302 else {
303 $contributionId = CRM_Core_DAO::singleValueQuery("
304 SELECT contribution_id
305 FROM civicrm_membership_payment
306 WHERE membership_id = $this->_id
307 ORDER BY contribution_id
308 DESC limit 1");
309
310 if ($contributionId) {
311 $defaults['record_contribution'] = $contributionId;
312 }
313 }
314 }
133e2c99 315
374a4dd6 316 //set Soft Credit Type to Gift by default
317 $scTypes = CRM_Core_OptionGroup::values("soft_credit_type");
318 $defaults['soft_credit_type_id'] = CRM_Utils_Array::value(ts('Gift'), array_flip($scTypes));
319
a7488080 320 if (!empty($defaults['record_contribution']) && !$this->_mode) {
6a488035
TO
321 $contributionParams = array('id' => $defaults['record_contribution']);
322 $contributionIds = array();
323
324 //keep main object campaign in hand.
325 $memberCampaignId = CRM_Utils_Array::value('campaign_id', $defaults);
326
327 CRM_Contribute_BAO_Contribution::getValues($contributionParams, $defaults, $contributionIds);
328
329 //get back original object campaign id.
330 $defaults['campaign_id'] = $memberCampaignId;
331
a7488080 332 if (!empty($defaults['receive_date'])) {
6a488035
TO
333 list($defaults['receive_date']) = CRM_Utils_Date::setDateDefaults($defaults['receive_date']);
334 }
335
336 // Contribution::getValues() over-writes the membership record's source field value - so we need to restore it.
a7488080 337 if (!empty($defaults['membership_source'])) {
6a488035
TO
338 $defaults['source'] = $defaults['membership_source'];
339 }
340 }
d96cf288 341 //CRM-13420
a7488080 342 if (empty($defaults['payment_instrument_id'])) {
d96cf288
DG
343 $defaults['payment_instrument_id'] = key(CRM_Core_OptionGroup::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1'));
344 }
6a488035
TO
345
346 // User must explicitly choose to send a receipt in both add and update mode.
347 $defaults['send_receipt'] = 0;
348
349 if ($this->_action & CRM_Core_Action::UPDATE) {
350 // in this mode by default uncheck this checkbox
351 unset($defaults['record_contribution']);
352 }
353
2e8b13d1 354 $subscriptionCancelled = FALSE;
a7488080 355 if (!empty($defaults['id'])) {
6a488035
TO
356 $subscriptionCancelled = CRM_Member_BAO_Membership::isSubscriptionCancelled($this->_id);
357 }
358
359 $alreadyAutoRenew = FALSE;
a7488080 360 if (!empty($defaults['contribution_recur_id']) && !$subscriptionCancelled) {
6a488035
TO
361 $defaults['auto_renew'] = 1;
362 $alreadyAutoRenew = TRUE;
363 }
364 $this->assign('alreadyAutoRenew', $alreadyAutoRenew);
365
366 $this->assign('member_is_test', CRM_Utils_Array::value('member_is_test', $defaults));
367
368 $this->assign('membership_status_id', CRM_Utils_Array::value('status_id', $defaults));
369
a7488080 370 if (!empty($defaults['is_pay_later'])) {
6a488035
TO
371 $this->assign('is_pay_later', TRUE);
372 }
373 if ($this->_mode) {
cc8ff73d
DG
374 // set default country from config if no country set
375 $config = CRM_Core_Config::singleton();
a7488080 376 if (empty($defaults["billing_country_id-{$this->_bltID}"])) {
cc8ff73d 377 $defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry;
6a488035 378 }
6a488035 379
a7488080 380 if (empty($defaults["billing_state_province_id-{$this->_bltID}"])) {
cc8ff73d 381 $defaults["billing_state_province_id-{$this->_bltID}"] = $config->defaultContactStateProvince;
6a488035
TO
382 }
383
cc8ff73d
DG
384 $billingDefaults = $this->getProfileDefaults('Billing', $this->_contactID);
385 $defaults = array_merge($defaults, $billingDefaults);
cc44e307 386
5e56c7a5 387 // hack to simplify credit card entry for testing
388 // $defaults['credit_card_type'] = 'Visa';
389 // $defaults['credit_card_number'] = '4807731747657838';
390 // $defaults['cvv2'] = '000';
391 // $defaults['credit_card_exp_date'] = array( 'Y' => '2012', 'M' => '05' );
6a488035
TO
392 }
393
394 $dates = array('join_date', 'start_date', 'end_date');
395 foreach ($dates as $key) {
a7488080 396 if (!empty($defaults[$key])) {
6a488035
TO
397 list($defaults[$key]) = CRM_Utils_Date::setDateDefaults(CRM_Utils_Array::value($key, $defaults));
398 }
399 }
400
401 //setting default join date if there is no join date
a7488080 402 if (empty($defaults['join_date'])) {
6a488035
TO
403 $defaults['join_date'] = $now;
404 }
405
a7488080 406 if (!empty($defaults['membership_end_date'])) {
6a488035
TO
407 $this->assign('endDate', $defaults['membership_end_date']);
408 }
409
410 return $defaults;
411 }
412
413 /**
fe482240 414 * Build the form object.
6a488035
TO
415 */
416 public function buildQuickForm() {
6a488035 417
6f87cd8d 418 $this->assign('taxRates', json_encode(CRM_Core_PseudoConstant::getTaxRates()));
579e1e0e
EM
419
420 $this->assign('currency', CRM_Core_Config::singleton()->defaultCurrencySymbol);
b09fe5ed 421 $invoiceSettings = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings');
46611472 422 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
9b873358 423 if (isset($invoicing)) {
46611472 424 $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings));
425 }
6a488035
TO
426 // build price set form.
427 $buildPriceSet = FALSE;
8cc574cf 428 if ($this->_priceSetId || !empty($_POST['price_set_id'])) {
a7488080 429 if (!empty($_POST['price_set_id'])) {
6a488035
TO
430 $buildPriceSet = TRUE;
431 }
432 $getOnlyPriceSetElements = TRUE;
433 if (!$this->_priceSetId) {
434 $this->_priceSetId = $_POST['price_set_id'];
435 $getOnlyPriceSetElements = FALSE;
436 }
437
438 $this->set('priceSetId', $this->_priceSetId);
9da8dc8c 439 CRM_Price_BAO_PriceSet::buildPriceSet($this);
6a488035
TO
440
441 $optionsMembershipTypes = array();
442 foreach ($this->_priceSet['fields'] as $pField) {
443 if (empty($pField['options'])) {
444 continue;
445 }
446 foreach ($pField['options'] as $opId => $opValues) {
447 $optionsMembershipTypes[$opId] = CRM_Utils_Array::value('membership_type_id', $opValues, 0);
448 }
449 }
450
9da8dc8c 451 $this->assign('autoRenewOption', CRM_Price_BAO_PriceSet::checkAutoRenewForPriceSet($this->_priceSetId));
6a488035
TO
452
453 $this->assign('optionsMembershipTypes', $optionsMembershipTypes);
5a9c4d4a 454 $this->assign('contributionType', CRM_Utils_Array::value('financial_type_id', $this->_priceSet));
6a488035
TO
455
456 // get only price set form elements.
457 if ($getOnlyPriceSetElements) {
458 return;
459 }
460 }
461
462 // use to build form during form rule.
463 $this->assign('buildPriceSet', $buildPriceSet);
464
465 if ($this->_action & CRM_Core_Action::ADD) {
466 $buildPriceSet = FALSE;
9da8dc8c 467 $priceSets = CRM_Price_BAO_PriceSet::getAssoc(FALSE, 'CiviMember');
6a488035
TO
468 if (!empty($priceSets)) {
469 $buildPriceSet = TRUE;
470 }
471
472 if ($buildPriceSet) {
473 $this->add('select', 'price_set_id', ts('Choose price set'),
474 array(
21dfd5f5 475 '' => ts('Choose price set'),
b11c92be 476 ) + $priceSets,
6a488035
TO
477 NULL, array('onchange' => "buildAmount( this.value );")
478 );
479 }
480 $this->assign('hasPriceSets', $buildPriceSet);
481 }
482
483 //need to assign custom data type and subtype to the template
484 $this->assign('customDataType', 'Membership');
485 $this->assign('customDataSubType', $this->_memType);
486 $this->assign('entityID', $this->_id);
487
488 if ($this->_action & CRM_Core_Action::DELETE) {
489 $this->addButtons(array(
490 array(
491 'type' => 'next',
492 'name' => ts('Delete'),
493 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
494 'isDefault' => TRUE,
495 ),
496 array(
497 'type' => 'cancel',
498 'name' => ts('Cancel'),
499 ),
500 )
501 );
502 return;
503 }
504
505 if ($this->_context == 'standalone') {
353ffa53
TO
506 $this->addEntityRef('contact_id', ts('Contact'), array(
507 'create' => TRUE,
79d7553f 508 'api' => array('extra' => array('email')),
353ffa53 509 ), TRUE);
6a488035
TO
510 }
511
512 $selOrgMemType[0][0] = $selMemTypeOrg[0] = ts('- select -');
513
6a488035 514 // retrieve all memberships
ab30e033
EM
515 $allMembershipInfo = array();
516 foreach ($this->allMembershipTypeDetails as $key => $values) {
517 if ($this->_mode && empty($values['minimum_fee'])) {
518 continue;
519 }
520 else {
521 $memberOfContactId = CRM_Utils_Array::value('member_of_contact_id', $values);
522 if (empty($selMemTypeOrg[$memberOfContactId])) {
523 $selMemTypeOrg[$memberOfContactId] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
524 $memberOfContactId,
525 'display_name',
526 'id'
527 );
6a488035 528
ab30e033 529 $selOrgMemType[$memberOfContactId][0] = ts('- select -');
6a488035 530 }
ab30e033
EM
531 if (empty($selOrgMemType[$memberOfContactId][$key])) {
532 $selOrgMemType[$memberOfContactId][$key] = CRM_Utils_Array::value('name', $values);
6a488035 533 }
6a488035 534 }
ab30e033
EM
535
536 // build membership info array, which is used when membership type is selected to:
537 // - set the payment information block
538 // - set the max related block
539 $allMembershipInfo[$key] = array(
540 'financial_type_id' => CRM_Utils_Array::value('financial_type_id', $values),
541 'total_amount' => CRM_Utils_Money::format($values['minimum_fee'], NULL, '%a'),
542 'total_amount_numeric' => CRM_Utils_Array::value('minimum_fee', $values),
543 'auto_renew' => CRM_Utils_Array::value('auto_renew', $values),
544 'has_related' => isset($values['relationship_type_id']),
545 'max_related' => CRM_Utils_Array::value('max_related', $values),
546 );
6a488035
TO
547 }
548
549 $this->assign('allMembershipInfo', json_encode($allMembershipInfo));
550
551 // show organization by default, if only one organization in
552 // the list
553 if (count($selMemTypeOrg) == 2) {
554 unset($selMemTypeOrg[0], $selOrgMemType[0][0]);
555 }
556 //sort membership organization and type, CRM-6099
557 natcasesort($selMemTypeOrg);
558 foreach ($selOrgMemType as $index => $orgMembershipType) {
559 natcasesort($orgMembershipType);
560 $selOrgMemType[$index] = $orgMembershipType;
561 }
562
ab30e033
EM
563 $memTypeJs = array(
564 'onChange' => "buildMaxRelated(this.value,true); CRM.buildCustomData('Membership', this.value);",
565 );
8deb20a3 566
ab30e033
EM
567 if (!empty($this->_recurPaymentProcessors)) {
568 $memTypeJs['onChange'] = "" . $memTypeJs['onChange'] . 'buildAutoRenew(this.value, null);';
569 }
6a488035 570
6a488035
TO
571 $this->add('text', 'max_related', ts('Max related'),
572 CRM_Core_DAO::getAttribute('CRM_Member_DAO_Membership', 'max_related')
573 );
574
353ffa53 575 $sel = &$this->addElement('hierselect',
6a488035
TO
576 'membership_type_id',
577 ts('Membership Organization and Type'),
578 $memTypeJs
579 );
580
581 $sel->setOptions(array($selMemTypeOrg, $selOrgMemType));
582 $elements = array();
583 if ($sel) {
584 $elements[] = $sel;
585 }
586
587 $this->applyFilter('__ALL__', 'trim');
588
589 if ($this->_action & CRM_Core_Action::ADD) {
590 $this->add('text', 'num_terms', ts('Number of Terms'), array('size' => 6));
591 }
592
593 $this->addDate('join_date', ts('Member Since'), FALSE, array('formatType' => 'activityDate'));
594 $this->addDate('start_date', ts('Start Date'), FALSE, array('formatType' => 'activityDate'));
595 $endDate = $this->addDate('end_date', ts('End Date'), FALSE, array('formatType' => 'activityDate'));
596 if ($endDate) {
597 $elements[] = $endDate;
598 }
599
600 $this->add('text', 'source', ts('Source'),
601 CRM_Core_DAO::getAttribute('CRM_Member_DAO_Membership', 'source')
602 );
603
604 //CRM-7362 --add campaigns.
605 $campaignId = NULL;
606 if ($this->_id) {
607 $campaignId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $this->_id, 'campaign_id');
608 }
609 CRM_Campaign_BAO_Campaign::addCampaign($this, $campaignId);
610
611 if (!$this->_mode) {
612 $this->add('select', 'status_id', ts('Membership Status'),
613 array('' => ts('- select -')) + CRM_Member_PseudoConstant::membershipStatus(NULL, NULL, 'label')
614 );
615 $statusOverride = $this->addElement('checkbox', 'is_override',
616 ts('Status Override?'), NULL,
617 array('onClick' => 'showHideMemberStatus()')
618 );
619 if ($statusOverride) {
620 $elements[] = $statusOverride;
621 }
622
623 $this->addElement('checkbox', 'record_contribution', ts('Record Membership Payment?'));
624
6a488035
TO
625 $this->add('text', 'total_amount', ts('Amount'));
626 $this->addRule('total_amount', ts('Please enter a valid amount.'), 'money');
627
5d86176b 628 $this->addDate('receive_date', ts('Received'), FALSE, array('formatType' => 'activityDateTime'));
6a488035
TO
629
630 $this->add('select', 'payment_instrument_id',
4db803dd 631 ts('Payment Method'),
6a488035
TO
632 array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::paymentInstrument(),
633 FALSE, array('onChange' => "return showHideByValue('payment_instrument_id','4','checkNumber','table-row','select',false);")
634 );
635 $this->add('text', 'trxn_id', ts('Transaction ID'));
636 $this->addRule('trxn_id', ts('Transaction ID already exists in Database.'),
5e56c7a5 637 'objectExists', array(
638 'CRM_Contribute_DAO_Contribution',
639 $this->_id,
640 'trxn_id',
641 )
6a488035
TO
642 );
643
644 $allowStatuses = array();
645 $statuses = CRM_Contribute_PseudoConstant::contributionStatus();
646 if ($this->_onlinePendingContributionId) {
647 $statusNames = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
648 foreach ($statusNames as $val => $name) {
649 if (in_array($name, array(
b11c92be 650 'In Progress',
21dfd5f5 651 'Overdue',
b11c92be 652 ))
653 ) {
6a488035
TO
654 continue;
655 }
656 $allowStatuses[$val] = $statuses[$val];
657 }
658 }
659 else {
660 $allowStatuses = $statuses;
661 }
662 $this->add('select', 'contribution_status_id',
663 ts('Payment Status'), $allowStatuses
664 );
665 $this->add('text', 'check_number', ts('Check Number'),
666 CRM_Core_DAO::getAttribute('CRM_Contribute_DAO_Contribution', 'check_number')
667 );
668 }
669 else {
670 //add field for amount to allow an amount to be entered that differs from minimum
671 $this->add('text', 'total_amount', ts('Amount'));
672 }
5a9c4d4a
PN
673 $this->add('select', 'financial_type_id',
674 ts('Financial Type'),
675 array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::financialType()
676 );
d80dbc14 677
d80dbc14 678 $this->addElement('checkbox', 'is_different_contribution_contact', ts('Record Payment from a Different Contact?'));
186a737c 679
d80dbc14 680 $this->addSelect('soft_credit_type_id', array('entity' => 'contribution_soft'));
681 $this->addEntityRef('soft_credit_contact_id', ts('Payment From'), array('create' => TRUE));
682
6a488035
TO
683 $this->addElement('checkbox',
684 'send_receipt',
685 ts('Send Confirmation and Receipt?'), NULL,
686 array('onclick' => "showHideByValue( 'send_receipt', '', 'notice', 'table-row', 'radio', false); showHideByValue( 'send_receipt', '', 'fromEmail', 'table-row', 'radio', false);")
687 );
688
689 $this->add('select', 'from_email_address', ts('Receipt From'), $this->_fromEmails);
690
186a737c 691 $this->add('textarea', 'receipt_text', ts('Receipt Message'));
6a488035
TO
692
693 // Retrieve the name and email of the contact - this will be the TO for receipt email
694 if ($this->_contactID) {
695 list($this->_memberDisplayName,
696 $this->_memberEmail
b11c92be 697 ) = CRM_Contact_BAO_Contact_Location::getEmailDetails($this->_contactID);
6a488035
TO
698
699 $this->assign('emailExists', $this->_memberEmail);
700 $this->assign('displayName', $this->_memberDisplayName);
701 }
702
703 $isRecur = FALSE;
704 if ($this->_action & CRM_Core_Action::UPDATE) {
705 $recurContributionId = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_Membership', $this->_id,
706 'contribution_recur_id'
707 );
708 if ($recurContributionId && !CRM_Member_BAO_Membership::isSubscriptionCancelled($this->_id)) {
709 $isRecur = TRUE;
710 if (CRM_Member_BAO_Membership::isCancelSubscriptionSupported($this->_id)) {
711 $this->assign('cancelAutoRenew',
712 CRM_Utils_System::url('civicrm/contribute/unsubscribe', "reset=1&mid={$this->_id}")
713 );
714 }
715 foreach ($elements as $elem) {
716 $elem->freeze();
717 }
718 }
719 }
720 $this->assign('isRecur', $isRecur);
721
722 $this->addFormRule(array('CRM_Member_Form_Membership', 'formRule'), $this);
723
0816949d
EM
724 $this->assign('outBound_option', CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
725 'mailing_backend'));
6a488035
TO
726
727 parent::buildQuickForm();
728 }
729
730 /**
fe482240 731 * Validation.
6a488035 732 *
b2363ea8
TO
733 * @param array $params
734 * (ref.) an assoc array of name/value pairs.
6a488035 735 *
5e56c7a5 736 * @param array $files
737 * @param CRM_Member_Form_Membership $self
2a6da8d7
EM
738 *
739 * @throws CiviCRM_API3_Exception
72b3a70c
CW
740 * @return bool|array
741 * mixed true or array of errors
6a488035 742 */
00be9182 743 public static function formRule($params, $files, $self) {
6a488035
TO
744 $errors = array();
745
746 $priceSetId = CRM_Utils_Array::value('price_set_id', $params);
e4a6290d 747 $priceSetDetails = CRM_Price_BAO_PriceSet::getSetDetail($priceSetId);
6a488035 748
e4a6290d 749 $selectedMemberships = self::getSelectedMemberships($priceSetDetails[$priceSetId], $params);
6a488035 750 if ($priceSetId) {
9da8dc8c 751 CRM_Price_BAO_PriceField::priceSetValidation($priceSetId, $params, $errors);
6a488035 752
e4a6290d 753 $priceFieldIDS = self::getPriceFieldIDs($params, $priceSetDetails);
6a488035
TO
754
755 if (!empty($priceFieldIDS)) {
756 $ids = implode(',', $priceFieldIDS);
757
9da8dc8c 758 $count = CRM_Price_BAO_PriceSet::getMembershipCount($ids);
2e8b13d1 759 foreach ($count as $occurrence) {
b44e3f84 760 if ($occurrence > 1) {
6a488035
TO
761 $errors['_qf_default'] = ts('Select at most one option associated with the same membership type.');
762 }
763 }
6a488035 764 }
867047cd 765 // Return error if empty $self->_memTypeSelected
766 if (empty($errors) && empty($selectedMemberships)) {
767 $errors['_qf_default'] = ts('Select at least one membership option.');
768 }
769 if (!$self->_mode && empty($params['record_contribution'])) {
770 $errors['record_contribution'] = ts('Record Membership Payment is required when you use a price set.');
771 }
6a488035 772 }
867047cd 773 else {
774 if (empty($params['membership_type_id'][1])) {
775 $errors['membership_type_id'] = ts('Please select a membership type.');
776 }
6a488035
TO
777 $numterms = CRM_Utils_Array::value('num_terms', $params);
778 if ($numterms && intval($numterms) != $numterms) {
779 $errors['num_terms'] = ts('Please enter an integer for the number of terms.');
780 }
6a488035 781
867047cd 782 if ($self->_mode || isset($params['record_contribution']) && empty($params['financial_type_id'])) {
783 $errors['financial_type_id'] = ts('Please enter the financial Type.');
784 }
6a488035
TO
785 }
786
18aa3e1e
EM
787 if (!empty($errors) && (count($selectedMemberships) > 1)) {
788 $memberOfContacts = CRM_Member_BAO_MembershipType::getMemberOfContactByMemTypes($selectedMemberships);
6a488035
TO
789 $duplicateMemberOfContacts = array_count_values($memberOfContacts);
790 foreach ($duplicateMemberOfContacts as $countDuplicate) {
791 if ($countDuplicate > 1) {
792 $errors['_qf_default'] = ts('Please do not select more than one membership associated with the same organization.');
793 }
794 }
795 }
796
6a488035
TO
797 if (!empty($errors)) {
798 return $errors;
799 }
800
133e2c99 801
8cc574cf 802 if (!empty($params['record_contribution']) && empty($params['payment_instrument_id'])) {
4db803dd 803 $errors['payment_instrument_id'] = ts('Payment Method is a required field.');
d96cf288 804 }
6a488035 805
a7488080
CW
806 if (!empty($params['is_different_contribution_contact'])) {
807 if (empty($params['soft_credit_type_id'])) {
133e2c99 808 $errors['soft_credit_type_id'] = ts('Please Select a Soft Credit Type');
809 }
d80dbc14 810 if (empty($params['soft_credit_contact_id'])) {
811 $errors['soft_credit_contact_id'] = ts('Please select a contact');
133e2c99 812 }
813 }
814
a7488080 815 if (!empty($params['payment_processor_id'])) {
a479fe60 816 // validate payment instrument (e.g. credit card number)
817 CRM_Core_Payment_Form::validatePaymentInstrument($params['payment_processor_id'], $params, $errors, $self);
6a488035
TO
818 }
819
820 $joinDate = NULL;
a7488080 821 if (!empty($params['join_date'])) {
6a488035
TO
822
823 $joinDate = CRM_Utils_Date::processDate($params['join_date']);
824
18aa3e1e 825 foreach ($selectedMemberships as $memType) {
6a488035 826 $startDate = NULL;
a7488080 827 if (!empty($params['start_date'])) {
6a488035
TO
828 $startDate = CRM_Utils_Date::processDate($params['start_date']);
829 }
830
831 // if end date is set, ensure that start date is also set
832 // and that end date is later than start date
6a488035 833 $endDate = NULL;
a7488080 834 if (!empty($params['end_date'])) {
6a488035
TO
835 $endDate = CRM_Utils_Date::processDate($params['end_date']);
836 }
837
838 $membershipDetails = CRM_Member_BAO_MembershipType::getMembershipTypeDetails($memType);
839
840 if ($startDate && CRM_Utils_Array::value('period_type', $membershipDetails) == 'rolling') {
841 if ($startDate < $joinDate) {
842 $errors['start_date'] = ts('Start date must be the same or later than Member since.');
843 }
844 }
845
846 if ($endDate) {
847 if ($membershipDetails['duration_unit'] == 'lifetime') {
f476dde8 848 // Check if status is NOT cancelled or similar. For lifetime memberships, there is no automated
ae3d69ec
SG
849 // process to update status based on end-date. The user must change the status now.
850 $result = civicrm_api3('MembershipStatus', 'get', array(
851 'sequential' => 1,
852 'is_current_member' => 0,
853 ));
f476dde8 854 $tmp_statuses = $result['values'];
8efea814 855 $status_ids = array();
22e263ad 856 foreach ($tmp_statuses as $cur_stat) {
8efea814 857 $status_ids[] = $cur_stat['id'];
f476dde8 858 }
481a74f4 859 if (empty($params['status_id']) || in_array($params['status_id'], $status_ids) == FALSE) {
f476dde8 860 $errors['status_id'] = ts('Please enter a status that does NOT represent a current membership status.');
353ffa53 861 $errors['is_override'] = ts('This must be checked because you set an End Date for a lifetime membership');
f476dde8 862 }
6a488035
TO
863 }
864 else {
865 if (!$startDate) {
866 $errors['start_date'] = ts('Start date must be set if end date is set.');
867 }
868 if ($endDate < $startDate) {
869 $errors['end_date'] = ts('End date must be the same or later than start date.');
870 }
871 }
872 }
873
5e56c7a5 874 // Default values for start and end dates if not supplied on the form.
6a488035
TO
875 $defaultDates = CRM_Member_BAO_MembershipType::getDatesForMembershipType($memType,
876 $joinDate,
877 $startDate,
878 $endDate
879 );
880
881 if (!$startDate) {
882 $startDate = CRM_Utils_Array::value('start_date',
883 $defaultDates
884 );
885 }
886 if (!$endDate) {
887 $endDate = CRM_Utils_Array::value('end_date',
888 $defaultDates
889 );
890 }
891
892 //CRM-3724, check for availability of valid membership status.
a7488080 893 if (empty($params['is_override']) && !isset($errors['_qf_default'])) {
6a488035
TO
894 $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($startDate,
895 $endDate,
896 $joinDate,
897 'today',
5f11bbcc
EM
898 TRUE,
899 $memType,
900 $params
6a488035
TO
901 );
902 if (empty($calcStatus)) {
903 $url = CRM_Utils_System::url('civicrm/admin/member/membershipStatus', 'reset=1&action=browse');
904 $errors['_qf_default'] = ts('There is no valid Membership Status available for selected membership dates.');
905 $status = ts('Oops, it looks like there is no valid membership status available for the given membership dates. You can <a href="%1">Configure Membership Status Rules</a>.', array(1 => $url));
906 if (!$self->_mode) {
907 $status .= ' ' . ts('OR You can sign up by setting Status Override? to true.');
908 }
909 CRM_Core_Session::setStatus($status, ts('Membership Status Error'), 'error');
910 }
911 }
912 }
913 }
914 else {
915 $errors['join_date'] = ts('Please enter the Member Since.');
916 }
917
918 if (isset($params['is_override']) &&
353ffa53
TO
919 $params['is_override'] && empty($params['status_id'])
920 ) {
6a488035
TO
921 $errors['status_id'] = ts('Please enter the status.');
922 }
923
924 //total amount condition arise when membership type having no
925 //minimum fee
926 if (isset($params['record_contribution'])) {
6a488035
TO
927 if (CRM_Utils_System::isNull($params['total_amount'])) {
928 $errors['total_amount'] = ts('Please enter the contribution.');
929 }
930 }
931
932 // validate contribution status for 'Failed'.
8cc574cf 933 if ($self->_onlinePendingContributionId && !empty($params['record_contribution']) &&
6a488035
TO
934 (CRM_Utils_Array::value('contribution_status_id', $params) ==
935 array_search('Failed', CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'))
936 )
937 ) {
938 $errors['contribution_status_id'] = ts('Please select a valid payment status before updating.');
939 }
940
941 return empty($errors) ? TRUE : $errors;
942 }
943
944 /**
fe482240 945 * Process the form submission.
6a488035
TO
946 */
947 public function postProcess() {
948 if ($this->_action & CRM_Core_Action::DELETE) {
3506b6cd 949 CRM_Member_BAO_Membership::del($this->_id);
6a488035
TO
950 return;
951 }
7865d848
EM
952 // get the submitted form values.
953 $this->_params = $this->controller->exportValues($this->_name);
6a488035 954
41709813 955 $this->submit($this->_params);
7865d848
EM
956
957 $this->setUserContext();
958 }
959
960 /**
961 * Send email receipt.
962 *
963 * @param CRM_Core_Form $form
964 * Form object.
965 * @param array $formValues
966 * @param object $membership
967 * Object.
968 *
969 * @return bool
970 * true if mail was sent successfully
971 */
972 public static function emailReceipt(&$form, &$formValues, &$membership) {
973 // retrieve 'from email id' for acknowledgement
974 $receiptFrom = $formValues['from_email_address'];
975
976 if (!empty($formValues['payment_instrument_id'])) {
977 $paymentInstrument = CRM_Contribute_PseudoConstant::paymentInstrument();
978 $formValues['paidBy'] = $paymentInstrument[$formValues['payment_instrument_id']];
979 }
980
981 // retrieve custom data
982 $customFields = $customValues = array();
983 if (property_exists($form, '_groupTree')
984 && !empty($form->_groupTree)
985 ) {
986 foreach ($form->_groupTree as $groupID => $group) {
987 if ($groupID == 'info') {
988 continue;
989 }
990 foreach ($group['fields'] as $k => $field) {
991 $field['title'] = $field['label'];
992 $customFields["custom_{$k}"] = $field;
993 }
994 }
995 }
996
997 $members = array(array('member_id', '=', $membership->id, 0, 0));
998 // check whether its a test drive
999 if ($form->_mode == 'test') {
1000 $members[] = array('member_test', '=', 1, 0, 0);
1001 }
1002
1003 CRM_Core_BAO_UFGroup::getValues($formValues['contact_id'], $customFields, $customValues, FALSE, $members);
0816949d 1004 $form->assign('customValues', $customValues);
7865d848
EM
1005
1006 if ($form->_mode) {
1007 $name = '';
1008 if (!empty($form->_params['billing_first_name'])) {
1009 $name = $form->_params['billing_first_name'];
1010 }
1011
1012 if (!empty($form->_params['billing_middle_name'])) {
1013 $name .= " {$form->_params['billing_middle_name']}";
1014 }
1015
1016 if (!empty($form->_params['billing_last_name'])) {
1017 $name .= " {$form->_params['billing_last_name']}";
1018 }
1019
1020 $form->assign('billingName', $name);
1021
1022 // assign the address formatted up for display
1023 $addressParts = array(
1024 "street_address-{$form->_bltID}",
1025 "city-{$form->_bltID}",
1026 "postal_code-{$form->_bltID}",
1027 "state_province-{$form->_bltID}",
1028 "country-{$form->_bltID}",
1029 );
1030 $addressFields = array();
1031 foreach ($addressParts as $part) {
1032 list($n, $id) = explode('-', $part);
1033 if (isset($form->_params['billing_' . $part])) {
1034 $addressFields[$n] = $form->_params['billing_' . $part];
1035 }
1036 }
1037 $form->assign('address', CRM_Utils_Address::format($addressFields));
7d193e45 1038
7865d848
EM
1039 $date = CRM_Utils_Date::format($form->_params['credit_card_exp_date']);
1040 $date = CRM_Utils_Date::mysqlToIso($date);
1041 $form->assign('credit_card_exp_date', $date);
1042 $form->assign('credit_card_number',
1043 CRM_Utils_System::mungeCreditCard($form->_params['credit_card_number'])
1044 );
1045 $form->assign('credit_card_type', $form->_params['credit_card_type']);
1046 $form->assign('contributeMode', 'direct');
1047 $form->assign('isAmountzero', 0);
1048 $form->assign('is_pay_later', 0);
1049 $form->assign('isPrimary', 1);
1050 }
1051
1052 $form->assign('module', 'Membership');
1053 $form->assign('contactID', $formValues['contact_id']);
1054
1055 $form->assign('membershipID', CRM_Utils_Array::value('membership_id', $form->_params, CRM_Utils_Array::value('membership_id', $form->_defaultValues)));
1056
1057 if (!empty($formValues['contribution_id'])) {
1058 $form->assign('contributionID', $formValues['contribution_id']);
1059 }
1060 elseif (isset($form->_onlinePendingContributionId)) {
1061 $form->assign('contributionID', $form->_onlinePendingContributionId);
1062 }
1063
1064 if (!empty($formValues['contribution_status_id'])) {
1065 $form->assign('contributionStatusID', $formValues['contribution_status_id']);
1066 $form->assign('contributionStatus', CRM_Contribute_PseudoConstant::contributionStatus($formValues['contribution_status_id'], 'name'));
1067 }
1068
1069 if (!empty($formValues['is_renew'])) {
1070 $form->assign('receiptType', 'membership renewal');
1071 }
1072 else {
1073 $form->assign('receiptType', 'membership signup');
1074 }
1075 $form->assign('receive_date', CRM_Utils_Date::processDate(CRM_Utils_Array::value('receive_date', $formValues)));
1076 $form->assign('formValues', $formValues);
1077
1078 if (empty($lineItem)) {
1079 $form->assign('mem_start_date', CRM_Utils_Date::customFormat($membership->start_date, '%B %E%f, %Y'));
1080 if (!CRM_Utils_System::isNull($membership->end_date)) {
1081 $form->assign('mem_end_date', CRM_Utils_Date::customFormat($membership->end_date, '%B %E%f, %Y'));
1082 }
1083 $form->assign('membership_name', CRM_Member_PseudoConstant::membershipType($membership->membership_type_id));
1084 }
1085
7865d848
EM
1086 $isBatchProcess = is_a($form, 'CRM_Batch_Form_Entry');
1087 if ((empty($form->_contributorDisplayName) || empty($form->_contributorEmail)) || $isBatchProcess) {
1088 // in this case the form is being called statically from the batch editing screen
1089 // having one class in the form layer call another statically is not greate
1090 // & we should aim to move this function to the BAO layer in future.
1091 // however, we can assume that the contact_id passed in by the batch
1092 // function will be the recipient
1093 list($form->_contributorDisplayName, $form->_contributorEmail)
1094 = CRM_Contact_BAO_Contact_Location::getEmailDetails($formValues['contact_id']);
1095 if (empty($form->_receiptContactId) || $isBatchProcess) {
1096 $form->_receiptContactId = $formValues['contact_id'];
1097 }
1098 }
1099 $template = CRM_Core_Smarty::singleton();
1100 $taxAmt = $template->get_template_vars('dataArray');
1101 $eventTaxAmt = $template->get_template_vars('totalTaxAmount');
1102 $prefixValue = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings');
1103 $invoicing = CRM_Utils_Array::value('invoicing', $prefixValue);
1104 if ((!empty($taxAmt) || isset($eventTaxAmt)) && (isset($invoicing) && isset($prefixValue['is_email_pdf']))) {
1105 $isEmailPdf = TRUE;
1106 }
1107 else {
1108 $isEmailPdf = FALSE;
1109 }
1110
1111 list($mailSend, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate(
1112 array(
1113 'groupName' => 'msg_tpl_workflow_membership',
1114 'valueName' => 'membership_offline_receipt',
1115 'contactId' => $form->_receiptContactId,
1116 'from' => $receiptFrom,
1117 'toName' => $form->_contributorDisplayName,
1118 'toEmail' => $form->_contributorEmail,
1119 'PDFFilename' => ts('receipt') . '.pdf',
1120 'isEmailPdf' => $isEmailPdf,
1121 'contributionId' => $formValues['contribution_id'],
1122 'isTest' => (bool) ($form->_action & CRM_Core_Action::PREVIEW),
1123 )
1124 );
1125
1126 return TRUE;
1127 }
1128
1129 /**
5e56c7a5 1130 * Submit function.
1131 *
1132 * This is also accessed by unit tests.
1133 *
c41bfe2b 1134 * @param array $formValues
5e56c7a5 1135 *
7865d848
EM
1136 * @return array
1137 */
1138 public function submit($formValues) {
4bd318e0 1139 $isTest = ($this->_mode == 'test') ? 1 : 0;
36ffcc5f 1140
7865d848
EM
1141 $joinDate = $startDate = $endDate = NULL;
1142 $membershipTypes = $membership = $calcDate = array();
41709813 1143 $membershipType = NULL;
7865d848 1144
41709813 1145 $mailSend = FALSE;
c41bfe2b 1146 $priceSetID = CRM_Utils_Array::value('price_set_id', $formValues);
7865d848
EM
1147
1148 $params = $softParams = $ids = array();
1149
1150 $allMemberStatus = CRM_Member_PseudoConstant::membershipStatus();
1151 $allContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus();
1152
08fd4b45
EM
1153 if ($this->_id) {
1154 $ids['membership'] = $params['id'] = $this->_id;
1155 }
1156 $ids['userId'] = CRM_Core_Session::singleton()->get('userID');
1157
7865d848
EM
1158 // Set variables that we normally get from context.
1159 // In form mode these are set in preProcess.
1160 //TODO: set memberships, fixme
1161 $this->setContextVariables($formValues);
e4a6290d 1162 $priceSetDetails = CRM_Price_BAO_PriceSet::getSetDetail($priceSetID);
1163 $this->_memTypeSelected = self::getSelectedMemberships(
1164 $priceSetDetails[$priceSetID],
1165 $formValues
1166 );
867047cd 1167 if (empty($formValues['financial_type_id'])) {
1168 $formValues['financial_type_id'] = $priceSetDetails[$priceSetID]['financial_type_id'];
1169 }
7865d848 1170
6a488035 1171 $config = CRM_Core_Config::singleton();
6a488035 1172
7865d848 1173 $this->convertDateFieldsToMySQL($formValues);
6a488035
TO
1174
1175 $membershipTypeValues = array();
1176 foreach ($this->_memTypeSelected as $memType) {
1177 $membershipTypeValues[$memType]['membership_type_id'] = $memType;
1178 }
1179
1180 //take the required membership recur values.
7865d848 1181 if ($this->_mode && !empty($formValues['auto_renew'])) {
6a488035
TO
1182 $params['is_recur'] = $this->_params['is_recur'] = $formValues['is_recur'] = TRUE;
1183 $mapping = array(
1184 'frequency_interval' => 'duration_interval',
1185 'frequency_unit' => 'duration_unit',
1186 );
1187
1188 $count = 0;
1189 foreach ($this->_memTypeSelected as $memType) {
1190 $recurMembershipTypeValues = CRM_Utils_Array::value($memType,
1191 $this->_recurMembershipTypes, array()
1192 );
1193 foreach ($mapping as $mapVal => $mapParam) {
1194 $membershipTypeValues[$memType][$mapVal] = CRM_Utils_Array::value($mapParam,
1195 $recurMembershipTypeValues
1196 );
1197 if (!$count) {
1198 $this->_params[$mapVal] = $formValues[$mapVal] = CRM_Utils_Array::value($mapParam,
1199 $recurMembershipTypeValues
1200 );
1201 }
1202 }
1203 $count++;
1204 }
6a488035
TO
1205 }
1206
1207 // process price set and get total amount and line items.
08fd4b45
EM
1208 if (!$priceSetID) {
1209 $priceSetID = CRM_Member_BAO_Membership::createLineItems(
1210 $this,
1211 $formValues['membership_type_id'],
1212 NULL
1213 );
1214 }
1215 $isQuickConfig = FALSE;
1216 if (CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetID, 'is_quick_config')) {
1217 $isQuickConfig = 1;
1218 }
6a488035 1219 $termsByType = array();
9f1bc5dc 1220
08fd4b45
EM
1221 $lineItem = array();
1222 CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'],
1223 $this->_params, $lineItem[$priceSetID]);
1224 if (CRM_Utils_Array::value('tax_amount', $this->_params)) {
1225 $params['tax_amount'] = $this->_params['tax_amount'];
1226 }
1227 $params['total_amount'] = CRM_Utils_Array::value('amount', $this->_params);
1228 $submittedFinancialType = CRM_Utils_Array::value('financial_type_id', $formValues);
1229 if (!empty($lineItem[$priceSetID])) {
1230 foreach ($lineItem[$priceSetID] as &$li) {
1231 if (!empty($li['membership_type_id'])) {
1232 if (!empty($li['membership_num_terms'])) {
1233 $termsByType[$li['membership_type_id']] = $li['membership_num_terms'];
6a488035 1234 }
08fd4b45 1235 }
6a488035 1236
08fd4b45
EM
1237 ///CRM-11529 for quick config backoffice transactions
1238 //when financial_type_id is passed in form, update the
1239 //lineitems with the financial type selected in form
1240 if ($isQuickConfig && $submittedFinancialType) {
1241 $li['financial_type_id'] = $submittedFinancialType;
6a488035
TO
1242 }
1243 }
1244 }
1245
1246 $this->storeContactFields($formValues);
1247
1248 $params['contact_id'] = $this->_contactID;
1249
1250 $fields = array(
1251 'status_id',
1252 'source',
1253 'is_override',
1254 'campaign_id',
1255 );
1256
1257 foreach ($fields as $f) {
1258 $params[$f] = CRM_Utils_Array::value($f, $formValues);
1259 }
1260
1261 // fix for CRM-3724
1262 // when is_override false ignore is_admin statuses during membership
1263 // status calculation. similarly we did fix for import in CRM-3570.
a7488080 1264 if (empty($params['is_override'])) {
6a488035
TO
1265 $params['exclude_is_admin'] = TRUE;
1266 }
1267
1268 // process date params to mysql date format.
1269 $dateTypes = array(
1270 'join_date' => 'joinDate',
1271 'start_date' => 'startDate',
1272 'end_date' => 'endDate',
1273 );
1274 foreach ($dateTypes as $dateField => $dateVariable) {
1275 $$dateVariable = CRM_Utils_Date::processDate($formValues[$dateField]);
1276 }
1277
b09fe5ed 1278 $memTypeNumTerms = empty($termsByType) ? CRM_Utils_Array::value('num_terms', $formValues) : NULL;
6a488035
TO
1279
1280 $calcDates = array();
1281 foreach ($this->_memTypeSelected as $memType) {
1693f081 1282 if (empty($memTypeNumTerms)) {
1283 $memTypeNumTerms = CRM_Utils_Array::value($memType, $termsByType, 1);
1284 }
6a488035
TO
1285 $calcDates[$memType] = CRM_Member_BAO_MembershipType::getDatesForMembershipType($memType,
1286 $joinDate, $startDate, $endDate, $memTypeNumTerms
1287 );
1288 }
1289
1290 foreach ($calcDates as $memType => $calcDate) {
5d86176b 1291 foreach (array_keys($dateTypes) as $d) {
6a488035
TO
1292 //first give priority to form values then calDates.
1293 $date = CRM_Utils_Array::value($d, $formValues);
1294 if (!$date) {
1295 $date = CRM_Utils_Array::value($d, $calcDate);
1296 }
1297
1298 $membershipTypeValues[$memType][$d] = CRM_Utils_Date::processDate($date);
6a488035
TO
1299 }
1300 }
1301
1302 // max related memberships - take from form or inherit from membership type
1303 foreach ($this->_memTypeSelected as $memType) {
1304 if (array_key_exists('max_related', $formValues)) {
1305 $membershipTypeValues[$memType]['max_related'] = CRM_Utils_Array::value('max_related', $formValues);
1306 }
6a488035 1307 $membershipTypeValues[$memType]['custom'] = CRM_Core_BAO_CustomField::postProcess($formValues,
6a488035
TO
1308 $this->_id,
1309 'Membership'
1310 );
6a488035
TO
1311 $membershipTypes[$memType] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
1312 $memType
1313 );
1314 }
1315
1316 $membershipType = implode(', ', $membershipTypes);
1317
1318 // Retrieve the name and email of the current user - this will be the FROM for the receipt email
ab30e033 1319 list($userName) = CRM_Contact_BAO_Contact_Location::getEmailDetails($ids['userId']);
6a488035 1320
d80dbc14 1321 //CRM-13981, allow different person as a soft-contributor of chosen type
b11c92be 1322 if ($this->_contributorContactID != $this->_contactID) {
91ef9be0 1323 $params['contribution_contact_id'] = $this->_contributorContactID;
a7488080 1324 if (!empty($this->_params['soft_credit_type_id'])) {
133e2c99 1325 $softParams['soft_credit_type_id'] = $this->_params['soft_credit_type_id'];
91ef9be0 1326 $softParams['contact_id'] = $this->_contactID;
6a488035
TO
1327 }
1328 }
a7488080 1329 if (!empty($formValues['record_contribution'])) {
6a488035 1330 $recordContribution = array(
b11c92be 1331 'total_amount',
b11c92be 1332 'financial_type_id',
1333 'payment_instrument_id',
1334 'trxn_id',
1335 'contribution_status_id',
1336 'check_number',
1337 'campaign_id',
1338 'receive_date',
6a488035
TO
1339 );
1340
1341 foreach ($recordContribution as $f) {
1342 $params[$f] = CRM_Utils_Array::value($f, $formValues);
1343 }
1344
1345 if (!$this->_onlinePendingContributionId) {
2286d173 1346 if (empty($formValues['source'])) {
353ffa53 1347 $params['contribution_source'] = ts('%1 Membership: Offline signup (by %2)', array(
7865d848
EM
1348 1 => $membershipType,
1349 2 => $userName,
1350 ));
2286d173
PD
1351 }
1352 else {
0e81467c 1353 $params['contribution_source'] = $formValues['source'];
2286d173 1354 }
0e81467c 1355 }
6a488035 1356
a7488080 1357 if (empty($params['is_override']) &&
6a488035
TO
1358 CRM_Utils_Array::value('contribution_status_id', $params) == array_search('Pending', CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'))
1359 ) {
7d193e45 1360 $params['status_id'] = array_search('Pending', $allMemberStatus);
6a488035
TO
1361 $params['skipStatusCal'] = TRUE;
1362 $params['is_pay_later'] = 1;
1363 $this->assign('is_pay_later', 1);
1364 }
1365
a7488080 1366 if (!empty($formValues['send_receipt'])) {
5d86176b 1367 $params['receipt_date'] = CRM_Utils_Array::value('receive_date', $formValues);
6a488035
TO
1368 }
1369
1370 //insert financial type name in receipt.
1371 $formValues['contributionType_name'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType',
1372 $formValues['financial_type_id']
1373 );
1374 }
1375
1376 // process line items, until no previous line items.
1377 if (!empty($lineItem)) {
1378 $params['lineItems'] = $lineItem;
1379 $params['processPriceSet'] = TRUE;
1380 }
1381 $createdMemberships = array();
1382 if ($this->_mode) {
41709813 1383 if (empty($formValues['total_amount']) && !$priceSetID) {
6a488035
TO
1384 // if total amount not provided minimum for membership type is used
1385 $params['total_amount'] = $formValues['total_amount'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
1386 $formValues['membership_type_id'][1], 'minimum_fee'
1387 );
1388 }
1389 else {
1390 $params['total_amount'] = CRM_Utils_Array::value('total_amount', $formValues, 0);
1391 }
a8d4ff25 1392
41709813 1393 if ($priceSetID && !$isQuickConfig) {
b11c92be 1394 $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet',
41709813 1395 $priceSetID,
6a488035
TO
1396 'financial_type_id'
1397 );
1398 }
1399 else {
5a9c4d4a 1400 $params['financial_type_id'] = CRM_Utils_Array::value('financial_type_id', $formValues);
6a488035
TO
1401 }
1402
1403 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($formValues['payment_processor_id'],
1404 $this->_mode
1405 );
1406
1407 //get the payment processor id as per mode.
1408 $params['payment_processor_id'] = $this->_params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor['id'];
1409
6a488035
TO
1410 $now = date('YmdHis');
1411 $fields = array();
1412
1413 // set email for primary location.
1414 $fields['email-Primary'] = 1;
1415 $formValues['email-5'] = $formValues['email-Primary'] = $this->_memberEmail;
1416 $params['register_date'] = $now;
1417
1418 // now set the values for the billing location.
1419 foreach ($this->_fields as $name => $dontCare) {
1420 $fields[$name] = 1;
1421 }
1422
1423 // also add location name to the array
1424 $formValues["address_name-{$this->_bltID}"] = CRM_Utils_Array::value('billing_first_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_middle_name', $formValues) . ' ' . CRM_Utils_Array::value('billing_last_name', $formValues);
1425
1426 $formValues["address_name-{$this->_bltID}"] = trim($formValues["address_name-{$this->_bltID}"]);
1427
1428 $fields["address_name-{$this->_bltID}"] = 1;
36417749 1429 //ensure we don't over-write the payer's email with the member's email
b11c92be 1430 if ($this->_contributorContactID == $this->_contactID) {
36417749 1431 $fields["email-{$this->_bltID}"] = 1;
1432 }
6a488035 1433
6a488035
TO
1434 $nameFields = array('first_name', 'middle_name', 'last_name');
1435
1436 foreach ($nameFields as $name) {
1437 $fields[$name] = 1;
1438 if (array_key_exists("billing_$name", $formValues)) {
1439 $formValues[$name] = $formValues["billing_{$name}"];
1440 $formValues['preserveDBName'] = TRUE;
1441 }
1442 }
abba096d
DG
1443 if ($this->_contributorContactID == $this->_contactID) {
1444 //see CRM-12869 for discussion of why we don't do this for separate payee payments
1445 CRM_Contact_BAO_Contact::createProfileContact($formValues, $fields,
66a078b1
EM
1446 $this->_contributorContactID, NULL, NULL,
1447 CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_contactID, 'contact_type')
abba096d
DG
1448 );
1449 }
6a488035 1450
a1a94e61 1451 // add all the additional payment params we need
a271822c
EM
1452 $this->_params["state_province-{$this->_bltID}"] = $this->_params["billing_state_province-{$this->_bltID}"]
1453 = CRM_Core_PseudoConstant::stateProvinceAbbreviation($formValues["billing_state_province_id-{$this->_bltID}"]);
a8215a8d 1454 $this->_params["country-{$this->_bltID}"] = $this->_params["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($formValues["billing_country_id-{$this->_bltID}"]);
6a488035 1455
a8215a8d
EM
1456 $this->_params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($formValues);
1457 $this->_params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($formValues);
6a488035
TO
1458 $this->_params['ip_address'] = CRM_Utils_System::ipAddress();
1459 $this->_params['amount'] = $params['total_amount'];
1460 $this->_params['currencyID'] = $config->defaultCurrency;
e47238e5 1461 $this->_params['description'] = ts("Contribution submitted by a staff person using member's credit card for signup");
6a488035
TO
1462 $this->_params['invoiceID'] = md5(uniqid(rand(), TRUE));
1463 $this->_params['financial_type_id'] = $params['financial_type_id'];
1464
1465 // at this point we've created a contact and stored its address etc
1466 // all the payment processors expect the name and address to be in the
1467 // so we copy stuff over to first_name etc.
1468 $paymentParams = $this->_params;
1469 $paymentParams['contactID'] = $this->_contributorContactID;
1470 //CRM-10377 if payment is by an alternate contact then we need to set that person
1471 // as the contact in the payment params
b11c92be 1472 if ($this->_contributorContactID != $this->_contactID) {
a7488080 1473 if (!empty($this->_params['soft_credit_type_id'])) {
133e2c99 1474 $softParams['contact_id'] = $params['contact_id'];
1475 $softParams['soft_credit_type_id'] = $this->_params['soft_credit_type_id'];
6a488035
TO
1476 }
1477 }
a7488080 1478 if (!empty($this->_params['send_receipt'])) {
6a488035
TO
1479 $paymentParams['email'] = $this->_contributorEmail;
1480 }
1481
1482 CRM_Core_Payment_Form::mapParams($this->_bltID, $this->_params, $paymentParams, TRUE);
1483
1484 // CRM-7137 -for recurring membership,
b44e3f84 1485 // we do need contribution and recurring records.
6a488035 1486 $result = NULL;
a7488080 1487 if (!empty($paymentParams['is_recur'])) {
8a7b41d1
EM
1488 $financialType = new CRM_Financial_DAO_FinancialType();
1489 $financialType->id = $params['financial_type_id'];
1490 $financialType->find(TRUE);
f6261e9d 1491
ba013eea 1492 $contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($this,
6a488035 1493 $paymentParams,
3febe800 1494 NULL,
f6261e9d 1495 array(
1496 'contact_id' => $this->_contributorContactID,
9b581f1d 1497 'line_item' => $lineItem,
f6261e9d 1498 'is_test' => $isTest,
1499 'campaign_id' => CRM_Utils_Array::value('campaign_id', $paymentParams),
1500 'contribution_page_id' => CRM_Utils_Array::value('contribution_page_id', $this->_params),
3febe800 1501 'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)),
1502 'thankyou_date' => CRM_Utils_Array::value('thankyou_date', $paymentParams),
1503 'payment_instrument_id' => $this->_paymentProcessor['payment_instrument_id'],
f6261e9d 1504 ),
8a7b41d1 1505 $financialType,
6a488035 1506 TRUE,
4bd318e0 1507 FALSE,
9fc4e1d9 1508 $this->_bltID
6a488035 1509 );
133e2c99 1510
1511 //create new soft-credit record, CRM-13981
00c1cd97
CW
1512 if ($softParams) {
1513 $softParams['contribution_id'] = $contribution->id;
1514 $softParams['currency'] = $contribution->currency;
1515 $softParams['amount'] = $contribution->total_amount;
1516 CRM_Contribute_BAO_ContributionSoft::add($softParams);
1517 }
133e2c99 1518
a22bd791 1519 $paymentParams['contactID'] = $this->_contactID;
6a488035 1520 $paymentParams['contributionID'] = $contribution->id;
b11c92be 1521 $paymentParams['contributionTypeID'] = $contribution->financial_type_id;
6a488035
TO
1522 $paymentParams['contributionPageID'] = $contribution->contribution_page_id;
1523 $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id;
1524 $ids['contribution'] = $contribution->id;
1525 $params['contribution_recur_id'] = $paymentParams['contributionRecurID'];
6a488035
TO
1526 }
1527
1528 if ($params['total_amount'] > 0.0) {
ab30e033 1529 $payment = $this->_paymentProcessor['object'];
06d062ce
EM
1530 try {
1531 $result = $payment->doPayment($paymentParams);
ab30e033
EM
1532 $this->_params = array_merge($this->_params, $result);
1533 // Assign amount to template if payment was successful.
1534 $this->assign('amount', $params['total_amount']);
6a488035 1535 }
06d062ce
EM
1536 catch (PaymentProcessorException $e) {
1537 if (!empty($paymentParams['contributionID'])) {
ab30e033
EM
1538 CRM_Contribute_BAO_Contribution::failPayment($paymentParams['contributionID'], $this->_contactID,
1539 $e->getMessage());
06d062ce
EM
1540 }
1541 if (!empty($paymentParams['contributionRecurID'])) {
1542 CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']);
1543 }
1544
1545 CRM_Core_Error::displaySessionError($result);
1546 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view/membership',
1547 "reset=1&action=add&cid={$this->_contactID}&context=&mode={$this->_mode}"
1548 ));
6a488035 1549
06d062ce 1550 }
6a488035
TO
1551 }
1552
8a7b41d1 1553 if ($this->_params['payment_status_id'] != array_search('Completed', $allContributionStatus)) {
7d193e45
LS
1554 $params['status_id'] = array_search('Pending', $allMemberStatus);
1555 $params['skipStatusCal'] = TRUE;
1556 // unset send-receipt option, since receipt will be sent when ipn is received.
1557 unset($this->_params['send_receipt'], $formValues['send_receipt']);
1558 //as membership is pending set dates to null.
1559 $memberDates = array(
1560 'join_date' => 'joinDate',
1561 'start_date' => 'startDate',
1562 'end_date' => 'endDate',
1563 );
2e8b13d1 1564 foreach ($memberDates as $dv) {
7d193e45
LS
1565 $$dv = NULL;
1566 foreach ($this->_memTypeSelected as $memType) {
1567 $membershipTypeValues[$memType][$dv] = NULL;
1568 }
1569 }
1570 }
6a488035
TO
1571 $params['receive_date'] = $now;
1572 $params['invoice_id'] = $this->_params['invoiceID'];
1573 $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)',
1574 array(1 => $membershipType, 2 => $userName)
1575 );
1576 $params['source'] = $formValues['source'] ? $formValues['source'] : $params['contribution_source'];
1577 $params['trxn_id'] = CRM_Utils_Array::value('trxn_id', $result);
1578 $params['payment_instrument_id'] = 1;
1579 $params['is_test'] = ($this->_mode == 'live') ? 0 : 1;
a7488080 1580 if (!empty($this->_params['send_receipt'])) {
6a488035
TO
1581 $params['receipt_date'] = $now;
1582 }
1583 else {
1584 $params['receipt_date'] = NULL;
1585 }
1586
1587 $this->set('params', $this->_params);
1588 $this->assign('trxn_id', CRM_Utils_Array::value('trxn_id', $result));
1589 $this->assign('receive_date',
1590 CRM_Utils_Date::mysqlToIso($params['receive_date'])
1591 );
1592
1593 // required for creating membership for related contacts
1594 $params['action'] = $this->_action;
1595
1596 //create membership record.
1597 $count = 0;
1598 foreach ($this->_memTypeSelected as $memType) {
1599 if ($count &&
1600 ($relateContribution = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id))
1601 ) {
1602 $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution;
1603 }
1604
1605 $membershipParams = array_merge($membershipTypeValues[$memType], $params);
87d0f881 1606 //CRM-15366
396e62d8 1607 if (!empty($softParams) && empty($paymentParams['is_recur'])) {
1608 $membershipParams['soft_credit'] = $softParams;
1609 }
8a7b41d1
EM
1610 // This is required to trigger the recording of the membership contribution in the
1611 // CRM_Member_BAO_Membership::Create function.
1612 // @todo stop setting this & 'teach' the create function to respond to something
1613 // appropriate as part of our 2-step always create the pending contribution & then finally add the payment
1614 // process -
1615 // @see http://wiki.civicrm.org/confluence/pages/viewpage.action?pageId=261062657#Payments&AccountsRoadmap-Movetowardsalwaysusinga2-steppaymentprocess
1616 $membershipParams['contribution_status_id'] = CRM_Utils_Array::value('payment_status_id', $result);
b2b59848 1617 unset($membershipParams['lineItems']);
6a488035 1618 $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids);
3e228d81
PN
1619 $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams);
1620 unset($params['lineItems']);
6a488035
TO
1621 $this->_membershipIDs[] = $membership->id;
1622 $createdMemberships[$memType] = $membership;
1623 $count++;
1624 }
1625
6a488035
TO
1626 }
1627 else {
1628 $params['action'] = $this->_action;
8cc574cf 1629 if ($this->_onlinePendingContributionId && !empty($formValues['record_contribution'])) {
6a488035
TO
1630
1631 // update membership as well as contribution object, CRM-4395
1632 $params['contribution_id'] = $this->_onlinePendingContributionId;
1633 $params['componentId'] = $params['id'];
1634 $params['componentName'] = 'contribute';
1635 $result = CRM_Contribute_BAO_Contribution::transitionComponents($params, TRUE);
8cc574cf 1636 if (!empty($result) && !empty($params['contribution_id'])) {
6a488035 1637 $lineItem = array();
7524682e 1638 $lineItems = CRM_Price_BAO_LineItem::getLineItems($params['contribution_id'], 'contribution', NULL, TRUE, TRUE);
b11c92be 1639 $itemId = key($lineItems);
1640 $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id');
4aa7d844 1641
6a488035
TO
1642 $lineItems[$itemId]['unit_price'] = $params['total_amount'];
1643 $lineItems[$itemId]['line_total'] = $params['total_amount'];
1644 $lineItems[$itemId]['id'] = $itemId;
1645 $lineItem[$priceSetId] = $lineItems;
8aa7457a
EM
1646 $contributionBAO = new CRM_Contribute_BAO_Contribution();
1647 $contributionBAO->id = $params['contribution_id'];
7524682e 1648 $contributionBAO->contact_id = $params['contact_id'];
8aa7457a
EM
1649 $contributionBAO->find();
1650 CRM_Price_BAO_LineItem::processPriceSet($params['contribution_id'], $lineItem, $contributionBAO, 'civicrm_membership');
133e2c99 1651
1652 //create new soft-credit record, CRM-13981
00c1cd97
CW
1653 if ($softParams) {
1654 $softParams['contribution_id'] = $params['contribution_id'];
1655 while ($contributionBAO->fetch()) {
1656 $softParams['currency'] = $contributionBAO->currency;
1657 $softParams['amount'] = $contributionBAO->total_amount;
1658 }
1659 CRM_Contribute_BAO_ContributionSoft::add($softParams);
133e2c99 1660 }
6a488035
TO
1661 }
1662
1663 //carry updated membership object.
1664 $membership = new CRM_Member_DAO_Membership();
1665 $membership->id = $this->_id;
1666 $membership->find(TRUE);
1667
1668 $cancelled = TRUE;
1669 if ($membership->end_date) {
1670 //display end date w/ status message.
1671 $endDate = $membership->end_date;
1672
b11c92be 1673 if (!in_array($membership->status_id, array(
7ff60806
PN
1674 // CRM-15475
1675 array_search('Cancelled', CRM_Member_PseudoConstant::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)),
1676 array_search('Expired', CRM_Member_PseudoConstant::membershipStatus()),
b11c92be 1677 ))
1678 ) {
6a488035
TO
1679 $cancelled = FALSE;
1680 }
1681 }
1682 // suppress form values in template.
1683 $this->assign('cancelled', $cancelled);
1684
6a488035
TO
1685 $createdMemberships[] = $membership;
1686 }
1687 else {
1688 $count = 0;
1689 foreach ($this->_memTypeSelected as $memType) {
8cc574cf 1690 if ($count && !empty($formValues['record_contribution']) &&
6a488035
TO
1691 ($relateContribution = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id))
1692 ) {
1693 $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution;
1694 }
1695
1696 $membershipParams = array_merge($params, $membershipTypeValues[$memType]);
a7488080 1697 if (!empty($formValues['int_amount'])) {
6a488035 1698 $init_amount = array();
b11c92be 1699 foreach ($formValues as $key => $value) {
1700 if (strstr($key, 'txt-price')) {
6a488035
TO
1701 $init_amount[$key] = $value;
1702 }
1703 }
1704 $membershipParams['init_amount'] = $init_amount;
1705 }
d80dbc14 1706
1707 if (!empty($softParams)) {
1708 $membershipParams['soft_credit'] = $softParams;
1709 }
1710
6a488035 1711 $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids);
3e228d81 1712 $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams);
d5b95619 1713 unset($params['lineItems']);
6a488035
TO
1714
1715 $this->_membershipIDs[] = $membership->id;
1716 $createdMemberships[$memType] = $membership;
1717 $count++;
1718 }
1719 }
1720 }
1721
c41bfe2b 1722 if (!empty($lineItem[$priceSetID])) {
b09fe5ed 1723 $invoiceSettings = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME, 'contribution_invoice_settings');
03b412ae 1724 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
01604562 1725 $taxAmount = FALSE;
79d001a2 1726 $totalTaxAmount = 0;
c41bfe2b 1727 foreach ($lineItem[$priceSetID] as & $priceFieldOp) {
a7488080 1728 if (!empty($priceFieldOp['membership_type_id'])) {
3b85fc04
PN
1729 $priceFieldOp['start_date'] = $membershipTypeValues[$priceFieldOp['membership_type_id']]['start_date'] ? CRM_Utils_Date::customFormat($membershipTypeValues[$priceFieldOp['membership_type_id']]['start_date'], '%B %E%f, %Y') : '-';
1730 $priceFieldOp['end_date'] = $membershipTypeValues[$priceFieldOp['membership_type_id']]['end_date'] ? CRM_Utils_Date::customFormat($membershipTypeValues[$priceFieldOp['membership_type_id']]['end_date'], '%B %E%f, %Y') : '-';
6a488035
TO
1731 }
1732 else {
1733 $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A';
1734 }
03b412ae 1735 if ($invoicing && isset($priceFieldOp['tax_amount'])) {
01604562 1736 $taxAmount = TRUE;
79d001a2
PB
1737 $totalTaxAmount += $priceFieldOp['tax_amount'];
1738 }
1739 }
03b412ae
PB
1740 if ($invoicing) {
1741 $dataArray = array();
c41bfe2b 1742 foreach ($lineItem[$priceSetID] as $key => $value) {
03b412ae
PB
1743 if (isset($value['tax_amount']) && isset($value['tax_rate'])) {
1744 if (isset($dataArray[$value['tax_rate']])) {
1745 $dataArray[$value['tax_rate']] = $dataArray[$value['tax_rate']] + CRM_Utils_Array::value('tax_amount', $value);
0db6c3e1
TO
1746 }
1747 else {
03b412ae
PB
1748 $dataArray[$value['tax_rate']] = CRM_Utils_Array::value('tax_amount', $value);
1749 }
0e81467c 1750 }
03b412ae 1751 }
01604562
PB
1752 if ($taxAmount) {
1753 $this->assign('totalTaxAmount', $totalTaxAmount);
1754 $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings));
1755 }
03b412ae 1756 $this->assign('dataArray', $dataArray);
6a488035
TO
1757 }
1758 }
1759 $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE);
1760
1761 $receiptSend = FALSE;
7d193e45
LS
1762 $contributionId = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id);
1763 $membershipIds = $this->_membershipIDs;
1764 if ($contributionId && !empty($membershipIds)) {
1765 $contributionDetails = CRM_Contribute_BAO_Contribution::getContributionDetails(
1766 CRM_Export_Form_Select::MEMBER_EXPORT, $this->_membershipIDs);
1767 if ($contributionDetails[$membership->id]['contribution_status'] == 'Completed') {
aadd21c2 1768 $receiptSend = TRUE;
7d193e45
LS
1769 }
1770 }
6a488035 1771
7d193e45 1772 if (!empty($formValues['send_receipt']) && $receiptSend) {
b11c92be 1773 $formValues['contact_id'] = $this->_contactID;
7d193e45 1774 $formValues['contribution_id'] = $contributionId;
186a737c
EM
1775 // We really don't need a distinct receipt_text_signup vs receipt_text_renewal as they are
1776 // handled in the receipt. But by setting one we avoid breaking templates for now
1777 // although at some point we should switch in the templates.
1778 $formValues['receipt_text_signup'] = $formValues['receipt_text'];
6a488035 1779 // send email receipt
b11c92be 1780 $mailSend = self::emailReceipt($this, $formValues, $membership);
6a488035 1781 }
6a488035 1782
7865d848
EM
1783 // finally set membership id if already not set
1784 if (!$this->_id) {
1785 $this->_id = $membership->id;
6a488035 1786 }
6a488035 1787
7865d848
EM
1788 $statusMsg = '';
1789 if (($this->_action & CRM_Core_Action::UPDATE)) {
1790 $statusMsg = $this->getStatusMessageForUpdate($membership, $endDate, $receiptSend);
6a488035 1791 }
7865d848
EM
1792 elseif (($this->_action & CRM_Core_Action::ADD)) {
1793 $statusMsg = $this->getStatusMessageForCreate($endDate, $receiptSend, $membershipTypes, $createdMemberships,
1794 $params, $calcDates, $mailSend);
6a488035
TO
1795 }
1796
1797 CRM_Core_Session::setStatus($statusMsg, ts('Complete'), 'success');
15aea64e
PN
1798 //CRM-15187
1799 // dusplay message when membership type is changed
e0e13972 1800 if (($this->_action & CRM_Core_Action::UPDATE) && $this->_id && !in_array($this->_memType, $this->_memTypeSelected)) {
15aea64e
PN
1801 CRM_Core_Session::setStatus(
1802 ts('The financial types associated with the old and new membership types are different. You may want to edit the contribution associated with this membership to adjust its financial type.'),
1803 ts('Warning')
1804 );
1805 CRM_Core_Session::setStatus(
1806 ts('The cost of the old and new membership types are different. You may want to edit the contribution associated with this membership to adjust its amount.'),
1807 ts('Warning')
1808 );
1809 }
7865d848
EM
1810 return $createdMemberships;
1811 }
1812
5e56c7a5 1813 /**
1814 * Set context in session.
1815 */
7865d848 1816 protected function setUserContext() {
6a488035 1817 $buttonName = $this->controller->getButtonName();
7865d848
EM
1818 $session = CRM_Core_Session::singleton();
1819
6a488035
TO
1820 if ($this->_context == 'standalone') {
1821 if ($buttonName == $this->getButtonName('upload', 'new')) {
1822 $session->replaceUserContext(CRM_Utils_System::url('civicrm/member/add',
b11c92be 1823 'reset=1&action=add&context=standalone'
1824 ));
6a488035
TO
1825 }
1826 else {
1827 $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view',
b11c92be 1828 "reset=1&cid={$this->_contactID}&selectedChild=member"
1829 ));
6a488035
TO
1830 }
1831 }
1832 elseif ($buttonName == $this->getButtonName('upload', 'new')) {
1833 $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/membership',
b11c92be 1834 "reset=1&action=add&context=membership&cid={$this->_contactID}"
1835 ));
6a488035
TO
1836 }
1837 }
1838
1839 /**
5e56c7a5 1840 * Get status message for updating membership.
1841 *
1842 * @param CRM_Member_BAO_Membership $membership
1843 * @param string $endDate
1844 * @param bool $receiptSend
1845 *
7865d848 1846 * @return string
6a488035 1847 */
7865d848 1848 protected function getStatusMessageForUpdate($membership, $endDate, $receiptSend) {
5e56c7a5 1849 // End date can be modified by hooks, so if end date is set then use it.
7865d848 1850 $endDate = ($membership->end_date) ? $membership->end_date : $endDate;
6a488035 1851
7865d848
EM
1852 $statusMsg = ts('Membership for %1 has been updated.', array(1 => $this->_memberDisplayName));
1853 if ($endDate && $endDate !== 'null') {
1854 $endDate = CRM_Utils_Date::customFormat($endDate);
1855 $statusMsg .= ' ' . ts('The membership End Date is %1.', array(1 => $endDate));
6a488035
TO
1856 }
1857
7865d848
EM
1858 if ($receiptSend) {
1859 $statusMsg .= ' ' . ts('A confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail));
6a488035 1860 }
7865d848
EM
1861 return $statusMsg;
1862 }
6a488035 1863
7865d848 1864 /**
5e56c7a5 1865 * Get status message for create action.
1866 *
1867 * @param string $endDate
1868 * @param bool $receiptSend
1869 * @param array $membershipTypes
1870 * @param array $createdMemberships
1871 * @param array $params
1872 * @param array $calcDates
1873 * @param bool $mailSent
1874 *
7865d848
EM
1875 * @return array|string
1876 */
1877 protected function getStatusMessageForCreate($endDate, $receiptSend, $membershipTypes, $createdMemberships,
1878 $params, $calcDates, $mailSent) {
1879 // FIX ME: fix status messages
1880
1881 $statusMsg = array();
1882 foreach ($membershipTypes as $memType => $membershipType) {
1883 $statusMsg[$memType] = ts('%1 membership for %2 has been added.', array(
1884 1 => $membershipType,
1885 2 => $this->_memberDisplayName,
1886 ));
6a488035 1887
7865d848
EM
1888 $membership = $createdMemberships[$memType];
1889 $memEndDate = ($membership->end_date) ? $membership->end_date : $endDate;
6a488035 1890
7865d848
EM
1891 //get the end date from calculated dates.
1892 if (!$memEndDate && empty($params['is_recur'])) {
1893 $memEndDate = CRM_Utils_Array::value('end_date', $calcDates[$memType]);
35fa23f8 1894 }
6a488035 1895
7865d848
EM
1896 if ($memEndDate && $memEndDate !== 'null') {
1897 $memEndDate = CRM_Utils_Date::customFormat($memEndDate);
1898 $statusMsg[$memType] .= ' ' . ts('The new membership End Date is %1.', array(1 => $memEndDate));
b11c92be 1899 }
6a488035 1900 }
7865d848 1901 $statusMsg = implode('<br/>', $statusMsg);
f85b8063 1902 if ($receiptSend && !empty($mailSent)) {
7865d848 1903 $statusMsg .= ' ' . ts('A membership confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail));
bf1f1351 1904 }
7865d848 1905 return $statusMsg;
6a488035 1906 }
96025800 1907
6a488035 1908}