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