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
{
37 use CRM_Financial_Form_FrontEndPaymentFormTrait
;
40 * The values for the contribution db object.
53 public $submitOnce = TRUE;
56 * Monetary fields that may be submitted.
58 * These should get a standardised format in the beginPostProcess function.
60 * These fields are common to many forms. Some may override this.
63 protected $submittableMoneyFields = ['total_amount', 'net_amount', 'non_deductible_amount', 'fee_amount', 'tax_amount', 'amount'];
66 * Set variables up before form is built.
68 public function preProcess() {
71 // lineItem isn't set until Register postProcess
72 $this->_lineItem
= $this->get('lineItem');
74 $this->_params
= $this->get('params');
75 $this->_params
[0]['tax_amount'] = $this->get('tax_amount');
77 $this->_params
[0]['is_pay_later'] = $this->get('is_pay_later');
78 $this->assign('is_pay_later', $this->_params
[0]['is_pay_later']);
79 if ($this->_params
[0]['is_pay_later']) {
80 $this->assign('pay_later_receipt', $this->_values
['event']['pay_later_receipt']);
83 CRM_Utils_Hook
::eventDiscount($this, $this->_params
);
85 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
86 $this->set('hookDiscount', $this->_params
[0]['discount']);
87 $this->assign('hookDiscount', $this->_params
[0]['discount']);
90 if (!$this->preProcessExpress()) {
91 //process only primary participant params.
92 $registerParams = $this->_params
[0];
93 if (isset($registerParams["billing_state_province_id-{$this->_bltID}"])
94 && $registerParams["billing_state_province_id-{$this->_bltID}"]
96 $registerParams["billing_state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant
::stateProvinceAbbreviation($registerParams["billing_state_province_id-{$this->_bltID}"]);
99 if (isset($registerParams["billing_country_id-{$this->_bltID}"]) && $registerParams["billing_country_id-{$this->_bltID}"]) {
100 $registerParams["billing_country-{$this->_bltID}"] = CRM_Core_PseudoConstant
::countryIsoCode($registerParams["billing_country_id-{$this->_bltID}"]);
102 if (isset($registerParams['credit_card_exp_date'])) {
103 $registerParams['year'] = CRM_Core_Payment_Form
::getCreditCardExpirationYear($registerParams);
104 $registerParams['month'] = CRM_Core_Payment_Form
::getCreditCardExpirationMonth($registerParams);
106 if ($this->_values
['event']['is_monetary']) {
107 $registerParams['ip_address'] = CRM_Utils_System
::ipAddress();
108 $registerParams['currencyID'] = $this->_params
[0]['currencyID'];
110 //assign back primary participant params.
111 $this->_params
[0] = $registerParams;
114 if ($this->_values
['event']['is_monetary']) {
115 $this->_params
[0]['invoiceID'] = $this->get('invoiceID');
117 $this->assign('defaultRole', FALSE);
118 if (CRM_Utils_Array
::value('defaultRole', $this->_params
[0]) == 1) {
119 $this->assign('defaultRole', TRUE);
122 if (empty($this->_params
[0]['participant_role_id']) &&
123 $this->_values
['event']['default_role_id']
125 $this->_params
[0]['participant_role_id'] = $this->_values
['event']['default_role_id'];
128 if (isset($this->_values
['event']['confirm_title'])) {
129 CRM_Utils_System
::setTitle($this->_values
['event']['confirm_title']);
132 // Personal campaign page
134 $params = CRM_Contribute_Form_Contribution_Confirm
::processPcp($this, $this->_params
[0]);
135 $this->_params
[0] = $params;
138 $this->set('params', $this->_params
);
142 * Pre process function for Paypal Express confirm.
143 * @todo this is just a step in refactor as payment processor specific code does not belong in generic forms
146 * @throws \CRM_Core_Exception
148 private function preProcessExpress() {
149 if ($this->_contributeMode
!== 'express') {
153 // rfp == redirect from paypal
154 // @fixme rfp is probably not required - the getPreApprovalDetails should deal with any payment-processor specific 'stuff'
155 $rfp = CRM_Utils_Request
::retrieve('rfp', 'Boolean', CRM_Core_DAO
::$_nullObject, FALSE, NULL, 'GET');
157 //we lost rfp in case of additional participant. So set it explicitly.
158 if ($rfp || CRM_Utils_Array
::value('additional_participants', $this->_params
[0], FALSE)) {
159 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
160 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
161 $params = array_merge($this->_params
, $preApprovalParams);
163 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $params, $params, FALSE);
165 // set a few other parameters that are not really specific to this method because we don't know what
166 // will break if we change this.
167 $params['amount'] = $this->_params
[0]['amount'];
168 if (!empty($this->_params
[0]['discount'])) {
169 $params['discount'] = $this->_params
[0]['discount'];
170 $params['discountAmount'] = $this->_params
[0]['discountAmount'];
171 $params['discountMessage'] = $this->_params
[0]['discountMessage'];
174 $params['amount_level'] = $this->_params
[0]['amount_level'];
175 $params['currencyID'] = $this->_params
[0]['currencyID'];
177 // also merge all the other values from the profile fields
178 $values = $this->controller
->exportValues('Register');
181 "street_address-{$this->_bltID}",
182 "city-{$this->_bltID}",
183 "state_province_id-{$this->_bltID}",
184 "postal_code-{$this->_bltID}",
185 "country_id-{$this->_bltID}",
188 foreach ($values as $name => $value) {
190 if (!in_array($name, $skipFields)) {
191 $params[$name] = $value;
193 if (substr($name, 0, 6) == 'price_') {
194 $params[$name] = $this->_params
[0][$name];
197 $this->set('getExpressCheckoutDetails', $params);
199 $this->_params
[0] = array_merge($this->_params
[0], $params);
200 $this->_params
[0]['is_primary'] = 1;
205 * Overwrite action, since we are only showing elements in frozen mode no help display needed.
209 public function getAction() {
210 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
211 return CRM_Core_Action
::VIEW | CRM_Core_Action
::PREVIEW
;
214 return CRM_Core_Action
::VIEW
;
219 * Build the form object.
221 public function buildQuickForm() {
222 $this->assignToTemplate();
224 if ($this->_values
['event']['is_monetary'] &&
225 ($this->_params
[0]['amount'] ||
$this->_params
[0]['amount'] == 0) &&
226 !$this->_requireApproval
231 foreach ($this->_params
as $k => $v) {
235 $individualTaxAmount = 0;
236 //display tax amount on confirmation page
237 $taxAmount +
= $v['tax_amount'];
239 $this->cleanMoneyFields($v);
244 if (isset($v['billing_' . $name]) &&
247 $v[$name] = $v['billing_' . $name];
251 if (!empty($v['first_name']) && !empty($v['last_name'])) {
252 $append = $v['first_name'] . ' ' . $v['last_name'];
255 //use an email if we have one
256 foreach ($v as $v_key => $v_val) {
257 if (substr($v_key, 0, 6) == 'email-') {
258 $append = $v[$v_key];
263 $this->_amount
[$k]['amount'] = $v['amount'];
264 if (!empty($v['discountAmount'])) {
265 $this->_amount
[$k]['amount'] -= $v['discountAmount'];
268 $this->_amount
[$k]['label'] = preg_replace('/\ 1/', '', $v['amount_level']) . ' - ' . $append;
269 $this->_part
[$k]['info'] = CRM_Utils_Array
::value('first_name', $v) . ' ' . CRM_Utils_Array
::value('last_name', $v);
270 if (empty($v['first_name'])) {
271 $this->_part
[$k]['info'] = $append;
275 $individual[$k]['totalAmtWithTax'] = $this->_amount
[$k]['amount'];
276 $individual[$k]['totalTaxAmt'] = $individualTaxAmount +
$v['tax_amount'];
277 $this->_totalAmount
= $this->_totalAmount +
$this->_amount
[$k]['amount'];
278 if (!empty($v['is_primary'])) {
279 $this->set('primaryParticipantAmount', $this->_amount
[$k]['amount']);
284 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
285 $this->assign('totalTaxAmount', $taxAmount);
286 $this->assign('taxTerm', CRM_Invoicing_Utils
::getTaxTerm());
287 $this->assign('individual', $individual);
288 $this->set('individual', $individual);
291 $this->assign('part', $this->_part
);
292 $this->set('part', $this->_part
);
293 $this->assign('amounts', $this->_amount
);
294 $this->assign('totalAmount', $this->_totalAmount
);
295 $this->set('totalAmount', $this->_totalAmount
);
298 if ($this->_priceSetId
&& !CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId
, 'is_quick_config')) {
299 $lineItemForTemplate = [];
300 if (!empty($this->_lineItem
) && is_array($this->_lineItem
)) {
301 foreach ($this->_lineItem
as $key => $value) {
302 if (!empty($value)) {
303 $lineItemForTemplate[$key] = $value;
307 if (!empty($lineItemForTemplate)) {
308 $this->assignLineItemsToTemplate($lineItemForTemplate);
312 //display additional participants profile.
313 self
::assignProfiles($this);
315 //consider total amount.
316 $this->assign('isAmountzero', ($this->_totalAmount
<= 0) ?
TRUE : FALSE);
318 $contribButton = ts('Continue');
322 'name' => ts('Go Back'),
326 'name' => $contribButton,
333 if (!empty($this->_fields
)) {
334 foreach ($this->_fields
as $name => $dontCare) {
338 $fields["billing_state_province-{$this->_bltID}"] = $fields["billing_country-{$this->_bltID}"] = $fields["email-{$this->_bltID}"] = 1;
339 foreach ($fields as $name => $dontCare) {
340 if (isset($this->_params
[0][$name])) {
341 $defaults[$name] = $this->_params
[0][$name];
342 if (substr($name, 0, 7) == 'custom_') {
343 $timeField = "{$name}_time";
344 if (isset($this->_params
[0][$timeField])) {
345 $defaults[$timeField] = $this->_params
[0][$timeField];
347 if (isset($this->_params
[0]["{$name}_id"])) {
348 $defaults["{$name}_id"] = $this->_params
[0]["{$name}_id"];
351 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)
352 && !empty($this->_params
[0][$name . '_custom'])
354 $defaults[$name . '_custom'] = $this->_params
[0][$name . '_custom'];
359 $this->setDefaults($defaults);
362 //lets give meaningful status message, CRM-4320.
363 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
364 $this->assign('isRequireApproval', $this->_requireApproval
);
366 // Assign Participant Count to Lineitem Table
367 $this->assign('pricesetFieldsCount', CRM_Price_BAO_PriceSet
::getPricesetCount($this->_priceSetId
));
368 $this->addFormRule(['CRM_Event_Form_Registration_Confirm', 'formRule'], $this);
374 * @param array $fields
375 * @param array $files
376 * @param CRM_Core_Form $self
380 public static function formRule($fields, $files, $self) {
382 $eventFull = CRM_Event_BAO_Participant
::eventFull($self->_eventId
, FALSE, CRM_Utils_Array
::value('has_waitlist', $self->_values
['event']));
383 if ($eventFull && empty($self->_allowConfirmation
)) {
384 if (empty($self->_allowWaitlist
)) {
385 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "reset=1&id={$self->_eventId}", FALSE, NULL, FALSE, TRUE));
388 $self->_feeBlock
= $self->_values
['fee'];
389 CRM_Event_Form_Registration_Register
::formatFieldsForOptionFull($self);
391 if (!empty($self->_priceSetId
) &&
392 !$self->_requireApproval
&& !$self->_allowWaitlist
394 $priceSetErrors = self
::validatePriceSet($self, $self->_params
);
395 if (!empty($priceSetErrors)) {
396 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');
397 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');
398 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "_qf_Register_display=true&qfKey={$fields['qfKey']}"));
402 return empty($priceSetErrors) ?
TRUE : $priceSetErrors;
406 * Process the form submission.
408 public function postProcess() {
409 $now = date('YmdHis');
411 $this->_params
= $this->get('params');
412 $this->cleanMoneyFields($this->_params
);
414 if (!empty($this->_params
[0]['contact_id'])) {
415 // unclear when this would be set & whether it could be checked in getContactID.
416 // perhaps it relates to when cid is in the url
417 //@todo someone who knows add comments on the various contactIDs in this form
418 $contactID = $this->_params
[0]['contact_id'];
421 $contactID = $this->getContactID();
424 // if a discount has been applied, lets now deduct it from the amount
425 // and fix the fee level
426 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
427 foreach ($this->_params
as $k => $v) {
428 if (CRM_Utils_Array
::value('amount', $this->_params
[$k]) > 0 && !empty($this->_params
[$k]['discountAmount'])) {
429 $this->_params
[$k]['amount'] -= $this->_params
[$k]['discountAmount'];
430 $this->_params
[$k]['amount_level'] .= CRM_Utils_Array
::value('discountMessage', $this->_params
[$k]);
433 $this->set('params', $this->_params
);
436 // CRM-4320, lets build array of cancelled additional participant ids
437 // those are drop or skip by primary at the time of confirmation.
438 // get all in and then unset those we want to process.
439 $cancelledIds = $this->_additionalParticipantIds
;
441 $params = $this->_params
;
442 if ($this->_values
['event']['is_monetary']) {
443 $this->set('finalAmount', $this->_amount
);
445 $participantCount = [];
448 //unset the skip participant from params.
449 //build the $participantCount array.
450 //maintain record for all participants.
451 foreach ($params as $participantNum => $record) {
452 if ($record == 'skip') {
453 unset($params[$participantNum]);
454 $participantCount[$participantNum] = 'skip';
456 elseif ($participantNum) {
457 $participantCount[$participantNum] = 'participant';
459 $totalTaxAmount +
= CRM_Utils_Array
::value('tax_amount', $record, 0);
460 if (CRM_Utils_Array
::value('is_primary', $record)) {
461 $taxAmount = &$params[$participantNum]['tax_amount'];
463 //lets get additional participant id to cancel.
464 if ($this->_allowConfirmation
&& is_array($cancelledIds)) {
465 $additonalId = CRM_Utils_Array
::value('participant_id', $record);
466 if ($additonalId && $key = array_search($additonalId, $cancelledIds)) {
467 unset($cancelledIds[$key]);
471 $taxAmount = $totalTaxAmount;
472 $payment = $registerByID = $primaryCurrencyID = $contribution = NULL;
473 $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.');
475 $this->participantIDS
= [];
477 foreach ($params as $key => $value) {
478 CRM_Event_Form_Registration_Confirm
::fixLocationFields($value, $fields, $this);
479 //unset the billing parameters if it is pay later mode
480 //to avoid creation of billing location
481 // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
482 // the form & if billing fields exist we create the address, relying on the form to collect
483 // only information we intend to store.
484 if ($this->_allowWaitlist
485 ||
$this->_requireApproval
486 ||
(!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater
)
487 ||
empty($value['is_primary'])
490 "email-{$this->_bltID}",
491 'billing_first_name',
492 'billing_middle_name',
494 "billing_street_address-{$this->_bltID}",
495 "billing_city-{$this->_bltID}",
496 "billing_state_province-{$this->_bltID}",
497 "billing_state_province_id-{$this->_bltID}",
498 "billing_postal_code-{$this->_bltID}",
499 "billing_country-{$this->_bltID}",
500 "billing_country_id-{$this->_bltID}",
501 "address_name-{$this->_bltID}",
503 foreach ($billingFields as $field) {
504 unset($value[$field]);
506 if (!empty($value['is_pay_later'])) {
507 $this->_values
['params']['is_pay_later'] = TRUE;
511 //Unset ContactID for additional participants and set RegisterBy Id.
512 if (empty($value['is_primary'])) {
513 $contactID = CRM_Utils_Array
::value('contact_id', $value);
514 $registerByID = $this->get('registerByID');
516 $value['registered_by_id'] = $registerByID;
520 $value['amount'] = $this->_totalAmount
;
523 $contactID = CRM_Event_Form_Registration_Confirm
::updateContactFields($contactID, $value, $fields, $this);
525 // lets store the contactID in the session
526 // we dont store in userID in case the user is doing multiple
528 // for things like tell a friend
529 if (!$this->getContactID() && !empty($value['is_primary'])) {
530 CRM_Core_Session
::singleton()->set('transaction.userID', $contactID);
533 $value['description'] = ts('Online Event Registration') . ': ' . $this->_values
['event']['title'];
534 $value['accountingCode'] = CRM_Utils_Array
::value('accountingCode',
535 $this->_values
['event']
539 if ($this->_allowWaitlist ||
$this->_requireApproval
) {
540 //get the participant statuses.
541 $waitingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Waiting'");
542 if ($this->_allowWaitlist
) {
543 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
546 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
549 //there might be case user selected pay later and
550 //now becomes part of run time waiting list.
551 $value['is_pay_later'] = FALSE;
553 elseif ($this->_values
['event']['is_monetary']) {
554 // required only if paid event
555 if (is_array($this->_paymentProcessor
)) {
556 $payment = $this->_paymentProcessor
['object'];
558 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
559 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
560 $value = array_merge($value, $preApprovalParams);
564 if (!empty($value['is_pay_later']) ||
565 $value['amount'] == 0 ||
566 // The concept of contributeMode is deprecated.
567 $this->_contributeMode
== 'checkout' ||
568 $this->_contributeMode
== 'notify'
570 if ($value['amount'] != 0) {
572 //get the participant statuses.
573 $pendingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Pending'");
574 $status = !empty($value['is_pay_later']) ?
'Pending from pay later' : 'Pending from incomplete transaction';
575 $value['participant_status_id'] = $value['participant_status'] = array_search($status, $pendingStatuses);
578 elseif (!empty($value['is_primary'])) {
579 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $value, $value, TRUE);
580 // payment email param can be empty for _bltID mapping
581 // thus provide mapping for it with a different email value
582 if (empty($value['email'])) {
583 $value['email'] = CRM_Utils_Array
::valueByRegexKey('/^email-/', $value);
586 if (is_object($payment)) {
587 // Not quite sure why we don't just user $value since it contains the data
589 // @todo ditch $result & retest.
590 list($result, $value) = $this->processPayment($payment, $value);
593 CRM_Core_Error
::fatal($paymentObjError);
597 $value['receive_date'] = $now;
598 if ($this->_allowConfirmation
) {
599 $value['participant_register_date'] = $this->_values
['participant']['register_date'];
602 $createContrib = ($value['amount'] != 0) ?
TRUE : FALSE;
603 // force to create zero amount contribution, CRM-5095
604 if (!$createContrib && ($value['amount'] == 0)
605 && $this->_priceSetId
&& $this->_lineItem
607 $createContrib = TRUE;
610 if ($createContrib && !empty($value['is_primary']) &&
611 !$this->_allowWaitlist
&& !$this->_requireApproval
613 // if paid event add a contribution record
614 //if primary participant contributing additional amount
615 //append (multiple participants) to its fee level. CRM-4196.
616 $isAdditionalAmount = FALSE;
617 if (count($params) > 1) {
618 $isAdditionalAmount = TRUE;
621 //passing contribution id is already registered.
622 $contribution = self
::processContribution($this, $value, $result, $contactID, $pending, $isAdditionalAmount, $this->_paymentProcessor
);
623 $value['contributionID'] = $contribution->id
;
624 $value['contributionTypeID'] = $contribution->financial_type_id
;
625 $value['receive_date'] = $contribution->receive_date
;
626 $value['trxn_id'] = $contribution->trxn_id
;
627 $value['contributionID'] = $contribution->id
;
628 $value['contributionTypeID'] = $contribution->financial_type_id
;
630 $value['contactID'] = $contactID;
631 $value['eventID'] = $this->_eventId
;
632 $value['item_name'] = $value['description'];
635 if (!empty($value['contributionID'])) {
636 $this->_values
['contributionId'] = $value['contributionID'];
640 if (!empty($value['is_primary'])) {
641 $primaryCurrencyID = CRM_Utils_Array
::value('currencyID', $value);
643 if (empty($value['currencyID'])) {
644 $value['currencyID'] = $primaryCurrencyID;
647 // CRM-11182 - Confirmation page might not be monetary
648 if ($this->_values
['event']['is_monetary']) {
649 if (!$pending && !empty($value['is_primary']) &&
650 !$this->_allowWaitlist
&& !$this->_requireApproval
652 // transactionID & receive date required while building email template
653 $this->assign('trxn_id', CRM_Utils_Array
::value('trxn_id', $value));
654 $this->assign('receive_date', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
655 $this->set('receiveDate', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
656 $this->set('trxnId', CRM_Utils_Array
::value('trxn_id', $value));
660 $value['fee_amount'] = CRM_Utils_Array
::value('amount', $value);
661 $this->set('value', $value);
663 // handle register date CRM-4320
664 if ($this->_allowConfirmation
) {
665 $registerDate = CRM_Utils_Array
::value('participant_register_date', $params);
667 elseif (!empty($params['participant_register_date']) &&
668 is_array($params['participant_register_date'])
670 $registerDate = CRM_Utils_Date
::format($params['participant_register_date']);
673 $registerDate = date('YmdHis');
675 $this->assign('register_date', $registerDate);
677 $this->confirmPostProcess($contactID, $contribution, $payment);
680 //handle if no additional participant.
681 if (!$registerByID) {
682 $registerByID = $this->get('registerByID');
685 $this->set('participantIDs', $this->_participantIDS
);
687 // create line items, CRM-5313
688 if ($this->_priceSetId
&&
689 !empty($this->_lineItem
)
691 // take all processed participant ids.
692 $allParticipantIds = $this->_participantIDS
;
694 // when participant re-walk wizard.
695 if ($this->_allowConfirmation
&&
696 !empty($this->_additionalParticipantIds
)
698 $allParticipantIds = array_merge([$registerByID], $this->_additionalParticipantIds
);
701 $entityTable = 'civicrm_participant';
704 foreach ($this->_lineItem
as $key => $value) {
705 if ($value == 'skip') {
708 if ($entityId = CRM_Utils_Array
::value($key, $allParticipantIds)) {
709 // do cleanup line items if participant re-walking wizard.
710 if ($this->_allowConfirmation
) {
711 CRM_Price_BAO_LineItem
::deleteLineItems($entityId, $entityTable);
713 $lineItem[$this->_priceSetId
] = $value;
714 CRM_Price_BAO_LineItem
::processPriceSet($entityId, $lineItem, $contribution, $entityTable);
716 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
717 foreach ($value as $line) {
718 if (isset($line['tax_amount']) && isset($line['tax_rate'])) {
719 $totalTaxAmount = $line['tax_amount'] +
$totalTaxAmount;
720 if (isset($dataArray[$line['tax_rate']])) {
721 $dataArray[$line['tax_rate']] = $dataArray[$line['tax_rate']] + CRM_Utils_Array
::value('tax_amount', $line);
724 $dataArray[$line['tax_rate']] = CRM_Utils_Array
::value('tax_amount', $line);
730 $this->assign('dataArray', $dataArray);
731 $this->assign('totalTaxAmount', $totalTaxAmount);
734 //update status and send mail to cancelled additional participants, CRM-4320
735 if ($this->_allowConfirmation
&& is_array($cancelledIds) && !empty($cancelledIds)) {
736 $cancelledId = array_search('Cancelled',
737 CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Negative'")
739 CRM_Event_BAO_Participant
::transitionParticipants($cancelledIds, $cancelledId);
743 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
747 // for Transfer checkout.
748 // The concept of contributeMode is deprecated.
749 if (($this->_contributeMode
== 'checkout' ||
750 $this->_contributeMode
== 'notify'
751 ) && empty($params[0]['is_pay_later']) &&
752 !$this->_allowWaitlist
&& !$this->_requireApproval
&&
753 $this->_totalAmount
> 0
756 $primaryParticipant = $this->get('primaryParticipant');
758 if (empty($primaryParticipant['participantID'])) {
759 $primaryParticipant['participantID'] = $registerByID;
762 //build an array of custom profile and assigning it to template
763 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, $this->_values
, NULL, $isTest);
764 if (count($customProfile)) {
765 $this->assign('customProfile', $customProfile);
766 $this->set('customProfile', $customProfile);
769 // do a transfer only if a monetary payment greater than 0
770 if ($this->_values
['event']['is_monetary'] && $primaryParticipant) {
771 if ($payment && is_object($payment)) {
772 //CRM 14512 provide line items of all participants to payment gateway
773 $primaryContactId = $this->get('primaryContactId');
775 //build an array of cId/pId of participants
776 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, NULL, $primaryContactId, $isTest, TRUE);
778 //need to copy, since we are unsetting on the way.
779 $copyParticipantCountLines = $participantCount;
781 //lets carry all participant params w/ values.
782 foreach ($additionalIDs as $participantID => $contactId) {
783 $participantNum = $participantID;
784 if ($participantID == $registerByID) {
785 // This is the is primary participant.
789 if ($participantNum = array_search('participant', $copyParticipantCountLines)) {
790 //if no participant found break.
791 if ($participantNum === NULL) {
794 //unset current participant so we don't check them again
795 unset($copyParticipantCountLines[$participantNum]);
798 // get values of line items
799 if ($this->_amount
) {
801 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
802 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
803 $params[$participantNum]['amounts'] = $amount;
806 if (!empty($this->_lineItem
)) {
807 $lineItems = $this->_lineItem
;
809 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
810 $lineItem[] = $lineItemValue;
812 $params[$participantNum]['lineItem'] = $lineItem;
815 //only add additional particpants and not the primary particpant as we already have that
816 //added to $primaryParticipant so that this change doesn't break or require changes to
817 //existing gateway implementations
818 $primaryParticipant['participants_info'][$participantID] = $params[$participantNum];
821 //get event custom field information
822 $groupTree = CRM_Core_BAO_CustomGroup
::getTree('Event', NULL, $this->_eventId
, 0, $this->_values
['event']['event_type_id']);
823 $primaryParticipant['eventCustomFields'] = $groupTree;
825 // call postprocess hook before leaving
826 $this->postProcessHook();
828 $this->processPayment($payment, $primaryParticipant);
831 CRM_Core_Error
::fatal($paymentObjError);
836 //otherwise send mail Confirmation/Receipt
837 $primaryContactId = $this->get('primaryContactId');
839 //build an array of cId/pId of participants
840 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID,
841 NULL, $primaryContactId, $isTest,
844 //let's send mails to all with meaningful text, CRM-4320.
845 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
846 $this->assign('isRequireApproval', $this->_requireApproval
);
848 //need to copy, since we are unsetting on the way.
849 $copyParticipantCount = $participantCount;
851 //let's carry all participant params w/ values.
852 foreach ($additionalIDs as $participantID => $contactId) {
853 $participantNum = NULL;
854 if ($participantID == $registerByID) {
858 if ($participantNum = array_search('participant', $copyParticipantCount)) {
859 unset($copyParticipantCount[$participantNum]);
862 if ($participantNum === NULL) {
866 //carry the participant submitted values.
867 $this->_values
['params'][$participantID] = $params[$participantNum];
870 foreach ($additionalIDs as $participantID => $contactId) {
872 if ($participantID == $registerByID) {
873 //set as Primary Participant
874 $this->assign('isPrimary', 1);
875 //build an array of custom profile and assigning it to template.
876 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($participantID, $this->_values
, NULL, $isTest);
878 if (count($customProfile)) {
879 $this->assign('customProfile', $customProfile);
880 $this->set('customProfile', $customProfile);
882 $this->_values
['params']['additionalParticipant'] = FALSE;
885 //take the Additional participant number.
886 if ($participantNum = array_search('participant', $participantCount)) {
887 unset($participantCount[$participantNum]);
889 // Change $this->_values['participant'] to include additional participant values
890 $ids = $participantValues = [];
891 $participantParams = ['id' => $participantID];
892 CRM_Event_BAO_Participant
::getValues($participantParams, $participantValues, $ids);
893 $this->_values
['participant'] = $participantValues[$participantID];
895 $this->assign('isPrimary', 0);
896 $this->assign('customProfile', NULL);
897 //Additional Participant should get only it's payment information
898 if (!empty($this->_amount
)) {
900 $params = $this->get('params');
901 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
902 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
903 $this->assign('amounts', $amount);
905 if ($this->_lineItem
) {
906 $lineItems = $this->_lineItem
;
908 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
909 $lineItem[] = $lineItemValue;
911 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
912 $individual = $this->get('individual');
913 $dataArray[key($dataArray)] = $individual[$participantNum]['totalTaxAmt'];
914 $this->assign('dataArray', $dataArray);
915 $this->assign('totalAmount', $individual[$participantNum]['totalAmtWithTax']);
916 $this->assign('totalTaxAmount', $individual[$participantNum]['totalTaxAmt']);
917 $this->assign('individual', [$individual[$participantNum]]);
919 $this->assign('lineItem', $lineItem);
921 $this->_values
['params']['additionalParticipant'] = TRUE;
922 $this->assign('isAdditionalParticipant', $this->_values
['params']['additionalParticipant']);
925 //pass these variables since these are run time calculated.
926 $this->_values
['params']['isOnWaitlist'] = $this->_allowWaitlist
;
927 $this->_values
['params']['isRequireApproval'] = $this->_requireApproval
;
929 //send mail to primary as well as additional participants.
930 $this->assign('contactID', $contactId);
931 $this->assign('participantID', $participantID);
932 CRM_Event_BAO_Event
::sendMail($contactId, $this->_values
, $participantID, $isTest);
938 * Process the contribution.
940 * @param CRM_Core_Form $form
941 * @param array $params
942 * @param array $result
943 * @param int $contactID
944 * @param bool $pending
945 * @param bool $isAdditionalAmount
946 * @param array $paymentProcessor
948 * @return \CRM_Contribute_BAO_Contribution
950 * @throws \CRM_Core_Exception
951 * @throws \CiviCRM_API3_Exception
953 public static function processContribution(
954 &$form, $params, $result, $contactID,
955 $pending = FALSE, $isAdditionalAmount = FALSE,
956 $paymentProcessor = NULL
958 $transaction = new CRM_Core_Transaction();
960 $now = date('YmdHis');
963 if (!empty($form->_values
['event']['is_email_confirm'])) {
967 if ($isAdditionalAmount) {
968 $params['amount_level'] = $params['amount_level'] . ts(' (multiple participants)') . CRM_Core_DAO
::VALUE_SEPARATOR
;
971 // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
972 CRM_Contribute_Form_AbstractEditPayment
::formatCreditCardDetails($params);
975 'contact_id' => $contactID,
976 'financial_type_id' => !empty($form->_values
['event']['financial_type_id']) ?
$form->_values
['event']['financial_type_id'] : $params['financial_type_id'],
977 'receive_date' => $now,
978 'total_amount' => $params['amount'],
979 'tax_amount' => $params['tax_amount'],
980 'amount_level' => $params['amount_level'],
981 'invoice_id' => $params['invoiceID'],
982 'currency' => $params['currencyID'],
983 'source' => !empty($params['participant_source']) ?
$params['participant_source'] : $params['description'],
984 'is_pay_later' => CRM_Utils_Array
::value('is_pay_later', $params, 0),
985 'campaign_id' => CRM_Utils_Array
::value('campaign_id', $params),
986 'card_type_id' => CRM_Utils_Array
::value('card_type_id', $params),
987 'pan_truncation' => CRM_Utils_Array
::value('pan_truncation', $params),
990 if ($paymentProcessor) {
991 $contribParams['payment_instrument_id'] = $paymentProcessor['payment_instrument_id'];
992 $contribParams['payment_processor'] = $paymentProcessor['id'];
995 if (!$pending && $result) {
997 'fee_amount' => CRM_Utils_Array
::value('fee_amount', $result),
998 'trxn_id' => $result['trxn_id'],
999 'receipt_date' => $receiptDate,
1003 $allStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
1004 $contribParams['contribution_status_id'] = array_search('Completed', $allStatuses);
1006 $contribParams['contribution_status_id'] = array_search('Pending', $allStatuses);
1009 $contribParams['is_test'] = 0;
1010 if ($form->_action
& CRM_Core_Action
::PREVIEW || CRM_Utils_Array
::value('mode', $params) == 'test') {
1011 $contribParams['is_test'] = 1;
1014 if (!empty($contribParams['invoice_id'])) {
1015 $contribParams['id'] = CRM_Core_DAO
::getFieldValue('CRM_Contribute_DAO_Contribution',
1016 $contribParams['invoice_id'],
1022 if (Civi
::settings()->get('deferred_revenue_enabled')) {
1023 $eventStartDate = CRM_Utils_Array
::value(
1025 CRM_Utils_Array
::value(
1030 if (strtotime($eventStartDate) > strtotime(date('Ymt'))) {
1031 $contribParams['revenue_recognition_date'] = date('Ymd', strtotime($eventStartDate));
1034 //create an contribution address
1035 // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although
1036 // currently that is only inherited by back-office forms.
1037 if ($form->_contributeMode
!= 'notify' && empty($params['is_pay_later'])) {
1038 $contribParams['address_id'] = CRM_Contribute_BAO_Contribution
::createAddress($params, $form->_bltID
);
1041 $contribParams['skipLineItem'] = 1;
1042 $contribParams['skipCleanMoney'] = 1;
1043 // create contribution record
1044 $contribution = CRM_Contribute_BAO_Contribution
::add($contribParams);
1046 CRM_Event_BAO_Participant
::createDiscountTrxn($form->_eventId
, $contribParams, NULL, CRM_Price_BAO_PriceSet
::parseFirstPriceSetValueIDFromParams($params));
1048 // process soft credit / pcp pages
1049 if (!empty($params['pcp_made_through_id'])) {
1050 CRM_Contribute_BAO_ContributionSoft
::formatSoftCreditParams($params, $form);
1051 CRM_Contribute_BAO_ContributionSoft
::processSoftContribution($params, $contribution);
1054 $transaction->commit();
1056 return $contribution;
1060 * Fix the Location Fields.
1062 * @todo Reconcile with the contribution method formatParamsForPaymentProcessor
1063 * rather than adding different logic to check when to keep the billing
1064 * fields. There might be a difference in handling guest/multiple
1065 * participants though.
1067 * @param array $params
1068 * @param array $fields
1069 * @param CRM_Core_Form $form
1071 public static function fixLocationFields(&$params, &$fields, &$form) {
1072 if (!empty($form->_fields
)) {
1073 foreach ($form->_fields
as $name => $dontCare) {
1078 // If there's no 'first_name' in the profile then overwrite the names from
1079 // the billing fields (if they are set)
1080 if (is_array($fields)) {
1081 if (!array_key_exists('first_name', $fields)) {
1082 $nameFields = ['first_name', 'middle_name', 'last_name'];
1083 foreach ($nameFields as $name) {
1085 if (array_key_exists("billing_$name", $params)) {
1086 $params[$name] = $params["billing_{$name}"];
1087 $params['preserveDBName'] = TRUE;
1093 // Add the billing names to the billing address, if a billing name is set
1094 if (!empty($params['billing_first_name'])) {
1095 $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);
1096 $fields["address_name-{$form->_bltID}"] = 1;
1099 $fields["email-{$form->_bltID}"] = 1;
1100 $fields['email-Primary'] = 1;
1102 //if its pay later or additional participant set email address as primary.
1103 if ((!empty($params['is_pay_later']) ||
empty($params['is_primary']) ||
1104 !$form->_values
['event']['is_monetary'] ||
1105 $form->_allowWaitlist ||
1106 $form->_requireApproval
1107 ) && !empty($params["email-{$form->_bltID}"])
1109 $params['email-Primary'] = $params["email-{$form->_bltID}"];
1114 * Update contact fields.
1116 * @param int $contactID
1117 * @param array $params
1118 * @param array $fields
1119 * @param CRM_Core_Form $form
1123 public static function updateContactFields($contactID, $params, $fields, &$form) {
1124 //add the contact to group, if add to group is selected for a
1125 //particular uf group
1127 // get the add to groups
1130 if (!empty($form->_fields
)) {
1131 foreach ($form->_fields
as $key => $value) {
1132 if (!empty($value['add_to_group_id'])) {
1133 $addToGroups[$value['add_to_group_id']] = $value['add_to_group_id'];
1138 // check for profile double opt-in and get groups to be subscribed
1139 $subscribeGroupIds = CRM_Core_BAO_UFGroup
::getDoubleOptInGroupIds($params, $contactID);
1141 foreach ($addToGroups as $k) {
1142 if (array_key_exists($k, $subscribeGroupIds)) {
1143 unset($addToGroups[$k]);
1147 // since we are directly adding contact to group lets unset it from mailing
1148 if (!empty($addToGroups)) {
1149 foreach ($addToGroups as $groupId) {
1150 if (isset($subscribeGroupIds[$groupId])) {
1151 unset($subscribeGroupIds[$groupId]);
1156 $ctype = CRM_Core_DAO
::getFieldValue(
1157 'CRM_Contact_DAO_Contact',
1162 if (array_key_exists('contact_id', $params) && empty($params['contact_id'])) {
1163 // we unset this here because the downstream function ignores the contactID we give it
1164 // if it is set & it is difficult to understand the implications of 'fixing' this downstream
1165 // but if we are passing a contact id into this function it's reasonable to assume we don't
1167 unset($params['contact_id']);
1170 $contactID = CRM_Contact_BAO_Contact
::createProfileContact(
1182 foreach (CRM_Contact_BAO_Contact
::$_greetingTypes as $greeting) {
1183 if (!isset($params[$greeting . '_id'])) {
1184 $params[$greeting . '_id'] = CRM_Contact_BAO_Contact_Utils
::defaultGreeting('Individual', $greeting);
1188 $contactID = CRM_Contact_BAO_Contact
::createProfileContact($params,
1196 $form->set('contactID', $contactID);
1199 //get email primary first if exist
1200 $subscribtionEmail = ['email' => CRM_Utils_Array
::value('email-Primary', $params)];
1201 if (!$subscribtionEmail['email']) {
1202 $subscribtionEmail['email'] = CRM_Utils_Array
::value("email-{$form->_bltID}", $params);
1204 // subscribing contact to groups
1205 if (!empty($subscribeGroupIds) && $subscribtionEmail['email']) {
1206 CRM_Mailing_Event_BAO_Subscribe
::commonSubscribe($subscribeGroupIds, $subscribtionEmail, $contactID);
1213 * Assign Profiles to the template.
1215 * @param CRM_Event_Form_Registration_Confirm $form
1217 * @throws \Exception
1219 public static function assignProfiles($form) {
1220 $participantParams = $form->_params
;
1221 $formattedValues = $profileFields = [];
1223 foreach ($participantParams as $participantNum => $participantValue) {
1224 if ($participantNum) {
1225 $prefix1 = 'additional';
1226 $prefix2 = 'additional_';
1232 if ($participantValue !== 'skip') {
1233 //get the customPre profile info
1234 if (!empty($form->_values
[$prefix2 . 'custom_pre_id'])) {
1235 $values = $groupName = [];
1236 CRM_Event_BAO_Event
::displayProfile($participantValue,
1237 $form->_values
[$prefix2 . 'custom_pre_id'],
1243 if (count($values)) {
1244 $formattedValues[$count][$prefix1 . 'CustomPre'] = $values;
1246 $formattedValues[$count][$prefix1 . 'CustomPreGroupTitle'] = CRM_Utils_Array
::value('groupTitle', $groupName);
1248 //get the customPost profile info
1249 if (!empty($form->_values
[$prefix2 . 'custom_post_id'])) {
1250 $values = $groupName = [];
1251 foreach ($form->_values
[$prefix2 . 'custom_post_id'] as $gids) {
1253 CRM_Event_BAO_Event
::displayProfile($participantValue,
1259 $values[$gids] = $val;
1260 $groupName[$gids] = $group;
1263 if (count($values)) {
1264 $formattedValues[$count][$prefix1 . 'CustomPost'] = $values;
1267 if (isset($formattedValues[$count][$prefix1 . 'CustomPre'])) {
1268 $formattedValues[$count][$prefix1 . 'CustomPost'] = array_diff_assoc($formattedValues[$count][$prefix1 . 'CustomPost'],
1269 $formattedValues[$count][$prefix1 . 'CustomPre']
1273 $formattedValues[$count][$prefix1 . 'CustomPostGroupTitle'] = $groupName;
1277 $form->_fields
= $profileFields;
1279 if (!empty($formattedValues)) {
1280 $form->assign('primaryParticipantProfile', $formattedValues[1]);
1281 $form->set('primaryParticipantProfile', $formattedValues[1]);
1283 unset($formattedValues[1]);
1284 $form->assign('addParticipantProfile', $formattedValues);
1285 $form->set('addParticipantProfile', $formattedValues);
1291 * Submit in test mode.
1295 public static function testSubmit($params) {
1296 $form = new CRM_Event_Form_Registration_Confirm();
1297 // This way the mocked up controller ignores the session stuff.
1298 $_SERVER['REQUEST_METHOD'] = 'GET';
1299 $_REQUEST['id'] = $form->_eventId
= $params['id'];
1300 $form->controller
= new CRM_Event_Controller_Registration();
1301 $form->_params
= $params['params'];
1302 // This happens in buildQuickForm so emulate here.
1303 $form->_amount
= $form->_totalAmount
= CRM_Utils_Rule
::cleanMoney(CRM_Utils_Array
::value('totalAmount', $params));
1304 $form->set('params', $params['params']);
1305 $form->_values
['custom_pre_id'] = [];
1306 $form->_values
['custom_post_id'] = [];
1307 $form->_values
['event'] = CRM_Utils_Array
::value('event', $params);
1308 $form->_contributeMode
= $params['contributeMode'];
1309 $eventParams = ['id' => $params['id']];
1310 CRM_Event_BAO_Event
::retrieve($eventParams, $form->_values
['event']);
1311 $form->set('registerByID', $params['registerByID']);
1312 if (!empty($params['paymentProcessorObj'])) {
1313 $form->_paymentProcessor
= $params['paymentProcessorObj'];
1315 $form->postProcess();
1319 * Process the payment, redirecting back to the page on error.
1321 * @param \CRM_Core_Payment $payment
1326 private function processPayment($payment, $value) {
1328 $result = $payment->doPayment($value, 'event');
1329 return [$result, $value];
1331 catch (\Civi\Payment\Exception\PaymentProcessorException
$e) {
1332 Civi
::log()->error('Payment processor exception: ' . $e->getMessage());
1333 CRM_Core_Session
::singleton()->setStatus($e->getMessage());
1334 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "id={$this->_eventId}"));
1340 * Clean money fields from the form.
1342 * @param array $params
1344 protected function cleanMoneyFields(&$params) {
1345 foreach ($this->submittableMoneyFields
as $moneyField) {
1346 foreach ($params as $index => $paramField) {
1347 if (isset($paramField[$moneyField])) {
1348 $params[$index][$moneyField] = CRM_Utils_Rule
::cleanMoney($paramField[$moneyField]);