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 $registerParams = $this->prepareParamsForPaymentProcessor($registerParams);
95 if (isset($registerParams['credit_card_exp_date'])) {
96 $registerParams['year'] = CRM_Core_Payment_Form
::getCreditCardExpirationYear($registerParams);
97 $registerParams['month'] = CRM_Core_Payment_Form
::getCreditCardExpirationMonth($registerParams);
99 if ($this->_values
['event']['is_monetary']) {
100 $registerParams['ip_address'] = CRM_Utils_System
::ipAddress();
101 $registerParams['currencyID'] = $this->_params
[0]['currencyID'];
103 //assign back primary participant params.
104 $this->_params
[0] = $registerParams;
107 if ($this->_values
['event']['is_monetary']) {
108 $this->_params
[0]['invoiceID'] = $this->get('invoiceID');
110 $this->assign('defaultRole', FALSE);
111 if (CRM_Utils_Array
::value('defaultRole', $this->_params
[0]) == 1) {
112 $this->assign('defaultRole', TRUE);
115 if (empty($this->_params
[0]['participant_role_id']) &&
116 $this->_values
['event']['default_role_id']
118 $this->_params
[0]['participant_role_id'] = $this->_values
['event']['default_role_id'];
121 if (isset($this->_values
['event']['confirm_title'])) {
122 CRM_Utils_System
::setTitle($this->_values
['event']['confirm_title']);
125 // Personal campaign page
127 $params = CRM_Contribute_Form_Contribution_Confirm
::processPcp($this, $this->_params
[0]);
128 $this->_params
[0] = $params;
131 $this->set('params', $this->_params
);
135 * Pre process function for Paypal Express confirm.
136 * @todo this is just a step in refactor as payment processor specific code does not belong in generic forms
139 * @throws \CRM_Core_Exception
141 private function preProcessExpress() {
142 if ($this->_contributeMode
!== 'express') {
146 // rfp == redirect from paypal
147 // @fixme rfp is probably not required - the getPreApprovalDetails should deal with any payment-processor specific 'stuff'
148 $rfp = CRM_Utils_Request
::retrieve('rfp', 'Boolean', CRM_Core_DAO
::$_nullObject, FALSE, NULL, 'GET');
150 //we lost rfp in case of additional participant. So set it explicitly.
151 if ($rfp || CRM_Utils_Array
::value('additional_participants', $this->_params
[0], FALSE)) {
152 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
153 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
154 $params = array_merge($this->_params
, $preApprovalParams);
156 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $params, $params, FALSE);
158 // set a few other parameters that are not really specific to this method because we don't know what
159 // will break if we change this.
160 $params['amount'] = $this->_params
[0]['amount'];
161 if (!empty($this->_params
[0]['discount'])) {
162 $params['discount'] = $this->_params
[0]['discount'];
163 $params['discountAmount'] = $this->_params
[0]['discountAmount'];
164 $params['discountMessage'] = $this->_params
[0]['discountMessage'];
167 $params['amount_level'] = $this->_params
[0]['amount_level'];
168 $params['currencyID'] = $this->_params
[0]['currencyID'];
170 // also merge all the other values from the profile fields
171 $values = $this->controller
->exportValues('Register');
174 "street_address-{$this->_bltID}",
175 "city-{$this->_bltID}",
176 "state_province_id-{$this->_bltID}",
177 "postal_code-{$this->_bltID}",
178 "country_id-{$this->_bltID}",
181 foreach ($values as $name => $value) {
183 if (!in_array($name, $skipFields)) {
184 $params[$name] = $value;
186 if (substr($name, 0, 6) == 'price_') {
187 $params[$name] = $this->_params
[0][$name];
190 $this->set('getExpressCheckoutDetails', $params);
192 $this->_params
[0] = array_merge($this->_params
[0], $params);
193 $this->_params
[0]['is_primary'] = 1;
198 * Overwrite action, since we are only showing elements in frozen mode no help display needed.
202 public function getAction() {
203 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
204 return CRM_Core_Action
::VIEW | CRM_Core_Action
::PREVIEW
;
207 return CRM_Core_Action
::VIEW
;
212 * Build the form object.
214 public function buildQuickForm() {
215 $this->assignToTemplate();
217 if ($this->_values
['event']['is_monetary'] &&
218 ($this->_params
[0]['amount'] ||
$this->_params
[0]['amount'] == 0) &&
219 !$this->_requireApproval
224 foreach ($this->_params
as $k => $v) {
228 $individualTaxAmount = 0;
229 //display tax amount on confirmation page
230 $taxAmount +
= $v['tax_amount'];
232 $this->cleanMoneyFields($v);
237 if (isset($v['billing_' . $name]) &&
240 $v[$name] = $v['billing_' . $name];
244 if (!empty($v['first_name']) && !empty($v['last_name'])) {
245 $append = $v['first_name'] . ' ' . $v['last_name'];
248 //use an email if we have one
249 foreach ($v as $v_key => $v_val) {
250 if (substr($v_key, 0, 6) == 'email-') {
251 $append = $v[$v_key];
256 $this->_amount
[$k]['amount'] = $v['amount'];
257 if (!empty($v['discountAmount'])) {
258 $this->_amount
[$k]['amount'] -= $v['discountAmount'];
261 $this->_amount
[$k]['label'] = preg_replace('/\ 1/', '', $v['amount_level']) . ' - ' . $append;
262 $this->_part
[$k]['info'] = CRM_Utils_Array
::value('first_name', $v) . ' ' . CRM_Utils_Array
::value('last_name', $v);
263 if (empty($v['first_name'])) {
264 $this->_part
[$k]['info'] = $append;
268 $individual[$k]['totalAmtWithTax'] = $this->_amount
[$k]['amount'];
269 $individual[$k]['totalTaxAmt'] = $individualTaxAmount +
$v['tax_amount'];
270 $this->_totalAmount
= $this->_totalAmount +
$this->_amount
[$k]['amount'];
271 if (!empty($v['is_primary'])) {
272 $this->set('primaryParticipantAmount', $this->_amount
[$k]['amount']);
277 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
278 $this->assign('totalTaxAmount', $taxAmount);
279 $this->assign('taxTerm', CRM_Invoicing_Utils
::getTaxTerm());
280 $this->assign('individual', $individual);
281 $this->set('individual', $individual);
284 $this->assign('part', $this->_part
);
285 $this->set('part', $this->_part
);
286 $this->assign('amounts', $this->_amount
);
287 $this->assign('totalAmount', $this->_totalAmount
);
288 $this->set('totalAmount', $this->_totalAmount
);
291 if ($this->_priceSetId
&& !CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId
, 'is_quick_config')) {
292 $lineItemForTemplate = [];
293 if (!empty($this->_lineItem
) && is_array($this->_lineItem
)) {
294 foreach ($this->_lineItem
as $key => $value) {
295 if (!empty($value)) {
296 $lineItemForTemplate[$key] = $value;
300 if (!empty($lineItemForTemplate)) {
301 $this->assignLineItemsToTemplate($lineItemForTemplate);
305 //display additional participants profile.
306 self
::assignProfiles($this);
308 //consider total amount.
309 $this->assign('isAmountzero', ($this->_totalAmount
<= 0) ?
TRUE : FALSE);
311 $contribButton = ts('Continue');
315 'name' => ts('Go Back'),
319 'name' => $contribButton,
326 if (!empty($this->_fields
)) {
327 foreach ($this->_fields
as $name => $dontCare) {
331 $fields["billing_state_province-{$this->_bltID}"] = $fields["billing_country-{$this->_bltID}"] = $fields["email-{$this->_bltID}"] = 1;
332 foreach ($fields as $name => $dontCare) {
333 if (isset($this->_params
[0][$name])) {
334 $defaults[$name] = $this->_params
[0][$name];
335 if (substr($name, 0, 7) == 'custom_') {
336 $timeField = "{$name}_time";
337 if (isset($this->_params
[0][$timeField])) {
338 $defaults[$timeField] = $this->_params
[0][$timeField];
340 if (isset($this->_params
[0]["{$name}_id"])) {
341 $defaults["{$name}_id"] = $this->_params
[0]["{$name}_id"];
344 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)
345 && !empty($this->_params
[0][$name . '_custom'])
347 $defaults[$name . '_custom'] = $this->_params
[0][$name . '_custom'];
352 $this->setDefaults($defaults);
355 //lets give meaningful status message, CRM-4320.
356 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
357 $this->assign('isRequireApproval', $this->_requireApproval
);
359 // Assign Participant Count to Lineitem Table
360 $this->assign('pricesetFieldsCount', CRM_Price_BAO_PriceSet
::getPricesetCount($this->_priceSetId
));
361 $this->addFormRule(['CRM_Event_Form_Registration_Confirm', 'formRule'], $this);
367 * @param array $fields
368 * @param array $files
369 * @param CRM_Core_Form $self
373 public static function formRule($fields, $files, $self) {
375 $eventFull = CRM_Event_BAO_Participant
::eventFull($self->_eventId
, FALSE, CRM_Utils_Array
::value('has_waitlist', $self->_values
['event']));
376 if ($eventFull && empty($self->_allowConfirmation
)) {
377 if (empty($self->_allowWaitlist
)) {
378 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "reset=1&id={$self->_eventId}", FALSE, NULL, FALSE, TRUE));
381 $self->_feeBlock
= $self->_values
['fee'];
382 CRM_Event_Form_Registration_Register
::formatFieldsForOptionFull($self);
384 if (!empty($self->_priceSetId
) &&
385 !$self->_requireApproval
&& !$self->_allowWaitlist
387 $priceSetErrors = self
::validatePriceSet($self, $self->_params
);
388 if (!empty($priceSetErrors)) {
389 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');
390 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');
391 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "_qf_Register_display=true&qfKey={$fields['qfKey']}"));
395 return empty($priceSetErrors) ?
TRUE : $priceSetErrors;
399 * Process the form submission.
401 public function postProcess() {
402 $now = date('YmdHis');
404 $this->_params
= $this->get('params');
405 $this->cleanMoneyFields($this->_params
);
407 if (!empty($this->_params
[0]['contact_id'])) {
408 // unclear when this would be set & whether it could be checked in getContactID.
409 // perhaps it relates to when cid is in the url
410 //@todo someone who knows add comments on the various contactIDs in this form
411 $contactID = $this->_params
[0]['contact_id'];
414 $contactID = $this->getContactID();
417 // if a discount has been applied, lets now deduct it from the amount
418 // and fix the fee level
419 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
420 foreach ($this->_params
as $k => $v) {
421 if (CRM_Utils_Array
::value('amount', $this->_params
[$k]) > 0 && !empty($this->_params
[$k]['discountAmount'])) {
422 $this->_params
[$k]['amount'] -= $this->_params
[$k]['discountAmount'];
423 $this->_params
[$k]['amount_level'] .= CRM_Utils_Array
::value('discountMessage', $this->_params
[$k]);
426 $this->set('params', $this->_params
);
429 // CRM-4320, lets build array of cancelled additional participant ids
430 // those are drop or skip by primary at the time of confirmation.
431 // get all in and then unset those we want to process.
432 $cancelledIds = $this->_additionalParticipantIds
;
434 $params = $this->_params
;
435 if ($this->_values
['event']['is_monetary']) {
436 $this->set('finalAmount', $this->_amount
);
438 $participantCount = [];
441 //unset the skip participant from params.
442 //build the $participantCount array.
443 //maintain record for all participants.
444 foreach ($params as $participantNum => $record) {
445 if ($record == 'skip') {
446 unset($params[$participantNum]);
447 $participantCount[$participantNum] = 'skip';
449 elseif ($participantNum) {
450 $participantCount[$participantNum] = 'participant';
452 $totalTaxAmount +
= CRM_Utils_Array
::value('tax_amount', $record, 0);
453 if (!empty($record['is_primary'])) {
454 $taxAmount = &$params[$participantNum]['tax_amount'];
456 //lets get additional participant id to cancel.
457 if ($this->_allowConfirmation
&& is_array($cancelledIds)) {
458 $additonalId = CRM_Utils_Array
::value('participant_id', $record);
459 if ($additonalId && $key = array_search($additonalId, $cancelledIds)) {
460 unset($cancelledIds[$key]);
464 $taxAmount = $totalTaxAmount;
465 $payment = $registerByID = $primaryCurrencyID = $contribution = NULL;
466 $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.');
468 $this->participantIDS
= [];
470 foreach ($params as $key => $value) {
471 CRM_Event_Form_Registration_Confirm
::fixLocationFields($value, $fields, $this);
472 //unset the billing parameters if it is pay later mode
473 //to avoid creation of billing location
474 // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
475 // the form & if billing fields exist we create the address, relying on the form to collect
476 // only information we intend to store.
477 if ($this->_allowWaitlist
478 ||
$this->_requireApproval
479 ||
(!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater
)
480 ||
empty($value['is_primary'])
483 "email-{$this->_bltID}",
484 'billing_first_name',
485 'billing_middle_name',
487 "billing_street_address-{$this->_bltID}",
488 "billing_city-{$this->_bltID}",
489 "billing_state_province-{$this->_bltID}",
490 "billing_state_province_id-{$this->_bltID}",
491 "billing_postal_code-{$this->_bltID}",
492 "billing_country-{$this->_bltID}",
493 "billing_country_id-{$this->_bltID}",
494 "address_name-{$this->_bltID}",
496 foreach ($billingFields as $field) {
497 unset($value[$field]);
499 if (!empty($value['is_pay_later'])) {
500 $this->_values
['params']['is_pay_later'] = TRUE;
504 //Unset ContactID for additional participants and set RegisterBy Id.
505 if (empty($value['is_primary'])) {
506 $contactID = CRM_Utils_Array
::value('contact_id', $value);
507 $registerByID = $this->get('registerByID');
509 $value['registered_by_id'] = $registerByID;
513 $value['amount'] = $this->_totalAmount
;
516 $contactID = CRM_Event_Form_Registration_Confirm
::updateContactFields($contactID, $value, $fields, $this);
518 // lets store the contactID in the session
519 // we dont store in userID in case the user is doing multiple
521 // for things like tell a friend
522 if (!$this->getContactID() && !empty($value['is_primary'])) {
523 CRM_Core_Session
::singleton()->set('transaction.userID', $contactID);
526 $value['description'] = ts('Online Event Registration') . ': ' . $this->_values
['event']['title'];
527 $value['accountingCode'] = CRM_Utils_Array
::value('accountingCode',
528 $this->_values
['event']
532 if ($this->_allowWaitlist ||
$this->_requireApproval
) {
533 //get the participant statuses.
534 $waitingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Waiting'");
535 if ($this->_allowWaitlist
) {
536 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
539 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
542 //there might be case user selected pay later and
543 //now becomes part of run time waiting list.
544 $value['is_pay_later'] = FALSE;
546 elseif ($this->_values
['event']['is_monetary']) {
547 // required only if paid event
548 if (is_array($this->_paymentProcessor
)) {
549 $payment = $this->_paymentProcessor
['object'];
551 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
552 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
553 $value = array_merge($value, $preApprovalParams);
557 if (!empty($value['is_pay_later']) ||
558 $value['amount'] == 0 ||
559 // The concept of contributeMode is deprecated.
560 $this->_contributeMode
== 'checkout' ||
561 $this->_contributeMode
== 'notify'
563 if ($value['amount'] != 0) {
565 //get the participant statuses.
566 $pendingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Pending'");
567 $status = !empty($value['is_pay_later']) ?
'Pending from pay later' : 'Pending from incomplete transaction';
568 $value['participant_status_id'] = $value['participant_status'] = array_search($status, $pendingStatuses);
571 elseif (!empty($value['is_primary'])) {
572 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $value, $value, TRUE);
573 // payment email param can be empty for _bltID mapping
574 // thus provide mapping for it with a different email value
575 if (empty($value['email'])) {
576 $value['email'] = CRM_Utils_Array
::valueByRegexKey('/^email-/', $value);
579 if (is_object($payment)) {
580 // Not quite sure why we don't just user $value since it contains the data
582 // @todo ditch $result & retest.
583 list($result, $value) = $this->processPayment($payment, $value);
586 CRM_Core_Error
::fatal($paymentObjError);
590 $value['receive_date'] = $now;
591 if ($this->_allowConfirmation
) {
592 $value['participant_register_date'] = $this->_values
['participant']['register_date'];
595 $createContrib = ($value['amount'] != 0) ?
TRUE : FALSE;
596 // force to create zero amount contribution, CRM-5095
597 if (!$createContrib && ($value['amount'] == 0)
598 && $this->_priceSetId
&& $this->_lineItem
600 $createContrib = TRUE;
603 if ($createContrib && !empty($value['is_primary']) &&
604 !$this->_allowWaitlist
&& !$this->_requireApproval
606 // if paid event add a contribution record
607 //if primary participant contributing additional amount
608 //append (multiple participants) to its fee level. CRM-4196.
609 $isAdditionalAmount = FALSE;
610 if (count($params) > 1) {
611 $isAdditionalAmount = TRUE;
614 //passing contribution id is already registered.
615 $contribution = self
::processContribution($this, $value, $result, $contactID, $pending, $isAdditionalAmount, $this->_paymentProcessor
);
616 $value['contributionID'] = $contribution->id
;
617 $value['contributionTypeID'] = $contribution->financial_type_id
;
618 $value['receive_date'] = $contribution->receive_date
;
619 $value['trxn_id'] = $contribution->trxn_id
;
620 $value['contributionID'] = $contribution->id
;
621 $value['contributionTypeID'] = $contribution->financial_type_id
;
623 $value['contactID'] = $contactID;
624 $value['eventID'] = $this->_eventId
;
625 $value['item_name'] = $value['description'];
628 if (!empty($value['contributionID'])) {
629 $this->_values
['contributionId'] = $value['contributionID'];
633 if (!empty($value['is_primary'])) {
634 $primaryCurrencyID = CRM_Utils_Array
::value('currencyID', $value);
636 if (empty($value['currencyID'])) {
637 $value['currencyID'] = $primaryCurrencyID;
640 // CRM-11182 - Confirmation page might not be monetary
641 if ($this->_values
['event']['is_monetary']) {
642 if (!$pending && !empty($value['is_primary']) &&
643 !$this->_allowWaitlist
&& !$this->_requireApproval
645 // transactionID & receive date required while building email template
646 $this->assign('trxn_id', CRM_Utils_Array
::value('trxn_id', $value));
647 $this->assign('receive_date', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
648 $this->set('receiveDate', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
649 $this->set('trxnId', CRM_Utils_Array
::value('trxn_id', $value));
653 $value['fee_amount'] = CRM_Utils_Array
::value('amount', $value);
654 $this->set('value', $value);
656 // handle register date CRM-4320
657 if ($this->_allowConfirmation
) {
658 $registerDate = CRM_Utils_Array
::value('participant_register_date', $params);
660 elseif (!empty($params['participant_register_date']) &&
661 is_array($params['participant_register_date'])
663 $registerDate = CRM_Utils_Date
::format($params['participant_register_date']);
666 $registerDate = date('YmdHis');
668 $this->assign('register_date', $registerDate);
670 $this->confirmPostProcess($contactID, $contribution, $payment);
673 //handle if no additional participant.
674 if (!$registerByID) {
675 $registerByID = $this->get('registerByID');
678 $this->set('participantIDs', $this->_participantIDS
);
680 // create line items, CRM-5313
681 if ($this->_priceSetId
&&
682 !empty($this->_lineItem
)
684 // take all processed participant ids.
685 $allParticipantIds = $this->_participantIDS
;
687 // when participant re-walk wizard.
688 if ($this->_allowConfirmation
&&
689 !empty($this->_additionalParticipantIds
)
691 $allParticipantIds = array_merge([$registerByID], $this->_additionalParticipantIds
);
694 $entityTable = 'civicrm_participant';
697 foreach ($this->_lineItem
as $key => $value) {
698 if ($value == 'skip') {
701 if ($entityId = CRM_Utils_Array
::value($key, $allParticipantIds)) {
702 // do cleanup line items if participant re-walking wizard.
703 if ($this->_allowConfirmation
) {
704 CRM_Price_BAO_LineItem
::deleteLineItems($entityId, $entityTable);
706 $lineItem[$this->_priceSetId
] = $value;
707 CRM_Price_BAO_LineItem
::processPriceSet($entityId, $lineItem, $contribution, $entityTable);
709 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
710 foreach ($value as $line) {
711 if (isset($line['tax_amount']) && isset($line['tax_rate'])) {
712 $totalTaxAmount = $line['tax_amount'] +
$totalTaxAmount;
713 if (isset($dataArray[$line['tax_rate']])) {
714 $dataArray[$line['tax_rate']] = $dataArray[$line['tax_rate']] + CRM_Utils_Array
::value('tax_amount', $line);
717 $dataArray[$line['tax_rate']] = CRM_Utils_Array
::value('tax_amount', $line);
723 $this->assign('dataArray', $dataArray);
724 $this->assign('totalTaxAmount', $totalTaxAmount);
727 //update status and send mail to cancelled additional participants, CRM-4320
728 if ($this->_allowConfirmation
&& is_array($cancelledIds) && !empty($cancelledIds)) {
729 $cancelledId = array_search('Cancelled',
730 CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Negative'")
732 CRM_Event_BAO_Participant
::transitionParticipants($cancelledIds, $cancelledId);
736 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
740 // for Transfer checkout.
741 // The concept of contributeMode is deprecated.
742 if (($this->_contributeMode
== 'checkout' ||
743 $this->_contributeMode
== 'notify'
744 ) && empty($params[0]['is_pay_later']) &&
745 !$this->_allowWaitlist
&& !$this->_requireApproval
&&
746 $this->_totalAmount
> 0
749 $primaryParticipant = $this->get('primaryParticipant');
751 if (empty($primaryParticipant['participantID'])) {
752 $primaryParticipant['participantID'] = $registerByID;
755 //build an array of custom profile and assigning it to template
756 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, $this->_values
, NULL, $isTest);
757 if (count($customProfile)) {
758 $this->assign('customProfile', $customProfile);
759 $this->set('customProfile', $customProfile);
762 // do a transfer only if a monetary payment greater than 0
763 if ($this->_values
['event']['is_monetary'] && $primaryParticipant) {
764 if ($payment && is_object($payment)) {
765 //CRM 14512 provide line items of all participants to payment gateway
766 $primaryContactId = $this->get('primaryContactId');
768 //build an array of cId/pId of participants
769 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, NULL, $primaryContactId, $isTest, TRUE);
771 //need to copy, since we are unsetting on the way.
772 $copyParticipantCountLines = $participantCount;
774 //lets carry all participant params w/ values.
775 foreach ($additionalIDs as $participantID => $contactId) {
776 $participantNum = $participantID;
777 if ($participantID == $registerByID) {
778 // This is the is primary participant.
782 if ($participantNum = array_search('participant', $copyParticipantCountLines)) {
783 //if no participant found break.
784 if ($participantNum === NULL) {
787 //unset current participant so we don't check them again
788 unset($copyParticipantCountLines[$participantNum]);
791 // get values of line items
792 if ($this->_amount
) {
794 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
795 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
796 $params[$participantNum]['amounts'] = $amount;
799 if (!empty($this->_lineItem
)) {
800 $lineItems = $this->_lineItem
;
802 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
803 $lineItem[] = $lineItemValue;
805 $params[$participantNum]['lineItem'] = $lineItem;
808 //only add additional particpants and not the primary particpant as we already have that
809 //added to $primaryParticipant so that this change doesn't break or require changes to
810 //existing gateway implementations
811 $primaryParticipant['participants_info'][$participantID] = $params[$participantNum];
814 //get event custom field information
815 $groupTree = CRM_Core_BAO_CustomGroup
::getTree('Event', NULL, $this->_eventId
, 0, $this->_values
['event']['event_type_id']);
816 $primaryParticipant['eventCustomFields'] = $groupTree;
818 // call postprocess hook before leaving
819 $this->postProcessHook();
821 $this->processPayment($payment, $primaryParticipant);
824 CRM_Core_Error
::fatal($paymentObjError);
829 //otherwise send mail Confirmation/Receipt
830 $primaryContactId = $this->get('primaryContactId');
832 //build an array of cId/pId of participants
833 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID,
834 NULL, $primaryContactId, $isTest,
837 //let's send mails to all with meaningful text, CRM-4320.
838 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
839 $this->assign('isRequireApproval', $this->_requireApproval
);
841 //need to copy, since we are unsetting on the way.
842 $copyParticipantCount = $participantCount;
844 //let's carry all participant params w/ values.
845 foreach ($additionalIDs as $participantID => $contactId) {
846 $participantNum = NULL;
847 if ($participantID == $registerByID) {
851 if ($participantNum = array_search('participant', $copyParticipantCount)) {
852 unset($copyParticipantCount[$participantNum]);
855 if ($participantNum === NULL) {
859 //carry the participant submitted values.
860 $this->_values
['params'][$participantID] = $params[$participantNum];
863 foreach ($additionalIDs as $participantID => $contactId) {
865 if ($participantID == $registerByID) {
866 //set as Primary Participant
867 $this->assign('isPrimary', 1);
868 //build an array of custom profile and assigning it to template.
869 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($participantID, $this->_values
, NULL, $isTest);
871 if (count($customProfile)) {
872 $this->assign('customProfile', $customProfile);
873 $this->set('customProfile', $customProfile);
875 $this->_values
['params']['additionalParticipant'] = FALSE;
878 //take the Additional participant number.
879 if ($participantNum = array_search('participant', $participantCount)) {
880 unset($participantCount[$participantNum]);
882 // Change $this->_values['participant'] to include additional participant values
883 $ids = $participantValues = [];
884 $participantParams = ['id' => $participantID];
885 CRM_Event_BAO_Participant
::getValues($participantParams, $participantValues, $ids);
886 $this->_values
['participant'] = $participantValues[$participantID];
888 $this->assign('isPrimary', 0);
889 $this->assign('customProfile', NULL);
890 //Additional Participant should get only it's payment information
891 if (!empty($this->_amount
)) {
893 $params = $this->get('params');
894 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
895 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
896 $this->assign('amounts', $amount);
898 if ($this->_lineItem
) {
899 $lineItems = $this->_lineItem
;
901 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
902 $lineItem[] = $lineItemValue;
904 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
905 $individual = $this->get('individual');
906 $dataArray[key($dataArray)] = $individual[$participantNum]['totalTaxAmt'];
907 $this->assign('dataArray', $dataArray);
908 $this->assign('totalAmount', $individual[$participantNum]['totalAmtWithTax']);
909 $this->assign('totalTaxAmount', $individual[$participantNum]['totalTaxAmt']);
910 $this->assign('individual', [$individual[$participantNum]]);
912 $this->assign('lineItem', $lineItem);
914 $this->_values
['params']['additionalParticipant'] = TRUE;
915 $this->assign('isAdditionalParticipant', $this->_values
['params']['additionalParticipant']);
918 //pass these variables since these are run time calculated.
919 $this->_values
['params']['isOnWaitlist'] = $this->_allowWaitlist
;
920 $this->_values
['params']['isRequireApproval'] = $this->_requireApproval
;
922 //send mail to primary as well as additional participants.
923 $this->assign('contactID', $contactId);
924 CRM_Event_BAO_Event
::sendMail($contactId, $this->_values
, $participantID, $isTest);
930 * Process the contribution.
932 * @param CRM_Core_Form $form
933 * @param array $params
934 * @param array $result
935 * @param int $contactID
936 * @param bool $pending
937 * @param bool $isAdditionalAmount
938 * @param array $paymentProcessor
940 * @return \CRM_Contribute_BAO_Contribution
942 * @throws \CRM_Core_Exception
943 * @throws \CiviCRM_API3_Exception
945 public static function processContribution(
946 &$form, $params, $result, $contactID,
947 $pending = FALSE, $isAdditionalAmount = FALSE,
948 $paymentProcessor = NULL
950 $transaction = new CRM_Core_Transaction();
952 $now = date('YmdHis');
955 if (!empty($form->_values
['event']['is_email_confirm'])) {
959 if ($isAdditionalAmount) {
960 $params['amount_level'] = $params['amount_level'] . ts(' (multiple participants)') . CRM_Core_DAO
::VALUE_SEPARATOR
;
963 // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
964 CRM_Contribute_Form_AbstractEditPayment
::formatCreditCardDetails($params);
967 'contact_id' => $contactID,
968 'financial_type_id' => !empty($form->_values
['event']['financial_type_id']) ?
$form->_values
['event']['financial_type_id'] : $params['financial_type_id'],
969 'receive_date' => $now,
970 'total_amount' => $params['amount'],
971 'tax_amount' => $params['tax_amount'],
972 'amount_level' => $params['amount_level'],
973 'invoice_id' => $params['invoiceID'],
974 'currency' => $params['currencyID'],
975 'source' => !empty($params['participant_source']) ?
$params['participant_source'] : $params['description'],
976 'is_pay_later' => CRM_Utils_Array
::value('is_pay_later', $params, 0),
977 'campaign_id' => CRM_Utils_Array
::value('campaign_id', $params),
978 'card_type_id' => CRM_Utils_Array
::value('card_type_id', $params),
979 'pan_truncation' => CRM_Utils_Array
::value('pan_truncation', $params),
982 if ($paymentProcessor) {
983 $contribParams['payment_instrument_id'] = $paymentProcessor['payment_instrument_id'];
984 $contribParams['payment_processor'] = $paymentProcessor['id'];
987 if (!$pending && $result) {
989 'fee_amount' => CRM_Utils_Array
::value('fee_amount', $result),
990 'trxn_id' => $result['trxn_id'],
991 'receipt_date' => $receiptDate,
995 $allStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
996 $contribParams['contribution_status_id'] = array_search('Completed', $allStatuses);
998 $contribParams['contribution_status_id'] = array_search('Pending', $allStatuses);
1001 $contribParams['is_test'] = 0;
1002 if ($form->_action
& CRM_Core_Action
::PREVIEW || CRM_Utils_Array
::value('mode', $params) == 'test') {
1003 $contribParams['is_test'] = 1;
1006 if (!empty($contribParams['invoice_id'])) {
1007 $contribParams['id'] = CRM_Core_DAO
::getFieldValue('CRM_Contribute_DAO_Contribution',
1008 $contribParams['invoice_id'],
1014 if (Civi
::settings()->get('deferred_revenue_enabled')) {
1015 $eventStartDate = CRM_Utils_Array
::value(
1017 CRM_Utils_Array
::value(
1022 if (strtotime($eventStartDate) > strtotime(date('Ymt'))) {
1023 $contribParams['revenue_recognition_date'] = date('Ymd', strtotime($eventStartDate));
1026 //create an contribution address
1027 // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although
1028 // currently that is only inherited by back-office forms.
1029 if ($form->_contributeMode
!= 'notify' && empty($params['is_pay_later'])) {
1030 $contribParams['address_id'] = CRM_Contribute_BAO_Contribution
::createAddress($params, $form->_bltID
);
1033 $contribParams['skipLineItem'] = 1;
1034 $contribParams['skipCleanMoney'] = 1;
1035 // create contribution record
1036 $contribution = CRM_Contribute_BAO_Contribution
::add($contribParams);
1038 CRM_Event_BAO_Participant
::createDiscountTrxn($form->_eventId
, $contribParams, NULL, CRM_Price_BAO_PriceSet
::parseFirstPriceSetValueIDFromParams($params));
1040 // process soft credit / pcp pages
1041 if (!empty($params['pcp_made_through_id'])) {
1042 CRM_Contribute_BAO_ContributionSoft
::formatSoftCreditParams($params, $form);
1043 CRM_Contribute_BAO_ContributionSoft
::processSoftContribution($params, $contribution);
1046 $transaction->commit();
1048 return $contribution;
1052 * Fix the Location Fields.
1054 * @todo Reconcile with the contribution method formatParamsForPaymentProcessor
1055 * rather than adding different logic to check when to keep the billing
1056 * fields. There might be a difference in handling guest/multiple
1057 * participants though.
1059 * @param array $params
1060 * @param array $fields
1061 * @param CRM_Core_Form $form
1063 public static function fixLocationFields(&$params, &$fields, &$form) {
1064 if (!empty($form->_fields
)) {
1065 foreach ($form->_fields
as $name => $dontCare) {
1070 // If there's no 'first_name' in the profile then overwrite the names from
1071 // the billing fields (if they are set)
1072 if (is_array($fields)) {
1073 if (!array_key_exists('first_name', $fields)) {
1074 $nameFields = ['first_name', 'middle_name', 'last_name'];
1075 foreach ($nameFields as $name) {
1077 if (array_key_exists("billing_$name", $params)) {
1078 $params[$name] = $params["billing_{$name}"];
1079 $params['preserveDBName'] = TRUE;
1085 // Add the billing names to the billing address, if a billing name is set
1086 if (!empty($params['billing_first_name'])) {
1087 $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);
1088 $fields["address_name-{$form->_bltID}"] = 1;
1091 $fields["email-{$form->_bltID}"] = 1;
1092 $fields['email-Primary'] = 1;
1094 //if its pay later or additional participant set email address as primary.
1095 if ((!empty($params['is_pay_later']) ||
empty($params['is_primary']) ||
1096 !$form->_values
['event']['is_monetary'] ||
1097 $form->_allowWaitlist ||
1098 $form->_requireApproval
1099 ) && !empty($params["email-{$form->_bltID}"])
1101 $params['email-Primary'] = $params["email-{$form->_bltID}"];
1106 * Update contact fields.
1108 * @param int $contactID
1109 * @param array $params
1110 * @param array $fields
1111 * @param CRM_Core_Form $form
1115 public static function updateContactFields($contactID, $params, $fields, &$form) {
1116 //add the contact to group, if add to group is selected for a
1117 //particular uf group
1119 // get the add to groups
1122 if (!empty($form->_fields
)) {
1123 foreach ($form->_fields
as $key => $value) {
1124 if (!empty($value['add_to_group_id'])) {
1125 $addToGroups[$value['add_to_group_id']] = $value['add_to_group_id'];
1130 // check for profile double opt-in and get groups to be subscribed
1131 $subscribeGroupIds = CRM_Core_BAO_UFGroup
::getDoubleOptInGroupIds($params, $contactID);
1133 foreach ($addToGroups as $k) {
1134 if (array_key_exists($k, $subscribeGroupIds)) {
1135 unset($addToGroups[$k]);
1139 // since we are directly adding contact to group lets unset it from mailing
1140 if (!empty($addToGroups)) {
1141 foreach ($addToGroups as $groupId) {
1142 if (isset($subscribeGroupIds[$groupId])) {
1143 unset($subscribeGroupIds[$groupId]);
1148 $ctype = CRM_Core_DAO
::getFieldValue(
1149 'CRM_Contact_DAO_Contact',
1154 if (array_key_exists('contact_id', $params) && empty($params['contact_id'])) {
1155 // we unset this here because the downstream function ignores the contactID we give it
1156 // if it is set & it is difficult to understand the implications of 'fixing' this downstream
1157 // but if we are passing a contact id into this function it's reasonable to assume we don't
1159 unset($params['contact_id']);
1162 $contactID = CRM_Contact_BAO_Contact
::createProfileContact(
1174 foreach (CRM_Contact_BAO_Contact
::$_greetingTypes as $greeting) {
1175 if (!isset($params[$greeting . '_id'])) {
1176 $params[$greeting . '_id'] = CRM_Contact_BAO_Contact_Utils
::defaultGreeting('Individual', $greeting);
1180 $contactID = CRM_Contact_BAO_Contact
::createProfileContact($params,
1188 $form->set('contactID', $contactID);
1191 //get email primary first if exist
1192 $subscribtionEmail = ['email' => CRM_Utils_Array
::value('email-Primary', $params)];
1193 if (!$subscribtionEmail['email']) {
1194 $subscribtionEmail['email'] = CRM_Utils_Array
::value("email-{$form->_bltID}", $params);
1196 // subscribing contact to groups
1197 if (!empty($subscribeGroupIds) && $subscribtionEmail['email']) {
1198 CRM_Mailing_Event_BAO_Subscribe
::commonSubscribe($subscribeGroupIds, $subscribtionEmail, $contactID);
1205 * Assign Profiles to the template.
1207 * @param CRM_Event_Form_Registration_Confirm $form
1209 * @throws \Exception
1211 public static function assignProfiles($form) {
1212 $participantParams = $form->_params
;
1213 $formattedValues = $profileFields = [];
1215 foreach ($participantParams as $participantNum => $participantValue) {
1216 if ($participantNum) {
1217 $prefix1 = 'additional';
1218 $prefix2 = 'additional_';
1224 if ($participantValue !== 'skip') {
1225 //get the customPre profile info
1226 if (!empty($form->_values
[$prefix2 . 'custom_pre_id'])) {
1227 $values = $groupName = [];
1228 CRM_Event_BAO_Event
::displayProfile($participantValue,
1229 $form->_values
[$prefix2 . 'custom_pre_id'],
1235 if (count($values)) {
1236 $formattedValues[$count][$prefix1 . 'CustomPre'] = $values;
1238 $formattedValues[$count][$prefix1 . 'CustomPreGroupTitle'] = CRM_Utils_Array
::value('groupTitle', $groupName);
1240 //get the customPost profile info
1241 if (!empty($form->_values
[$prefix2 . 'custom_post_id'])) {
1242 $values = $groupName = [];
1243 foreach ($form->_values
[$prefix2 . 'custom_post_id'] as $gids) {
1245 CRM_Event_BAO_Event
::displayProfile($participantValue,
1251 $values[$gids] = $val;
1252 $groupName[$gids] = $group;
1255 if (count($values)) {
1256 $formattedValues[$count][$prefix1 . 'CustomPost'] = $values;
1259 if (isset($formattedValues[$count][$prefix1 . 'CustomPre'])) {
1260 $formattedValues[$count][$prefix1 . 'CustomPost'] = array_diff_assoc($formattedValues[$count][$prefix1 . 'CustomPost'],
1261 $formattedValues[$count][$prefix1 . 'CustomPre']
1265 $formattedValues[$count][$prefix1 . 'CustomPostGroupTitle'] = $groupName;
1269 $form->_fields
= $profileFields;
1271 if (!empty($formattedValues)) {
1272 $form->assign('primaryParticipantProfile', $formattedValues[1]);
1273 $form->set('primaryParticipantProfile', $formattedValues[1]);
1275 unset($formattedValues[1]);
1276 $form->assign('addParticipantProfile', $formattedValues);
1277 $form->set('addParticipantProfile', $formattedValues);
1283 * Submit in test mode.
1287 public static function testSubmit($params) {
1288 $form = new CRM_Event_Form_Registration_Confirm();
1289 // This way the mocked up controller ignores the session stuff.
1290 $_SERVER['REQUEST_METHOD'] = 'GET';
1291 $_REQUEST['id'] = $form->_eventId
= $params['id'];
1292 $form->controller
= new CRM_Event_Controller_Registration();
1293 $form->_params
= $params['params'];
1294 // This happens in buildQuickForm so emulate here.
1295 $form->_amount
= $form->_totalAmount
= CRM_Utils_Rule
::cleanMoney(CRM_Utils_Array
::value('totalAmount', $params));
1296 $form->set('params', $params['params']);
1297 $form->_values
['custom_pre_id'] = CRM_Utils_Array
::value('custom_pre_id', $params);
1298 $form->_values
['custom_post_id'] = CRM_Utils_Array
::value('custom_post_id', $params);
1299 $form->_values
['event'] = CRM_Utils_Array
::value('event', $params);
1300 $form->_contributeMode
= $params['contributeMode'];
1301 $eventParams = ['id' => $params['id']];
1302 CRM_Event_BAO_Event
::retrieve($eventParams, $form->_values
['event']);
1303 $form->set('registerByID', $params['registerByID']);
1304 if (!empty($params['paymentProcessorObj'])) {
1305 $form->_paymentProcessor
= $params['paymentProcessorObj'];
1307 $form->postProcess();
1311 * Process the payment, redirecting back to the page on error.
1313 * @param \CRM_Core_Payment $payment
1318 private function processPayment($payment, $value) {
1320 $result = $payment->doPayment($value, 'event');
1321 return [$result, $value];
1323 catch (\Civi\Payment\Exception\PaymentProcessorException
$e) {
1324 Civi
::log()->error('Payment processor exception: ' . $e->getMessage());
1325 CRM_Core_Session
::singleton()->setStatus($e->getMessage());
1326 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "id={$this->_eventId}"));
1332 * Clean money fields from the form.
1334 * @param array $params
1336 protected function cleanMoneyFields(&$params) {
1337 foreach ($this->submittableMoneyFields
as $moneyField) {
1338 foreach ($params as $index => $paramField) {
1339 if (isset($paramField[$moneyField])) {
1340 $params[$index][$moneyField] = CRM_Utils_Rule
::cleanMoney($paramField[$moneyField]);