3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
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 +--------------------------------------------------------------------+
30 * @copyright CiviCRM LLC (c) 2004-2019
34 * This class generates form components for processing Event.
36 class CRM_Event_Form_Registration_Confirm
extends CRM_Event_Form_Registration
{
39 * The values for the contribution db object.
52 public $submitOnce = TRUE;
55 * Monetary fields that may be submitted.
57 * These should get a standardised format in the beginPostProcess function.
59 * These fields are common to many forms. Some may override this.
62 protected $submittableMoneyFields = ['total_amount', 'net_amount', 'non_deductible_amount', 'fee_amount', 'tax_amount', 'amount'];
65 * Set variables up before form is built.
67 public function preProcess() {
70 // lineItem isn't set until Register postProcess
71 $this->_lineItem
= $this->get('lineItem');
73 $this->_params
= $this->get('params');
74 $this->_params
[0]['tax_amount'] = $this->get('tax_amount');
76 $this->_params
[0]['is_pay_later'] = $this->get('is_pay_later');
77 $this->assign('is_pay_later', $this->_params
[0]['is_pay_later']);
78 if ($this->_params
[0]['is_pay_later']) {
79 $this->assign('pay_later_receipt', $this->_values
['event']['pay_later_receipt']);
82 CRM_Utils_Hook
::eventDiscount($this, $this->_params
);
84 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
85 $this->set('hookDiscount', $this->_params
[0]['discount']);
86 $this->assign('hookDiscount', $this->_params
[0]['discount']);
89 if (!$this->preProcessExpress()) {
90 //process only primary participant params.
91 $registerParams = $this->_params
[0];
92 if (isset($registerParams["billing_state_province_id-{$this->_bltID}"])
93 && $registerParams["billing_state_province_id-{$this->_bltID}"]
95 $registerParams["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant
::stateProvinceAbbreviation($registerParams["billing_state_province_id-{$this->_bltID}"]);
98 if (isset($registerParams["billing_country_id-{$this->_bltID}"]) && $registerParams["billing_country_id-{$this->_bltID}"]) {
99 $registerParams["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant
::countryIsoCode($registerParams["billing_country_id-{$this->_bltID}"]);
101 if (isset($registerParams['credit_card_exp_date'])) {
102 $registerParams['year'] = CRM_Core_Payment_Form
::getCreditCardExpirationYear($registerParams);
103 $registerParams['month'] = CRM_Core_Payment_Form
::getCreditCardExpirationMonth($registerParams);
105 if ($this->_values
['event']['is_monetary']) {
106 $registerParams['ip_address'] = CRM_Utils_System
::ipAddress();
107 $registerParams['currencyID'] = $this->_params
[0]['currencyID'];
109 //assign back primary participant params.
110 $this->_params
[0] = $registerParams;
113 if ($this->_values
['event']['is_monetary']) {
114 $this->_params
[0]['invoiceID'] = $this->get('invoiceID');
116 $this->assign('defaultRole', FALSE);
117 if (CRM_Utils_Array
::value('defaultRole', $this->_params
[0]) == 1) {
118 $this->assign('defaultRole', TRUE);
121 if (empty($this->_params
[0]['participant_role_id']) &&
122 $this->_values
['event']['default_role_id']
124 $this->_params
[0]['participant_role_id'] = $this->_values
['event']['default_role_id'];
127 if (isset($this->_values
['event']['confirm_title'])) {
128 CRM_Utils_System
::setTitle($this->_values
['event']['confirm_title']);
131 // Personal campaign page
133 $params = CRM_Contribute_Form_Contribution_Confirm
::processPcp($this, $this->_params
[0]);
134 $this->_params
[0] = $params;
137 $this->set('params', $this->_params
);
141 * Pre process function for Paypal Express confirm.
142 * @todo this is just a step in refactor as payment processor specific code does not belong in generic forms
145 * @throws \CRM_Core_Exception
147 private function preProcessExpress() {
148 if ($this->_contributeMode
!== 'express') {
152 // rfp == redirect from paypal
153 // @fixme rfp is probably not required - the getPreApprovalDetails should deal with any payment-processor specific 'stuff'
154 $rfp = CRM_Utils_Request
::retrieve('rfp', 'Boolean', CRM_Core_DAO
::$_nullObject, FALSE, NULL, 'GET');
156 //we lost rfp in case of additional participant. So set it explicitly.
157 if ($rfp || CRM_Utils_Array
::value('additional_participants', $this->_params
[0], FALSE)) {
158 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
159 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
160 $params = array_merge($this->_params
, $preApprovalParams);
162 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $params, $params, FALSE);
164 // set a few other parameters that are not really specific to this method because we don't know what
165 // will break if we change this.
166 $params['amount'] = $this->_params
[0]['amount'];
167 if (!empty($this->_params
[0]['discount'])) {
168 $params['discount'] = $this->_params
[0]['discount'];
169 $params['discountAmount'] = $this->_params
[0]['discountAmount'];
170 $params['discountMessage'] = $this->_params
[0]['discountMessage'];
173 $params['amount_level'] = $this->_params
[0]['amount_level'];
174 $params['currencyID'] = $this->_params
[0]['currencyID'];
176 // also merge all the other values from the profile fields
177 $values = $this->controller
->exportValues('Register');
180 "street_address-{$this->_bltID}",
181 "city-{$this->_bltID}",
182 "state_province_id-{$this->_bltID}",
183 "postal_code-{$this->_bltID}",
184 "country_id-{$this->_bltID}",
187 foreach ($values as $name => $value) {
189 if (!in_array($name, $skipFields)) {
190 $params[$name] = $value;
192 if (substr($name, 0, 6) == 'price_') {
193 $params[$name] = $this->_params
[0][$name];
196 $this->set('getExpressCheckoutDetails', $params);
198 $this->_params
[0] = array_merge($this->_params
[0], $params);
199 $this->_params
[0]['is_primary'] = 1;
204 * Overwrite action, since we are only showing elements in frozen mode no help display needed.
208 public function getAction() {
209 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
210 return CRM_Core_Action
::VIEW | CRM_Core_Action
::PREVIEW
;
213 return CRM_Core_Action
::VIEW
;
218 * Build the form object.
220 public function buildQuickForm() {
221 $this->assignToTemplate();
223 if ($this->_values
['event']['is_monetary'] &&
224 ($this->_params
[0]['amount'] ||
$this->_params
[0]['amount'] == 0) &&
225 !$this->_requireApproval
230 foreach ($this->_params
as $k => $v) {
234 $individualTaxAmount = 0;
235 //display tax amount on confirmation page
236 $taxAmount +
= $v['tax_amount'];
238 $this->cleanMoneyFields($v);
243 if (isset($v['billing_' . $name]) &&
246 $v[$name] = $v['billing_' . $name];
250 if (!empty($v['first_name']) && !empty($v['last_name'])) {
251 $append = $v['first_name'] . ' ' . $v['last_name'];
254 //use an email if we have one
255 foreach ($v as $v_key => $v_val) {
256 if (substr($v_key, 0, 6) == 'email-') {
257 $append = $v[$v_key];
262 $this->_amount
[$k]['amount'] = $v['amount'];
263 if (!empty($v['discountAmount'])) {
264 $this->_amount
[$k]['amount'] -= $v['discountAmount'];
267 $this->_amount
[$k]['label'] = preg_replace('/\ 1/', '', $v['amount_level']) . ' - ' . $append;
268 $this->_part
[$k]['info'] = CRM_Utils_Array
::value('first_name', $v) . ' ' . CRM_Utils_Array
::value('last_name', $v);
269 if (empty($v['first_name'])) {
270 $this->_part
[$k]['info'] = $append;
274 $individual[$k]['totalAmtWithTax'] = $this->_amount
[$k]['amount'];
275 $individual[$k]['totalTaxAmt'] = $individualTaxAmount +
$v['tax_amount'];
276 $this->_totalAmount
= $this->_totalAmount +
$this->_amount
[$k]['amount'];
277 if (!empty($v['is_primary'])) {
278 $this->set('primaryParticipantAmount', $this->_amount
[$k]['amount']);
283 $invoiceSettings = Civi
::settings()->get('contribution_invoice_settings');
284 $taxTerm = CRM_Utils_Array
::value('tax_term', $invoiceSettings);
285 $invoicing = CRM_Utils_Array
::value('invoicing', $invoiceSettings);
287 $this->assign('totalTaxAmount', $taxAmount);
288 $this->assign('taxTerm', $taxTerm);
289 $this->assign('individual', $individual);
290 $this->set('individual', $individual);
293 $this->assign('part', $this->_part
);
294 $this->set('part', $this->_part
);
295 $this->assign('amounts', $this->_amount
);
296 $this->assign('totalAmount', $this->_totalAmount
);
297 $this->set('totalAmount', $this->_totalAmount
);
300 if ($this->_priceSetId
&& !CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId
, 'is_quick_config')) {
301 $lineItemForTemplate = [];
302 $getTaxDetails = FALSE;
303 if (!empty($this->_lineItem
) && is_array($this->_lineItem
)) {
304 foreach ($this->_lineItem
as $key => $value) {
305 if (!empty($value)) {
306 $lineItemForTemplate[$key] = $value;
309 foreach ($value as $v) {
310 if (isset($v['tax_rate'])) {
311 $getTaxDetails = TRUE;
317 if (!empty($lineItemForTemplate)) {
318 $this->assign('lineItem', $lineItemForTemplate);
320 $this->assign('getTaxDetails', $getTaxDetails);
323 //display additional participants profile.
324 self
::assignProfiles($this);
326 //consider total amount.
327 $this->assign('isAmountzero', ($this->_totalAmount
<= 0) ?
TRUE : FALSE);
329 $contribButton = ts('Continue');
333 'name' => ts('Go Back'),
337 'name' => $contribButton,
344 if (!empty($this->_fields
)) {
345 foreach ($this->_fields
as $name => $dontCare) {
349 $fields["billing_state_province-{$this->_bltID}"] = $fields["billing_country-{$this->_bltID}"] = $fields["email-{$this->_bltID}"] = 1;
350 foreach ($fields as $name => $dontCare) {
351 if (isset($this->_params
[0][$name])) {
352 $defaults[$name] = $this->_params
[0][$name];
353 if (substr($name, 0, 7) == 'custom_') {
354 $timeField = "{$name}_time";
355 if (isset($this->_params
[0][$timeField])) {
356 $defaults[$timeField] = $this->_params
[0][$timeField];
358 if (isset($this->_params
[0]["{$name}_id"])) {
359 $defaults["{$name}_id"] = $this->_params
[0]["{$name}_id"];
362 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)
363 && !empty($this->_params
[0][$name . '_custom'])
365 $defaults[$name . '_custom'] = $this->_params
[0][$name . '_custom'];
370 $this->setDefaults($defaults);
373 //lets give meaningful status message, CRM-4320.
374 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
375 $this->assign('isRequireApproval', $this->_requireApproval
);
377 // Assign Participant Count to Lineitem Table
378 $this->assign('pricesetFieldsCount', CRM_Price_BAO_PriceSet
::getPricesetCount($this->_priceSetId
));
379 $this->addFormRule(['CRM_Event_Form_Registration_Confirm', 'formRule'], $this);
385 * @param array $fields
386 * @param array $files
387 * @param CRM_Core_Form $self
391 public static function formRule($fields, $files, $self) {
393 $eventFull = CRM_Event_BAO_Participant
::eventFull($self->_eventId
, FALSE, CRM_Utils_Array
::value('has_waitlist', $self->_values
['event']));
394 if ($eventFull && empty($self->_allowConfirmation
)) {
395 if (empty($self->_allowWaitlist
)) {
396 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "reset=1&id={$self->_eventId}", FALSE, NULL, FALSE, TRUE));
399 $self->_feeBlock
= $self->_values
['fee'];
400 CRM_Event_Form_Registration_Register
::formatFieldsForOptionFull($self);
402 if (!empty($self->_priceSetId
) &&
403 !$self->_requireApproval
&& !$self->_allowWaitlist
405 $priceSetErrors = self
::validatePriceSet($self, $self->_params
);
406 if (!empty($priceSetErrors)) {
407 CRM_Core_Session
::setStatus(ts('You have been returned to the start of the registration process and any sold out events have been removed from your selections. You will not be able to continue until you review your booking and select different events if you wish.'), ts('Unfortunately some of your options have now sold out for one or more participants.'), 'error');
408 CRM_Core_Session
::setStatus(ts('Please note that the options which are marked or selected are sold out for participant being viewed.'), ts('Sold out:'), 'error');
409 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "_qf_Register_display=true&qfKey={$fields['qfKey']}"));
413 return empty($priceSetErrors) ?
TRUE : $priceSetErrors;
417 * Process the form submission.
419 public function postProcess() {
420 $now = date('YmdHis');
422 $this->_params
= $this->get('params');
423 $this->cleanMoneyFields($this->_params
);
425 if (!empty($this->_params
[0]['contact_id'])) {
426 // unclear when this would be set & whether it could be checked in getContactID.
427 // perhaps it relates to when cid is in the url
428 //@todo someone who knows add comments on the various contactIDs in this form
429 $contactID = $this->_params
[0]['contact_id'];
432 $contactID = $this->getContactID();
435 // if a discount has been applied, lets now deduct it from the amount
436 // and fix the fee level
437 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
438 foreach ($this->_params
as $k => $v) {
439 if (CRM_Utils_Array
::value('amount', $this->_params
[$k]) > 0 && !empty($this->_params
[$k]['discountAmount'])) {
440 $this->_params
[$k]['amount'] -= $this->_params
[$k]['discountAmount'];
441 $this->_params
[$k]['amount_level'] .= CRM_Utils_Array
::value('discountMessage', $this->_params
[$k]);
444 $this->set('params', $this->_params
);
447 // CRM-4320, lets build array of cancelled additional participant ids
448 // those are drop or skip by primary at the time of confirmation.
449 // get all in and then unset those we want to process.
450 $cancelledIds = $this->_additionalParticipantIds
;
452 $params = $this->_params
;
453 if ($this->_values
['event']['is_monetary']) {
454 $this->set('finalAmount', $this->_amount
);
456 $participantCount = [];
457 $taxAmount = $totalTaxAmount = 0;
459 //unset the skip participant from params.
460 //build the $participantCount array.
461 //maintain record for all participants.
462 foreach ($params as $participantNum => $record) {
463 if ($record == 'skip') {
464 unset($params[$participantNum]);
465 $participantCount[$participantNum] = 'skip';
467 elseif ($participantNum) {
468 $participantCount[$participantNum] = 'participant';
470 $totalTaxAmount +
= CRM_Utils_Array
::value('tax_amount', $record, 0);
471 if (CRM_Utils_Array
::value('is_primary', $record)) {
472 $taxAmount = &$params[$participantNum]['tax_amount'];
474 //lets get additional participant id to cancel.
475 if ($this->_allowConfirmation
&& is_array($cancelledIds)) {
476 $additonalId = CRM_Utils_Array
::value('participant_id', $record);
477 if ($additonalId && $key = array_search($additonalId, $cancelledIds)) {
478 unset($cancelledIds[$key]);
482 $taxAmount = $totalTaxAmount;
483 $payment = $registerByID = $primaryCurrencyID = $contribution = NULL;
484 $paymentObjError = ts('The system did not record payment details for this payment and so could not process the transaction. Please report this error to the site administrator.');
486 $this->participantIDS
= [];
488 foreach ($params as $key => $value) {
489 CRM_Event_Form_Registration_Confirm
::fixLocationFields($value, $fields, $this);
490 //unset the billing parameters if it is pay later mode
491 //to avoid creation of billing location
492 // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
493 // the form & if billing fields exist we create the address, relying on the form to collect
494 // only information we intend to store.
495 if ($this->_allowWaitlist
496 ||
$this->_requireApproval
497 ||
(!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater
)
498 ||
empty($value['is_primary'])
501 "email-{$this->_bltID}",
502 'billing_first_name',
503 'billing_middle_name',
505 "billing_street_address-{$this->_bltID}",
506 "billing_city-{$this->_bltID}",
507 "billing_state_province-{$this->_bltID}",
508 "billing_state_province_id-{$this->_bltID}",
509 "billing_postal_code-{$this->_bltID}",
510 "billing_country-{$this->_bltID}",
511 "billing_country_id-{$this->_bltID}",
512 "address_name-{$this->_bltID}",
514 foreach ($billingFields as $field) {
515 unset($value[$field]);
517 if (!empty($value['is_pay_later'])) {
518 $this->_values
['params']['is_pay_later'] = TRUE;
522 //Unset ContactID for additional participants and set RegisterBy Id.
523 if (empty($value['is_primary'])) {
524 $contactID = CRM_Utils_Array
::value('contact_id', $value);
525 $registerByID = $this->get('registerByID');
527 $value['registered_by_id'] = $registerByID;
531 $value['amount'] = $this->_totalAmount
;
534 $contactID = CRM_Event_Form_Registration_Confirm
::updateContactFields($contactID, $value, $fields, $this);
536 // lets store the contactID in the session
537 // we dont store in userID in case the user is doing multiple
539 // for things like tell a friend
540 if (!$this->getContactID() && !empty($value['is_primary'])) {
541 CRM_Core_Session
::singleton()->set('transaction.userID', $contactID);
544 $value['description'] = ts('Online Event Registration') . ': ' . $this->_values
['event']['title'];
545 $value['accountingCode'] = CRM_Utils_Array
::value('accountingCode',
546 $this->_values
['event']
550 if ($this->_allowWaitlist ||
$this->_requireApproval
) {
551 //get the participant statuses.
552 $waitingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Waiting'");
553 if ($this->_allowWaitlist
) {
554 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
557 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
560 //there might be case user selected pay later and
561 //now becomes part of run time waiting list.
562 $value['is_pay_later'] = FALSE;
564 elseif ($this->_values
['event']['is_monetary']) {
565 // required only if paid event
566 if (is_array($this->_paymentProcessor
)) {
567 $payment = $this->_paymentProcessor
['object'];
569 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
570 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
571 $value = array_merge($value, $preApprovalParams);
575 if (!empty($value['is_pay_later']) ||
576 $value['amount'] == 0 ||
577 // The concept of contributeMode is deprecated.
578 $this->_contributeMode
== 'checkout' ||
579 $this->_contributeMode
== 'notify'
581 if ($value['amount'] != 0) {
583 //get the participant statuses.
584 $pendingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Pending'");
585 $status = !empty($value['is_pay_later']) ?
'Pending from pay later' : 'Pending from incomplete transaction';
586 $value['participant_status_id'] = $value['participant_status'] = array_search($status, $pendingStatuses);
589 elseif (!empty($value['is_primary'])) {
590 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $value, $value, TRUE);
591 // payment email param can be empty for _bltID mapping
592 // thus provide mapping for it with a different email value
593 if (empty($value['email'])) {
594 $value['email'] = CRM_Utils_Array
::valueByRegexKey('/^email-/', $value);
597 if (is_object($payment)) {
598 // Not quite sure why we don't just user $value since it contains the data
600 // @todo ditch $result & retest.
601 list($result, $value) = $this->processPayment($payment, $value);
604 CRM_Core_Error
::fatal($paymentObjError);
608 $value['receive_date'] = $now;
609 if ($this->_allowConfirmation
) {
610 $value['participant_register_date'] = $this->_values
['participant']['register_date'];
613 $createContrib = ($value['amount'] != 0) ?
TRUE : FALSE;
614 // force to create zero amount contribution, CRM-5095
615 if (!$createContrib && ($value['amount'] == 0)
616 && $this->_priceSetId
&& $this->_lineItem
618 $createContrib = TRUE;
621 if ($createContrib && !empty($value['is_primary']) &&
622 !$this->_allowWaitlist
&& !$this->_requireApproval
624 // if paid event add a contribution record
625 //if primary participant contributing additional amount
626 //append (multiple participants) to its fee level. CRM-4196.
627 $isAdditionalAmount = FALSE;
628 if (count($params) > 1) {
629 $isAdditionalAmount = TRUE;
632 //passing contribution id is already registered.
633 $contribution = self
::processContribution($this, $value, $result, $contactID, $pending, $isAdditionalAmount, $this->_paymentProcessor
);
634 $value['contributionID'] = $contribution->id
;
635 $value['contributionTypeID'] = $contribution->financial_type_id
;
636 $value['receive_date'] = $contribution->receive_date
;
637 $value['trxn_id'] = $contribution->trxn_id
;
638 $value['contributionID'] = $contribution->id
;
639 $value['contributionTypeID'] = $contribution->financial_type_id
;
641 $value['contactID'] = $contactID;
642 $value['eventID'] = $this->_eventId
;
643 $value['item_name'] = $value['description'];
646 if (!empty($value['contributionID'])) {
647 $this->_values
['contributionId'] = $value['contributionID'];
651 if (!empty($value['is_primary'])) {
652 $primaryCurrencyID = CRM_Utils_Array
::value('currencyID', $value);
654 if (empty($value['currencyID'])) {
655 $value['currencyID'] = $primaryCurrencyID;
658 // CRM-11182 - Confirmation page might not be monetary
659 if ($this->_values
['event']['is_monetary']) {
660 if (!$pending && !empty($value['is_primary']) &&
661 !$this->_allowWaitlist
&& !$this->_requireApproval
663 // transactionID & receive date required while building email template
664 $this->assign('trxn_id', CRM_Utils_Array
::value('trxn_id', $value));
665 $this->assign('receive_date', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
666 $this->set('receiveDate', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
667 $this->set('trxnId', CRM_Utils_Array
::value('trxn_id', $value));
671 $value['fee_amount'] = CRM_Utils_Array
::value('amount', $value);
672 $this->set('value', $value);
674 // handle register date CRM-4320
675 if ($this->_allowConfirmation
) {
676 $registerDate = CRM_Utils_Array
::value('participant_register_date', $params);
678 elseif (!empty($params['participant_register_date']) &&
679 is_array($params['participant_register_date']) &&
680 !empty($params['participant_register_date'])
682 $registerDate = CRM_Utils_Date
::format($params['participant_register_date']);
685 $registerDate = date('YmdHis');
687 $this->assign('register_date', $registerDate);
689 $this->confirmPostProcess($contactID, $contribution, $payment);
692 //handle if no additional participant.
693 if (!$registerByID) {
694 $registerByID = $this->get('registerByID');
697 $this->set('participantIDs', $this->_participantIDS
);
699 // create line items, CRM-5313
700 if ($this->_priceSetId
&&
701 !empty($this->_lineItem
)
703 // take all processed participant ids.
704 $allParticipantIds = $this->_participantIDS
;
706 // when participant re-walk wizard.
707 if ($this->_allowConfirmation
&&
708 !empty($this->_additionalParticipantIds
)
710 $allParticipantIds = array_merge([$registerByID], $this->_additionalParticipantIds
);
713 $entityTable = 'civicrm_participant';
714 $invoiceSettings = Civi
::settings()->get('contribution_invoice_settings');
715 $invoicing = CRM_Utils_Array
::value('invoicing', $invoiceSettings);
718 foreach ($this->_lineItem
as $key => $value) {
719 if ($value == 'skip') {
722 if ($entityId = CRM_Utils_Array
::value($key, $allParticipantIds)) {
723 // do cleanup line items if participant re-walking wizard.
724 if ($this->_allowConfirmation
) {
725 CRM_Price_BAO_LineItem
::deleteLineItems($entityId, $entityTable);
727 $lineItem[$this->_priceSetId
] = $value;
728 CRM_Price_BAO_LineItem
::processPriceSet($entityId, $lineItem, $contribution, $entityTable);
731 foreach ($value as $line) {
732 if (isset($line['tax_amount']) && isset($line['tax_rate'])) {
733 $totalTaxAmount = $line['tax_amount'] +
$totalTaxAmount;
734 if (isset($dataArray[$line['tax_rate']])) {
735 $dataArray[$line['tax_rate']] = $dataArray[$line['tax_rate']] + CRM_Utils_Array
::value('tax_amount', $line);
738 $dataArray[$line['tax_rate']] = CRM_Utils_Array
::value('tax_amount', $line);
745 $this->assign('dataArray', $dataArray);
746 $this->assign('totalTaxAmount', $totalTaxAmount);
750 //update status and send mail to cancelled additional participants, CRM-4320
751 if ($this->_allowConfirmation
&& is_array($cancelledIds) && !empty($cancelledIds)) {
752 $cancelledId = array_search('Cancelled',
753 CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Negative'")
755 CRM_Event_BAO_Participant
::transitionParticipants($cancelledIds, $cancelledId);
759 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
763 // for Transfer checkout.
764 // The concept of contributeMode is deprecated.
765 if (($this->_contributeMode
== 'checkout' ||
766 $this->_contributeMode
== 'notify'
767 ) && empty($params[0]['is_pay_later']) &&
768 !$this->_allowWaitlist
&& !$this->_requireApproval
&&
769 $this->_totalAmount
> 0
772 $primaryParticipant = $this->get('primaryParticipant');
774 if (empty($primaryParticipant['participantID'])) {
775 $primaryParticipant['participantID'] = $registerByID;
778 //build an array of custom profile and assigning it to template
779 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, $this->_values
, NULL, $isTest);
780 if (count($customProfile)) {
781 $this->assign('customProfile', $customProfile);
782 $this->set('customProfile', $customProfile);
785 // do a transfer only if a monetary payment greater than 0
786 if ($this->_values
['event']['is_monetary'] && $primaryParticipant) {
787 if ($payment && is_object($payment)) {
788 //CRM 14512 provide line items of all participants to payment gateway
789 $primaryContactId = $this->get('primaryContactId');
791 //build an array of cId/pId of participants
792 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, NULL, $primaryContactId, $isTest, TRUE);
794 //need to copy, since we are unsetting on the way.
795 $copyParticipantCountLines = $participantCount;
797 //lets carry all participant params w/ values.
798 foreach ($additionalIDs as $participantID => $contactId) {
799 $participantNum = NULL;
800 $participantNum = $participantID;
801 if ($participantID == $registerByID) {
802 // This is the is primary participant.
806 if ($participantNum = array_search('participant', $copyParticipantCountLines)) {
807 //if no participant found break.
808 if ($participantNum === NULL) {
811 //unset current participant so we don't check them again
812 unset($copyParticipantCountLines[$participantNum]);
815 // get values of line items
816 if ($this->_amount
) {
818 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
819 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
820 $params[$participantNum]['amounts'] = $amount;
823 if (!empty($this->_lineItem
)) {
824 $lineItems = $this->_lineItem
;
826 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
827 $lineItem[] = $lineItemValue;
829 $params[$participantNum]['lineItem'] = $lineItem;
832 //only add additional particpants and not the primary particpant as we already have that
833 //added to $primaryParticipant so that this change doesn't break or require changes to
834 //existing gateway implementations
835 $primaryParticipant['participants_info'][$participantID] = $params[$participantNum];
838 //get event custom field information
839 $groupTree = CRM_Core_BAO_CustomGroup
::getTree('Event', NULL, $this->_eventId
, 0, $this->_values
['event']['event_type_id']);
840 $primaryParticipant['eventCustomFields'] = $groupTree;
842 // call postprocess hook before leaving
843 $this->postProcessHook();
845 $this->processPayment($payment, $primaryParticipant);
848 CRM_Core_Error
::fatal($paymentObjError);
853 //otherwise send mail Confirmation/Receipt
854 $primaryContactId = $this->get('primaryContactId');
856 //build an array of cId/pId of participants
857 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID,
858 NULL, $primaryContactId, $isTest,
861 //let's send mails to all with meaningful text, CRM-4320.
862 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
863 $this->assign('isRequireApproval', $this->_requireApproval
);
865 //need to copy, since we are unsetting on the way.
866 $copyParticipantCount = $participantCount;
868 //let's carry all participant params w/ values.
869 foreach ($additionalIDs as $participantID => $contactId) {
870 $participantNum = NULL;
871 if ($participantID == $registerByID) {
875 if ($participantNum = array_search('participant', $copyParticipantCount)) {
876 unset($copyParticipantCount[$participantNum]);
879 if ($participantNum === NULL) {
883 //carry the participant submitted values.
884 $this->_values
['params'][$participantID] = $params[$participantNum];
887 foreach ($additionalIDs as $participantID => $contactId) {
889 if ($participantID == $registerByID) {
890 //set as Primary Participant
891 $this->assign('isPrimary', 1);
892 //build an array of custom profile and assigning it to template.
893 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($participantID, $this->_values
, NULL, $isTest);
895 if (count($customProfile)) {
896 $this->assign('customProfile', $customProfile);
897 $this->set('customProfile', $customProfile);
899 $this->_values
['params']['additionalParticipant'] = FALSE;
902 //take the Additional participant number.
903 if ($participantNum = array_search('participant', $participantCount)) {
904 unset($participantCount[$participantNum]);
906 // Change $this->_values['participant'] to include additional participant values
907 $ids = $participantValues = [];
908 $participantParams = ['id' => $participantID];
909 CRM_Event_BAO_Participant
::getValues($participantParams, $participantValues, $ids);
910 $this->_values
['participant'] = $participantValues[$participantID];
912 $this->assign('isPrimary', 0);
913 $this->assign('customProfile', NULL);
914 //Additional Participant should get only it's payment information
915 if (!empty($this->_amount
)) {
917 $params = $this->get('params');
918 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
919 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
920 $this->assign('amounts', $amount);
922 if ($this->_lineItem
) {
923 $lineItems = $this->_lineItem
;
925 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
926 $lineItem[] = $lineItemValue;
929 $individual = $this->get('individual');
930 $dataArray[key($dataArray)] = $individual[$participantNum]['totalTaxAmt'];
931 $this->assign('dataArray', $dataArray);
932 $this->assign('totalAmount', $individual[$participantNum]['totalAmtWithTax']);
933 $this->assign('totalTaxAmount', $individual[$participantNum]['totalTaxAmt']);
934 $this->assign('individual', [$individual[$participantNum]]);
936 $this->assign('lineItem', $lineItem);
938 $this->_values
['params']['additionalParticipant'] = TRUE;
939 $this->assign('isAdditionalParticipant', $this->_values
['params']['additionalParticipant']);
942 //pass these variables since these are run time calculated.
943 $this->_values
['params']['isOnWaitlist'] = $this->_allowWaitlist
;
944 $this->_values
['params']['isRequireApproval'] = $this->_requireApproval
;
946 //send mail to primary as well as additional participants.
947 $this->assign('contactID', $contactId);
948 $this->assign('participantID', $participantID);
949 CRM_Event_BAO_Event
::sendMail($contactId, $this->_values
, $participantID, $isTest);
955 * Process the contribution.
957 * @param CRM_Core_Form $form
958 * @param array $params
959 * @param array $result
960 * @param int $contactID
961 * @param bool $pending
962 * @param bool $isAdditionalAmount
963 * @param array $paymentProcessor
965 * @return \CRM_Contribute_BAO_Contribution
966 * @throws \CRM_Core_Exception
968 public static function processContribution(
969 &$form, $params, $result, $contactID,
970 $pending = FALSE, $isAdditionalAmount = FALSE,
971 $paymentProcessor = NULL
973 $transaction = new CRM_Core_Transaction();
975 $now = date('YmdHis');
978 if (!empty($form->_values
['event']['is_email_confirm'])) {
982 if ($isAdditionalAmount) {
983 $params['amount_level'] = $params['amount_level'] . ts(' (multiple participants)') . CRM_Core_DAO
::VALUE_SEPARATOR
;
986 // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
987 CRM_Contribute_Form_AbstractEditPayment
::formatCreditCardDetails($params);
990 'contact_id' => $contactID,
991 'financial_type_id' => !empty($form->_values
['event']['financial_type_id']) ?
$form->_values
['event']['financial_type_id'] : $params['financial_type_id'],
992 'receive_date' => $now,
993 'total_amount' => $params['amount'],
994 'tax_amount' => $params['tax_amount'],
995 'amount_level' => $params['amount_level'],
996 'invoice_id' => $params['invoiceID'],
997 'currency' => $params['currencyID'],
998 'source' => !empty($params['participant_source']) ?
$params['participant_source'] : $params['description'],
999 'is_pay_later' => CRM_Utils_Array
::value('is_pay_later', $params, 0),
1000 'campaign_id' => CRM_Utils_Array
::value('campaign_id', $params),
1001 'card_type_id' => CRM_Utils_Array
::value('card_type_id', $params),
1002 'pan_truncation' => CRM_Utils_Array
::value('pan_truncation', $params),
1005 if ($paymentProcessor) {
1006 $contribParams['payment_instrument_id'] = $paymentProcessor['payment_instrument_id'];
1007 $contribParams['payment_processor'] = $paymentProcessor['id'];
1010 if (!$pending && $result) {
1012 'fee_amount' => CRM_Utils_Array
::value('fee_amount', $result),
1013 'net_amount' => CRM_Utils_Array
::value('net_amount', $result, $params['amount']),
1014 'trxn_id' => $result['trxn_id'],
1015 'receipt_date' => $receiptDate,
1019 $allStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
1020 $contribParams['contribution_status_id'] = array_search('Completed', $allStatuses);
1022 $contribParams['contribution_status_id'] = array_search('Pending', $allStatuses);
1025 $contribParams['is_test'] = 0;
1026 if ($form->_action
& CRM_Core_Action
::PREVIEW || CRM_Utils_Array
::value('mode', $params) == 'test') {
1027 $contribParams['is_test'] = 1;
1030 if (!empty($contribParams['invoice_id'])) {
1031 $contribParams['id'] = CRM_Core_DAO
::getFieldValue('CRM_Contribute_DAO_Contribution',
1032 $contribParams['invoice_id'],
1038 if (Civi
::settings()->get('deferred_revenue_enabled')) {
1039 $eventStartDate = CRM_Utils_Array
::value(
1041 CRM_Utils_Array
::value(
1046 if (strtotime($eventStartDate) > strtotime(date('Ymt'))) {
1047 $contribParams['revenue_recognition_date'] = date('Ymd', strtotime($eventStartDate));
1050 //create an contribution address
1051 // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although
1052 // currently that is only inherited by back-office forms.
1053 if ($form->_contributeMode
!= 'notify' && empty($params['is_pay_later'])) {
1054 $contribParams['address_id'] = CRM_Contribute_BAO_Contribution
::createAddress($params, $form->_bltID
);
1057 $contribParams['skipLineItem'] = 1;
1058 $contribParams['skipCleanMoney'] = 1;
1059 // create contribution record
1060 $contribution = CRM_Contribute_BAO_Contribution
::add($contribParams);
1062 CRM_Event_BAO_Participant
::createDiscountTrxn($form->_eventId
, $contribParams, NULL, CRM_Price_BAO_PriceSet
::parseFirstPriceSetValueIDFromParams($params));
1064 // process soft credit / pcp pages
1065 if (!empty($params['pcp_made_through_id'])) {
1066 CRM_Contribute_BAO_ContributionSoft
::formatSoftCreditParams($params, $form);
1067 CRM_Contribute_BAO_ContributionSoft
::processSoftContribution($params, $contribution);
1070 $transaction->commit();
1072 return $contribution;
1076 * Fix the Location Fields.
1078 * @todo Reconcile with the contribution method formatParamsForPaymentProcessor
1079 * rather than adding different logic to check when to keep the billing
1080 * fields. There might be a difference in handling guest/multiple
1081 * participants though.
1083 * @param array $params
1084 * @param array $fields
1085 * @param CRM_Core_Form $form
1087 public static function fixLocationFields(&$params, &$fields, &$form) {
1088 if (!empty($form->_fields
)) {
1089 foreach ($form->_fields
as $name => $dontCare) {
1094 // If there's no 'first_name' in the profile then overwrite the names from
1095 // the billing fields (if they are set)
1096 if (is_array($fields)) {
1097 if (!array_key_exists('first_name', $fields)) {
1098 $nameFields = ['first_name', 'middle_name', 'last_name'];
1099 foreach ($nameFields as $name) {
1101 if (array_key_exists("billing_$name", $params)) {
1102 $params[$name] = $params["billing_{$name}"];
1103 $params['preserveDBName'] = TRUE;
1109 // Add the billing names to the billing address, if a billing name is set
1110 if (!empty($params['billing_first_name'])) {
1111 $params["address_name-{$form->_bltID}"] = CRM_Utils_Array
::value('billing_first_name', $params) . ' ' . CRM_Utils_Array
::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array
::value('billing_last_name', $params);
1112 $fields["address_name-{$form->_bltID}"] = 1;
1115 $fields["email-{$form->_bltID}"] = 1;
1116 $fields['email-Primary'] = 1;
1118 //if its pay later or additional participant set email address as primary.
1119 if ((!empty($params['is_pay_later']) ||
empty($params['is_primary']) ||
1120 !$form->_values
['event']['is_monetary'] ||
1121 $form->_allowWaitlist ||
1122 $form->_requireApproval
1123 ) && !empty($params["email-{$form->_bltID}"])
1125 $params['email-Primary'] = $params["email-{$form->_bltID}"];
1130 * Update contact fields.
1132 * @param int $contactID
1133 * @param array $params
1134 * @param array $fields
1135 * @param CRM_Core_Form $form
1139 public static function updateContactFields($contactID, $params, $fields, &$form) {
1140 //add the contact to group, if add to group is selected for a
1141 //particular uf group
1143 // get the add to groups
1146 if (!empty($form->_fields
)) {
1147 foreach ($form->_fields
as $key => $value) {
1148 if (!empty($value['add_to_group_id'])) {
1149 $addToGroups[$value['add_to_group_id']] = $value['add_to_group_id'];
1154 // check for profile double opt-in and get groups to be subscribed
1155 $subscribeGroupIds = CRM_Core_BAO_UFGroup
::getDoubleOptInGroupIds($params, $contactID);
1157 foreach ($addToGroups as $k) {
1158 if (array_key_exists($k, $subscribeGroupIds)) {
1159 unset($addToGroups[$k]);
1163 // since we are directly adding contact to group lets unset it from mailing
1164 if (!empty($addToGroups)) {
1165 foreach ($addToGroups as $groupId) {
1166 if (isset($subscribeGroupIds[$groupId])) {
1167 unset($subscribeGroupIds[$groupId]);
1172 $ctype = CRM_Core_DAO
::getFieldValue(
1173 'CRM_Contact_DAO_Contact',
1178 if (array_key_exists('contact_id', $params) && empty($params['contact_id'])) {
1179 // we unset this here because the downstream function ignores the contactID we give it
1180 // if it is set & it is difficult to understand the implications of 'fixing' this downstream
1181 // but if we are passing a contact id into this function it's reasonable to assume we don't
1183 unset($params['contact_id']);
1186 $contactID = CRM_Contact_BAO_Contact
::createProfileContact(
1198 foreach (CRM_Contact_BAO_Contact
::$_greetingTypes as $greeting) {
1199 if (!isset($params[$greeting . '_id'])) {
1200 $params[$greeting . '_id'] = CRM_Contact_BAO_Contact_Utils
::defaultGreeting('Individual', $greeting);
1204 $contactID = CRM_Contact_BAO_Contact
::createProfileContact($params,
1212 $form->set('contactID', $contactID);
1215 //get email primary first if exist
1216 $subscribtionEmail = ['email' => CRM_Utils_Array
::value('email-Primary', $params)];
1217 if (!$subscribtionEmail['email']) {
1218 $subscribtionEmail['email'] = CRM_Utils_Array
::value("email-{$form->_bltID}", $params);
1220 // subscribing contact to groups
1221 if (!empty($subscribeGroupIds) && $subscribtionEmail['email']) {
1222 CRM_Mailing_Event_BAO_Subscribe
::commonSubscribe($subscribeGroupIds, $subscribtionEmail, $contactID);
1231 * @param CRM_Core_Form $form
1233 public static function assignProfiles(&$form) {
1234 $participantParams = $form->_params
;
1235 $formattedValues = $profileFields = [];
1237 foreach ($participantParams as $participantNum => $participantValue) {
1238 if ($participantNum) {
1239 $prefix1 = 'additional';
1240 $prefix2 = 'additional_';
1246 if ($participantValue != 'skip') {
1247 //get the customPre profile info
1248 if (!empty($form->_values
[$prefix2 . 'custom_pre_id'])) {
1249 $values = $groupName = [];
1250 CRM_Event_BAO_Event
::displayProfile($participantValue,
1251 $form->_values
[$prefix2 . 'custom_pre_id'],
1257 if (count($values)) {
1258 $formattedValues[$count][$prefix1 . 'CustomPre'] = $values;
1260 $formattedValues[$count][$prefix1 . 'CustomPreGroupTitle'] = CRM_Utils_Array
::value('groupTitle', $groupName);
1262 //get the customPost profile info
1263 if (!empty($form->_values
[$prefix2 . 'custom_post_id'])) {
1264 $values = $groupName = [];
1265 foreach ($form->_values
[$prefix2 . 'custom_post_id'] as $gids) {
1267 CRM_Event_BAO_Event
::displayProfile($participantValue,
1273 $values[$gids] = $val;
1274 $groupName[$gids] = $group;
1277 if (count($values)) {
1278 $formattedValues[$count][$prefix1 . 'CustomPost'] = $values;
1281 if (isset($formattedValues[$count][$prefix1 . 'CustomPre'])) {
1282 $formattedValues[$count][$prefix1 . 'CustomPost'] = array_diff_assoc($formattedValues[$count][$prefix1 . 'CustomPost'],
1283 $formattedValues[$count][$prefix1 . 'CustomPre']
1287 $formattedValues[$count][$prefix1 . 'CustomPostGroupTitle'] = $groupName;
1291 $form->_fields
= $profileFields;
1293 if (!empty($formattedValues)) {
1294 $form->assign('primaryParticipantProfile', $formattedValues[1]);
1295 $form->set('primaryParticipantProfile', $formattedValues[1]);
1297 unset($formattedValues[1]);
1298 $form->assign('addParticipantProfile', $formattedValues);
1299 $form->set('addParticipantProfile', $formattedValues);
1305 * Submit in test mode.
1309 public static function testSubmit($params) {
1310 $form = new CRM_Event_Form_Registration_Confirm();
1311 // This way the mocked up controller ignores the session stuff.
1312 $_SERVER['REQUEST_METHOD'] = 'GET';
1313 $_REQUEST['id'] = $form->_eventId
= $params['id'];
1314 $form->controller
= new CRM_Event_Controller_Registration();
1315 $form->_params
= $params['params'];
1316 // This happens in buildQuickForm so emulate here.
1317 $form->_amount
= $form->_totalAmount
= CRM_Utils_Rule
::cleanMoney(CRM_Utils_Array
::value('totalAmount', $params));
1318 $form->set('params', $params['params']);
1319 $form->_values
['custom_pre_id'] = [];
1320 $form->_values
['custom_post_id'] = [];
1321 $form->_values
['event'] = CRM_Utils_Array
::value('event', $params);
1322 $form->_contributeMode
= $params['contributeMode'];
1323 $eventParams = ['id' => $params['id']];
1324 CRM_Event_BAO_Event
::retrieve($eventParams, $form->_values
['event']);
1325 $form->set('registerByID', $params['registerByID']);
1326 if (!empty($params['paymentProcessorObj'])) {
1327 $form->_paymentProcessor
= $params['paymentProcessorObj'];
1329 $form->postProcess();
1333 * Process the payment, redirecting back to the page on error.
1340 private function processPayment($payment, $value) {
1342 $result = $payment->doPayment($value, 'event');
1343 return [$result, $value];
1345 catch (\Civi\Payment\Exception\PaymentProcessorException
$e) {
1346 Civi
::log()->error('Payment processor exception: ' . $e->getMessage());
1347 CRM_Core_Session
::singleton()->setStatus($e->getMessage());
1348 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "id={$this->_eventId}"));
1354 * Clean money fields from the form.
1356 * @param array $params
1358 protected function cleanMoneyFields(&$params) {
1359 foreach ($this->submittableMoneyFields
as $moneyField) {
1360 foreach ($params as $index => $paramField) {
1361 if (isset($paramField[$moneyField])) {
1362 $params[$index][$moneyField] = CRM_Utils_Rule
::cleanMoney($paramField[$moneyField]);