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 $this->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);
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 = $record['participant_id'] ??
NULL;
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 if ($this->_allowWaitlist
443 ||
$this->_requireApproval
444 ||
(!empty($value['is_pay_later']) && !$this->_isBillingAddressRequiredForPayLater
)
445 ||
empty($value['is_primary'])
447 // This is confusing because unnecessary code around it has been removed. It is not
448 // clear why we do this / whether we should.
449 if (!empty($value['is_pay_later'])) {
450 $this->_values
['params']['is_pay_later'] = TRUE;
454 //Unset ContactID for additional participants and set RegisterBy Id.
455 if (empty($value['is_primary'])) {
456 $contactID = $value['contact_id'] ??
NULL;
457 $registerByID = $this->get('registerByID');
459 $value['registered_by_id'] = $registerByID;
463 $value['amount'] = $this->_totalAmount
;
466 $contactID = CRM_Event_Form_Registration_Confirm
::updateContactFields($contactID, $value, $fields, $this);
468 // lets store the contactID in the session
469 // we dont store in userID in case the user is doing multiple
471 // for things like tell a friend
472 if (!$this->getContactID() && !empty($value['is_primary'])) {
473 CRM_Core_Session
::singleton()->set('transaction.userID', $contactID);
476 $value['description'] = ts('Online Event Registration') . ': ' . $this->_values
['event']['title'];
477 $value['accountingCode'] = CRM_Utils_Array
::value('accountingCode',
478 $this->_values
['event']
482 if ($this->_allowWaitlist ||
$this->_requireApproval
) {
483 //get the participant statuses.
484 $waitingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Waiting'");
485 if ($this->_allowWaitlist
) {
486 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
489 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
492 //there might be case user selected pay later and
493 //now becomes part of run time waiting list.
494 $value['is_pay_later'] = FALSE;
496 elseif ($this->_values
['event']['is_monetary']) {
497 // required only if paid event
498 if (is_array($this->_paymentProcessor
)) {
499 $payment = $this->_paymentProcessor
['object'];
501 if (!empty($this->_paymentProcessor
) && $this->_paymentProcessor
['object']->supports('preApproval')) {
502 $preApprovalParams = $this->_paymentProcessor
['object']->getPreApprovalDetails($this->get('pre_approval_parameters'));
503 $value = array_merge($value, $preApprovalParams);
507 if (!empty($value['is_pay_later']) ||
508 $value['amount'] == 0 ||
509 // The concept of contributeMode is deprecated.
510 $this->_contributeMode
== 'checkout' ||
511 $this->_contributeMode
== 'notify'
513 if ($value['amount'] != 0) {
515 //get the participant statuses.
516 $pendingStatuses = CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Pending'");
517 $status = !empty($value['is_pay_later']) ?
'Pending from pay later' : 'Pending from incomplete transaction';
518 $value['participant_status_id'] = $value['participant_status'] = array_search($status, $pendingStatuses);
521 elseif (!empty($value['is_primary'])) {
522 CRM_Core_Payment_Form
::mapParams($this->_bltID
, $value, $value, TRUE);
523 // payment email param can be empty for _bltID mapping
524 // thus provide mapping for it with a different email value
525 if (empty($value['email'])) {
526 $value['email'] = CRM_Utils_Array
::valueByRegexKey('/^email-/', $value);
529 if (is_object($payment)) {
530 // Not quite sure why we don't just user $value since it contains the data
532 // @todo ditch $result & retest.
533 list($result, $value) = $this->processPayment($payment, $value);
536 throw new CRM_Core_Exception($paymentObjError);
540 $value['receive_date'] = $now;
541 if ($this->_allowConfirmation
) {
542 $value['participant_register_date'] = $this->_values
['participant']['register_date'];
545 $createContrib = $value['amount'] != 0;
546 // force to create zero amount contribution, CRM-5095
547 if (!$createContrib && ($value['amount'] == 0)
548 && $this->_priceSetId
&& $this->_lineItem
550 $createContrib = TRUE;
553 if ($createContrib && !empty($value['is_primary']) &&
554 !$this->_allowWaitlist
&& !$this->_requireApproval
556 // if paid event add a contribution record
557 //if primary participant contributing additional amount
558 //append (multiple participants) to its fee level. CRM-4196.
559 if (count($params) > 1) {
560 $value['amount_level'] .= ts(' (multiple participants)') . CRM_Core_DAO
::VALUE_SEPARATOR
;
563 //passing contribution id is already registered.
564 $contribution = $this->processContribution($this, $value, $result, $contactID, $pending, $this->_paymentProcessor
);
565 $value['contributionID'] = $contribution->id
;
566 $value['receive_date'] = $contribution->receive_date
;
567 $value['trxn_id'] = $contribution->trxn_id
;
568 $value['contributionID'] = $contribution->id
;
570 $value['contactID'] = $contactID;
571 $value['eventID'] = $this->_eventId
;
572 $value['item_name'] = $value['description'];
575 if (!empty($value['contributionID'])) {
576 $this->_values
['contributionId'] = $value['contributionID'];
580 if (!empty($value['is_primary'])) {
581 $primaryCurrencyID = $value['currencyID'] ??
NULL;
583 if (empty($value['currencyID'])) {
584 $value['currencyID'] = $primaryCurrencyID;
587 // CRM-11182 - Confirmation page might not be monetary
588 if ($this->_values
['event']['is_monetary']) {
589 if (!$pending && !empty($value['is_primary']) &&
590 !$this->_allowWaitlist
&& !$this->_requireApproval
592 // transactionID & receive date required while building email template
593 $this->assign('trxn_id', CRM_Utils_Array
::value('trxn_id', $value));
594 $this->assign('receive_date', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
595 $this->set('receiveDate', CRM_Utils_Date
::mysqlToIso(CRM_Utils_Array
::value('receive_date', $value)));
596 $this->set('trxnId', CRM_Utils_Array
::value('trxn_id', $value));
600 $value['fee_amount'] = $value['amount'] ??
NULL;
601 $this->set('value', $value);
603 // handle register date CRM-4320
604 if ($this->_allowConfirmation
) {
605 $registerDate = $params['participant_register_date'] ??
NULL;
607 elseif (!empty($params['participant_register_date']) &&
608 is_array($params['participant_register_date'])
610 $registerDate = CRM_Utils_Date
::format($params['participant_register_date']);
613 $registerDate = date('YmdHis');
615 $this->assign('register_date', $registerDate);
617 $this->confirmPostProcess($contactID, $contribution);
620 //handle if no additional participant.
621 if (!$registerByID) {
622 $registerByID = $this->get('registerByID');
625 $this->set('participantIDs', $this->_participantIDS
);
627 // create line items, CRM-5313
628 if ($this->_priceSetId
&&
629 !empty($this->_lineItem
)
631 // take all processed participant ids.
632 $allParticipantIds = $this->_participantIDS
;
634 // when participant re-walk wizard.
635 if ($this->_allowConfirmation
&&
636 !empty($this->_additionalParticipantIds
)
638 $allParticipantIds = array_merge([$registerByID], $this->_additionalParticipantIds
);
641 $entityTable = 'civicrm_participant';
644 foreach ($this->_lineItem
as $key => $value) {
645 if ($value == 'skip') {
648 if ($entityId = CRM_Utils_Array
::value($key, $allParticipantIds)) {
649 // do cleanup line items if participant re-walking wizard.
650 if ($this->_allowConfirmation
) {
651 CRM_Price_BAO_LineItem
::deleteLineItems($entityId, $entityTable);
653 $lineItem[$this->_priceSetId
] = $value;
654 CRM_Price_BAO_LineItem
::processPriceSet($entityId, $lineItem, $contribution, $entityTable);
656 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
657 foreach ($value as $line) {
658 if (isset($line['tax_amount']) && isset($line['tax_rate'])) {
659 $totalTaxAmount = $line['tax_amount'] +
$totalTaxAmount;
660 if (isset($dataArray[$line['tax_rate']])) {
661 $dataArray[$line['tax_rate']] = $dataArray[$line['tax_rate']] + CRM_Utils_Array
::value('tax_amount', $line);
664 $dataArray[$line['tax_rate']] = $line['tax_amount'] ??
NULL;
670 $this->assign('dataArray', $dataArray);
671 $this->assign('totalTaxAmount', $totalTaxAmount);
674 //update status and send mail to cancelled additional participants, CRM-4320
675 if ($this->_allowConfirmation
&& is_array($cancelledIds) && !empty($cancelledIds)) {
676 $cancelledId = array_search('Cancelled',
677 CRM_Event_PseudoConstant
::participantStatus(NULL, "class = 'Negative'")
679 CRM_Event_BAO_Participant
::transitionParticipants($cancelledIds, $cancelledId);
683 if ($this->_action
& CRM_Core_Action
::PREVIEW
) {
687 $primaryParticipant = $this->get('primaryParticipant');
689 if (empty($primaryParticipant['participantID'])) {
690 CRM_Core_Error
::deprecatedFunctionWarning('This line is not logically reachable.');
691 $primaryParticipant['participantID'] = $registerByID;
693 //otherwise send mail Confirmation/Receipt
694 $primaryContactId = $this->get('primaryContactId');
696 // for Transfer checkout.
697 // The concept of contributeMode is deprecated.
698 if (($this->_contributeMode
== 'checkout' ||
699 $this->_contributeMode
== 'notify'
700 ) && empty($params[0]['is_pay_later']) &&
701 !$this->_allowWaitlist
&& !$this->_requireApproval
&&
702 $this->_totalAmount
> 0
705 //build an array of custom profile and assigning it to template
706 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, $this->_values
, NULL, $isTest);
707 if (count($customProfile)) {
708 $this->assign('customProfile', $customProfile);
709 $this->set('customProfile', $customProfile);
712 // do a transfer only if a monetary payment greater than 0
713 if ($this->_values
['event']['is_monetary'] && $primaryParticipant) {
714 if ($payment && is_object($payment)) {
715 //CRM 14512 provide line items of all participants to payment gateway
716 $primaryContactId = $this->get('primaryContactId');
718 //build an array of cId/pId of participants
719 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID, NULL, $primaryContactId, $isTest, TRUE);
721 //need to copy, since we are unsetting on the way.
722 $copyParticipantCountLines = $participantCount;
724 //lets carry all participant params w/ values.
725 foreach ($additionalIDs as $participantID => $contactId) {
726 $participantNum = $participantID;
727 if ($participantID == $registerByID) {
728 // This is the is primary participant.
732 if ($participantNum = array_search('participant', $copyParticipantCountLines)) {
733 //if no participant found break.
734 if ($participantNum === NULL) {
737 //unset current participant so we don't check them again
738 unset($copyParticipantCountLines[$participantNum]);
741 // get values of line items
742 if ($this->_amount
) {
744 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
745 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
746 $params[$participantNum]['amounts'] = $amount;
749 if (!empty($this->_lineItem
)) {
750 $lineItems = $this->_lineItem
;
752 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
753 $lineItem[] = $lineItemValue;
755 $params[$participantNum]['lineItem'] = $lineItem;
758 //only add additional participants and not the primary participant as we already have that
759 //added to $primaryParticipant so that this change doesn't break or require changes to
760 //existing gateway implementations
761 $primaryParticipant['participants_info'][$participantID] = $params[$participantNum];
764 //get event custom field information
765 $groupTree = CRM_Core_BAO_CustomGroup
::getTree('Event', NULL, $this->_eventId
, 0, $this->_values
['event']['event_type_id']);
766 $primaryParticipant['eventCustomFields'] = $groupTree;
768 // call postprocess hook before leaving
769 $this->postProcessHook();
771 $this->processPayment($payment, $primaryParticipant);
774 throw new CRM_Core_Exception($paymentObjError);
780 //build an array of cId/pId of participants
781 $additionalIDs = CRM_Event_BAO_Event
::buildCustomProfile($registerByID,
782 NULL, $primaryContactId, $isTest,
785 //let's send mails to all with meaningful text, CRM-4320.
786 $this->assign('isOnWaitlist', $this->_allowWaitlist
);
787 $this->assign('isRequireApproval', $this->_requireApproval
);
789 //need to copy, since we are unsetting on the way.
790 $copyParticipantCount = $participantCount;
792 //let's carry all participant params w/ values.
793 foreach ($additionalIDs as $participantID => $contactId) {
794 $participantNum = NULL;
795 if ($participantID == $registerByID) {
799 if ($participantNum = array_search('participant', $copyParticipantCount)) {
800 unset($copyParticipantCount[$participantNum]);
803 if ($participantNum === NULL) {
807 //carry the participant submitted values.
808 $this->_values
['params'][$participantID] = $params[$participantNum];
811 foreach ($additionalIDs as $participantID => $contactId) {
813 if ($participantID == $registerByID) {
814 //set as Primary Participant
815 $this->assign('isPrimary', 1);
816 //build an array of custom profile and assigning it to template.
817 $customProfile = CRM_Event_BAO_Event
::buildCustomProfile($participantID, $this->_values
, NULL, $isTest);
819 if (count($customProfile)) {
820 $this->assign('customProfile', $customProfile);
821 $this->set('customProfile', $customProfile);
823 $this->_values
['params']['additionalParticipant'] = FALSE;
826 //take the Additional participant number.
827 if ($participantNum = array_search('participant', $participantCount)) {
828 unset($participantCount[$participantNum]);
830 // Change $this->_values['participant'] to include additional participant values
831 $ids = $participantValues = [];
832 $participantParams = ['id' => $participantID];
833 CRM_Event_BAO_Participant
::getValues($participantParams, $participantValues, $ids);
834 $this->_values
['participant'] = $participantValues[$participantID];
836 $this->assign('isPrimary', 0);
837 $this->assign('customProfile', NULL);
838 //Additional Participant should get only it's payment information
839 if (!empty($this->_amount
)) {
841 $params = $this->get('params');
842 $amount[$participantNum]['label'] = preg_replace('/\ 1/', '', $params[$participantNum]['amount_level']);
843 $amount[$participantNum]['amount'] = $params[$participantNum]['amount'];
844 $this->assign('amounts', $amount);
846 if ($this->_lineItem
) {
847 $lineItems = $this->_lineItem
;
849 if ($lineItemValue = CRM_Utils_Array
::value($participantNum, $lineItems)) {
850 $lineItem[] = $lineItemValue;
852 if (CRM_Invoicing_Utils
::isInvoicingEnabled()) {
853 $individual = $this->get('individual');
854 $dataArray[key($dataArray)] = $individual[$participantNum]['totalTaxAmt'];
855 $this->assign('dataArray', $dataArray);
856 $this->assign('totalAmount', $individual[$participantNum]['totalAmtWithTax']);
857 $this->assign('totalTaxAmount', $individual[$participantNum]['totalTaxAmt']);
858 $this->assign('individual', [$individual[$participantNum]]);
860 $this->assign('lineItem', $lineItem);
862 $this->_values
['params']['additionalParticipant'] = TRUE;
863 $this->assign('isAdditionalParticipant', $this->_values
['params']['additionalParticipant']);
866 //pass these variables since these are run time calculated.
867 $this->_values
['params']['isOnWaitlist'] = $this->_allowWaitlist
;
868 $this->_values
['params']['isRequireApproval'] = $this->_requireApproval
;
870 //send mail to primary as well as additional participants.
871 CRM_Event_BAO_Event
::sendMail($contactId, $this->_values
, $participantID, $isTest);
877 * Process the contribution.
879 * @param CRM_Core_Form $form
880 * @param array $params
881 * @param array $result
882 * @param int $contactID
883 * @param bool $pending
884 * @param array $paymentProcessor
886 * @return \CRM_Contribute_BAO_Contribution
888 * @throws \CRM_Core_Exception
889 * @throws \CiviCRM_API3_Exception
891 protected function processContribution(
892 &$form, $params, $result, $contactID,
894 $paymentProcessor = NULL
896 // Note this used to be shared with the backoffice form & no longer is, some code may no longer be required.
897 $transaction = new CRM_Core_Transaction();
899 $now = date('YmdHis');
902 if (!empty($form->_values
['event']['is_email_confirm'])) {
906 // CRM-20264: fetch CC type ID and number (last 4 digit) and assign it back to $params
907 CRM_Contribute_Form_AbstractEditPayment
::formatCreditCardDetails($params);
910 'contact_id' => $contactID,
911 'financial_type_id' => !empty($form->_values
['event']['financial_type_id']) ?
$form->_values
['event']['financial_type_id'] : $params['financial_type_id'],
912 'receive_date' => $now,
913 'total_amount' => $params['amount'],
914 'tax_amount' => $params['tax_amount'],
915 'amount_level' => $params['amount_level'],
916 'invoice_id' => $params['invoiceID'],
917 'currency' => $params['currencyID'],
918 'source' => !empty($params['participant_source']) ?
$params['participant_source'] : $params['description'],
919 'is_pay_later' => CRM_Utils_Array
::value('is_pay_later', $params, 0),
920 'campaign_id' => $params['campaign_id'] ??
NULL,
921 'card_type_id' => $params['card_type_id'] ??
NULL,
922 'pan_truncation' => $params['pan_truncation'] ??
NULL,
925 if ($paymentProcessor) {
926 $contribParams['payment_instrument_id'] = $paymentProcessor['payment_instrument_id'];
927 $contribParams['payment_processor'] = $paymentProcessor['id'];
930 if (!$pending && $result) {
932 'fee_amount' => $result['fee_amount'] ??
NULL,
933 'trxn_id' => $result['trxn_id'],
934 'receipt_date' => $receiptDate,
938 $allStatuses = CRM_Contribute_PseudoConstant
::contributionStatus(NULL, 'name');
939 $contribParams['contribution_status_id'] = array_search('Completed', $allStatuses);
941 $contribParams['contribution_status_id'] = array_search('Pending', $allStatuses);
944 $contribParams['is_test'] = 0;
945 if ($form->_action
& CRM_Core_Action
::PREVIEW || CRM_Utils_Array
::value('mode', $params) == 'test') {
946 $contribParams['is_test'] = 1;
949 if (!empty($contribParams['invoice_id'])) {
950 $contribParams['id'] = CRM_Core_DAO
::getFieldValue('CRM_Contribute_DAO_Contribution',
951 $contribParams['invoice_id'],
957 if (Civi
::settings()->get('deferred_revenue_enabled')) {
958 $eventStartDate = CRM_Utils_Array
::value(
960 CRM_Utils_Array
::value(
965 if (strtotime($eventStartDate) > strtotime(date('Ymt'))) {
966 $contribParams['revenue_recognition_date'] = date('Ymd', strtotime($eventStartDate));
969 //create an contribution address
970 // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although
971 // currently that is only inherited by back-office forms.
972 if ($form->_contributeMode
!= 'notify' && empty($params['is_pay_later'])) {
973 $contribParams['address_id'] = CRM_Contribute_BAO_Contribution
::createAddress($params, $form->_bltID
);
976 $contribParams['skipLineItem'] = 1;
977 $contribParams['skipCleanMoney'] = 1;
978 // create contribution record
979 $contribution = CRM_Contribute_BAO_Contribution
::add($contribParams);
981 CRM_Event_BAO_Participant
::createDiscountTrxn($form->_eventId
, $contribParams, NULL, CRM_Price_BAO_PriceSet
::parseFirstPriceSetValueIDFromParams($params));
983 // process soft credit / pcp pages
984 if (!empty($params['pcp_made_through_id'])) {
985 CRM_Contribute_BAO_ContributionSoft
::formatSoftCreditParams($params, $form);
986 CRM_Contribute_BAO_ContributionSoft
::processSoftContribution($params, $contribution);
989 $transaction->commit();
991 return $contribution;
995 * Fix the Location Fields.
997 * @todo Reconcile with the contribution method formatParamsForPaymentProcessor
998 * rather than adding different logic to check when to keep the billing
999 * fields. There might be a difference in handling guest/multiple
1000 * participants though.
1002 * @param array $params
1003 * @param array $fields
1004 * @param CRM_Core_Form $form
1006 public static function fixLocationFields(&$params, &$fields, &$form) {
1007 if (!empty($form->_fields
)) {
1008 foreach ($form->_fields
as $name => $dontCare) {
1013 // If there's no 'first_name' in the profile then overwrite the names from
1014 // the billing fields (if they are set)
1015 if (is_array($fields)) {
1016 if (!array_key_exists('first_name', $fields)) {
1017 $nameFields = ['first_name', 'middle_name', 'last_name'];
1018 foreach ($nameFields as $name) {
1020 if (array_key_exists("billing_$name", $params)) {
1021 $params[$name] = $params["billing_{$name}"];
1022 $params['preserveDBName'] = TRUE;
1028 // Add the billing names to the billing address, if a billing name is set
1029 if (!empty($params['billing_first_name'])) {
1030 $params["address_name-{$form->_bltID}"] = $params['billing_first_name'] . ' ' . CRM_Utils_Array
::value('billing_middle_name', $params) . ' ' . CRM_Utils_Array
::value('billing_last_name', $params);
1031 $fields["address_name-{$form->_bltID}"] = 1;
1034 $fields["email-{$form->_bltID}"] = 1;
1035 $fields['email-Primary'] = 1;
1037 //if its pay later or additional participant set email address as primary.
1038 if ((!empty($params['is_pay_later']) ||
empty($params['is_primary']) ||
1039 !$form->_values
['event']['is_monetary'] ||
1040 $form->_allowWaitlist ||
1041 $form->_requireApproval
1042 ) && !empty($params["email-{$form->_bltID}"])
1044 $params['email-Primary'] = $params["email-{$form->_bltID}"];
1049 * Update contact fields.
1051 * @param int $contactID
1052 * @param array $params
1053 * @param array $fields
1054 * @param CRM_Core_Form $form
1058 public static function updateContactFields($contactID, $params, $fields, &$form) {
1059 //add the contact to group, if add to group is selected for a
1060 //particular uf group
1062 // get the add to groups
1065 if (!empty($form->_fields
)) {
1066 foreach ($form->_fields
as $key => $value) {
1067 if (!empty($value['add_to_group_id'])) {
1068 $addToGroups[$value['add_to_group_id']] = $value['add_to_group_id'];
1073 // check for profile double opt-in and get groups to be subscribed
1074 $subscribeGroupIds = CRM_Core_BAO_UFGroup
::getDoubleOptInGroupIds($params, $contactID);
1076 foreach ($addToGroups as $k) {
1077 if (array_key_exists($k, $subscribeGroupIds)) {
1078 unset($addToGroups[$k]);
1082 // since we are directly adding contact to group lets unset it from mailing
1083 if (!empty($addToGroups)) {
1084 foreach ($addToGroups as $groupId) {
1085 if (isset($subscribeGroupIds[$groupId])) {
1086 unset($subscribeGroupIds[$groupId]);
1091 $ctype = CRM_Core_DAO
::getFieldValue(
1092 'CRM_Contact_DAO_Contact',
1097 if (array_key_exists('contact_id', $params) && empty($params['contact_id'])) {
1098 // we unset this here because the downstream function ignores the contactID we give it
1099 // if it is set & it is difficult to understand the implications of 'fixing' this downstream
1100 // but if we are passing a contact id into this function it's reasonable to assume we don't
1102 unset($params['contact_id']);
1105 $contactID = CRM_Contact_BAO_Contact
::createProfileContact(
1117 foreach (CRM_Contact_BAO_Contact
::$_greetingTypes as $greeting) {
1118 if (!isset($params[$greeting . '_id'])) {
1119 $params[$greeting . '_id'] = CRM_Contact_BAO_Contact_Utils
::defaultGreeting('Individual', $greeting);
1123 $contactID = CRM_Contact_BAO_Contact
::createProfileContact($params,
1131 $form->set('contactID', $contactID);
1134 //get email primary first if exist
1135 $subscriptionEmail = ['email' => CRM_Utils_Array
::value('email-Primary', $params)];
1136 if (!$subscriptionEmail['email']) {
1137 $subscriptionEmail['email'] = $params["email-{$form->_bltID}"] ??
NULL;
1139 // subscribing contact to groups
1140 if (!empty($subscribeGroupIds) && $subscriptionEmail['email']) {
1141 CRM_Mailing_Event_BAO_Subscribe
::commonSubscribe($subscribeGroupIds, $subscriptionEmail, $contactID);
1148 * Assign Profiles to the template.
1150 * @param CRM_Event_Form_Registration_Confirm $form
1152 * @throws \Exception
1154 public static function assignProfiles($form) {
1155 $participantParams = $form->_params
;
1156 $formattedValues = $profileFields = [];
1158 foreach ($participantParams as $participantNum => $participantValue) {
1159 if ($participantNum) {
1160 $prefix1 = 'additional';
1161 $prefix2 = 'additional_';
1167 if ($participantValue !== 'skip') {
1168 //get the customPre profile info
1169 if (!empty($form->_values
[$prefix2 . 'custom_pre_id'])) {
1170 $values = $groupName = [];
1171 CRM_Event_BAO_Event
::displayProfile($participantValue,
1172 $form->_values
[$prefix2 . 'custom_pre_id'],
1178 if (count($values)) {
1179 $formattedValues[$count][$prefix1 . 'CustomPre'] = $values;
1181 $formattedValues[$count][$prefix1 . 'CustomPreGroupTitle'] = $groupName['groupTitle'] ??
NULL;
1183 //get the customPost profile info
1184 if (!empty($form->_values
[$prefix2 . 'custom_post_id'])) {
1185 $values = $groupName = [];
1186 foreach ($form->_values
[$prefix2 . 'custom_post_id'] as $gids) {
1188 CRM_Event_BAO_Event
::displayProfile($participantValue,
1194 $values[$gids] = $val;
1195 $groupName[$gids] = $group;
1198 if (count($values)) {
1199 $formattedValues[$count][$prefix1 . 'CustomPost'] = $values;
1202 if (isset($formattedValues[$count][$prefix1 . 'CustomPre'])) {
1203 $formattedValues[$count][$prefix1 . 'CustomPost'] = array_diff_assoc($formattedValues[$count][$prefix1 . 'CustomPost'],
1204 $formattedValues[$count][$prefix1 . 'CustomPre']
1208 $formattedValues[$count][$prefix1 . 'CustomPostGroupTitle'] = $groupName;
1212 $form->_fields
= $profileFields;
1214 if (!empty($formattedValues)) {
1215 $form->assign('primaryParticipantProfile', $formattedValues[1]);
1216 $form->set('primaryParticipantProfile', $formattedValues[1]);
1218 unset($formattedValues[1]);
1219 $form->assign('addParticipantProfile', $formattedValues);
1220 $form->set('addParticipantProfile', $formattedValues);
1226 * Submit in test mode.
1230 public static function testSubmit($params) {
1231 $form = new CRM_Event_Form_Registration_Confirm();
1232 // This way the mocked up controller ignores the session stuff.
1233 $_SERVER['REQUEST_METHOD'] = 'GET';
1234 $_REQUEST['id'] = $form->_eventId
= $params['id'];
1235 $form->controller
= new CRM_Event_Controller_Registration();
1236 $form->_params
= $params['params'];
1237 // This happens in buildQuickForm so emulate here.
1238 $form->_amount
= $form->_totalAmount
= CRM_Utils_Rule
::cleanMoney(CRM_Utils_Array
::value('totalAmount', $params));
1239 $form->set('params', $params['params']);
1240 $form->_values
['custom_pre_id'] = $params['custom_pre_id'] ??
NULL;
1241 $form->_values
['custom_post_id'] = $params['custom_post_id'] ??
NULL;
1242 $form->_values
['event'] = $params['event'] ??
NULL;
1243 $form->_contributeMode
= $params['contributeMode'];
1244 $eventParams = ['id' => $params['id']];
1245 CRM_Event_BAO_Event
::retrieve($eventParams, $form->_values
['event']);
1246 $form->set('registerByID', $params['registerByID']);
1247 if (!empty($params['paymentProcessorObj'])) {
1248 $form->_paymentProcessor
= $params['paymentProcessorObj'];
1250 $form->postProcess();
1254 * Process the payment, redirecting back to the page on error.
1256 * @param \CRM_Core_Payment $payment
1261 private function processPayment($payment, $value) {
1263 $params = $this->prepareParamsForPaymentProcessor($value);
1264 $result = $payment->doPayment($params, 'event');
1265 return [$result, $value];
1267 catch (\Civi\Payment\Exception\PaymentProcessorException
$e) {
1268 Civi
::log()->error('Payment processor exception: ' . $e->getMessage());
1269 CRM_Core_Session
::singleton()->setStatus($e->getMessage());
1270 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/event/register', "id={$this->_eventId}"));
1276 * Clean money fields from the form.
1278 * @param array $params
1280 protected function cleanMoneyFields(&$params) {
1281 foreach ($this->submittableMoneyFields
as $moneyField) {
1282 foreach ($params as $index => $paramField) {
1283 if (isset($paramField[$moneyField])) {
1284 $params[$index][$moneyField] = CRM_Utils_Rule
::cleanMoney($paramField[$moneyField]);