3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
14 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 * This class generates form components for processing Event.
20 class CRM_Event_Form_Registration_Confirm
extends CRM_Event_Form_Registration
{
23 * The values for the contribution db object.
36 public $submitOnce = TRUE;
39 * Monetary fields that may be submitted.
41 * These should get a standardised format in the beginPostProcess function.
43 * These fields are common to many forms. Some may override this.
46 protected $submittableMoneyFields = ['total_amount', 'net_amount', 'non_deductible_amount', 'fee_amount', 'tax_amount', 'amount'];
49 * Set variables up before form is built.
51 public function preProcess() {
54 // lineItem isn't set until Register postProcess
55 $this->_lineItem
= $this->get('lineItem');
57 $this->_params
= $this->get('params');
58 $this->_params
[0]['tax_amount'] = $this->get('tax_amount');
60 $this->_params
[0]['is_pay_later'] = $this->get('is_pay_later');
61 $this->assign('is_pay_later', $this->_params
[0]['is_pay_later']);
62 if ($this->_params
[0]['is_pay_later']) {
63 $this->assign('pay_later_receipt', $this->_values
['event']['pay_later_receipt']);
66 CRM_Utils_Hook
::eventDiscount($this, $this->_params
);
68 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
69 $this->set('hookDiscount', $this->_params
[0]['discount']);
70 $this->assign('hookDiscount', $this->_params
[0]['discount']);
73 $this->preProcessExpress();
75 if ($this->_values
['event']['is_monetary']) {
76 $this->_params
[0]['invoiceID'] = $this->get('invoiceID');
78 $this->assign('defaultRole', FALSE);
79 if (CRM_Utils_Array
::value('defaultRole', $this->_params
[0]) == 1) {
80 $this->assign('defaultRole', TRUE);
83 if (empty($this->_params
[0]['participant_role_id']) &&
84 $this->_values
['event']['default_role_id']
86 $this->_params
[0]['participant_role_id'] = $this->_values
['event']['default_role_id'];
89 if (isset($this->_values
['event']['confirm_title'])) {
90 CRM_Utils_System
::setTitle($this->_values
['event']['confirm_title']);
93 // Personal campaign page
95 $params = CRM_Contribute_Form_Contribution_Confirm
::processPcp($this, $this->_params
[0]);
96 $this->_params
[0] = $params;
99 $this->set('params', $this->_params
);
103 * Pre process function for Paypal Express confirm.
104 * @todo this is just a step in refactor as payment processor specific code does not belong in generic forms
107 * @throws \CRM_Core_Exception
109 private function preProcessExpress() {
110 if ($this->_contributeMode
!== 'express') {
114 // rfp == redirect from paypal
115 // @fixme rfp is probably not required - the getPreApprovalDetails should deal with any payment-processor specific 'stuff'
116 $rfp = CRM_Utils_Request
::retrieve('rfp', 'Boolean', CRM_Core_DAO
::$_nullObject, FALSE, NULL, 'GET');
118 //we lost rfp in case of additional participant. So set it explicitly.
119 if ($rfp || CRM_Utils_Array
::value('additional_participants', $this->_params
[0], FALSE)) {
120 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
121 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
122 $params = array_merge($this->_params
, $preApprovalParams);
124 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $params, $params, FALSE);
126 // set a few other parameters that are not really specific to this method because we don't know what
127 // will break if we change this.
128 $params['amount'] = $this->_params
[0]['amount'];
129 if (!empty($this->_params
[0]['discount'])) {
130 $params['discount'] = $this->_params
[0]['discount'];
131 $params['discountAmount'] = $this->_params
[0]['discountAmount'];
132 $params['discountMessage'] = $this->_params
[0]['discountMessage'];
135 $params['amount_level'] = $this->_params
[0]['amount_level'];
136 $params['currencyID'] = $this->_params
[0]['currencyID'];
138 // also merge all the other values from the profile fields
139 $values = $this->controller
->exportValues('Register');
142 "street_address-{$this->_bltID}",
143 "city-{$this->_bltID}",
144 "state_province_id-{$this->_bltID}",
145 "postal_code-{$this->_bltID}",
146 "country_id-{$this->_bltID}",
149 foreach ($values as $name => $value) {
151 if (!in_array($name, $skipFields)) {
152 $params[$name] = $value;
154 if (substr($name, 0, 6) == 'price_') {
155 $params[$name] = $this->_params
[0][$name];
158 $this->set('getExpressCheckoutDetails', $params);
160 $this->_params
[0] = array_merge($this->_params
[0], $params);
161 $this->_params
[0]['is_primary'] = 1;
166 * Overwrite action, since we are only showing elements in frozen mode no help display needed.
170 public function getAction() {
171 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
172 return CRM_Core_Action
::VIEW | CRM_Core_Action
::PREVIEW
;
175 return CRM_Core_Action
::VIEW
;
180 * Build the form object.
182 public function buildQuickForm() {
183 $this->assignToTemplate();
185 if ($this->_values
['event']['is_monetary'] &&
186 ($this->_params
[0]['amount'] ||
$this->_params
[0]['amount'] == 0) &&
187 !$this->_requireApproval
192 foreach ($this->_params
as $k => $v) {
196 $individualTaxAmount = 0;
197 //display tax amount on confirmation page
198 $taxAmount +
= $v['tax_amount'];
200 $this->cleanMoneyFields($v);
205 if (isset($v['billing_' . $name]) &&
208 $v[$name] = $v['billing_' . $name];
212 if (!empty($v['first_name']) && !empty($v['last_name'])) {
213 $append = $v['first_name'] . ' ' . $v['last_name'];
216 //use an email if we have one
217 foreach ($v as $v_key => $v_val) {
218 if (substr($v_key, 0, 6) == 'email-') {
219 $append = $v[$v_key];
224 $this->_amount
[$k]['amount'] = $v['amount'];
225 if (!empty($v['discountAmount'])) {
226 $this->_amount
[$k]['amount'] -= $v['discountAmount'];
229 $this->_amount
[$k]['label'] = preg_replace('/\ 1/', '', $v['amount_level']) . ' - ' . $append;
230 $this->_part
[$k]['info'] = CRM_Utils_Array
::value('first_name', $v) . ' ' . CRM_Utils_Array
::value('last_name', $v);
231 if (empty($v['first_name'])) {
232 $this->_part
[$k]['info'] = $append;
236 $individual[$k]['totalAmtWithTax'] = $this->_amount
[$k]['amount'];
237 $individual[$k]['totalTaxAmt'] = $individualTaxAmount +
$v['tax_amount'];
238 $this->_totalAmount
= $this->_totalAmount +
$this->_amount
[$k]['amount'];
239 if (!empty($v['is_primary'])) {
240 $this->set('primaryParticipantAmount', $this->_amount
[$k]['amount']);
245 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
246 $this->assign('totalTaxAmount', $taxAmount);
247 $this->assign('taxTerm', CRM_Invoicing_Utils
::getTaxTerm());
248 $this->assign('individual', $individual);
249 $this->set('individual', $individual);
252 $this->assign('part', $this->_part
);
253 $this->set('part', $this->_part
);
254 $this->assign('amounts', $this->_amount
);
255 $this->assign('totalAmount', $this->_totalAmount
);
256 $this->set('totalAmount', $this->_totalAmount
);
259 if ($this->_priceSetId
&& !CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_PriceSet', $this->_priceSetId
, 'is_quick_config')) {
260 $lineItemForTemplate = [];
261 if (!empty($this->_lineItem
) && is_array($this->_lineItem
)) {
262 foreach ($this->_lineItem
as $key => $value) {
263 if (!empty($value)) {
264 $lineItemForTemplate[$key] = $value;
268 if (!empty($lineItemForTemplate)) {
269 $this->assignLineItemsToTemplate($lineItemForTemplate);
273 //display additional participants profile.
274 self
::assignProfiles($this);
276 //consider total amount.
277 $this->assign('isAmountzero', ($this->_totalAmount
<= 0) ?
TRUE : FALSE);
279 $contribButton = ts('Continue');
283 'name' => ts('Go Back'),
287 'name' => $contribButton,
294 if (!empty($this->_fields
)) {
295 foreach ($this->_fields
as $name => $dontCare) {
299 $fields["billing_state_province-{$this->_bltID}"] = $fields["billing_country-{$this->_bltID}"] = $fields["email-{$this->_bltID}"] = 1;
300 foreach ($fields as $name => $dontCare) {
301 if (isset($this->_params
[0][$name])) {
302 $defaults[$name] = $this->_params
[0][$name];
303 if (substr($name, 0, 7) == 'custom_') {
304 $timeField = "{$name}_time";
305 if (isset($this->_params
[0][$timeField])) {
306 $defaults[$timeField] = $this->_params
[0][$timeField];
308 if (isset($this->_params
[0]["{$name}_id"])) {
309 $defaults["{$name}_id"] = $this->_params
[0]["{$name}_id"];
312 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)
313 && !empty($this->_params
[0][$name . '_custom'])
315 $defaults[$name . '_custom'] = $this->_params
[0][$name . '_custom'];
320 $this->setDefaults($defaults);
323 //lets give meaningful status message, CRM-4320.
324 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
325 $this->assign('isRequireApproval', $this->_requireApproval
);
327 // Assign Participant Count to Lineitem Table
328 $this->assign('pricesetFieldsCount', CRM_Price_BAO_PriceSet
::getPricesetCount($this->_priceSetId
));
329 $this->addFormRule(['CRM_Event_Form_Registration_Confirm', 'formRule'], $this);
335 * @param array $fields
336 * @param array $files
337 * @param CRM_Core_Form $self
341 public static function formRule($fields, $files, $self) {
343 $eventFull = CRM_Event_BAO_Participant
::eventFull($self->_eventId
, FALSE, CRM_Utils_Array
::value('has_waitlist', $self->_values
['event']));
344 if ($eventFull && empty($self->_allowConfirmation
)) {
345 if (empty($self->_allowWaitlist
)) {
346 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "reset=1&id={$self->_eventId}", FALSE, NULL, FALSE, TRUE));
349 $self->_feeBlock
= $self->_values
['fee'];
350 CRM_Event_Form_Registration_Register
::formatFieldsForOptionFull($self);
352 if (!empty($self->_priceSetId
) &&
353 !$self->_requireApproval
&& !$self->_allowWaitlist
355 $priceSetErrors = self
::validatePriceSet($self, $self->_params
);
356 if (!empty($priceSetErrors)) {
357 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');
358 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');
359 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "_qf_Register_display=true&qfKey={$fields['qfKey']}"));
363 return empty($priceSetErrors) ?
TRUE : $priceSetErrors;
367 * Process the form submission.
369 * @throws \CiviCRM_API3_Exception
370 * @throws \CRM_Core_Exception
372 public function postProcess() {
373 $now = date('YmdHis');
375 $this->_params
= $this->get('params');
376 $this->cleanMoneyFields($this->_params
);
378 if (!empty($this->_params
[0]['contact_id'])) {
379 // unclear when this would be set & whether it could be checked in getContactID.
380 // perhaps it relates to when cid is in the url
381 //@todo someone who knows add comments on the various contactIDs in this form
382 $contactID = $this->_params
[0]['contact_id'];
385 $contactID = $this->getContactID();
388 // if a discount has been applied, lets now deduct it from the amount
389 // and fix the fee level
390 if (!empty($this->_params
[0]['discount']) && !empty($this->_params
[0]['discount']['applied'])) {
391 foreach ($this->_params
as $k => $v) {
392 if (CRM_Utils_Array
::value('amount', $this->_params
[$k]) > 0 && !empty($this->_params
[$k]['discountAmount'])) {
393 $this->_params
[$k]['amount'] -= $this->_params
[$k]['discountAmount'];
394 $this->_params
[$k]['amount_level'] .= CRM_Utils_Array
::value('discountMessage', $this->_params
[$k]);
397 $this->set('params', $this->_params
);
400 // CRM-4320, lets build array of cancelled additional participant ids
401 // those are drop or skip by primary at the time of confirmation.
402 // get all in and then unset those we want to process.
403 $cancelledIds = $this->_additionalParticipantIds
;
405 $params = $this->_params
;
406 if ($this->_values
['event']['is_monetary']) {
407 $this->set('finalAmount', $this->_amount
);
409 $participantCount = [];
412 //unset the skip participant from params.
413 //build the $participantCount array.
414 //maintain record for all participants.
415 foreach ($params as $participantNum => $record) {
416 if ($record == 'skip') {
417 unset($params[$participantNum]);
418 $participantCount[$participantNum] = 'skip';
420 elseif ($participantNum) {
421 $participantCount[$participantNum] = 'participant';
423 $totalTaxAmount +
= CRM_Utils_Array
::value('tax_amount', $record, 0);
424 if (!empty($record['is_primary'])) {
425 $taxAmount = &$params[$participantNum]['tax_amount'];
427 //lets get additional participant id to cancel.
428 if ($this->_allowConfirmation
&& is_array($cancelledIds)) {
429 $additonalId = CRM_Utils_Array
::value('participant_id', $record);
430 if ($additonalId && $key = array_search($additonalId, $cancelledIds)) {
431 unset($cancelledIds[$key]);
435 $taxAmount = $totalTaxAmount;
436 $payment = $registerByID = $primaryCurrencyID = $contribution = NULL;
437 $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.');
440 foreach ($params as $key => $value) {
441 CRM_Event_Form_Registration_Confirm
::fixLocationFields($value, $fields, $this);
442 //unset the billing parameters if it is pay later mode
443 //to avoid creation of billing location
444 // @todo - the reasoning for this is unclear - elsewhere we check what fields are provided by
445 // the form & if billing fields exist we create the address, relying on the form to collect
446 // only information we intend to store.
447 if ($this->_allowWaitlist
448 ||
$this->_requireApproval
449 ||
(!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater
)
450 ||
empty($value['is_primary'])
453 "email-{$this->_bltID}",
454 'billing_first_name',
455 'billing_middle_name',
457 "billing_street_address-{$this->_bltID}",
458 "billing_city-{$this->_bltID}",
459 "billing_state_province-{$this->_bltID}",
460 "billing_state_province_id-{$this->_bltID}",
461 "billing_postal_code-{$this->_bltID}",
462 "billing_country-{$this->_bltID}",
463 "billing_country_id-{$this->_bltID}",
464 "address_name-{$this->_bltID}",
466 foreach ($billingFields as $field) {
467 unset($value[$field]);
469 if (!empty($value['is_pay_later'])) {
470 $this->_values
['params']['is_pay_later'] = TRUE;
474 //Unset ContactID for additional participants and set RegisterBy Id.
475 if (empty($value['is_primary'])) {
476 $contactID = CRM_Utils_Array
::value('contact_id', $value);
477 $registerByID = $this->get('registerByID');
479 $value['registered_by_id'] = $registerByID;
483 $value['amount'] = $this->_totalAmount
;
486 $contactID = CRM_Event_Form_Registration_Confirm
::updateContactFields($contactID, $value, $fields, $this);
488 // lets store the contactID in the session
489 // we dont store in userID in case the user is doing multiple
491 // for things like tell a friend
492 if (!$this->getContactID() && !empty($value['is_primary'])) {
493 CRM_Core_Session
::singleton()->set('transaction.userID', $contactID);
496 $value['description'] = ts('Online Event Registration') . ': ' . $this->_values
['event']['title'];
497 $value['accountingCode'] = CRM_Utils_Array
::value('accountingCode',
498 $this->_values
['event']
502 if ($this->_allowWaitlist ||
$this->_requireApproval
) {
503 //get the participant statuses.
504 $waitingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Waiting'");
505 if ($this->_allowWaitlist
) {
506 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
509 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
512 //there might be case user selected pay later and
513 //now becomes part of run time waiting list.
514 $value['is_pay_later'] = FALSE;
516 elseif ($this->_values
['event']['is_monetary']) {
517 // required only if paid event
518 if (is_array($this->_paymentProcessor
)) {
519 $payment = $this->_paymentProcessor
['object'];
521 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
522 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
523 $value = array_merge($value, $preApprovalParams);
527 if (!empty($value['is_pay_later']) ||
528 $value['amount'] == 0 ||
529 // The concept of contributeMode is deprecated.
530 $this->_contributeMode
== 'checkout' ||
531 $this->_contributeMode
== 'notify'
533 if ($value['amount'] != 0) {
535 //get the participant statuses.
536 $pendingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Pending'");
537 $status = !empty($value['is_pay_later']) ?
'Pending from pay later' : 'Pending from incomplete transaction';
538 $value['participant_status_id'] = $value['participant_status'] = array_search($status, $pendingStatuses);
541 elseif (!empty($value['is_primary'])) {
542 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $value, $value, TRUE);
543 // payment email param can be empty for _bltID mapping
544 // thus provide mapping for it with a different email value
545 if (empty($value['email'])) {
546 $value['email'] = CRM_Utils_Array
::valueByRegexKey('/^email-/', $value);
549 if (is_object($payment)) {
550 // Not quite sure why we don't just user $value since it contains the data
552 // @todo ditch $result & retest.
553 list($result, $value) = $this->processPayment($payment, $value);
556 throw new CRM_Core_Exception($paymentObjError);
560 $value['receive_date'] = $now;
561 if ($this->_allowConfirmation
) {
562 $value['participant_register_date'] = $this->_values
['participant']['register_date'];
565 $createContrib = ($value['amount'] != 0) ?
TRUE : FALSE;
566 // force to create zero amount contribution, CRM-5095
567 if (!$createContrib && ($value['amount'] == 0)
568 && $this->_priceSetId
&& $this->_lineItem
570 $createContrib = TRUE;
573 if ($createContrib && !empty($value['is_primary']) &&
574 !$this->_allowWaitlist
&& !$this->_requireApproval
576 // if paid event add a contribution record
577 //if primary participant contributing additional amount
578 //append (multiple participants) to its fee level. CRM-4196.
579 if (count($params) > 1) {
580 $value['amount_level'] .= ts(' (multiple participants)') . CRM_Core_DAO
::VALUE_SEPARATOR
;
583 //passing contribution id is already registered.
584 $contribution = $this->processContribution($this, $value, $result, $contactID, $pending, $this->_paymentProcessor
);
585 $value['contributionID'] = $contribution->id
;
586 $value['contributionTypeID'] = $contribution->financial_type_id
;
587 $value['receive_date'] = $contribution->receive_date
;
588 $value['trxn_id'] = $contribution->trxn_id
;
589 $value['contributionID'] = $contribution->id
;
590 $value['contributionTypeID'] = $contribution->financial_type_id
;
592 $value['contactID'] = $contactID;
593 $value['eventID'] = $this->_eventId
;
594 $value['item_name'] = $value['description'];
597 if (!empty($value['contributionID'])) {
598 $this->_values
['contributionId'] = $value['contributionID'];
602 if (!empty($value['is_primary'])) {
603 $primaryCurrencyID = CRM_Utils_Array
::value('currencyID', $value);
605 if (empty($value['currencyID'])) {
606 $value['currencyID'] = $primaryCurrencyID;
609 // CRM-11182 - Confirmation page might not be monetary
610 if ($this->_values
['event']['is_monetary']) {
611 if (!$pending && !empty($value['is_primary']) &&
612 !$this->_allowWaitlist
&& !$this->_requireApproval
614 // transactionID & receive date required while building email template
615 $this->assign('trxn_id', CRM_Utils_Array
::value('trxn_id', $value));
616 $this->assign('receive_date', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
617 $this->set('receiveDate', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
618 $this->set('trxnId', CRM_Utils_Array
::value('trxn_id', $value));
622 $value['fee_amount'] = CRM_Utils_Array
::value('amount', $value);
623 $this->set('value', $value);
625 // handle register date CRM-4320
626 if ($this->_allowConfirmation
) {
627 $registerDate = CRM_Utils_Array
::value('participant_register_date', $params);
629 elseif (!empty($params['participant_register_date']) &&
630 is_array($params['participant_register_date'])
632 $registerDate = CRM_Utils_Date
::format($params['participant_register_date']);
635 $registerDate = date('YmdHis');
637 $this->assign('register_date', $registerDate);
639 $this->confirmPostProcess($contactID, $contribution);
642 //handle if no additional participant.
643 if (!$registerByID) {
644 $registerByID = $this->get('registerByID');
647 $this->set('participantIDs', $this->_participantIDS
);
649 // create line items, CRM-5313
650 if ($this->_priceSetId
&&
651 !empty($this->_lineItem
)
653 // take all processed participant ids.
654 $allParticipantIds = $this->_participantIDS
;
656 // when participant re-walk wizard.
657 if ($this->_allowConfirmation
&&
658 !empty($this->_additionalParticipantIds
)
660 $allParticipantIds = array_merge([$registerByID], $this->_additionalParticipantIds
);
663 $entityTable = 'civicrm_participant';
666 foreach ($this->_lineItem
as $key => $value) {
667 if ($value == 'skip') {
670 if ($entityId = CRM_Utils_Array
::value($key, $allParticipantIds)) {
671 // do cleanup line items if participant re-walking wizard.
672 if ($this->_allowConfirmation
) {
673 CRM_Price_BAO_LineItem
::deleteLineItems($entityId, $entityTable);
675 $lineItem[$this->_priceSetId
] = $value;
676 CRM_Price_BAO_LineItem
::processPriceSet($entityId, $lineItem, $contribution, $entityTable);
678 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
679 foreach ($value as $line) {
680 if (isset($line['tax_amount']) && isset($line['tax_rate'])) {
681 $totalTaxAmount = $line['tax_amount'] +
$totalTaxAmount;
682 if (isset($dataArray[$line['tax_rate']])) {
683 $dataArray[$line['tax_rate']] = $dataArray[$line['tax_rate']] + CRM_Utils_Array
::value('tax_amount', $line);
686 $dataArray[$line['tax_rate']] = CRM_Utils_Array
::value('tax_amount', $line);
692 $this->assign('dataArray', $dataArray);
693 $this->assign('totalTaxAmount', $totalTaxAmount);
696 //update status and send mail to cancelled additional participants, CRM-4320
697 if ($this->_allowConfirmation
&& is_array($cancelledIds) && !empty($cancelledIds)) {
698 $cancelledId = array_search('Cancelled',
699 CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Negative'")
701 CRM_Event_BAO_Participant
::transitionParticipants($cancelledIds, $cancelledId);
705 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
709 $primaryParticipant = $this->get('primaryParticipant');
711 if (empty($primaryParticipant['participantID'])) {
712 CRM_Core_Error
::deprecatedFunctionWarning('This line is not logically reachable.');
713 $primaryParticipant['participantID'] = $registerByID;
715 //otherwise send mail Confirmation/Receipt
716 $primaryContactId = $this->get('primaryContactId');
718 // for Transfer checkout.
719 // The concept of contributeMode is deprecated.
720 if (($this->_contributeMode
== 'checkout' ||
721 $this->_contributeMode
== 'notify'
722 ) && empty($params[0]['is_pay_later']) &&
723 !$this->_allowWaitlist
&& !$this->_requireApproval
&&
724 $this->_totalAmount
> 0
727 //build an array of custom profile and assigning it to template
728 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, $this->_values
, NULL, $isTest);
729 if (count($customProfile)) {
730 $this->assign('customProfile', $customProfile);
731 $this->set('customProfile', $customProfile);
734 // do a transfer only if a monetary payment greater than 0
735 if ($this->_values
['event']['is_monetary'] && $primaryParticipant) {
736 if ($payment && is_object($payment)) {
737 //CRM 14512 provide line items of all participants to payment gateway
738 $primaryContactId = $this->get('primaryContactId');
740 //build an array of cId/pId of participants
741 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, NULL, $primaryContactId, $isTest, TRUE);
743 //need to copy, since we are unsetting on the way.
744 $copyParticipantCountLines = $participantCount;
746 //lets carry all participant params w/ values.
747 foreach ($additionalIDs as $participantID => $contactId) {
748 $participantNum = $participantID;
749 if ($participantID == $registerByID) {
750 // This is the is primary participant.
754 if ($participantNum = array_search('participant', $copyParticipantCountLines)) {
755 //if no participant found break.
756 if ($participantNum === NULL) {
759 //unset current participant so we don't check them again
760 unset($copyParticipantCountLines[$participantNum]);
763 // get values of line items
764 if ($this->_amount
) {
766 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
767 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
768 $params[$participantNum]['amounts'] = $amount;
771 if (!empty($this->_lineItem
)) {
772 $lineItems = $this->_lineItem
;
774 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
775 $lineItem[] = $lineItemValue;
777 $params[$participantNum]['lineItem'] = $lineItem;
780 //only add additional particpants and not the primary particpant as we already have that
781 //added to $primaryParticipant so that this change doesn't break or require changes to
782 //existing gateway implementations
783 $primaryParticipant['participants_info'][$participantID] = $params[$participantNum];
786 //get event custom field information
787 $groupTree = CRM_Core_BAO_CustomGroup
::getTree('Event', NULL, $this->_eventId
, 0, $this->_values
['event']['event_type_id']);
788 $primaryParticipant['eventCustomFields'] = $groupTree;
790 // call postprocess hook before leaving
791 $this->postProcessHook();
793 $this->processPayment($payment, $primaryParticipant);
796 throw new CRM_Core_Exception($paymentObjError);
802 //build an array of cId/pId of participants
803 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID,
804 NULL, $primaryContactId, $isTest,
807 //let's send mails to all with meaningful text, CRM-4320.
808 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
809 $this->assign('isRequireApproval', $this->_requireApproval
);
811 //need to copy, since we are unsetting on the way.
812 $copyParticipantCount = $participantCount;
814 //let's carry all participant params w/ values.
815 foreach ($additionalIDs as $participantID => $contactId) {
816 $participantNum = NULL;
817 if ($participantID == $registerByID) {
821 if ($participantNum = array_search('participant', $copyParticipantCount)) {
822 unset($copyParticipantCount[$participantNum]);
825 if ($participantNum === NULL) {
829 //carry the participant submitted values.
830 $this->_values
['params'][$participantID] = $params[$participantNum];
833 foreach ($additionalIDs as $participantID => $contactId) {
835 if ($participantID == $registerByID) {
836 //set as Primary Participant
837 $this->assign('isPrimary', 1);
838 //build an array of custom profile and assigning it to template.
839 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($participantID, $this->_values
, NULL, $isTest);
841 if (count($customProfile)) {
842 $this->assign('customProfile', $customProfile);
843 $this->set('customProfile', $customProfile);
845 $this->_values
['params']['additionalParticipant'] = FALSE;
848 //take the Additional participant number.
849 if ($participantNum = array_search('participant', $participantCount)) {
850 unset($participantCount[$participantNum]);
852 // Change $this->_values['participant'] to include additional participant values
853 $ids = $participantValues = [];
854 $participantParams = ['id' => $participantID];
855 CRM_Event_BAO_Participant
::getValues($participantParams, $participantValues, $ids);
856 $this->_values
['participant'] = $participantValues[$participantID];
858 $this->assign('isPrimary', 0);
859 $this->assign('customProfile', NULL);
860 //Additional Participant should get only it's payment information
861 if (!empty($this->_amount
)) {
863 $params = $this->get('params');
864 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
865 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
866 $this->assign('amounts', $amount);
868 if ($this->_lineItem
) {
869 $lineItems = $this->_lineItem
;
871 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
872 $lineItem[] = $lineItemValue;
874 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
875 $individual = $this->get('individual');
876 $dataArray[key($dataArray)] = $individual[$participantNum]['totalTaxAmt'];
877 $this->assign('dataArray', $dataArray);
878 $this->assign('totalAmount', $individual[$participantNum]['totalAmtWithTax']);
879 $this->assign('totalTaxAmount', $individual[$participantNum]['totalTaxAmt']);
880 $this->assign('individual', [$individual[$participantNum]]);
882 $this->assign('lineItem', $lineItem);
884 $this->_values
['params']['additionalParticipant'] = TRUE;
885 $this->assign('isAdditionalParticipant', $this->_values
['params']['additionalParticipant']);
888 //pass these variables since these are run time calculated.
889 $this->_values
['params']['isOnWaitlist'] = $this->_allowWaitlist
;
890 $this->_values
['params']['isRequireApproval'] = $this->_requireApproval
;
892 //send mail to primary as well as additional participants.
893 CRM_Event_BAO_Event
::sendMail($contactId, $this->_values
, $participantID, $isTest);
899 * Process the contribution.
901 * @param CRM_Core_Form $form
902 * @param array $params
903 * @param array $result
904 * @param int $contactID
905 * @param bool $pending
906 * @param array $paymentProcessor
908 * @return \CRM_Contribute_BAO_Contribution
910 * @throws \CRM_Core_Exception
911 * @throws \CiviCRM_API3_Exception
913 protected function processContribution(
914 &$form, $params, $result, $contactID,
916 $paymentProcessor = NULL
918 // Note this used to be shared with the backoffice form & no longer is, some code may no longer be required.
919 $transaction = new CRM_Core_Transaction();
921 $now = date('YmdHis');
924 if (!empty($form->_values
['event']['is_email_confirm'])) {
928 // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
929 CRM_Contribute_Form_AbstractEditPayment
::formatCreditCardDetails($params);
932 'contact_id' => $contactID,
933 'financial_type_id' => !empty($form->_values
['event']['financial_type_id']) ?
$form->_values
['event']['financial_type_id'] : $params['financial_type_id'],
934 'receive_date' => $now,
935 'total_amount' => $params['amount'],
936 'tax_amount' => $params['tax_amount'],
937 'amount_level' => $params['amount_level'],
938 'invoice_id' => $params['invoiceID'],
939 'currency' => $params['currencyID'],
940 'source' => !empty($params['participant_source']) ?
$params['participant_source'] : $params['description'],
941 'is_pay_later' => CRM_Utils_Array
::value('is_pay_later', $params, 0),
942 'campaign_id' => $params['campaign_id'] ??
NULL,
943 'card_type_id' => $params['card_type_id'] ??
NULL,
944 'pan_truncation' => $params['pan_truncation'] ??
NULL,
947 if ($paymentProcessor) {
948 $contribParams['payment_instrument_id'] = $paymentProcessor['payment_instrument_id'];
949 $contribParams['payment_processor'] = $paymentProcessor['id'];
952 if (!$pending && $result) {
954 'fee_amount' => $result['fee_amount'] ??
NULL,
955 'trxn_id' => $result['trxn_id'],
956 'receipt_date' => $receiptDate,
960 $allStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
961 $contribParams['contribution_status_id'] = array_search('Completed', $allStatuses);
963 $contribParams['contribution_status_id'] = array_search('Pending', $allStatuses);
966 $contribParams['is_test'] = 0;
967 if ($form->_action
& CRM_Core_Action
::PREVIEW || CRM_Utils_Array
::value('mode', $params) == 'test') {
968 $contribParams['is_test'] = 1;
971 if (!empty($contribParams['invoice_id'])) {
972 $contribParams['id'] = CRM_Core_DAO
::getFieldValue('CRM_Contribute_DAO_Contribution',
973 $contribParams['invoice_id'],
979 if (Civi
::settings()->get('deferred_revenue_enabled')) {
980 $eventStartDate = CRM_Utils_Array
::value(
982 CRM_Utils_Array
::value(
987 if (strtotime($eventStartDate) > strtotime(date('Ymt'))) {
988 $contribParams['revenue_recognition_date'] = date('Ymd', strtotime($eventStartDate));
991 //create an contribution address
992 // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although
993 // currently that is only inherited by back-office forms.
994 if ($form->_contributeMode
!= 'notify' && empty($params['is_pay_later'])) {
995 $contribParams['address_id'] = CRM_Contribute_BAO_Contribution
::createAddress($params, $form->_bltID
);
998 $contribParams['skipLineItem'] = 1;
999 $contribParams['skipCleanMoney'] = 1;
1000 // create contribution record
1001 $contribution = CRM_Contribute_BAO_Contribution
::add($contribParams);
1003 CRM_Event_BAO_Participant
::createDiscountTrxn($form->_eventId
, $contribParams, NULL, CRM_Price_BAO_PriceSet
::parseFirstPriceSetValueIDFromParams($params));
1005 // process soft credit / pcp pages
1006 if (!empty($params['pcp_made_through_id'])) {
1007 CRM_Contribute_BAO_ContributionSoft
::formatSoftCreditParams($params, $form);
1008 CRM_Contribute_BAO_ContributionSoft
::processSoftContribution($params, $contribution);
1011 $transaction->commit();
1013 return $contribution;
1017 * Fix the Location Fields.
1019 * @todo Reconcile with the contribution method formatParamsForPaymentProcessor
1020 * rather than adding different logic to check when to keep the billing
1021 * fields. There might be a difference in handling guest/multiple
1022 * participants though.
1024 * @param array $params
1025 * @param array $fields
1026 * @param CRM_Core_Form $form
1028 public static function fixLocationFields(&$params, &$fields, &$form) {
1029 if (!empty($form->_fields
)) {
1030 foreach ($form->_fields
as $name => $dontCare) {
1035 // If there's no 'first_name' in the profile then overwrite the names from
1036 // the billing fields (if they are set)
1037 if (is_array($fields)) {
1038 if (!array_key_exists('first_name', $fields)) {
1039 $nameFields = ['first_name', 'middle_name', 'last_name'];
1040 foreach ($nameFields as $name) {
1042 if (array_key_exists("billing_$name", $params)) {
1043 $params[$name] = $params["billing_{$name}"];
1044 $params['preserveDBName'] = TRUE;
1050 // Add the billing names to the billing address, if a billing name is set
1051 if (!empty($params['billing_first_name'])) {
1052 $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);
1053 $fields["address_name-{$form->_bltID}"] = 1;
1056 $fields["email-{$form->_bltID}"] = 1;
1057 $fields['email-Primary'] = 1;
1059 //if its pay later or additional participant set email address as primary.
1060 if ((!empty($params['is_pay_later']) ||
empty($params['is_primary']) ||
1061 !$form->_values
['event']['is_monetary'] ||
1062 $form->_allowWaitlist ||
1063 $form->_requireApproval
1064 ) && !empty($params["email-{$form->_bltID}"])
1066 $params['email-Primary'] = $params["email-{$form->_bltID}"];
1071 * Update contact fields.
1073 * @param int $contactID
1074 * @param array $params
1075 * @param array $fields
1076 * @param CRM_Core_Form $form
1080 public static function updateContactFields($contactID, $params, $fields, &$form) {
1081 //add the contact to group, if add to group is selected for a
1082 //particular uf group
1084 // get the add to groups
1087 if (!empty($form->_fields
)) {
1088 foreach ($form->_fields
as $key => $value) {
1089 if (!empty($value['add_to_group_id'])) {
1090 $addToGroups[$value['add_to_group_id']] = $value['add_to_group_id'];
1095 // check for profile double opt-in and get groups to be subscribed
1096 $subscribeGroupIds = CRM_Core_BAO_UFGroup
::getDoubleOptInGroupIds($params, $contactID);
1098 foreach ($addToGroups as $k) {
1099 if (array_key_exists($k, $subscribeGroupIds)) {
1100 unset($addToGroups[$k]);
1104 // since we are directly adding contact to group lets unset it from mailing
1105 if (!empty($addToGroups)) {
1106 foreach ($addToGroups as $groupId) {
1107 if (isset($subscribeGroupIds[$groupId])) {
1108 unset($subscribeGroupIds[$groupId]);
1113 $ctype = CRM_Core_DAO
::getFieldValue(
1114 'CRM_Contact_DAO_Contact',
1119 if (array_key_exists('contact_id', $params) && empty($params['contact_id'])) {
1120 // we unset this here because the downstream function ignores the contactID we give it
1121 // if it is set & it is difficult to understand the implications of 'fixing' this downstream
1122 // but if we are passing a contact id into this function it's reasonable to assume we don't
1124 unset($params['contact_id']);
1127 $contactID = CRM_Contact_BAO_Contact
::createProfileContact(
1139 foreach (CRM_Contact_BAO_Contact
::$_greetingTypes as $greeting) {
1140 if (!isset($params[$greeting . '_id'])) {
1141 $params[$greeting . '_id'] = CRM_Contact_BAO_Contact_Utils
::defaultGreeting('Individual', $greeting);
1145 $contactID = CRM_Contact_BAO_Contact
::createProfileContact($params,
1153 $form->set('contactID', $contactID);
1156 //get email primary first if exist
1157 $subscriptionEmail = ['email' => CRM_Utils_Array
::value('email-Primary', $params)];
1158 if (!$subscriptionEmail['email']) {
1159 $subscriptionEmail['email'] = CRM_Utils_Array
::value("email-{$form->_bltID}", $params);
1161 // subscribing contact to groups
1162 if (!empty($subscribeGroupIds) && $subscriptionEmail['email']) {
1163 CRM_Mailing_Event_BAO_Subscribe
::commonSubscribe($subscribeGroupIds, $subscriptionEmail, $contactID);
1170 * Assign Profiles to the template.
1172 * @param CRM_Event_Form_Registration_Confirm $form
1174 * @throws \Exception
1176 public static function assignProfiles($form) {
1177 $participantParams = $form->_params
;
1178 $formattedValues = $profileFields = [];
1180 foreach ($participantParams as $participantNum => $participantValue) {
1181 if ($participantNum) {
1182 $prefix1 = 'additional';
1183 $prefix2 = 'additional_';
1189 if ($participantValue !== 'skip') {
1190 //get the customPre profile info
1191 if (!empty($form->_values
[$prefix2 . 'custom_pre_id'])) {
1192 $values = $groupName = [];
1193 CRM_Event_BAO_Event
::displayProfile($participantValue,
1194 $form->_values
[$prefix2 . 'custom_pre_id'],
1200 if (count($values)) {
1201 $formattedValues[$count][$prefix1 . 'CustomPre'] = $values;
1203 $formattedValues[$count][$prefix1 . 'CustomPreGroupTitle'] = CRM_Utils_Array
::value('groupTitle', $groupName);
1205 //get the customPost profile info
1206 if (!empty($form->_values
[$prefix2 . 'custom_post_id'])) {
1207 $values = $groupName = [];
1208 foreach ($form->_values
[$prefix2 . 'custom_post_id'] as $gids) {
1210 CRM_Event_BAO_Event
::displayProfile($participantValue,
1216 $values[$gids] = $val;
1217 $groupName[$gids] = $group;
1220 if (count($values)) {
1221 $formattedValues[$count][$prefix1 . 'CustomPost'] = $values;
1224 if (isset($formattedValues[$count][$prefix1 . 'CustomPre'])) {
1225 $formattedValues[$count][$prefix1 . 'CustomPost'] = array_diff_assoc($formattedValues[$count][$prefix1 . 'CustomPost'],
1226 $formattedValues[$count][$prefix1 . 'CustomPre']
1230 $formattedValues[$count][$prefix1 . 'CustomPostGroupTitle'] = $groupName;
1234 $form->_fields
= $profileFields;
1236 if (!empty($formattedValues)) {
1237 $form->assign('primaryParticipantProfile', $formattedValues[1]);
1238 $form->set('primaryParticipantProfile', $formattedValues[1]);
1240 unset($formattedValues[1]);
1241 $form->assign('addParticipantProfile', $formattedValues);
1242 $form->set('addParticipantProfile', $formattedValues);
1248 * Submit in test mode.
1252 public static function testSubmit($params) {
1253 $form = new CRM_Event_Form_Registration_Confirm();
1254 // This way the mocked up controller ignores the session stuff.
1255 $_SERVER['REQUEST_METHOD'] = 'GET';
1256 $_REQUEST['id'] = $form->_eventId
= $params['id'];
1257 $form->controller
= new CRM_Event_Controller_Registration();
1258 $form->_params
= $params['params'];
1259 // This happens in buildQuickForm so emulate here.
1260 $form->_amount
= $form->_totalAmount
= CRM_Utils_Rule
::cleanMoney(CRM_Utils_Array
::value('totalAmount', $params));
1261 $form->set('params', $params['params']);
1262 $form->_values
['custom_pre_id'] = CRM_Utils_Array
::value('custom_pre_id', $params);
1263 $form->_values
['custom_post_id'] = CRM_Utils_Array
::value('custom_post_id', $params);
1264 $form->_values
['event'] = CRM_Utils_Array
::value('event', $params);
1265 $form->_contributeMode
= $params['contributeMode'];
1266 $eventParams = ['id' => $params['id']];
1267 CRM_Event_BAO_Event
::retrieve($eventParams, $form->_values
['event']);
1268 $form->set('registerByID', $params['registerByID']);
1269 if (!empty($params['paymentProcessorObj'])) {
1270 $form->_paymentProcessor
= $params['paymentProcessorObj'];
1272 $form->postProcess();
1276 * Process the payment, redirecting back to the page on error.
1278 * @param \CRM_Core_Payment $payment
1283 private function processPayment($payment, $value) {
1285 $params = $this->prepareParamsForPaymentProcessor($value);
1286 $result = $payment->doPayment($params, 'event');
1287 return [$result, $value];
1289 catch (\Civi\Payment\Exception\PaymentProcessorException
$e) {
1290 Civi
::log()->error('Payment processor exception: ' . $e->getMessage());
1291 CRM_Core_Session
::singleton()->setStatus($e->getMessage());
1292 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "id={$this->_eventId}"));
1298 * Clean money fields from the form.
1300 * @param array $params
1302 protected function cleanMoneyFields(&$params) {
1303 foreach ($this->submittableMoneyFields
as $moneyField) {
1304 foreach ($params as $index => $paramField) {
1305 if (isset($paramField[$moneyField])) {
1306 $params[$index][$moneyField] = CRM_Utils_Rule
::cleanMoney($paramField[$moneyField]);