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