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