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