Merge pull request #7443 from eileenmcnaughton/CRM-17700
[civicrm-core.git] / CRM / Member / Form / Membership.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
e7112fa7 31 * @copyright CiviCRM LLC (c) 2004-2015
6a488035
TO
32 */
33
34/**
5e56c7a5 35 * This class generates form components for offline membership form.
6a488035
TO
36 */
37class CRM_Member_Form_Membership extends CRM_Member_Form {
38
39 protected $_memType = NULL;
40
41 protected $_onlinePendingContributionId;
42
43 public $_mode;
44
45 public $_contributeMode = 'direct';
46
47 protected $_recurMembershipTypes;
48
49 protected $_memTypeSelected;
50
d424ffde 51 /**
fe482240 52 * Display name of the member.
d424ffde
CW
53 *
54 * @var string
6a488035 55 */
b11c92be 56 protected $_memberDisplayName = NULL;
6a488035 57
d424ffde
CW
58 /**
59 * email of the person paying for the membership (used for receipts)
60 */
b11c92be 61 protected $_memberEmail = NULL;
6a488035 62
d424ffde 63 /**
fe482240 64 * Contact ID of the member.
d424ffde
CW
65 *
66 * @var int
67 */
cc984198 68 public $_contactID = NULL;
6a488035 69
d424ffde
CW
70 /**
71 * Display name of the person paying for the membership (used for receipts)
72 *
73 * @var string
74 */
b11c92be 75 protected $_contributorDisplayName = NULL;
6a488035 76
d424ffde 77 /**
b11c92be 78 * email of the person paying for the membership (used for receipts)
79 */
80 protected $_contributorEmail = NULL;
6a488035 81
d424ffde
CW
82 /**
83 * email of the person paying for the membership (used for receipts)
84 *
85 * @var int
86 */
b11c92be 87 protected $_contributorContactID = NULL;
6a488035 88
d424ffde 89 /**
fe482240 90 * ID of the person the receipt is to go to.
d424ffde
CW
91 *
92 * @var int
b11c92be 93 */
94 protected $_receiptContactId = NULL;
6a488035 95
d424ffde 96 /**
4aa7d844 97 * Keep a class variable for ALL membership IDs so
6a488035 98 * postProcess hook function can do something with it
d424ffde
CW
99 *
100 * @var array
6a488035 101 */
b11c92be 102 protected $_membershipIDs = array();
6a488035 103
5d86176b 104 /**
4aa7d844 105 * An array to hold a list of date fields on the form
5d86176b 106 * so that they can be converted to ISO in a consistent manner
107 *
108 * @var array
109 */
110 protected $_dateFields = array(
111 'receive_date' => array('default' => 'now'),
112 );
113
18aa3e1e 114 /**
4efc56ef
EM
115 * Get selected membership type from the form values.
116 *
e4a6290d 117 * @param array $priceSet
4efc56ef
EM
118 * @param array $params
119 *
120 * @return array
18aa3e1e 121 */
e4a6290d 122 public static function getSelectedMemberships($priceSet, $params) {
18aa3e1e 123 $memTypeSelected = array();
e4a6290d 124 $priceFieldIDS = self::getPriceFieldIDs($params, $priceSet);
ccb02c2d 125 if (isset($params['membership_type_id']) && !empty($params['membership_type_id'][1])) {
126 $memTypeSelected = array($params['membership_type_id'][1] => $params['membership_type_id'][1]);
127 }
128 else {
18aa3e1e
EM
129 foreach ($priceFieldIDS as $priceFieldId) {
130 if ($id = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_type_id')) {
131 $memTypeSelected[$id] = $id;
132 }
133 }
134 }
18aa3e1e
EM
135 return $memTypeSelected;
136 }
137
138 /**
139 * Extract price set fields and values from $params.
140 *
5e56c7a5 141 * @param array $params
e4a6290d 142 * @param array $priceSet
5e56c7a5 143 *
18aa3e1e
EM
144 * @return array
145 */
e4a6290d 146 public static function getPriceFieldIDs($params, $priceSet) {
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) {
c905ba98 190 if (!CRM_Member_BAO_Membership::statusAvailabilty($this->_contactID)) {
191 // all possible statuses are disabled - redirect back to contact form
192 CRM_Core_Error::statusBounce(ts('There are no configured membership statuses. You cannot add this membership until your membership statuses are correctly configured'));
193 }
6a488035
TO
194 if ($this->_contactID) {
195 //check whether contact has a current membership so we can alert user that they may want to do a renewal instead
4256ea25
AH
196 $contactMemberships = array();
197 $memParams = array('contact_id' => $this->_contactID);
198 CRM_Member_BAO_Membership::getValues($memParams, $contactMemberships, TRUE);
199 $cMemTypes = array();
200 foreach ($contactMemberships as $mem) {
201 $cMemTypes[] = $mem['membership_type_id'];
202 }
203 if (count($cMemTypes) > 0) {
204 $memberorgs = CRM_Member_BAO_MembershipType::getMemberOfContactByMemTypes($cMemTypes);
205 $mems_by_org = array();
2e8b13d1 206 foreach ($contactMemberships as $mem) {
4256ea25 207 $mem['member_of_contact_id'] = CRM_Utils_Array::value($mem['membership_type_id'], $memberorgs);
a7488080 208 if (!empty($mem['membership_end_date'])) {
4256ea25
AH
209 $mem['membership_end_date'] = CRM_Utils_Date::customformat($mem['membership_end_date']);
210 }
211 $mem['membership_type'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
212 $mem['membership_type_id'],
213 'name', 'id'
6a488035 214 );
4256ea25
AH
215 $mem['membership_status'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus',
216 $mem['status_id'],
217 'label', 'id'
6a488035 218 );
74dd0d90
CW
219 $mem['renewUrl'] = CRM_Utils_System::url('civicrm/contact/view/membership',
220 "reset=1&action=renew&cid={$this->_contactID}&id={$mem['id']}&context=membership&selectedChild=member"
221 . ($this->_mode ? '&mode=live' : '')
222 );
4256ea25
AH
223 $mem['membershipTab'] = CRM_Utils_System::url('civicrm/contact/view',
224 "reset=1&force=1&cid={$this->_contactID}&selectedChild=member"
225 );
226 $mems_by_org[$mem['member_of_contact_id']] = $mem;
6a488035 227 }
74dd0d90 228 $this->assign('existingContactMemberships', $mems_by_org);
6a488035
TO
229 }
230 }
1001e556 231 else {
74dd0d90 232 // In standalone mode we don't have a contact id yet so lookup will be done client-side with this script:
d292601b
AH
233 $resources = CRM_Core_Resources::singleton();
234 $resources->addScriptFile('civicrm', 'templates/CRM/Member/Form/MembershipStandalone.js');
d292601b
AH
235 $passthru = array(
236 'typeorgs' => CRM_Member_BAO_MembershipType::getMembershipTypeOrganization(),
74dd0d90
CW
237 'memtypes' => CRM_Core_PseudoConstant::get('CRM_Member_BAO_Membership', 'membership_type_id'),
238 'statuses' => CRM_Core_PseudoConstant::get('CRM_Member_BAO_Membership', 'status_id'),
d292601b
AH
239 );
240 $resources->addSetting(array('existingMems' => $passthru));
241 }
6a488035
TO
242 }
243
97ae4877 244 if (!$this->_memType) {
245 $params = CRM_Utils_Request::exportValues();
71098424 246 if (isset($params['membership_type_id'][1])) {
247 $this->_memType = $params['membership_type_id'][1];
248 }
97ae4877 249 }
6a488035 250 // when custom data is included in this page
a7488080 251 if (!empty($_POST['hidden_custom'])) {
c5366541 252 CRM_Custom_Form_CustomData::preProcess($this, NULL, $this->_memType, 1, 'Membership', $this->_id);
6a488035
TO
253 CRM_Custom_Form_CustomData::buildQuickForm($this);
254 CRM_Custom_Form_CustomData::setDefaultValues($this);
255 }
256
257 // CRM-4395, get the online pending contribution id.
258 $this->_onlinePendingContributionId = NULL;
259 if (!$this->_mode && $this->_id && ($this->_action & CRM_Core_Action::UPDATE)) {
260 $this->_onlinePendingContributionId = CRM_Contribute_BAO_Contribution::checkOnlinePendingContribution($this->_id,
261 'Membership'
262 );
263 }
264 $this->assign('onlinePendingContributionId', $this->_onlinePendingContributionId);
6a488035 265
e2046b33 266 $this->setPageTitle(ts('Membership'));
6a488035
TO
267 }
268
269 /**
5e56c7a5 270 * Set default values for the form.
6a488035
TO
271 */
272 public function setDefaultValues() {
6a488035
TO
273
274 if ($this->_priceSetId) {
9da8dc8c 275 return CRM_Price_BAO_PriceSet::setDefaultPriceSet($this, $defaults);
6a488035
TO
276 }
277
278 $defaults = parent::setDefaultValues();
279
280 //setting default join date and receive date
569fe706 281 list($now, $currentTime) = CRM_Utils_Date::setDateDefaults();
6a488035
TO
282 if ($this->_action == CRM_Core_Action::ADD) {
283 $defaults['receive_date'] = $now;
569fe706 284 $defaults['receive_date_time'] = $currentTime;
6a488035
TO
285 }
286
287 if (is_numeric($this->_memType)) {
288 $defaults['membership_type_id'] = array();
289 $defaults['membership_type_id'][0] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
290 $this->_memType,
291 'member_of_contact_id',
292 'id'
293 );
294 $defaults['membership_type_id'][1] = $this->_memType;
295 }
296 else {
297 $defaults['membership_type_id'] = $this->_memType;
298 }
299
300 $defaults['num_terms'] = 1;
301
a7488080 302 if (!empty($defaults['id'])) {
6a488035
TO
303 if ($this->_onlinePendingContributionId) {
304 $defaults['record_contribution'] = $this->_onlinePendingContributionId;
305 }
306 else {
307 $contributionId = CRM_Core_DAO::singleValueQuery("
308 SELECT contribution_id
309 FROM civicrm_membership_payment
310 WHERE membership_id = $this->_id
311 ORDER BY contribution_id
312 DESC limit 1");
313
314 if ($contributionId) {
315 $defaults['record_contribution'] = $contributionId;
316 }
317 }
318 }
133e2c99 319
374a4dd6 320 //set Soft Credit Type to Gift by default
321 $scTypes = CRM_Core_OptionGroup::values("soft_credit_type");
322 $defaults['soft_credit_type_id'] = CRM_Utils_Array::value(ts('Gift'), array_flip($scTypes));
323
a7488080 324 if (!empty($defaults['record_contribution']) && !$this->_mode) {
6a488035
TO
325 $contributionParams = array('id' => $defaults['record_contribution']);
326 $contributionIds = array();
327
328 //keep main object campaign in hand.
329 $memberCampaignId = CRM_Utils_Array::value('campaign_id', $defaults);
330
331 CRM_Contribute_BAO_Contribution::getValues($contributionParams, $defaults, $contributionIds);
332
333 //get back original object campaign id.
334 $defaults['campaign_id'] = $memberCampaignId;
335
a7488080 336 if (!empty($defaults['receive_date'])) {
6a488035
TO
337 list($defaults['receive_date']) = CRM_Utils_Date::setDateDefaults($defaults['receive_date']);
338 }
339
340 // Contribution::getValues() over-writes the membership record's source field value - so we need to restore it.
a7488080 341 if (!empty($defaults['membership_source'])) {
6a488035
TO
342 $defaults['source'] = $defaults['membership_source'];
343 }
344 }
d96cf288 345 //CRM-13420
a7488080 346 if (empty($defaults['payment_instrument_id'])) {
d96cf288
DG
347 $defaults['payment_instrument_id'] = key(CRM_Core_OptionGroup::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1'));
348 }
6a488035
TO
349
350 // User must explicitly choose to send a receipt in both add and update mode.
351 $defaults['send_receipt'] = 0;
352
353 if ($this->_action & CRM_Core_Action::UPDATE) {
354 // in this mode by default uncheck this checkbox
355 unset($defaults['record_contribution']);
356 }
357
2e8b13d1 358 $subscriptionCancelled = FALSE;
a7488080 359 if (!empty($defaults['id'])) {
6a488035
TO
360 $subscriptionCancelled = CRM_Member_BAO_Membership::isSubscriptionCancelled($this->_id);
361 }
362
363 $alreadyAutoRenew = FALSE;
a7488080 364 if (!empty($defaults['contribution_recur_id']) && !$subscriptionCancelled) {
6a488035
TO
365 $defaults['auto_renew'] = 1;
366 $alreadyAutoRenew = TRUE;
367 }
368 $this->assign('alreadyAutoRenew', $alreadyAutoRenew);
369
370 $this->assign('member_is_test', CRM_Utils_Array::value('member_is_test', $defaults));
371
372 $this->assign('membership_status_id', CRM_Utils_Array::value('status_id', $defaults));
373
a7488080 374 if (!empty($defaults['is_pay_later'])) {
6a488035
TO
375 $this->assign('is_pay_later', TRUE);
376 }
377 if ($this->_mode) {
3b8e6c3f 378 $defaults = $this->getBillingDefaults($defaults);
5e56c7a5 379 // hack to simplify credit card entry for testing
380 // $defaults['credit_card_type'] = 'Visa';
381 // $defaults['credit_card_number'] = '4807731747657838';
382 // $defaults['cvv2'] = '000';
383 // $defaults['credit_card_exp_date'] = array( 'Y' => '2012', 'M' => '05' );
6a488035
TO
384 }
385
386 $dates = array('join_date', 'start_date', 'end_date');
387 foreach ($dates as $key) {
a7488080 388 if (!empty($defaults[$key])) {
6a488035
TO
389 list($defaults[$key]) = CRM_Utils_Date::setDateDefaults(CRM_Utils_Array::value($key, $defaults));
390 }
391 }
392
393 //setting default join date if there is no join date
a7488080 394 if (empty($defaults['join_date'])) {
6a488035
TO
395 $defaults['join_date'] = $now;
396 }
397
a7488080 398 if (!empty($defaults['membership_end_date'])) {
6a488035
TO
399 $this->assign('endDate', $defaults['membership_end_date']);
400 }
401
402 return $defaults;
403 }
404
405 /**
fe482240 406 * Build the form object.
6a488035
TO
407 */
408 public function buildQuickForm() {
6a488035 409
6f87cd8d 410 $this->assign('taxRates', json_encode(CRM_Core_PseudoConstant::getTaxRates()));
579e1e0e
EM
411
412 $this->assign('currency', CRM_Core_Config::singleton()->defaultCurrencySymbol);
aaffa79f 413 $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
46611472 414 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
9b873358 415 if (isset($invoicing)) {
46611472 416 $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings));
417 }
6a488035
TO
418 // build price set form.
419 $buildPriceSet = FALSE;
8cc574cf 420 if ($this->_priceSetId || !empty($_POST['price_set_id'])) {
a7488080 421 if (!empty($_POST['price_set_id'])) {
6a488035
TO
422 $buildPriceSet = TRUE;
423 }
424 $getOnlyPriceSetElements = TRUE;
425 if (!$this->_priceSetId) {
426 $this->_priceSetId = $_POST['price_set_id'];
427 $getOnlyPriceSetElements = FALSE;
428 }
429
430 $this->set('priceSetId', $this->_priceSetId);
9da8dc8c 431 CRM_Price_BAO_PriceSet::buildPriceSet($this);
6a488035
TO
432
433 $optionsMembershipTypes = array();
434 foreach ($this->_priceSet['fields'] as $pField) {
435 if (empty($pField['options'])) {
436 continue;
437 }
438 foreach ($pField['options'] as $opId => $opValues) {
439 $optionsMembershipTypes[$opId] = CRM_Utils_Array::value('membership_type_id', $opValues, 0);
440 }
441 }
442
9da8dc8c 443 $this->assign('autoRenewOption', CRM_Price_BAO_PriceSet::checkAutoRenewForPriceSet($this->_priceSetId));
6a488035
TO
444
445 $this->assign('optionsMembershipTypes', $optionsMembershipTypes);
5a9c4d4a 446 $this->assign('contributionType', CRM_Utils_Array::value('financial_type_id', $this->_priceSet));
6a488035
TO
447
448 // get only price set form elements.
449 if ($getOnlyPriceSetElements) {
450 return;
451 }
452 }
453
454 // use to build form during form rule.
455 $this->assign('buildPriceSet', $buildPriceSet);
456
457 if ($this->_action & CRM_Core_Action::ADD) {
458 $buildPriceSet = FALSE;
9da8dc8c 459 $priceSets = CRM_Price_BAO_PriceSet::getAssoc(FALSE, 'CiviMember');
6a488035
TO
460 if (!empty($priceSets)) {
461 $buildPriceSet = TRUE;
462 }
463
464 if ($buildPriceSet) {
465 $this->add('select', 'price_set_id', ts('Choose price set'),
466 array(
21dfd5f5 467 '' => ts('Choose price set'),
b11c92be 468 ) + $priceSets,
6a488035
TO
469 NULL, array('onchange' => "buildAmount( this.value );")
470 );
471 }
472 $this->assign('hasPriceSets', $buildPriceSet);
473 }
474
475 //need to assign custom data type and subtype to the template
476 $this->assign('customDataType', 'Membership');
477 $this->assign('customDataSubType', $this->_memType);
478 $this->assign('entityID', $this->_id);
479
480 if ($this->_action & CRM_Core_Action::DELETE) {
481 $this->addButtons(array(
482 array(
483 'type' => 'next',
484 'name' => ts('Delete'),
485 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
486 'isDefault' => TRUE,
487 ),
488 array(
489 'type' => 'cancel',
490 'name' => ts('Cancel'),
491 ),
492 )
493 );
494 return;
495 }
496
497 if ($this->_context == 'standalone') {
353ffa53
TO
498 $this->addEntityRef('contact_id', ts('Contact'), array(
499 'create' => TRUE,
79d7553f 500 'api' => array('extra' => array('email')),
353ffa53 501 ), TRUE);
6a488035
TO
502 }
503
504 $selOrgMemType[0][0] = $selMemTypeOrg[0] = ts('- select -');
505
c60d2e2c 506 // Throw status bounce when no Membership type or priceset is present
7284a1a4
PN
507 if (CRM_Financial_BAO_FinancialType::isACLFinancialTypeStatus()
508 && empty($this->allMembershipTypeDetails) && empty($priceSets)
509 ) {
c60d2e2c
PN
510 CRM_Core_Error::statusBounce(ts('You do not have all the permissions needed for this page.'));
511 }
6a488035 512 // retrieve all memberships
ab30e033
EM
513 $allMembershipInfo = array();
514 foreach ($this->allMembershipTypeDetails as $key => $values) {
515 if ($this->_mode && empty($values['minimum_fee'])) {
516 continue;
517 }
518 else {
519 $memberOfContactId = CRM_Utils_Array::value('member_of_contact_id', $values);
520 if (empty($selMemTypeOrg[$memberOfContactId])) {
521 $selMemTypeOrg[$memberOfContactId] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
522 $memberOfContactId,
523 'display_name',
524 'id'
525 );
6a488035 526
ab30e033 527 $selOrgMemType[$memberOfContactId][0] = ts('- select -');
6a488035 528 }
ab30e033
EM
529 if (empty($selOrgMemType[$memberOfContactId][$key])) {
530 $selOrgMemType[$memberOfContactId][$key] = CRM_Utils_Array::value('name', $values);
6a488035 531 }
6a488035 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),
538 'total_amount' => CRM_Utils_Money::format($values['minimum_fee'], NULL, '%a'),
539 'total_amount_numeric' => CRM_Utils_Array::value('minimum_fee', $values),
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) {
7865d848
EM
1003 // assign the address formatted up for display
1004 $addressParts = array(
1005 "street_address-{$form->_bltID}",
1006 "city-{$form->_bltID}",
1007 "postal_code-{$form->_bltID}",
1008 "state_province-{$form->_bltID}",
1009 "country-{$form->_bltID}",
1010 );
1011 $addressFields = array();
1012 foreach ($addressParts as $part) {
1013 list($n, $id) = explode('-', $part);
1014 if (isset($form->_params['billing_' . $part])) {
1015 $addressFields[$n] = $form->_params['billing_' . $part];
1016 }
1017 }
1018 $form->assign('address', CRM_Utils_Address::format($addressFields));
7d193e45 1019
7865d848
EM
1020 $date = CRM_Utils_Date::format($form->_params['credit_card_exp_date']);
1021 $date = CRM_Utils_Date::mysqlToIso($date);
1022 $form->assign('credit_card_exp_date', $date);
1023 $form->assign('credit_card_number',
1024 CRM_Utils_System::mungeCreditCard($form->_params['credit_card_number'])
1025 );
1026 $form->assign('credit_card_type', $form->_params['credit_card_type']);
1027 $form->assign('contributeMode', 'direct');
1028 $form->assign('isAmountzero', 0);
1029 $form->assign('is_pay_later', 0);
1030 $form->assign('isPrimary', 1);
1031 }
1032
1033 $form->assign('module', 'Membership');
1034 $form->assign('contactID', $formValues['contact_id']);
1035
1036 $form->assign('membershipID', CRM_Utils_Array::value('membership_id', $form->_params, CRM_Utils_Array::value('membership_id', $form->_defaultValues)));
1037
1038 if (!empty($formValues['contribution_id'])) {
1039 $form->assign('contributionID', $formValues['contribution_id']);
1040 }
1041 elseif (isset($form->_onlinePendingContributionId)) {
1042 $form->assign('contributionID', $form->_onlinePendingContributionId);
1043 }
1044
1045 if (!empty($formValues['contribution_status_id'])) {
1046 $form->assign('contributionStatusID', $formValues['contribution_status_id']);
1047 $form->assign('contributionStatus', CRM_Contribute_PseudoConstant::contributionStatus($formValues['contribution_status_id'], 'name'));
1048 }
1049
1050 if (!empty($formValues['is_renew'])) {
1051 $form->assign('receiptType', 'membership renewal');
1052 }
1053 else {
1054 $form->assign('receiptType', 'membership signup');
1055 }
1056 $form->assign('receive_date', CRM_Utils_Date::processDate(CRM_Utils_Array::value('receive_date', $formValues)));
1057 $form->assign('formValues', $formValues);
1058
1059 if (empty($lineItem)) {
1060 $form->assign('mem_start_date', CRM_Utils_Date::customFormat($membership->start_date, '%B %E%f, %Y'));
1061 if (!CRM_Utils_System::isNull($membership->end_date)) {
1062 $form->assign('mem_end_date', CRM_Utils_Date::customFormat($membership->end_date, '%B %E%f, %Y'));
1063 }
1064 $form->assign('membership_name', CRM_Member_PseudoConstant::membershipType($membership->membership_type_id));
1065 }
1066
7865d848
EM
1067 $isBatchProcess = is_a($form, 'CRM_Batch_Form_Entry');
1068 if ((empty($form->_contributorDisplayName) || empty($form->_contributorEmail)) || $isBatchProcess) {
1069 // in this case the form is being called statically from the batch editing screen
1070 // having one class in the form layer call another statically is not greate
1071 // & we should aim to move this function to the BAO layer in future.
1072 // however, we can assume that the contact_id passed in by the batch
1073 // function will be the recipient
1074 list($form->_contributorDisplayName, $form->_contributorEmail)
1075 = CRM_Contact_BAO_Contact_Location::getEmailDetails($formValues['contact_id']);
1076 if (empty($form->_receiptContactId) || $isBatchProcess) {
1077 $form->_receiptContactId = $formValues['contact_id'];
1078 }
1079 }
1080 $template = CRM_Core_Smarty::singleton();
1081 $taxAmt = $template->get_template_vars('dataArray');
1082 $eventTaxAmt = $template->get_template_vars('totalTaxAmount');
aaffa79f 1083 $prefixValue = Civi::settings()->get('contribution_invoice_settings');
7865d848
EM
1084 $invoicing = CRM_Utils_Array::value('invoicing', $prefixValue);
1085 if ((!empty($taxAmt) || isset($eventTaxAmt)) && (isset($invoicing) && isset($prefixValue['is_email_pdf']))) {
1086 $isEmailPdf = TRUE;
1087 }
1088 else {
1089 $isEmailPdf = FALSE;
1090 }
1091
1092 list($mailSend, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate::sendTemplate(
1093 array(
1094 'groupName' => 'msg_tpl_workflow_membership',
1095 'valueName' => 'membership_offline_receipt',
1096 'contactId' => $form->_receiptContactId,
1097 'from' => $receiptFrom,
1098 'toName' => $form->_contributorDisplayName,
1099 'toEmail' => $form->_contributorEmail,
1100 'PDFFilename' => ts('receipt') . '.pdf',
1101 'isEmailPdf' => $isEmailPdf,
1102 'contributionId' => $formValues['contribution_id'],
1103 'isTest' => (bool) ($form->_action & CRM_Core_Action::PREVIEW),
1104 )
1105 );
1106
1107 return TRUE;
1108 }
1109
1110 /**
5e56c7a5 1111 * Submit function.
1112 *
1113 * This is also accessed by unit tests.
1114 *
7865d848
EM
1115 * @return array
1116 */
09108d7d 1117 public function submit() {
4bd318e0 1118 $isTest = ($this->_mode == 'test') ? 1 : 0;
09108d7d 1119 $this->storeContactFields($this->_params);
ba2f3f65 1120 $this->beginPostProcess();
09108d7d 1121 $formValues = $this->_params;
7865d848
EM
1122 $joinDate = $startDate = $endDate = NULL;
1123 $membershipTypes = $membership = $calcDate = array();
41709813 1124 $membershipType = NULL;
7865d848 1125
41709813 1126 $mailSend = FALSE;
ccb02c2d 1127 $formValues = $this->setPriceSetParameters($formValues);
7865d848
EM
1128 $params = $softParams = $ids = array();
1129
1130 $allMemberStatus = CRM_Member_PseudoConstant::membershipStatus();
1131 $allContributionStatus = CRM_Contribute_PseudoConstant::contributionStatus();
09108d7d 1132 $this->processBillingAddress();
7865d848 1133
08fd4b45
EM
1134 if ($this->_id) {
1135 $ids['membership'] = $params['id'] = $this->_id;
1136 }
1137 $ids['userId'] = CRM_Core_Session::singleton()->get('userID');
1138
7865d848
EM
1139 // Set variables that we normally get from context.
1140 // In form mode these are set in preProcess.
1141 //TODO: set memberships, fixme
1142 $this->setContextVariables($formValues);
ccb02c2d 1143
e4a6290d 1144 $this->_memTypeSelected = self::getSelectedMemberships(
ccb02c2d 1145 $this->_priceSet,
e4a6290d 1146 $formValues
1147 );
867047cd 1148 if (empty($formValues['financial_type_id'])) {
ccb02c2d 1149 $formValues['financial_type_id'] = $this->_priceSet['financial_type_id'];
867047cd 1150 }
7865d848 1151
6a488035 1152 $config = CRM_Core_Config::singleton();
6a488035 1153
7865d848 1154 $this->convertDateFieldsToMySQL($formValues);
6a488035
TO
1155
1156 $membershipTypeValues = array();
1157 foreach ($this->_memTypeSelected as $memType) {
1158 $membershipTypeValues[$memType]['membership_type_id'] = $memType;
1159 }
1160
1161 //take the required membership recur values.
7865d848 1162 if ($this->_mode && !empty($formValues['auto_renew'])) {
ccb02c2d 1163 $params['is_recur'] = $formValues['is_recur'] = TRUE;
6a488035
TO
1164 $mapping = array(
1165 'frequency_interval' => 'duration_interval',
1166 'frequency_unit' => 'duration_unit',
1167 );
1168
1169 $count = 0;
1170 foreach ($this->_memTypeSelected as $memType) {
1171 $recurMembershipTypeValues = CRM_Utils_Array::value($memType,
1172 $this->_recurMembershipTypes, array()
1173 );
1174 foreach ($mapping as $mapVal => $mapParam) {
1175 $membershipTypeValues[$memType][$mapVal] = CRM_Utils_Array::value($mapParam,
1176 $recurMembershipTypeValues
1177 );
1178 if (!$count) {
ccb02c2d 1179 $formValues[$mapVal] = CRM_Utils_Array::value($mapParam,
6a488035
TO
1180 $recurMembershipTypeValues
1181 );
1182 }
1183 }
1184 $count++;
1185 }
6a488035
TO
1186 }
1187
ccb02c2d 1188 $isQuickConfig = $this->_priceSet['is_quick_config'];
1189
6a488035 1190 $termsByType = array();
9f1bc5dc 1191
ccb02c2d 1192 $lineItem = array($this->_priceSetId => array());
1193
08fd4b45 1194 CRM_Price_BAO_PriceSet::processAmount($this->_priceSet['fields'],
ccb02c2d 1195 $formValues, $lineItem[$this->_priceSetId]);
1196
1197 if (CRM_Utils_Array::value('tax_amount', $formValues)) {
1198 $params['tax_amount'] = $formValues['tax_amount'];
08fd4b45 1199 }
ccb02c2d 1200 $params['total_amount'] = CRM_Utils_Array::value('amount', $formValues);
08fd4b45 1201 $submittedFinancialType = CRM_Utils_Array::value('financial_type_id', $formValues);
ccb02c2d 1202 if (!empty($lineItem[$this->_priceSetId])) {
1203 foreach ($lineItem[$this->_priceSetId] as &$li) {
08fd4b45
EM
1204 if (!empty($li['membership_type_id'])) {
1205 if (!empty($li['membership_num_terms'])) {
1206 $termsByType[$li['membership_type_id']] = $li['membership_num_terms'];
6a488035 1207 }
08fd4b45 1208 }
6a488035 1209
08fd4b45
EM
1210 ///CRM-11529 for quick config backoffice transactions
1211 //when financial_type_id is passed in form, update the
1212 //lineitems with the financial type selected in form
1213 if ($isQuickConfig && $submittedFinancialType) {
1214 $li['financial_type_id'] = $submittedFinancialType;
6a488035
TO
1215 }
1216 }
1217 }
1218
6a488035
TO
1219 $params['contact_id'] = $this->_contactID;
1220
1221 $fields = array(
1222 'status_id',
1223 'source',
1224 'is_override',
1225 'campaign_id',
1226 );
1227
1228 foreach ($fields as $f) {
1229 $params[$f] = CRM_Utils_Array::value($f, $formValues);
1230 }
1231
1232 // fix for CRM-3724
1233 // when is_override false ignore is_admin statuses during membership
1234 // status calculation. similarly we did fix for import in CRM-3570.
a7488080 1235 if (empty($params['is_override'])) {
6a488035
TO
1236 $params['exclude_is_admin'] = TRUE;
1237 }
1238
1239 // process date params to mysql date format.
1240 $dateTypes = array(
1241 'join_date' => 'joinDate',
1242 'start_date' => 'startDate',
1243 'end_date' => 'endDate',
1244 );
1245 foreach ($dateTypes as $dateField => $dateVariable) {
1246 $$dateVariable = CRM_Utils_Date::processDate($formValues[$dateField]);
1247 }
1248
b09fe5ed 1249 $memTypeNumTerms = empty($termsByType) ? CRM_Utils_Array::value('num_terms', $formValues) : NULL;
6a488035
TO
1250
1251 $calcDates = array();
1252 foreach ($this->_memTypeSelected as $memType) {
1693f081 1253 if (empty($memTypeNumTerms)) {
1254 $memTypeNumTerms = CRM_Utils_Array::value($memType, $termsByType, 1);
1255 }
6a488035
TO
1256 $calcDates[$memType] = CRM_Member_BAO_MembershipType::getDatesForMembershipType($memType,
1257 $joinDate, $startDate, $endDate, $memTypeNumTerms
1258 );
1259 }
1260
1261 foreach ($calcDates as $memType => $calcDate) {
5d86176b 1262 foreach (array_keys($dateTypes) as $d) {
6a488035
TO
1263 //first give priority to form values then calDates.
1264 $date = CRM_Utils_Array::value($d, $formValues);
1265 if (!$date) {
1266 $date = CRM_Utils_Array::value($d, $calcDate);
1267 }
1268
1269 $membershipTypeValues[$memType][$d] = CRM_Utils_Date::processDate($date);
6a488035
TO
1270 }
1271 }
1272
1273 // max related memberships - take from form or inherit from membership type
1274 foreach ($this->_memTypeSelected as $memType) {
1275 if (array_key_exists('max_related', $formValues)) {
1276 $membershipTypeValues[$memType]['max_related'] = CRM_Utils_Array::value('max_related', $formValues);
1277 }
6a488035 1278 $membershipTypeValues[$memType]['custom'] = CRM_Core_BAO_CustomField::postProcess($formValues,
6a488035
TO
1279 $this->_id,
1280 'Membership'
1281 );
6a488035
TO
1282 $membershipTypes[$memType] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
1283 $memType
1284 );
1285 }
1286
1287 $membershipType = implode(', ', $membershipTypes);
1288
1289 // Retrieve the name and email of the current user - this will be the FROM for the receipt email
ab30e033 1290 list($userName) = CRM_Contact_BAO_Contact_Location::getEmailDetails($ids['userId']);
6a488035 1291
d80dbc14 1292 //CRM-13981, allow different person as a soft-contributor of chosen type
b11c92be 1293 if ($this->_contributorContactID != $this->_contactID) {
91ef9be0 1294 $params['contribution_contact_id'] = $this->_contributorContactID;
ccb02c2d 1295 if (!empty($formValues['soft_credit_type_id'])) {
1296 $softParams['soft_credit_type_id'] = $formValues['soft_credit_type_id'];
91ef9be0 1297 $softParams['contact_id'] = $this->_contactID;
6a488035
TO
1298 }
1299 }
a7488080 1300 if (!empty($formValues['record_contribution'])) {
6a488035 1301 $recordContribution = array(
b11c92be 1302 'total_amount',
b11c92be 1303 'financial_type_id',
1304 'payment_instrument_id',
1305 'trxn_id',
1306 'contribution_status_id',
1307 'check_number',
1308 'campaign_id',
1309 'receive_date',
6a488035
TO
1310 );
1311
1312 foreach ($recordContribution as $f) {
1313 $params[$f] = CRM_Utils_Array::value($f, $formValues);
1314 }
1315
1316 if (!$this->_onlinePendingContributionId) {
2286d173 1317 if (empty($formValues['source'])) {
353ffa53 1318 $params['contribution_source'] = ts('%1 Membership: Offline signup (by %2)', array(
7865d848
EM
1319 1 => $membershipType,
1320 2 => $userName,
1321 ));
2286d173
PD
1322 }
1323 else {
0e81467c 1324 $params['contribution_source'] = $formValues['source'];
2286d173 1325 }
0e81467c 1326 }
6a488035 1327
a7488080 1328 if (empty($params['is_override']) &&
6a488035
TO
1329 CRM_Utils_Array::value('contribution_status_id', $params) == array_search('Pending', CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'))
1330 ) {
7d193e45 1331 $params['status_id'] = array_search('Pending', $allMemberStatus);
6a488035
TO
1332 $params['skipStatusCal'] = TRUE;
1333 $params['is_pay_later'] = 1;
1334 $this->assign('is_pay_later', 1);
1335 }
1336
a7488080 1337 if (!empty($formValues['send_receipt'])) {
5d86176b 1338 $params['receipt_date'] = CRM_Utils_Array::value('receive_date', $formValues);
6a488035
TO
1339 }
1340
1341 //insert financial type name in receipt.
1342 $formValues['contributionType_name'] = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_FinancialType',
1343 $formValues['financial_type_id']
1344 );
1345 }
1346
1347 // process line items, until no previous line items.
1348 if (!empty($lineItem)) {
1349 $params['lineItems'] = $lineItem;
1350 $params['processPriceSet'] = TRUE;
1351 }
1352 $createdMemberships = array();
1353 if ($this->_mode) {
ccb02c2d 1354 $params['total_amount'] = CRM_Utils_Array::value('total_amount', $formValues, 0);
a8d4ff25 1355
ccb02c2d 1356 if (!$isQuickConfig) {
b11c92be 1357 $params['financial_type_id'] = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet',
ccb02c2d 1358 $this->_priceSetId,
6a488035
TO
1359 'financial_type_id'
1360 );
1361 }
1362 else {
5a9c4d4a 1363 $params['financial_type_id'] = CRM_Utils_Array::value('financial_type_id', $formValues);
6a488035
TO
1364 }
1365
ba2f3f65 1366 // @todo - test removing this line. The beginPostProcess Function should have done it for us.
6a488035
TO
1367 $this->_paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($formValues['payment_processor_id'],
1368 $this->_mode
1369 );
1370
ba2f3f65 1371 //get the payment processor id as per mode. Try removing in favour of beginPostProcess.
ccb02c2d 1372 $params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor['id'];
09108d7d 1373 $params['register_date'] = date('YmdHis');
6a488035 1374
a1a94e61 1375 // add all the additional payment params we need
ba2f3f65 1376 // @todo the country & state values should be set by the call to $this->assignBillingAddress.
ccb02c2d 1377 $formValues["state_province-{$this->_bltID}"] = $formValues["billing_state_province-{$this->_bltID}"]
a271822c 1378 = CRM_Core_PseudoConstant::stateProvinceAbbreviation($formValues["billing_state_province_id-{$this->_bltID}"]);
ccb02c2d 1379 $formValues["country-{$this->_bltID}"] = $formValues["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($formValues["billing_country_id-{$this->_bltID}"]);
6a488035 1380
ccb02c2d 1381 $formValues['amount'] = $params['total_amount'];
ba2f3f65 1382 // @todo this is a candidate for beginPostProcessFunction.
ccb02c2d 1383 $formValues['currencyID'] = $config->defaultCurrency;
1384 $formValues['description'] = ts("Contribution submitted by a staff person using member's credit card for signup");
1385 $formValues['invoiceID'] = md5(uniqid(rand(), TRUE));
1386 $formValues['financial_type_id'] = $params['financial_type_id'];
6a488035
TO
1387
1388 // at this point we've created a contact and stored its address etc
1389 // all the payment processors expect the name and address to be in the
1390 // so we copy stuff over to first_name etc.
ccb02c2d 1391 $paymentParams = $formValues;
6a488035
TO
1392 $paymentParams['contactID'] = $this->_contributorContactID;
1393 //CRM-10377 if payment is by an alternate contact then we need to set that person
1394 // as the contact in the payment params
b11c92be 1395 if ($this->_contributorContactID != $this->_contactID) {
ccb02c2d 1396 if (!empty($formValues['soft_credit_type_id'])) {
133e2c99 1397 $softParams['contact_id'] = $params['contact_id'];
ccb02c2d 1398 $softParams['soft_credit_type_id'] = $formValues['soft_credit_type_id'];
6a488035
TO
1399 }
1400 }
ccb02c2d 1401 if (!empty($formValues['send_receipt'])) {
6a488035
TO
1402 $paymentParams['email'] = $this->_contributorEmail;
1403 }
1404
ba2f3f65 1405 // This is a candidate for shared beginPostProcess function.
ccb02c2d 1406 CRM_Core_Payment_Form::mapParams($this->_bltID, $formValues, $paymentParams, TRUE);
6a488035 1407 // CRM-7137 -for recurring membership,
b44e3f84 1408 // we do need contribution and recurring records.
6a488035 1409 $result = NULL;
a7488080 1410 if (!empty($paymentParams['is_recur'])) {
8a7b41d1
EM
1411 $financialType = new CRM_Financial_DAO_FinancialType();
1412 $financialType->id = $params['financial_type_id'];
1413 $financialType->find(TRUE);
ccb02c2d 1414 $this->_params = $formValues;
ba013eea 1415 $contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution($this,
6a488035 1416 $paymentParams,
3febe800 1417 NULL,
f6261e9d 1418 array(
1419 'contact_id' => $this->_contributorContactID,
9b581f1d 1420 'line_item' => $lineItem,
f6261e9d 1421 'is_test' => $isTest,
1422 'campaign_id' => CRM_Utils_Array::value('campaign_id', $paymentParams),
ccb02c2d 1423 'contribution_page_id' => CRM_Utils_Array::value('contribution_page_id', $formValues),
3febe800 1424 'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)),
1425 'thankyou_date' => CRM_Utils_Array::value('thankyou_date', $paymentParams),
1426 'payment_instrument_id' => $this->_paymentProcessor['payment_instrument_id'],
f6261e9d 1427 ),
8a7b41d1 1428 $financialType,
4bd318e0 1429 FALSE,
449f4c90 1430 $this->_bltID,
1431 TRUE
6a488035 1432 );
133e2c99 1433
1434 //create new soft-credit record, CRM-13981
00c1cd97
CW
1435 if ($softParams) {
1436 $softParams['contribution_id'] = $contribution->id;
1437 $softParams['currency'] = $contribution->currency;
1438 $softParams['amount'] = $contribution->total_amount;
1439 CRM_Contribute_BAO_ContributionSoft::add($softParams);
1440 }
133e2c99 1441
a22bd791 1442 $paymentParams['contactID'] = $this->_contactID;
6a488035 1443 $paymentParams['contributionID'] = $contribution->id;
b11c92be 1444 $paymentParams['contributionTypeID'] = $contribution->financial_type_id;
6a488035
TO
1445 $paymentParams['contributionPageID'] = $contribution->contribution_page_id;
1446 $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id;
1447 $ids['contribution'] = $contribution->id;
1448 $params['contribution_recur_id'] = $paymentParams['contributionRecurID'];
6a488035
TO
1449 }
1450
1451 if ($params['total_amount'] > 0.0) {
ab30e033 1452 $payment = $this->_paymentProcessor['object'];
06d062ce
EM
1453 try {
1454 $result = $payment->doPayment($paymentParams);
ccb02c2d 1455 $formValues = array_merge($formValues, $result);
ab30e033
EM
1456 // Assign amount to template if payment was successful.
1457 $this->assign('amount', $params['total_amount']);
6a488035 1458 }
06d062ce
EM
1459 catch (PaymentProcessorException $e) {
1460 if (!empty($paymentParams['contributionID'])) {
ab30e033
EM
1461 CRM_Contribute_BAO_Contribution::failPayment($paymentParams['contributionID'], $this->_contactID,
1462 $e->getMessage());
06d062ce
EM
1463 }
1464 if (!empty($paymentParams['contributionRecurID'])) {
1465 CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']);
1466 }
1467
1468 CRM_Core_Error::displaySessionError($result);
1469 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view/membership',
1470 "reset=1&action=add&cid={$this->_contactID}&context=&mode={$this->_mode}"
1471 ));
6a488035 1472
06d062ce 1473 }
6a488035
TO
1474 }
1475
ccb02c2d 1476 if ($formValues['payment_status_id'] != array_search('Completed', $allContributionStatus)) {
7d193e45
LS
1477 $params['status_id'] = array_search('Pending', $allMemberStatus);
1478 $params['skipStatusCal'] = TRUE;
1479 // unset send-receipt option, since receipt will be sent when ipn is received.
ccb02c2d 1480 unset($formValues['send_receipt'], $formValues['send_receipt']);
7d193e45
LS
1481 //as membership is pending set dates to null.
1482 $memberDates = array(
1483 'join_date' => 'joinDate',
1484 'start_date' => 'startDate',
1485 'end_date' => 'endDate',
1486 );
2e8b13d1 1487 foreach ($memberDates as $dv) {
7d193e45
LS
1488 $$dv = NULL;
1489 foreach ($this->_memTypeSelected as $memType) {
1490 $membershipTypeValues[$memType][$dv] = NULL;
1491 }
1492 }
1493 }
09108d7d 1494 $now = date('YmdHis');
6a488035 1495 $params['receive_date'] = $now;
ccb02c2d 1496 $params['invoice_id'] = $formValues['invoiceID'];
6a488035
TO
1497 $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)',
1498 array(1 => $membershipType, 2 => $userName)
1499 );
1500 $params['source'] = $formValues['source'] ? $formValues['source'] : $params['contribution_source'];
1501 $params['trxn_id'] = CRM_Utils_Array::value('trxn_id', $result);
1502 $params['payment_instrument_id'] = 1;
1503 $params['is_test'] = ($this->_mode == 'live') ? 0 : 1;
ccb02c2d 1504 if (!empty($formValues['send_receipt'])) {
6a488035
TO
1505 $params['receipt_date'] = $now;
1506 }
1507 else {
1508 $params['receipt_date'] = NULL;
1509 }
1510
ccb02c2d 1511 $this->set('params', $formValues);
6a488035
TO
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);
87d0f881 1530 //CRM-15366
396e62d8 1531 if (!empty($softParams) && empty($paymentParams['is_recur'])) {
1532 $membershipParams['soft_credit'] = $softParams;
1533 }
77623a96 1534 if (isset($result['fee_amount'])) {
1535 $membershipParams['fee_amount'] = $result['fee_amount'];
1536 }
8a7b41d1
EM
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);
ccb02c2d 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
6a488035 1550 $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids);
3e228d81
PN
1551 $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams);
1552 unset($params['lineItems']);
6a488035
TO
1553 $this->_membershipIDs[] = $membership->id;
1554 $createdMemberships[$memType] = $membership;
1555 $count++;
1556 }
1557
6a488035
TO
1558 }
1559 else {
1560 $params['action'] = $this->_action;
8cc574cf 1561 if ($this->_onlinePendingContributionId && !empty($formValues['record_contribution'])) {
6a488035
TO
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);
8cc574cf 1568 if (!empty($result) && !empty($params['contribution_id'])) {
6a488035 1569 $lineItem = array();
7524682e 1570 $lineItems = CRM_Price_BAO_LineItem::getLineItems($params['contribution_id'], 'contribution', NULL, TRUE, TRUE);
b11c92be 1571 $itemId = key($lineItems);
1572 $priceSetId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id');
4aa7d844 1573
6a488035
TO
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;
8aa7457a
EM
1578 $contributionBAO = new CRM_Contribute_BAO_Contribution();
1579 $contributionBAO->id = $params['contribution_id'];
7524682e 1580 $contributionBAO->contact_id = $params['contact_id'];
8aa7457a
EM
1581 $contributionBAO->find();
1582 CRM_Price_BAO_LineItem::processPriceSet($params['contribution_id'], $lineItem, $contributionBAO, 'civicrm_membership');
133e2c99 1583
1584 //create new soft-credit record, CRM-13981
00c1cd97
CW
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);
133e2c99 1592 }
6a488035
TO
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
b11c92be 1605 if (!in_array($membership->status_id, array(
7ff60806
PN
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()),
b11c92be 1609 ))
1610 ) {
6a488035
TO
1611 $cancelled = FALSE;
1612 }
1613 }
1614 // suppress form values in template.
1615 $this->assign('cancelled', $cancelled);
1616
6a488035
TO
1617 $createdMemberships[] = $membership;
1618 }
1619 else {
1620 $count = 0;
1621 foreach ($this->_memTypeSelected as $memType) {
8cc574cf 1622 if ($count && !empty($formValues['record_contribution']) &&
6a488035
TO
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]);
a7488080 1629 if (!empty($formValues['int_amount'])) {
6a488035 1630 $init_amount = array();
b11c92be 1631 foreach ($formValues as $key => $value) {
1632 if (strstr($key, 'txt-price')) {
6a488035
TO
1633 $init_amount[$key] = $value;
1634 }
1635 }
1636 $membershipParams['init_amount'] = $init_amount;
1637 }
d80dbc14 1638
1639 if (!empty($softParams)) {
1640 $membershipParams['soft_credit'] = $softParams;
1641 }
1642
6a488035 1643 $membership = CRM_Member_BAO_Membership::create($membershipParams, $ids);
3e228d81 1644 $params['contribution'] = CRM_Utils_Array::value('contribution', $membershipParams);
d5b95619 1645 unset($params['lineItems']);
6a488035
TO
1646
1647 $this->_membershipIDs[] = $membership->id;
1648 $createdMemberships[$memType] = $membership;
1649 $count++;
1650 }
1651 }
1652 }
1653
ccb02c2d 1654 if (!empty($lineItem[$this->_priceSetId])) {
aaffa79f 1655 $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
03b412ae 1656 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
01604562 1657 $taxAmount = FALSE;
79d001a2 1658 $totalTaxAmount = 0;
ccb02c2d 1659 foreach ($lineItem[$this->_priceSetId] as & $priceFieldOp) {
a7488080 1660 if (!empty($priceFieldOp['membership_type_id'])) {
3b85fc04
PN
1661 $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') : '-';
1662 $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
1663 }
1664 else {
1665 $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A';
1666 }
03b412ae 1667 if ($invoicing && isset($priceFieldOp['tax_amount'])) {
01604562 1668 $taxAmount = TRUE;
79d001a2
PB
1669 $totalTaxAmount += $priceFieldOp['tax_amount'];
1670 }
1671 }
03b412ae
PB
1672 if ($invoicing) {
1673 $dataArray = array();
ccb02c2d 1674 foreach ($lineItem[$this->_priceSetId] as $key => $value) {
03b412ae
PB
1675 if (isset($value['tax_amount']) && isset($value['tax_rate'])) {
1676 if (isset($dataArray[$value['tax_rate']])) {
1677 $dataArray[$value['tax_rate']] = $dataArray[$value['tax_rate']] + CRM_Utils_Array::value('tax_amount', $value);
0db6c3e1
TO
1678 }
1679 else {
03b412ae
PB
1680 $dataArray[$value['tax_rate']] = CRM_Utils_Array::value('tax_amount', $value);
1681 }
0e81467c 1682 }
03b412ae 1683 }
01604562
PB
1684 if ($taxAmount) {
1685 $this->assign('totalTaxAmount', $totalTaxAmount);
1686 $this->assign('taxTerm', CRM_Utils_Array::value('tax_term', $invoiceSettings));
1687 }
03b412ae 1688 $this->assign('dataArray', $dataArray);
6a488035
TO
1689 }
1690 }
1691 $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ? $lineItem : FALSE);
1692
1693 $receiptSend = FALSE;
7d193e45
LS
1694 $contributionId = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id);
1695 $membershipIds = $this->_membershipIDs;
1696 if ($contributionId && !empty($membershipIds)) {
1697 $contributionDetails = CRM_Contribute_BAO_Contribution::getContributionDetails(
1698 CRM_Export_Form_Select::MEMBER_EXPORT, $this->_membershipIDs);
1699 if ($contributionDetails[$membership->id]['contribution_status'] == 'Completed') {
aadd21c2 1700 $receiptSend = TRUE;
7d193e45
LS
1701 }
1702 }
6a488035 1703
7d193e45 1704 if (!empty($formValues['send_receipt']) && $receiptSend) {
b11c92be 1705 $formValues['contact_id'] = $this->_contactID;
7d193e45 1706 $formValues['contribution_id'] = $contributionId;
186a737c
EM
1707 // We really don't need a distinct receipt_text_signup vs receipt_text_renewal as they are
1708 // handled in the receipt. But by setting one we avoid breaking templates for now
1709 // although at some point we should switch in the templates.
1710 $formValues['receipt_text_signup'] = $formValues['receipt_text'];
6a488035 1711 // send email receipt
09108d7d 1712 $this->assignBillingName();
b11c92be 1713 $mailSend = self::emailReceipt($this, $formValues, $membership);
6a488035 1714 }
6a488035 1715
7865d848
EM
1716 // finally set membership id if already not set
1717 if (!$this->_id) {
1718 $this->_id = $membership->id;
6a488035 1719 }
6a488035 1720
5b217d3f 1721 $isRecur = CRM_Utils_Array::value('is_recur', $params);
1722 $this->setStatusMessage($membership, $endDate, $receiptSend, $membershipTypes, $createdMemberships, $isRecur, $calcDates, $mailSend);
7865d848
EM
1723 return $createdMemberships;
1724 }
1725
5e56c7a5 1726 /**
1727 * Set context in session.
1728 */
7865d848 1729 protected function setUserContext() {
6a488035 1730 $buttonName = $this->controller->getButtonName();
7865d848
EM
1731 $session = CRM_Core_Session::singleton();
1732
6a488035
TO
1733 if ($this->_context == 'standalone') {
1734 if ($buttonName == $this->getButtonName('upload', 'new')) {
1735 $session->replaceUserContext(CRM_Utils_System::url('civicrm/member/add',
b11c92be 1736 'reset=1&action=add&context=standalone'
1737 ));
6a488035
TO
1738 }
1739 else {
1740 $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view',
b11c92be 1741 "reset=1&cid={$this->_contactID}&selectedChild=member"
1742 ));
6a488035
TO
1743 }
1744 }
1745 elseif ($buttonName == $this->getButtonName('upload', 'new')) {
1746 $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/membership',
b11c92be 1747 "reset=1&action=add&context=membership&cid={$this->_contactID}"
1748 ));
6a488035
TO
1749 }
1750 }
1751
1752 /**
5e56c7a5 1753 * Get status message for updating membership.
1754 *
1755 * @param CRM_Member_BAO_Membership $membership
1756 * @param string $endDate
1757 * @param bool $receiptSend
1758 *
7865d848 1759 * @return string
6a488035 1760 */
7865d848 1761 protected function getStatusMessageForUpdate($membership, $endDate, $receiptSend) {
5e56c7a5 1762 // End date can be modified by hooks, so if end date is set then use it.
7865d848 1763 $endDate = ($membership->end_date) ? $membership->end_date : $endDate;
6a488035 1764
7865d848
EM
1765 $statusMsg = ts('Membership for %1 has been updated.', array(1 => $this->_memberDisplayName));
1766 if ($endDate && $endDate !== 'null') {
1767 $endDate = CRM_Utils_Date::customFormat($endDate);
1768 $statusMsg .= ' ' . ts('The membership End Date is %1.', array(1 => $endDate));
6a488035
TO
1769 }
1770
7865d848
EM
1771 if ($receiptSend) {
1772 $statusMsg .= ' ' . ts('A confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail));
6a488035 1773 }
7865d848
EM
1774 return $statusMsg;
1775 }
6a488035 1776
7865d848 1777 /**
5e56c7a5 1778 * Get status message for create action.
1779 *
1780 * @param string $endDate
1781 * @param bool $receiptSend
1782 * @param array $membershipTypes
1783 * @param array $createdMemberships
5b217d3f 1784 * @param bool $isRecur
5e56c7a5 1785 * @param array $calcDates
1786 * @param bool $mailSent
1787 *
7865d848
EM
1788 * @return array|string
1789 */
1790 protected function getStatusMessageForCreate($endDate, $receiptSend, $membershipTypes, $createdMemberships,
5b217d3f 1791 $isRecur, $calcDates, $mailSent) {
7865d848
EM
1792 // FIX ME: fix status messages
1793
1794 $statusMsg = array();
1795 foreach ($membershipTypes as $memType => $membershipType) {
1796 $statusMsg[$memType] = ts('%1 membership for %2 has been added.', array(
1797 1 => $membershipType,
1798 2 => $this->_memberDisplayName,
1799 ));
6a488035 1800
7865d848
EM
1801 $membership = $createdMemberships[$memType];
1802 $memEndDate = ($membership->end_date) ? $membership->end_date : $endDate;
6a488035 1803
7865d848 1804 //get the end date from calculated dates.
5b217d3f 1805 if (!$memEndDate && !$isRecur) {
7865d848 1806 $memEndDate = CRM_Utils_Array::value('end_date', $calcDates[$memType]);
35fa23f8 1807 }
6a488035 1808
7865d848
EM
1809 if ($memEndDate && $memEndDate !== 'null') {
1810 $memEndDate = CRM_Utils_Date::customFormat($memEndDate);
1811 $statusMsg[$memType] .= ' ' . ts('The new membership End Date is %1.', array(1 => $memEndDate));
b11c92be 1812 }
6a488035 1813 }
7865d848 1814 $statusMsg = implode('<br/>', $statusMsg);
f85b8063 1815 if ($receiptSend && !empty($mailSent)) {
7865d848 1816 $statusMsg .= ' ' . ts('A membership confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail));
bf1f1351 1817 }
7865d848 1818 return $statusMsg;
6a488035 1819 }
96025800 1820
5b217d3f 1821 /**
1822 * @param $membership
1823 * @param $endDate
1824 * @param $receiptSend
1825 * @param $membershipTypes
1826 * @param $createdMemberships
1827 * @param $isRecur
1828 * @param $calcDates
1829 * @param $mailSend
1830 */
1831 protected function setStatusMessage($membership, $endDate, $receiptSend, $membershipTypes, $createdMemberships, $isRecur, $calcDates, $mailSend) {
1832 $statusMsg = '';
1833 if (($this->_action & CRM_Core_Action::UPDATE)) {
1834 $statusMsg = $this->getStatusMessageForUpdate($membership, $endDate, $receiptSend);
1835 }
1836 elseif (($this->_action & CRM_Core_Action::ADD)) {
1837 $statusMsg = $this->getStatusMessageForCreate($endDate, $receiptSend, $membershipTypes, $createdMemberships,
1838 $isRecur, $calcDates, $mailSend);
1839 }
1840
1841 CRM_Core_Session::setStatus($statusMsg, ts('Complete'), 'success');
1842 //CRM-15187
1843 // display message when membership type is changed
1844 if (($this->_action & CRM_Core_Action::UPDATE) && $this->_id && !in_array($this->_memType, $this->_memTypeSelected)) {
0fedbc88
PN
1845 $lineItem = CRM_Price_BAO_LineItem::getLineItems($this->_id, 'membership');
1846 $maxID = max(array_keys($lineItem));
1847 $lineItem = $lineItem[$maxID];
1848 $membershipTypeDetails = $this->allMembershipTypeDetails[$membership->membership_type_id];
1849 if ($membershipTypeDetails['financial_type_id'] != $lineItem['financial_type_id']) {
1850 CRM_Core_Session::setStatus(
1851 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.'),
1852 ts('Warning')
1853 );
1854 }
1855 if ($membershipTypeDetails['minimum_fee'] != $lineItem['line_total']) {
1856 CRM_Core_Session::setStatus(
1857 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.'),
1858 ts('Warning')
1859 );
1860 }
5b217d3f 1861 }
1862 }
1863
6a488035 1864}