3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2015
35 * This class generates form components for offline membership form.
37 class CRM_Member_Form_Membership
extends CRM_Member_Form
{
39 protected $_memType = NULL;
41 protected $_onlinePendingContributionId;
45 public $_contributeMode = 'direct';
47 protected $_recurMembershipTypes;
49 protected $_memTypeSelected;
52 * Display name of the member.
56 protected $_memberDisplayName = NULL;
59 * email of the person paying for the membership (used for receipts)
61 protected $_memberEmail = NULL;
64 * Contact ID of the member.
68 public $_contactID = NULL;
71 * Display name of the person paying for the membership (used for receipts)
75 protected $_contributorDisplayName = NULL;
78 * email of the person paying for the membership (used for receipts)
80 protected $_contributorEmail = NULL;
83 * email of the person paying for the membership (used for receipts)
87 protected $_contributorContactID = NULL;
90 * ID of the person the receipt is to go to.
94 protected $_receiptContactId = NULL;
97 * Keep a class variable for ALL membership IDs so
98 * postProcess hook function can do something with it
102 protected $_membershipIDs = array();
105 * An array to hold a list of date fields on the form
106 * so that they can be converted to ISO in a consistent manner
110 protected $_dateFields = array(
111 'receive_date' => array('default' => 'now'),
115 * Get selected membership type from the form values.
117 * @param array $priceSet
118 * @param array $params
122 public static function getSelectedMemberships($priceSet, $params) {
123 $memTypeSelected = array();
124 $priceFieldIDS = self
::getPriceFieldIDs($params, $priceSet);
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]);
129 foreach ($priceFieldIDS as $priceFieldId) {
130 if ($id = CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceFieldValue', $priceFieldId, 'membership_type_id')) {
131 $memTypeSelected[$id] = $id;
135 return $memTypeSelected;
139 * Extract price set fields and values from $params.
141 * @param array $params
142 * @param array $priceSet
146 public static function getPriceFieldIDs($params, $priceSet) {
147 $priceFieldIDS = $fieldIds = array();
148 if (isset($priceSet['fields']) && is_array($priceSet['fields'])) {
149 $fieldIds = array_keys($priceSet['fields']);
150 foreach ($fieldIds as $fieldId) {
151 if (!empty($params['price_' . $fieldId])) {
152 if (is_array($params['price_' . $fieldId])) {
153 foreach ($params['price_' . $fieldId] as $priceFldVal => $isSet) {
155 $priceFieldIDS[] = $priceFldVal;
160 $priceFieldIDS[] = $params['price_' . $fieldId];
165 return $priceFieldIDS;
169 * Form preProcess function.
173 public function preProcess() {
174 // This string makes up part of the class names, differentiating them (not sure why) from the membership fields.
175 $this->assign('formClass', 'membership');
176 parent
::preProcess();
178 $this->_priceSetId
= CRM_Utils_Array
::value('priceSetId', $_GET);
179 $this->set('priceSetId', $this->_priceSetId
);
180 $this->assign('priceSetId', $this->_priceSetId
);
182 if ($this->_action
& CRM_Core_Action
::DELETE
) {
183 $contributionID = CRM_Member_BAO_Membership
::getMembershipContributionId($this->_id
);
184 // check delete permission for contribution
185 if ($this->_id
&& $contributionID && !CRM_Core_Permission
::checkActionPermission('CiviContribute', $this->_action
)) {
186 CRM_Core_Error
::fatal(ts("This Membership is linked to a contribution. You must have 'delete in CiviContribute' permission in order to delete this record."));
190 if ($this->_action
& CRM_Core_Action
::ADD
) {
191 if (!CRM_Member_BAO_Membership
::statusAvailabilty($this->_contactID
)) {
192 // all possible statuses are disabled - redirect back to contact form
193 CRM_Core_Error
::statusBounce(ts('There are no configured membership statuses. You cannot add this membership until your membership statuses are correctly configured'));
195 if ($this->_contactID
) {
196 //check whether contact has a current membership so we can alert user that they may want to do a renewal instead
197 $contactMemberships = array();
198 $memParams = array('contact_id' => $this->_contactID
);
199 CRM_Member_BAO_Membership
::getValues($memParams, $contactMemberships, TRUE);
200 $cMemTypes = array();
201 foreach ($contactMemberships as $mem) {
202 $cMemTypes[] = $mem['membership_type_id'];
204 if (count($cMemTypes) > 0) {
205 $memberorgs = CRM_Member_BAO_MembershipType
::getMemberOfContactByMemTypes($cMemTypes);
206 $mems_by_org = array();
207 foreach ($contactMemberships as $mem) {
208 $mem['member_of_contact_id'] = CRM_Utils_Array
::value($mem['membership_type_id'], $memberorgs);
209 if (!empty($mem['membership_end_date'])) {
210 $mem['membership_end_date'] = CRM_Utils_Date
::customformat($mem['membership_end_date']);
212 $mem['membership_type'] = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipType',
213 $mem['membership_type_id'],
216 $mem['membership_status'] = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipStatus',
220 $mem['renewUrl'] = CRM_Utils_System
::url('civicrm/contact/view/membership',
221 "reset=1&action=renew&cid={$this->_contactID}&id={$mem['id']}&context=membership&selectedChild=member"
222 . ($this->_mode ?
'&mode=live' : '')
224 $mem['membershipTab'] = CRM_Utils_System
::url('civicrm/contact/view',
225 "reset=1&force=1&cid={$this->_contactID}&selectedChild=member"
227 $mems_by_org[$mem['member_of_contact_id']] = $mem;
229 $this->assign('existingContactMemberships', $mems_by_org);
233 // In standalone mode we don't have a contact id yet so lookup will be done client-side with this script:
234 $resources = CRM_Core_Resources
::singleton();
235 $resources->addScriptFile('civicrm', 'templates/CRM/Member/Form/MembershipStandalone.js');
237 'typeorgs' => CRM_Member_BAO_MembershipType
::getMembershipTypeOrganization(),
238 'memtypes' => CRM_Core_PseudoConstant
::get('CRM_Member_BAO_Membership', 'membership_type_id'),
239 'statuses' => CRM_Core_PseudoConstant
::get('CRM_Member_BAO_Membership', 'status_id'),
241 $resources->addSetting(array('existingMems' => $passthru));
245 // when custom data is included in this page
246 if (!empty($_POST['hidden_custom'])) {
247 CRM_Custom_Form_CustomData
::preProcess($this);
248 CRM_Custom_Form_CustomData
::buildQuickForm($this);
249 CRM_Custom_Form_CustomData
::setDefaultValues($this);
252 // CRM-4395, get the online pending contribution id.
253 $this->_onlinePendingContributionId
= NULL;
254 if (!$this->_mode
&& $this->_id
&& ($this->_action
& CRM_Core_Action
::UPDATE
)) {
255 $this->_onlinePendingContributionId
= CRM_Contribute_BAO_Contribution
::checkOnlinePendingContribution($this->_id
,
259 $this->assign('onlinePendingContributionId', $this->_onlinePendingContributionId
);
261 $this->setPageTitle(ts('Membership'));
265 * Set default values for the form.
267 public function setDefaultValues() {
269 if ($this->_priceSetId
) {
270 return CRM_Price_BAO_PriceSet
::setDefaultPriceSet($this, $defaults);
273 $defaults = parent
::setDefaultValues();
275 //setting default join date and receive date
276 list($now, $currentTime) = CRM_Utils_Date
::setDateDefaults();
277 if ($this->_action
== CRM_Core_Action
::ADD
) {
278 $defaults['receive_date'] = $now;
279 $defaults['receive_date_time'] = $currentTime;
282 if (is_numeric($this->_memType
)) {
283 $defaults['membership_type_id'] = array();
284 $defaults['membership_type_id'][0] = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipType',
286 'member_of_contact_id',
289 $defaults['membership_type_id'][1] = $this->_memType
;
292 $defaults['membership_type_id'] = $this->_memType
;
295 $defaults['num_terms'] = 1;
297 if (!empty($defaults['id'])) {
298 if ($this->_onlinePendingContributionId
) {
299 $defaults['record_contribution'] = $this->_onlinePendingContributionId
;
302 $contributionId = CRM_Core_DAO
::singleValueQuery("
303 SELECT contribution_id
304 FROM civicrm_membership_payment
305 WHERE membership_id = $this->_id
306 ORDER BY contribution_id
309 if ($contributionId) {
310 $defaults['record_contribution'] = $contributionId;
315 //set Soft Credit Type to Gift by default
316 $scTypes = CRM_Core_OptionGroup
::values("soft_credit_type");
317 $defaults['soft_credit_type_id'] = CRM_Utils_Array
::value(ts('Gift'), array_flip($scTypes));
319 if (!empty($defaults['record_contribution']) && !$this->_mode
) {
320 $contributionParams = array('id' => $defaults['record_contribution']);
321 $contributionIds = array();
323 //keep main object campaign in hand.
324 $memberCampaignId = CRM_Utils_Array
::value('campaign_id', $defaults);
326 CRM_Contribute_BAO_Contribution
::getValues($contributionParams, $defaults, $contributionIds);
328 //get back original object campaign id.
329 $defaults['campaign_id'] = $memberCampaignId;
331 if (!empty($defaults['receive_date'])) {
332 list($defaults['receive_date']) = CRM_Utils_Date
::setDateDefaults($defaults['receive_date']);
335 // Contribution::getValues() over-writes the membership record's source field value - so we need to restore it.
336 if (!empty($defaults['membership_source'])) {
337 $defaults['source'] = $defaults['membership_source'];
341 if (empty($defaults['payment_instrument_id'])) {
342 $defaults['payment_instrument_id'] = key(CRM_Core_OptionGroup
::values('payment_instrument', FALSE, FALSE, FALSE, 'AND is_default = 1'));
345 // User must explicitly choose to send a receipt in both add and update mode.
346 $defaults['send_receipt'] = 0;
348 if ($this->_action
& CRM_Core_Action
::UPDATE
) {
349 // in this mode by default uncheck this checkbox
350 unset($defaults['record_contribution']);
353 $subscriptionCancelled = FALSE;
354 if (!empty($defaults['id'])) {
355 $subscriptionCancelled = CRM_Member_BAO_Membership
::isSubscriptionCancelled($this->_id
);
358 $alreadyAutoRenew = FALSE;
359 if (!empty($defaults['contribution_recur_id']) && !$subscriptionCancelled) {
360 $defaults['auto_renew'] = 1;
361 $alreadyAutoRenew = TRUE;
363 $this->assign('alreadyAutoRenew', $alreadyAutoRenew);
365 $this->assign('member_is_test', CRM_Utils_Array
::value('member_is_test', $defaults));
367 $this->assign('membership_status_id', CRM_Utils_Array
::value('status_id', $defaults));
369 if (!empty($defaults['is_pay_later'])) {
370 $this->assign('is_pay_later', TRUE);
373 $defaults = $this->getBillingDefaults($defaults);
374 // hack to simplify credit card entry for testing
375 // $defaults['credit_card_type'] = 'Visa';
376 // $defaults['credit_card_number'] = '4807731747657838';
377 // $defaults['cvv2'] = '000';
378 // $defaults['credit_card_exp_date'] = array( 'Y' => '2012', 'M' => '05' );
381 $dates = array('join_date', 'start_date', 'end_date');
382 foreach ($dates as $key) {
383 if (!empty($defaults[$key])) {
384 list($defaults[$key]) = CRM_Utils_Date
::setDateDefaults(CRM_Utils_Array
::value($key, $defaults));
388 //setting default join date if there is no join date
389 if (empty($defaults['join_date'])) {
390 $defaults['join_date'] = $now;
393 if (!empty($defaults['membership_end_date'])) {
394 $this->assign('endDate', $defaults['membership_end_date']);
401 * Build the form object.
403 public function buildQuickForm() {
405 $this->assign('taxRates', json_encode(CRM_Core_PseudoConstant
::getTaxRates()));
407 $this->assign('currency', CRM_Core_Config
::singleton()->defaultCurrencySymbol
);
408 $invoiceSettings = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::CONTRIBUTE_PREFERENCES_NAME
, 'contribution_invoice_settings');
409 $invoicing = CRM_Utils_Array
::value('invoicing', $invoiceSettings);
410 if (isset($invoicing)) {
411 $this->assign('taxTerm', CRM_Utils_Array
::value('tax_term', $invoiceSettings));
413 // build price set form.
414 $buildPriceSet = FALSE;
415 if ($this->_priceSetId ||
!empty($_POST['price_set_id'])) {
416 if (!empty($_POST['price_set_id'])) {
417 $buildPriceSet = TRUE;
419 $getOnlyPriceSetElements = TRUE;
420 if (!$this->_priceSetId
) {
421 $this->_priceSetId
= $_POST['price_set_id'];
422 $getOnlyPriceSetElements = FALSE;
425 $this->set('priceSetId', $this->_priceSetId
);
426 CRM_Price_BAO_PriceSet
::buildPriceSet($this);
428 $optionsMembershipTypes = array();
429 foreach ($this->_priceSet
['fields'] as $pField) {
430 if (empty($pField['options'])) {
433 foreach ($pField['options'] as $opId => $opValues) {
434 $optionsMembershipTypes[$opId] = CRM_Utils_Array
::value('membership_type_id', $opValues, 0);
438 $this->assign('autoRenewOption', CRM_Price_BAO_PriceSet
::checkAutoRenewForPriceSet($this->_priceSetId
));
440 $this->assign('optionsMembershipTypes', $optionsMembershipTypes);
441 $this->assign('contributionType', CRM_Utils_Array
::value('financial_type_id', $this->_priceSet
));
443 // get only price set form elements.
444 if ($getOnlyPriceSetElements) {
449 // use to build form during form rule.
450 $this->assign('buildPriceSet', $buildPriceSet);
452 if ($this->_action
& CRM_Core_Action
::ADD
) {
453 $buildPriceSet = FALSE;
454 $priceSets = CRM_Price_BAO_PriceSet
::getAssoc(FALSE, 'CiviMember');
455 if (!empty($priceSets)) {
456 $buildPriceSet = TRUE;
459 if ($buildPriceSet) {
460 $this->add('select', 'price_set_id', ts('Choose price set'),
462 '' => ts('Choose price set'),
464 NULL, array('onchange' => "buildAmount( this.value );")
467 $this->assign('hasPriceSets', $buildPriceSet);
470 //need to assign custom data type and subtype to the template
471 $this->assign('customDataType', 'Membership');
472 $this->assign('customDataSubType', $this->_memType
);
473 $this->assign('entityID', $this->_id
);
475 if ($this->_action
& CRM_Core_Action
::DELETE
) {
476 $this->addButtons(array(
479 'name' => ts('Delete'),
480 'spacing' => ' ',
485 'name' => ts('Cancel'),
492 if ($this->_context
== 'standalone') {
493 $this->addEntityRef('contact_id', ts('Contact'), array(
495 'api' => array('extra' => array('email')),
499 $selOrgMemType[0][0] = $selMemTypeOrg[0] = ts('- select -');
501 // retrieve all memberships
502 $allMembershipInfo = array();
503 foreach ($this->allMembershipTypeDetails
as $key => $values) {
504 if ($this->_mode
&& empty($values['minimum_fee'])) {
508 $memberOfContactId = CRM_Utils_Array
::value('member_of_contact_id', $values);
509 if (empty($selMemTypeOrg[$memberOfContactId])) {
510 $selMemTypeOrg[$memberOfContactId] = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
516 $selOrgMemType[$memberOfContactId][0] = ts('- select -');
518 if (empty($selOrgMemType[$memberOfContactId][$key])) {
519 $selOrgMemType[$memberOfContactId][$key] = CRM_Utils_Array
::value('name', $values);
523 // build membership info array, which is used when membership type is selected to:
524 // - set the payment information block
525 // - set the max related block
526 $allMembershipInfo[$key] = array(
527 'financial_type_id' => CRM_Utils_Array
::value('financial_type_id', $values),
528 'total_amount' => CRM_Utils_Money
::format($values['minimum_fee'], NULL, '%a'),
529 'total_amount_numeric' => CRM_Utils_Array
::value('minimum_fee', $values),
530 'auto_renew' => CRM_Utils_Array
::value('auto_renew', $values),
531 'has_related' => isset($values['relationship_type_id']),
532 'max_related' => CRM_Utils_Array
::value('max_related', $values),
536 $this->assign('allMembershipInfo', json_encode($allMembershipInfo));
538 // show organization by default, if only one organization in
540 if (count($selMemTypeOrg) == 2) {
541 unset($selMemTypeOrg[0], $selOrgMemType[0][0]);
543 //sort membership organization and type, CRM-6099
544 natcasesort($selMemTypeOrg);
545 foreach ($selOrgMemType as $index => $orgMembershipType) {
546 natcasesort($orgMembershipType);
547 $selOrgMemType[$index] = $orgMembershipType;
551 'onChange' => "buildMaxRelated(this.value,true); CRM.buildCustomData('Membership', this.value);",
554 if (!empty($this->_recurPaymentProcessors
)) {
555 $memTypeJs['onChange'] = "" . $memTypeJs['onChange'] . 'buildAutoRenew(this.value, null);';
558 $this->add('text', 'max_related', ts('Max related'),
559 CRM_Core_DAO
::getAttribute('CRM_Member_DAO_Membership', 'max_related')
562 $sel = &$this->addElement('hierselect',
563 'membership_type_id',
564 ts('Membership Organization and Type'),
568 $sel->setOptions(array($selMemTypeOrg, $selOrgMemType));
574 $this->applyFilter('__ALL__', 'trim');
576 if ($this->_action
& CRM_Core_Action
::ADD
) {
577 $this->add('text', 'num_terms', ts('Number of Terms'), array('size' => 6));
580 $this->addDate('join_date', ts('Member Since'), FALSE, array('formatType' => 'activityDate'));
581 $this->addDate('start_date', ts('Start Date'), FALSE, array('formatType' => 'activityDate'));
582 $endDate = $this->addDate('end_date', ts('End Date'), FALSE, array('formatType' => 'activityDate'));
584 $elements[] = $endDate;
587 $this->add('text', 'source', ts('Source'),
588 CRM_Core_DAO
::getAttribute('CRM_Member_DAO_Membership', 'source')
591 //CRM-7362 --add campaigns.
594 $campaignId = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_Membership', $this->_id
, 'campaign_id');
596 CRM_Campaign_BAO_Campaign
::addCampaign($this, $campaignId);
599 $this->add('select', 'status_id', ts('Membership Status'),
600 array('' => ts('- select -')) + CRM_Member_PseudoConstant
::membershipStatus(NULL, NULL, 'label')
602 $statusOverride = $this->addElement('checkbox', 'is_override',
603 ts('Status Override?'), NULL,
604 array('onClick' => 'showHideMemberStatus()')
606 if ($statusOverride) {
607 $elements[] = $statusOverride;
610 $this->addElement('checkbox', 'record_contribution', ts('Record Membership Payment?'));
612 $this->add('text', 'total_amount', ts('Amount'));
613 $this->addRule('total_amount', ts('Please enter a valid amount.'), 'money');
615 $this->addDate('receive_date', ts('Received'), FALSE, array('formatType' => 'activityDateTime'));
617 $this->add('select', 'payment_instrument_id',
618 ts('Payment Method'),
619 array('' => ts('- select -')) + CRM_Contribute_PseudoConstant
::paymentInstrument(),
620 FALSE, array('onChange' => "return showHideByValue('payment_instrument_id','4','checkNumber','table-row','select',false);")
622 $this->add('text', 'trxn_id', ts('Transaction ID'));
623 $this->addRule('trxn_id', ts('Transaction ID already exists in Database.'),
624 'objectExists', array(
625 'CRM_Contribute_DAO_Contribution',
631 $allowStatuses = array();
632 $statuses = CRM_Contribute_PseudoConstant
::contributionStatus();
633 if ($this->_onlinePendingContributionId
) {
634 $statusNames = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
635 foreach ($statusNames as $val => $name) {
636 if (in_array($name, array(
643 $allowStatuses[$val] = $statuses[$val];
647 $allowStatuses = $statuses;
649 $this->add('select', 'contribution_status_id',
650 ts('Payment Status'), $allowStatuses
652 $this->add('text', 'check_number', ts('Check Number'),
653 CRM_Core_DAO
::getAttribute('CRM_Contribute_DAO_Contribution', 'check_number')
657 //add field for amount to allow an amount to be entered that differs from minimum
658 $this->add('text', 'total_amount', ts('Amount'));
660 $this->add('select', 'financial_type_id',
661 ts('Financial Type'),
662 array('' => ts('- select -')) + CRM_Contribute_PseudoConstant
::financialType()
665 $this->addElement('checkbox', 'is_different_contribution_contact', ts('Record Payment from a Different Contact?'));
667 $this->addSelect('soft_credit_type_id', array('entity' => 'contribution_soft'));
668 $this->addEntityRef('soft_credit_contact_id', ts('Payment From'), array('create' => TRUE));
670 $this->addElement('checkbox',
672 ts('Send Confirmation and Receipt?'), NULL,
673 array('onclick' => "showHideByValue( 'send_receipt', '', 'notice', 'table-row', 'radio', false); showHideByValue( 'send_receipt', '', 'fromEmail', 'table-row', 'radio', false);")
676 $this->add('select', 'from_email_address', ts('Receipt From'), $this->_fromEmails
);
678 $this->add('textarea', 'receipt_text', ts('Receipt Message'));
680 // Retrieve the name and email of the contact - this will be the TO for receipt email
681 if ($this->_contactID
) {
682 list($this->_memberDisplayName
,
684 ) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($this->_contactID
);
686 $this->assign('emailExists', $this->_memberEmail
);
687 $this->assign('displayName', $this->_memberDisplayName
);
691 if ($this->_action
& CRM_Core_Action
::UPDATE
) {
692 $recurContributionId = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_Membership', $this->_id
,
693 'contribution_recur_id'
695 if ($recurContributionId && !CRM_Member_BAO_Membership
::isSubscriptionCancelled($this->_id
)) {
697 if (CRM_Member_BAO_Membership
::isCancelSubscriptionSupported($this->_id
)) {
698 $this->assign('cancelAutoRenew',
699 CRM_Utils_System
::url('civicrm/contribute/unsubscribe', "reset=1&mid={$this->_id}")
702 foreach ($elements as $elem) {
707 $this->assign('isRecur', $isRecur);
709 $this->addFormRule(array('CRM_Member_Form_Membership', 'formRule'), $this);
711 $this->assign('outBound_option', CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::MAILING_PREFERENCES_NAME
,
714 parent
::buildQuickForm();
720 * @param array $params
721 * (ref.) an assoc array of name/value pairs.
723 * @param array $files
724 * @param CRM_Member_Form_Membership $self
726 * @throws CiviCRM_API3_Exception
728 * mixed true or array of errors
730 public static function formRule($params, $files, $self) {
733 $priceSetId = self
::getPriceSetID($params);
734 $priceSetDetails = self
::getPriceSetDetails($params);
736 $selectedMemberships = self
::getSelectedMemberships($priceSetDetails[$priceSetId], $params);
738 if (!empty($params['price_set_id'])) {
739 CRM_Price_BAO_PriceField
::priceSetValidation($priceSetId, $params, $errors);
741 $priceFieldIDS = self
::getPriceFieldIDs($params, $priceSetDetails);
743 if (!empty($priceFieldIDS)) {
744 $ids = implode(',', $priceFieldIDS);
746 $count = CRM_Price_BAO_PriceSet
::getMembershipCount($ids);
747 foreach ($count as $occurrence) {
748 if ($occurrence > 1) {
749 $errors['_qf_default'] = ts('Select at most one option associated with the same membership type.');
753 // Return error if empty $self->_memTypeSelected
754 if (empty($errors) && empty($selectedMemberships)) {
755 $errors['_qf_default'] = ts('Select at least one membership option.');
757 if (!$self->_mode
&& empty($params['record_contribution'])) {
758 $errors['record_contribution'] = ts('Record Membership Payment is required when you use a price set.');
762 if (empty($params['membership_type_id'][1])) {
763 $errors['membership_type_id'] = ts('Please select a membership type.');
765 $numterms = CRM_Utils_Array
::value('num_terms', $params);
766 if ($numterms && intval($numterms) != $numterms) {
767 $errors['num_terms'] = ts('Please enter an integer for the number of terms.');
770 if (($self->_mode ||
isset($params['record_contribution'])) && empty($params['financial_type_id'])) {
771 $errors['financial_type_id'] = ts('Please enter the financial Type.');
775 if (!empty($errors) && (count($selectedMemberships) > 1)) {
776 $memberOfContacts = CRM_Member_BAO_MembershipType
::getMemberOfContactByMemTypes($selectedMemberships);
777 $duplicateMemberOfContacts = array_count_values($memberOfContacts);
778 foreach ($duplicateMemberOfContacts as $countDuplicate) {
779 if ($countDuplicate > 1) {
780 $errors['_qf_default'] = ts('Please do not select more than one membership associated with the same organization.');
785 if (!empty($errors)) {
789 if (!empty($params['record_contribution']) && empty($params['payment_instrument_id'])) {
790 $errors['payment_instrument_id'] = ts('Payment Method is a required field.');
793 if (!empty($params['is_different_contribution_contact'])) {
794 if (empty($params['soft_credit_type_id'])) {
795 $errors['soft_credit_type_id'] = ts('Please Select a Soft Credit Type');
797 if (empty($params['soft_credit_contact_id'])) {
798 $errors['soft_credit_contact_id'] = ts('Please select a contact');
802 if (!empty($params['payment_processor_id'])) {
803 // validate payment instrument (e.g. credit card number)
804 CRM_Core_Payment_Form
::validatePaymentInstrument($params['payment_processor_id'], $params, $errors, $self);
808 if (!empty($params['join_date'])) {
810 $joinDate = CRM_Utils_Date
::processDate($params['join_date']);
812 foreach ($selectedMemberships as $memType) {
814 if (!empty($params['start_date'])) {
815 $startDate = CRM_Utils_Date
::processDate($params['start_date']);
818 // if end date is set, ensure that start date is also set
819 // and that end date is later than start date
821 if (!empty($params['end_date'])) {
822 $endDate = CRM_Utils_Date
::processDate($params['end_date']);
825 $membershipDetails = CRM_Member_BAO_MembershipType
::getMembershipTypeDetails($memType);
827 if ($startDate && CRM_Utils_Array
::value('period_type', $membershipDetails) == 'rolling') {
828 if ($startDate < $joinDate) {
829 $errors['start_date'] = ts('Start date must be the same or later than Member since.');
834 if ($membershipDetails['duration_unit'] == 'lifetime') {
835 // Check if status is NOT cancelled or similar. For lifetime memberships, there is no automated
836 // process to update status based on end-date. The user must change the status now.
837 $result = civicrm_api3('MembershipStatus', 'get', array(
839 'is_current_member' => 0,
841 $tmp_statuses = $result['values'];
842 $status_ids = array();
843 foreach ($tmp_statuses as $cur_stat) {
844 $status_ids[] = $cur_stat['id'];
846 if (empty($params['status_id']) ||
in_array($params['status_id'], $status_ids) == FALSE) {
847 $errors['status_id'] = ts('Please enter a status that does NOT represent a current membership status.');
848 $errors['is_override'] = ts('This must be checked because you set an End Date for a lifetime membership');
853 $errors['start_date'] = ts('Start date must be set if end date is set.');
855 if ($endDate < $startDate) {
856 $errors['end_date'] = ts('End date must be the same or later than start date.');
861 // Default values for start and end dates if not supplied on the form.
862 $defaultDates = CRM_Member_BAO_MembershipType
::getDatesForMembershipType($memType,
869 $startDate = CRM_Utils_Array
::value('start_date',
874 $endDate = CRM_Utils_Array
::value('end_date',
879 //CRM-3724, check for availability of valid membership status.
880 if (empty($params['is_override']) && !isset($errors['_qf_default'])) {
881 $calcStatus = CRM_Member_BAO_MembershipStatus
::getMembershipStatusByDate($startDate,
889 if (empty($calcStatus)) {
890 $url = CRM_Utils_System
::url('civicrm/admin/member/membershipStatus', 'reset=1&action=browse');
891 $errors['_qf_default'] = ts('There is no valid Membership Status available for selected membership dates.');
892 $status = ts('Oops, it looks like there is no valid membership status available for the given membership dates. You can <a href="%1">Configure Membership Status Rules</a>.', array(1 => $url));
894 $status .= ' ' . ts('OR You can sign up by setting Status Override? to true.');
896 CRM_Core_Session
::setStatus($status, ts('Membership Status Error'), 'error');
902 $errors['join_date'] = ts('Please enter the Member Since.');
905 if (isset($params['is_override']) &&
906 $params['is_override'] && empty($params['status_id'])
908 $errors['status_id'] = ts('Please enter the status.');
911 //total amount condition arise when membership type having no
913 if (isset($params['record_contribution'])) {
914 if (CRM_Utils_System
::isNull($params['total_amount'])) {
915 $errors['total_amount'] = ts('Please enter the contribution.');
919 // validate contribution status for 'Failed'.
920 if ($self->_onlinePendingContributionId
&& !empty($params['record_contribution']) &&
921 (CRM_Utils_Array
::value('contribution_status_id', $params) ==
922 array_search('Failed', CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name'))
925 $errors['contribution_status_id'] = ts('Please select a valid payment status before updating.');
928 return empty($errors) ?
TRUE : $errors;
932 * Process the form submission.
934 public function postProcess() {
935 if ($this->_action
& CRM_Core_Action
::DELETE
) {
936 CRM_Member_BAO_Membership
::del($this->_id
);
939 // get the submitted form values.
940 $this->_params
= $this->controller
->exportValues($this->_name
);
944 $this->setUserContext();
948 * Send email receipt.
950 * @param CRM_Core_Form $form
952 * @param array $formValues
953 * @param object $membership
957 * true if mail was sent successfully
959 public static function emailReceipt(&$form, &$formValues, &$membership) {
960 // retrieve 'from email id' for acknowledgement
961 $receiptFrom = $formValues['from_email_address'];
963 if (!empty($formValues['payment_instrument_id'])) {
964 $paymentInstrument = CRM_Contribute_PseudoConstant
::paymentInstrument();
965 $formValues['paidBy'] = $paymentInstrument[$formValues['payment_instrument_id']];
968 // retrieve custom data
969 $customFields = $customValues = array();
970 if (property_exists($form, '_groupTree')
971 && !empty($form->_groupTree
)
973 foreach ($form->_groupTree
as $groupID => $group) {
974 if ($groupID == 'info') {
977 foreach ($group['fields'] as $k => $field) {
978 $field['title'] = $field['label'];
979 $customFields["custom_{$k}"] = $field;
984 $members = array(array('member_id', '=', $membership->id
, 0, 0));
985 // check whether its a test drive
986 if ($form->_mode
== 'test') {
987 $members[] = array('member_test', '=', 1, 0, 0);
990 CRM_Core_BAO_UFGroup
::getValues($formValues['contact_id'], $customFields, $customValues, FALSE, $members);
991 $form->assign('customValues', $customValues);
994 // assign the address formatted up for display
995 $addressParts = array(
996 "street_address-{$form->_bltID}",
997 "city-{$form->_bltID}",
998 "postal_code-{$form->_bltID}",
999 "state_province-{$form->_bltID}",
1000 "country-{$form->_bltID}",
1002 $addressFields = array();
1003 foreach ($addressParts as $part) {
1004 list($n, $id) = explode('-', $part);
1005 if (isset($form->_params
['billing_' . $part])) {
1006 $addressFields[$n] = $form->_params
['billing_' . $part];
1009 $form->assign('address', CRM_Utils_Address
::format($addressFields));
1011 $date = CRM_Utils_Date
::format($form->_params
['credit_card_exp_date']);
1012 $date = CRM_Utils_Date
::mysqlToIso($date);
1013 $form->assign('credit_card_exp_date', $date);
1014 $form->assign('credit_card_number',
1015 CRM_Utils_System
::mungeCreditCard($form->_params
['credit_card_number'])
1017 $form->assign('credit_card_type', $form->_params
['credit_card_type']);
1018 $form->assign('contributeMode', 'direct');
1019 $form->assign('isAmountzero', 0);
1020 $form->assign('is_pay_later', 0);
1021 $form->assign('isPrimary', 1);
1024 $form->assign('module', 'Membership');
1025 $form->assign('contactID', $formValues['contact_id']);
1027 $form->assign('membershipID', CRM_Utils_Array
::value('membership_id', $form->_params
, CRM_Utils_Array
::value('membership_id', $form->_defaultValues
)));
1029 if (!empty($formValues['contribution_id'])) {
1030 $form->assign('contributionID', $formValues['contribution_id']);
1032 elseif (isset($form->_onlinePendingContributionId
)) {
1033 $form->assign('contributionID', $form->_onlinePendingContributionId
);
1036 if (!empty($formValues['contribution_status_id'])) {
1037 $form->assign('contributionStatusID', $formValues['contribution_status_id']);
1038 $form->assign('contributionStatus', CRM_Contribute_PseudoConstant
::contributionStatus($formValues['contribution_status_id'], 'name'));
1041 if (!empty($formValues['is_renew'])) {
1042 $form->assign('receiptType', 'membership renewal');
1045 $form->assign('receiptType', 'membership signup');
1047 $form->assign('receive_date', CRM_Utils_Date
::processDate(CRM_Utils_Array
::value('receive_date', $formValues)));
1048 $form->assign('formValues', $formValues);
1050 if (empty($lineItem)) {
1051 $form->assign('mem_start_date', CRM_Utils_Date
::customFormat($membership->start_date
, '%B %E%f, %Y'));
1052 if (!CRM_Utils_System
::isNull($membership->end_date
)) {
1053 $form->assign('mem_end_date', CRM_Utils_Date
::customFormat($membership->end_date
, '%B %E%f, %Y'));
1055 $form->assign('membership_name', CRM_Member_PseudoConstant
::membershipType($membership->membership_type_id
));
1058 $isBatchProcess = is_a($form, 'CRM_Batch_Form_Entry');
1059 if ((empty($form->_contributorDisplayName
) ||
empty($form->_contributorEmail
)) ||
$isBatchProcess) {
1060 // in this case the form is being called statically from the batch editing screen
1061 // having one class in the form layer call another statically is not greate
1062 // & we should aim to move this function to the BAO layer in future.
1063 // however, we can assume that the contact_id passed in by the batch
1064 // function will be the recipient
1065 list($form->_contributorDisplayName
, $form->_contributorEmail
)
1066 = CRM_Contact_BAO_Contact_Location
::getEmailDetails($formValues['contact_id']);
1067 if (empty($form->_receiptContactId
) ||
$isBatchProcess) {
1068 $form->_receiptContactId
= $formValues['contact_id'];
1071 $template = CRM_Core_Smarty
::singleton();
1072 $taxAmt = $template->get_template_vars('dataArray');
1073 $eventTaxAmt = $template->get_template_vars('totalTaxAmount');
1074 $prefixValue = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::CONTRIBUTE_PREFERENCES_NAME
, 'contribution_invoice_settings');
1075 $invoicing = CRM_Utils_Array
::value('invoicing', $prefixValue);
1076 if ((!empty($taxAmt) ||
isset($eventTaxAmt)) && (isset($invoicing) && isset($prefixValue['is_email_pdf']))) {
1080 $isEmailPdf = FALSE;
1083 list($mailSend, $subject, $message, $html) = CRM_Core_BAO_MessageTemplate
::sendTemplate(
1085 'groupName' => 'msg_tpl_workflow_membership',
1086 'valueName' => 'membership_offline_receipt',
1087 'contactId' => $form->_receiptContactId
,
1088 'from' => $receiptFrom,
1089 'toName' => $form->_contributorDisplayName
,
1090 'toEmail' => $form->_contributorEmail
,
1091 'PDFFilename' => ts('receipt') . '.pdf',
1092 'isEmailPdf' => $isEmailPdf,
1093 'contributionId' => $formValues['contribution_id'],
1094 'isTest' => (bool) ($form->_action
& CRM_Core_Action
::PREVIEW
),
1104 * This is also accessed by unit tests.
1108 public function submit() {
1109 $isTest = ($this->_mode
== 'test') ?
1 : 0;
1110 $this->storeContactFields($this->_params
);
1111 $this->beginPostProcess();
1112 $formValues = $this->_params
;
1113 $joinDate = $startDate = $endDate = NULL;
1114 $membershipTypes = $membership = $calcDate = array();
1115 $membershipType = NULL;
1118 $formValues = $this->setPriceSetParameters($formValues);
1119 $params = $softParams = $ids = array();
1121 $allMemberStatus = CRM_Member_PseudoConstant
::membershipStatus();
1122 $allContributionStatus = CRM_Contribute_PseudoConstant
::contributionStatus();
1123 $this->processBillingAddress();
1126 $ids['membership'] = $params['id'] = $this->_id
;
1128 $ids['userId'] = CRM_Core_Session
::singleton()->get('userID');
1130 // Set variables that we normally get from context.
1131 // In form mode these are set in preProcess.
1132 //TODO: set memberships, fixme
1133 $this->setContextVariables($formValues);
1135 $this->_memTypeSelected
= self
::getSelectedMemberships(
1139 if (empty($formValues['financial_type_id'])) {
1140 $formValues['financial_type_id'] = $this->_priceSet
['financial_type_id'];
1143 $config = CRM_Core_Config
::singleton();
1145 $this->convertDateFieldsToMySQL($formValues);
1147 $membershipTypeValues = array();
1148 foreach ($this->_memTypeSelected
as $memType) {
1149 $membershipTypeValues[$memType]['membership_type_id'] = $memType;
1152 //take the required membership recur values.
1153 if ($this->_mode
&& !empty($formValues['auto_renew'])) {
1154 $params['is_recur'] = $formValues['is_recur'] = TRUE;
1156 'frequency_interval' => 'duration_interval',
1157 'frequency_unit' => 'duration_unit',
1161 foreach ($this->_memTypeSelected
as $memType) {
1162 $recurMembershipTypeValues = CRM_Utils_Array
::value($memType,
1163 $this->_recurMembershipTypes
, array()
1165 foreach ($mapping as $mapVal => $mapParam) {
1166 $membershipTypeValues[$memType][$mapVal] = CRM_Utils_Array
::value($mapParam,
1167 $recurMembershipTypeValues
1170 $formValues[$mapVal] = CRM_Utils_Array
::value($mapParam,
1171 $recurMembershipTypeValues
1179 $isQuickConfig = $this->_priceSet
['is_quick_config'];
1181 $termsByType = array();
1183 $lineItem = array($this->_priceSetId
=> array());
1185 CRM_Price_BAO_PriceSet
::processAmount($this->_priceSet
['fields'],
1186 $formValues, $lineItem[$this->_priceSetId
]);
1188 if (CRM_Utils_Array
::value('tax_amount', $formValues)) {
1189 $params['tax_amount'] = $formValues['tax_amount'];
1191 $params['total_amount'] = CRM_Utils_Array
::value('amount', $formValues);
1192 $submittedFinancialType = CRM_Utils_Array
::value('financial_type_id', $formValues);
1193 if (!empty($lineItem[$this->_priceSetId
])) {
1194 foreach ($lineItem[$this->_priceSetId
] as &$li) {
1195 if (!empty($li['membership_type_id'])) {
1196 if (!empty($li['membership_num_terms'])) {
1197 $termsByType[$li['membership_type_id']] = $li['membership_num_terms'];
1201 ///CRM-11529 for quick config backoffice transactions
1202 //when financial_type_id is passed in form, update the
1203 //lineitems with the financial type selected in form
1204 if ($isQuickConfig && $submittedFinancialType) {
1205 $li['financial_type_id'] = $submittedFinancialType;
1210 $params['contact_id'] = $this->_contactID
;
1219 foreach ($fields as $f) {
1220 $params[$f] = CRM_Utils_Array
::value($f, $formValues);
1224 // when is_override false ignore is_admin statuses during membership
1225 // status calculation. similarly we did fix for import in CRM-3570.
1226 if (empty($params['is_override'])) {
1227 $params['exclude_is_admin'] = TRUE;
1230 // process date params to mysql date format.
1232 'join_date' => 'joinDate',
1233 'start_date' => 'startDate',
1234 'end_date' => 'endDate',
1236 foreach ($dateTypes as $dateField => $dateVariable) {
1237 $
$dateVariable = CRM_Utils_Date
::processDate($formValues[$dateField]);
1240 $memTypeNumTerms = empty($termsByType) ? CRM_Utils_Array
::value('num_terms', $formValues) : NULL;
1242 $calcDates = array();
1243 foreach ($this->_memTypeSelected
as $memType) {
1244 if (empty($memTypeNumTerms)) {
1245 $memTypeNumTerms = CRM_Utils_Array
::value($memType, $termsByType, 1);
1247 $calcDates[$memType] = CRM_Member_BAO_MembershipType
::getDatesForMembershipType($memType,
1248 $joinDate, $startDate, $endDate, $memTypeNumTerms
1252 foreach ($calcDates as $memType => $calcDate) {
1253 foreach (array_keys($dateTypes) as $d) {
1254 //first give priority to form values then calDates.
1255 $date = CRM_Utils_Array
::value($d, $formValues);
1257 $date = CRM_Utils_Array
::value($d, $calcDate);
1260 $membershipTypeValues[$memType][$d] = CRM_Utils_Date
::processDate($date);
1264 // max related memberships - take from form or inherit from membership type
1265 foreach ($this->_memTypeSelected
as $memType) {
1266 if (array_key_exists('max_related', $formValues)) {
1267 $membershipTypeValues[$memType]['max_related'] = CRM_Utils_Array
::value('max_related', $formValues);
1269 $membershipTypeValues[$memType]['custom'] = CRM_Core_BAO_CustomField
::postProcess($formValues,
1273 $membershipTypes[$memType] = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipType',
1278 $membershipType = implode(', ', $membershipTypes);
1280 // Retrieve the name and email of the current user - this will be the FROM for the receipt email
1281 list($userName) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($ids['userId']);
1283 //CRM-13981, allow different person as a soft-contributor of chosen type
1284 if ($this->_contributorContactID
!= $this->_contactID
) {
1285 $params['contribution_contact_id'] = $this->_contributorContactID
;
1286 if (!empty($formValues['soft_credit_type_id'])) {
1287 $softParams['soft_credit_type_id'] = $formValues['soft_credit_type_id'];
1288 $softParams['contact_id'] = $this->_contactID
;
1291 if (!empty($formValues['record_contribution'])) {
1292 $recordContribution = array(
1294 'financial_type_id',
1295 'payment_instrument_id',
1297 'contribution_status_id',
1303 foreach ($recordContribution as $f) {
1304 $params[$f] = CRM_Utils_Array
::value($f, $formValues);
1307 if (!$this->_onlinePendingContributionId
) {
1308 if (empty($formValues['source'])) {
1309 $params['contribution_source'] = ts('%1 Membership: Offline signup (by %2)', array(
1310 1 => $membershipType,
1315 $params['contribution_source'] = $formValues['source'];
1319 if (empty($params['is_override']) &&
1320 CRM_Utils_Array
::value('contribution_status_id', $params) == array_search('Pending', CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name'))
1322 $params['status_id'] = array_search('Pending', $allMemberStatus);
1323 $params['skipStatusCal'] = TRUE;
1324 $params['is_pay_later'] = 1;
1325 $this->assign('is_pay_later', 1);
1328 if (!empty($formValues['send_receipt'])) {
1329 $params['receipt_date'] = CRM_Utils_Array
::value('receive_date', $formValues);
1332 //insert financial type name in receipt.
1333 $formValues['contributionType_name'] = CRM_Core_DAO
::getFieldValue('CRM_Financial_DAO_FinancialType',
1334 $formValues['financial_type_id']
1338 // process line items, until no previous line items.
1339 if (!empty($lineItem)) {
1340 $params['lineItems'] = $lineItem;
1341 $params['processPriceSet'] = TRUE;
1343 $createdMemberships = array();
1345 $params['total_amount'] = CRM_Utils_Array
::value('total_amount', $formValues, 0);
1347 if (!$isQuickConfig) {
1348 $params['financial_type_id'] = CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceSet',
1354 $params['financial_type_id'] = CRM_Utils_Array
::value('financial_type_id', $formValues);
1357 // @todo - test removing this line. The beginPostProcess Function should have done it for us.
1358 $this->_paymentProcessor
= CRM_Financial_BAO_PaymentProcessor
::getPayment($formValues['payment_processor_id'],
1362 //get the payment processor id as per mode. Try removing in favour of beginPostProcess.
1363 $params['payment_processor_id'] = $formValues['payment_processor_id'] = $this->_paymentProcessor
['id'];
1364 $params['register_date'] = date('YmdHis');
1366 // add all the additional payment params we need
1367 // @todo the country & state values should be set by the call to $this->assignBillingAddress.
1368 $formValues["state_province-{$this->_bltID}"] = $formValues["billing_state_province-{$this->_bltID}"]
1369 = CRM_Core_PseudoConstant
::stateProvinceAbbreviation($formValues["billing_state_province_id-{$this->_bltID}"]);
1370 $formValues["country-{$this->_bltID}"] = $formValues["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant
::countryIsoCode($formValues["billing_country_id-{$this->_bltID}"]);
1372 $formValues['amount'] = $params['total_amount'];
1373 // @todo this is a candidate for beginPostProcessFunction.
1374 $formValues['currencyID'] = $config->defaultCurrency
;
1375 $formValues['description'] = ts("Contribution submitted by a staff person using member's credit card for signup");
1376 $formValues['invoiceID'] = md5(uniqid(rand(), TRUE));
1377 $formValues['financial_type_id'] = $params['financial_type_id'];
1379 // at this point we've created a contact and stored its address etc
1380 // all the payment processors expect the name and address to be in the
1381 // so we copy stuff over to first_name etc.
1382 $paymentParams = $formValues;
1383 $paymentParams['contactID'] = $this->_contributorContactID
;
1384 //CRM-10377 if payment is by an alternate contact then we need to set that person
1385 // as the contact in the payment params
1386 if ($this->_contributorContactID
!= $this->_contactID
) {
1387 if (!empty($formValues['soft_credit_type_id'])) {
1388 $softParams['contact_id'] = $params['contact_id'];
1389 $softParams['soft_credit_type_id'] = $formValues['soft_credit_type_id'];
1392 if (!empty($formValues['send_receipt'])) {
1393 $paymentParams['email'] = $this->_contributorEmail
;
1396 // This is a candidate for shared beginPostProcess function.
1397 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $formValues, $paymentParams, TRUE);
1398 // CRM-7137 -for recurring membership,
1399 // we do need contribution and recurring records.
1401 if (!empty($paymentParams['is_recur'])) {
1402 $financialType = new CRM_Financial_DAO_FinancialType();
1403 $financialType->id
= $params['financial_type_id'];
1404 $financialType->find(TRUE);
1405 $this->_params
= $formValues;
1406 $contribution = CRM_Contribute_Form_Contribution_Confirm
::processFormContribution($this,
1410 'contact_id' => $this->_contributorContactID
,
1411 'line_item' => $lineItem,
1412 'is_test' => $isTest,
1413 'campaign_id' => CRM_Utils_Array
::value('campaign_id', $paymentParams),
1414 'contribution_page_id' => CRM_Utils_Array
::value('contribution_page_id', $formValues),
1415 'source' => CRM_Utils_Array
::value('source', $paymentParams, CRM_Utils_Array
::value('description', $paymentParams)),
1416 'thankyou_date' => CRM_Utils_Array
::value('thankyou_date', $paymentParams),
1417 'payment_instrument_id' => $this->_paymentProcessor
['payment_instrument_id'],
1424 //create new soft-credit record, CRM-13981
1426 $softParams['contribution_id'] = $contribution->id
;
1427 $softParams['currency'] = $contribution->currency
;
1428 $softParams['amount'] = $contribution->total_amount
;
1429 CRM_Contribute_BAO_ContributionSoft
::add($softParams);
1432 $paymentParams['contactID'] = $this->_contactID
;
1433 $paymentParams['contributionID'] = $contribution->id
;
1434 $paymentParams['contributionTypeID'] = $contribution->financial_type_id
;
1435 $paymentParams['contributionPageID'] = $contribution->contribution_page_id
;
1436 $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id
;
1437 $ids['contribution'] = $contribution->id
;
1438 $params['contribution_recur_id'] = $paymentParams['contributionRecurID'];
1441 if ($params['total_amount'] > 0.0) {
1442 $payment = $this->_paymentProcessor
['object'];
1444 $result = $payment->doPayment($paymentParams);
1445 $formValues = array_merge($formValues, $result);
1446 // Assign amount to template if payment was successful.
1447 $this->assign('amount', $params['total_amount']);
1449 catch (PaymentProcessorException
$e) {
1450 if (!empty($paymentParams['contributionID'])) {
1451 CRM_Contribute_BAO_Contribution
::failPayment($paymentParams['contributionID'], $this->_contactID
,
1454 if (!empty($paymentParams['contributionRecurID'])) {
1455 CRM_Contribute_BAO_ContributionRecur
::deleteRecurContribution($paymentParams['contributionRecurID']);
1458 CRM_Core_Error
::displaySessionError($result);
1459 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/contact/view/membership',
1460 "reset=1&action=add&cid={$this->_contactID}&context=&mode={$this->_mode}"
1466 if ($formValues['payment_status_id'] != array_search('Completed', $allContributionStatus)) {
1467 $params['status_id'] = array_search('Pending', $allMemberStatus);
1468 $params['skipStatusCal'] = TRUE;
1469 // unset send-receipt option, since receipt will be sent when ipn is received.
1470 unset($formValues['send_receipt'], $formValues['send_receipt']);
1471 //as membership is pending set dates to null.
1472 $memberDates = array(
1473 'join_date' => 'joinDate',
1474 'start_date' => 'startDate',
1475 'end_date' => 'endDate',
1477 foreach ($memberDates as $dv) {
1479 foreach ($this->_memTypeSelected
as $memType) {
1480 $membershipTypeValues[$memType][$dv] = NULL;
1484 $now = date('YmdHis');
1485 $params['receive_date'] = $now;
1486 $params['invoice_id'] = $formValues['invoiceID'];
1487 $params['contribution_source'] = ts('%1 Membership Signup: Credit card or direct debit (by %2)',
1488 array(1 => $membershipType, 2 => $userName)
1490 $params['source'] = $formValues['source'] ?
$formValues['source'] : $params['contribution_source'];
1491 $params['trxn_id'] = CRM_Utils_Array
::value('trxn_id', $result);
1492 $params['payment_instrument_id'] = 1;
1493 $params['is_test'] = ($this->_mode
== 'live') ?
0 : 1;
1494 if (!empty($formValues['send_receipt'])) {
1495 $params['receipt_date'] = $now;
1498 $params['receipt_date'] = NULL;
1501 $this->set('params', $formValues);
1502 $this->assign('trxn_id', CRM_Utils_Array
::value('trxn_id', $result));
1503 $this->assign('receive_date',
1504 CRM_Utils_Date
::mysqlToIso($params['receive_date'])
1507 // required for creating membership for related contacts
1508 $params['action'] = $this->_action
;
1510 //create membership record.
1512 foreach ($this->_memTypeSelected
as $memType) {
1514 ($relateContribution = CRM_Member_BAO_Membership
::getMembershipContributionId($membership->id
))
1516 $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution;
1519 $membershipParams = array_merge($membershipTypeValues[$memType], $params);
1521 if (!empty($softParams) && empty($paymentParams['is_recur'])) {
1522 $membershipParams['soft_credit'] = $softParams;
1524 if (isset($result['fee_amount'])) {
1525 $membershipParams['fee_amount'] = $result['fee_amount'];
1527 // This is required to trigger the recording of the membership contribution in the
1528 // CRM_Member_BAO_Membership::Create function.
1529 // @todo stop setting this & 'teach' the create function to respond to something
1530 // appropriate as part of our 2-step always create the pending contribution & then finally add the payment
1532 // @see http://wiki.civicrm.org/confluence/pages/viewpage.action?pageId=261062657#Payments&AccountsRoadmap-Movetowardsalwaysusinga2-steppaymentprocess
1533 $membershipParams['contribution_status_id'] = CRM_Utils_Array
::value('payment_status_id', $result);
1534 if (!empty($paymentParams['is_recur'])) {
1535 // The earlier process created the line items (although we want to get rid of the earlier one in favour
1536 // of a single path!
1537 unset($membershipParams['lineItems']);
1540 $membership = CRM_Member_BAO_Membership
::create($membershipParams, $ids);
1541 $params['contribution'] = CRM_Utils_Array
::value('contribution', $membershipParams);
1542 unset($params['lineItems']);
1543 $this->_membershipIDs
[] = $membership->id
;
1544 $createdMemberships[$memType] = $membership;
1550 $params['action'] = $this->_action
;
1551 if ($this->_onlinePendingContributionId
&& !empty($formValues['record_contribution'])) {
1553 // update membership as well as contribution object, CRM-4395
1554 $params['contribution_id'] = $this->_onlinePendingContributionId
;
1555 $params['componentId'] = $params['id'];
1556 $params['componentName'] = 'contribute';
1557 $result = CRM_Contribute_BAO_Contribution
::transitionComponents($params, TRUE);
1558 if (!empty($result) && !empty($params['contribution_id'])) {
1559 $lineItem = array();
1560 $lineItems = CRM_Price_BAO_LineItem
::getLineItems($params['contribution_id'], 'contribution', NULL, TRUE, TRUE);
1561 $itemId = key($lineItems);
1562 $priceSetId = CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceField', $lineItems[$itemId]['price_field_id'], 'price_set_id');
1564 $lineItems[$itemId]['unit_price'] = $params['total_amount'];
1565 $lineItems[$itemId]['line_total'] = $params['total_amount'];
1566 $lineItems[$itemId]['id'] = $itemId;
1567 $lineItem[$priceSetId] = $lineItems;
1568 $contributionBAO = new CRM_Contribute_BAO_Contribution();
1569 $contributionBAO->id
= $params['contribution_id'];
1570 $contributionBAO->contact_id
= $params['contact_id'];
1571 $contributionBAO->find();
1572 CRM_Price_BAO_LineItem
::processPriceSet($params['contribution_id'], $lineItem, $contributionBAO, 'civicrm_membership');
1574 //create new soft-credit record, CRM-13981
1576 $softParams['contribution_id'] = $params['contribution_id'];
1577 while ($contributionBAO->fetch()) {
1578 $softParams['currency'] = $contributionBAO->currency
;
1579 $softParams['amount'] = $contributionBAO->total_amount
;
1581 CRM_Contribute_BAO_ContributionSoft
::add($softParams);
1585 //carry updated membership object.
1586 $membership = new CRM_Member_DAO_Membership();
1587 $membership->id
= $this->_id
;
1588 $membership->find(TRUE);
1591 if ($membership->end_date
) {
1592 //display end date w/ status message.
1593 $endDate = $membership->end_date
;
1595 if (!in_array($membership->status_id
, array(
1597 array_search('Cancelled', CRM_Member_PseudoConstant
::membershipStatus(NULL, " name = 'Cancelled' ", 'name', FALSE, TRUE)),
1598 array_search('Expired', CRM_Member_PseudoConstant
::membershipStatus()),
1604 // suppress form values in template.
1605 $this->assign('cancelled', $cancelled);
1607 $createdMemberships[] = $membership;
1611 foreach ($this->_memTypeSelected
as $memType) {
1612 if ($count && !empty($formValues['record_contribution']) &&
1613 ($relateContribution = CRM_Member_BAO_Membership
::getMembershipContributionId($membership->id
))
1615 $membershipTypeValues[$memType]['relate_contribution_id'] = $relateContribution;
1618 $membershipParams = array_merge($params, $membershipTypeValues[$memType]);
1619 if (!empty($formValues['int_amount'])) {
1620 $init_amount = array();
1621 foreach ($formValues as $key => $value) {
1622 if (strstr($key, 'txt-price')) {
1623 $init_amount[$key] = $value;
1626 $membershipParams['init_amount'] = $init_amount;
1629 if (!empty($softParams)) {
1630 $membershipParams['soft_credit'] = $softParams;
1633 $membership = CRM_Member_BAO_Membership
::create($membershipParams, $ids);
1634 $params['contribution'] = CRM_Utils_Array
::value('contribution', $membershipParams);
1635 unset($params['lineItems']);
1637 $this->_membershipIDs
[] = $membership->id
;
1638 $createdMemberships[$memType] = $membership;
1644 if (!empty($lineItem[$this->_priceSetId
])) {
1645 $invoiceSettings = CRM_Core_BAO_Setting
::getItem(CRM_Core_BAO_Setting
::CONTRIBUTE_PREFERENCES_NAME
, 'contribution_invoice_settings');
1646 $invoicing = CRM_Utils_Array
::value('invoicing', $invoiceSettings);
1648 $totalTaxAmount = 0;
1649 foreach ($lineItem[$this->_priceSetId
] as & $priceFieldOp) {
1650 if (!empty($priceFieldOp['membership_type_id'])) {
1651 $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') : '-';
1652 $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') : '-';
1655 $priceFieldOp['start_date'] = $priceFieldOp['end_date'] = 'N/A';
1657 if ($invoicing && isset($priceFieldOp['tax_amount'])) {
1659 $totalTaxAmount +
= $priceFieldOp['tax_amount'];
1663 $dataArray = array();
1664 foreach ($lineItem[$this->_priceSetId
] as $key => $value) {
1665 if (isset($value['tax_amount']) && isset($value['tax_rate'])) {
1666 if (isset($dataArray[$value['tax_rate']])) {
1667 $dataArray[$value['tax_rate']] = $dataArray[$value['tax_rate']] + CRM_Utils_Array
::value('tax_amount', $value);
1670 $dataArray[$value['tax_rate']] = CRM_Utils_Array
::value('tax_amount', $value);
1675 $this->assign('totalTaxAmount', $totalTaxAmount);
1676 $this->assign('taxTerm', CRM_Utils_Array
::value('tax_term', $invoiceSettings));
1678 $this->assign('dataArray', $dataArray);
1681 $this->assign('lineItem', !empty($lineItem) && !$isQuickConfig ?
$lineItem : FALSE);
1683 $receiptSend = FALSE;
1684 $contributionId = CRM_Member_BAO_Membership
::getMembershipContributionId($membership->id
);
1685 $membershipIds = $this->_membershipIDs
;
1686 if ($contributionId && !empty($membershipIds)) {
1687 $contributionDetails = CRM_Contribute_BAO_Contribution
::getContributionDetails(
1688 CRM_Export_Form_Select
::MEMBER_EXPORT
, $this->_membershipIDs
);
1689 if ($contributionDetails[$membership->id
]['contribution_status'] == 'Completed') {
1690 $receiptSend = TRUE;
1694 if (!empty($formValues['send_receipt']) && $receiptSend) {
1695 $formValues['contact_id'] = $this->_contactID
;
1696 $formValues['contribution_id'] = $contributionId;
1697 // We really don't need a distinct receipt_text_signup vs receipt_text_renewal as they are
1698 // handled in the receipt. But by setting one we avoid breaking templates for now
1699 // although at some point we should switch in the templates.
1700 $formValues['receipt_text_signup'] = $formValues['receipt_text'];
1701 // send email receipt
1702 $this->assignBillingName();
1703 $mailSend = self
::emailReceipt($this, $formValues, $membership);
1706 // finally set membership id if already not set
1708 $this->_id
= $membership->id
;
1711 $isRecur = CRM_Utils_Array
::value('is_recur', $params);
1712 $this->setStatusMessage($membership, $endDate, $receiptSend, $membershipTypes, $createdMemberships, $isRecur, $calcDates, $mailSend);
1713 return $createdMemberships;
1717 * Set context in session.
1719 protected function setUserContext() {
1720 $buttonName = $this->controller
->getButtonName();
1721 $session = CRM_Core_Session
::singleton();
1723 if ($this->_context
== 'standalone') {
1724 if ($buttonName == $this->getButtonName('upload', 'new')) {
1725 $session->replaceUserContext(CRM_Utils_System
::url('civicrm/member/add',
1726 'reset=1&action=add&context=standalone'
1730 $session->replaceUserContext(CRM_Utils_System
::url('civicrm/contact/view',
1731 "reset=1&cid={$this->_contactID}&selectedChild=member"
1735 elseif ($buttonName == $this->getButtonName('upload', 'new')) {
1736 $session->replaceUserContext(CRM_Utils_System
::url('civicrm/contact/view/membership',
1737 "reset=1&action=add&context=membership&cid={$this->_contactID}"
1743 * Get status message for updating membership.
1745 * @param CRM_Member_BAO_Membership $membership
1746 * @param string $endDate
1747 * @param bool $receiptSend
1751 protected function getStatusMessageForUpdate($membership, $endDate, $receiptSend) {
1752 // End date can be modified by hooks, so if end date is set then use it.
1753 $endDate = ($membership->end_date
) ?
$membership->end_date
: $endDate;
1755 $statusMsg = ts('Membership for %1 has been updated.', array(1 => $this->_memberDisplayName
));
1756 if ($endDate && $endDate !== 'null') {
1757 $endDate = CRM_Utils_Date
::customFormat($endDate);
1758 $statusMsg .= ' ' . ts('The membership End Date is %1.', array(1 => $endDate));
1761 if (!empty($formValues['send_receipt']) && $receiptSend) {
1762 $statusMsg .= ' ' . ts('A confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail
));
1768 * Get status message for create action.
1770 * @param string $endDate
1771 * @param bool $receiptSend
1772 * @param array $membershipTypes
1773 * @param array $createdMemberships
1774 * @param bool $isRecur
1775 * @param array $calcDates
1776 * @param bool $mailSent
1778 * @return array|string
1780 protected function getStatusMessageForCreate($endDate, $receiptSend, $membershipTypes, $createdMemberships,
1781 $isRecur, $calcDates, $mailSent) {
1782 // FIX ME: fix status messages
1784 $statusMsg = array();
1785 foreach ($membershipTypes as $memType => $membershipType) {
1786 $statusMsg[$memType] = ts('%1 membership for %2 has been added.', array(
1787 1 => $membershipType,
1788 2 => $this->_memberDisplayName
,
1791 $membership = $createdMemberships[$memType];
1792 $memEndDate = ($membership->end_date
) ?
$membership->end_date
: $endDate;
1794 //get the end date from calculated dates.
1795 if (!$memEndDate && !$isRecur) {
1796 $memEndDate = CRM_Utils_Array
::value('end_date', $calcDates[$memType]);
1799 if ($memEndDate && $memEndDate !== 'null') {
1800 $memEndDate = CRM_Utils_Date
::customFormat($memEndDate);
1801 $statusMsg[$memType] .= ' ' . ts('The new membership End Date is %1.', array(1 => $memEndDate));
1804 $statusMsg = implode('<br/>', $statusMsg);
1805 if ($receiptSend && !empty($mailSent)) {
1806 $statusMsg .= ' ' . ts('A membership confirmation and receipt has been sent to %1.', array(1 => $this->_contributorEmail
));
1812 * @param $membership
1814 * @param $receiptSend
1815 * @param $membershipTypes
1816 * @param $createdMemberships
1821 protected function setStatusMessage($membership, $endDate, $receiptSend, $membershipTypes, $createdMemberships, $isRecur, $calcDates, $mailSend) {
1823 if (($this->_action
& CRM_Core_Action
::UPDATE
)) {
1824 $statusMsg = $this->getStatusMessageForUpdate($membership, $endDate, $receiptSend);
1826 elseif (($this->_action
& CRM_Core_Action
::ADD
)) {
1827 $statusMsg = $this->getStatusMessageForCreate($endDate, $receiptSend, $membershipTypes, $createdMemberships,
1828 $isRecur, $calcDates, $mailSend);
1831 CRM_Core_Session
::setStatus($statusMsg, ts('Complete'), 'success');
1833 // display message when membership type is changed
1834 if (($this->_action
& CRM_Core_Action
::UPDATE
) && $this->_id
&& !in_array($this->_memType
, $this->_memTypeSelected
)) {
1835 $lineItem = CRM_Price_BAO_LineItem
::getLineItems($this->_id
, 'membership');
1836 $maxID = max(array_keys($lineItem));
1837 $lineItem = $lineItem[$maxID];
1838 $membershipTypeDetails = $this->allMembershipTypeDetails
[$membership->membership_type_id
];
1839 if ($membershipTypeDetails['financial_type_id'] != $lineItem['financial_type_id']) {
1840 CRM_Core_Session
::setStatus(
1841 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.'),
1845 if ($membershipTypeDetails['minimum_fee'] != $lineItem['line_total']) {
1846 CRM_Core_Session
::setStatus(
1847 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.'),