/** * Implementation of hook_civicrm_buildAmount() * * If the event id of the form being loaded has a discount code, calculate the * the discount and update the price and label. Apply the initial autodiscount * based on a users membership. * * Check all priceset items and only apply the discount to the discounted items. * * @param string $pageType * @param CRM_Core_Form $form * @param $amounts */ function cividiscount_civicrm_buildAmount($pagetype, &$form, &$amounts) { if ((!$form->getVar('_action') || $form->getVar('_action') & CRM_Core_Action::PREVIEW || $form->getVar('_action') & CRM_Core_Action::ADD || $form->getVar('_action') & CRM_Core_Action::UPDATE) && !empty($amounts) && is_array($amounts) && ($pagetype == 'event' || $pagetype == 'membership')) { if (!$pagetype == 'membership' && in_array(get_class($form), array('CRM_Contribute_Form_Contribution', 'CRM_Contribute_Form_Contribution_Main'))) { return; } // // Don't provide Discount if the logged in user already subscribed to any membership types in the form $currentMemberships = $form->_currentMemberships; //if logged in if (!empty($currentMemberships)) { $new_member = FALSE; } else { // if not logged in $new_member = validate_email_for_discount($form); } /* Check if a payment type is set for discounts */ $payids = array(); $payids = _cividiscount_get_discounted_paymentProcessor_type_ids(); $selectedProcessorValue = $form->_paymentProcessor['payment_processor_type_id']; $contact_id = _cividiscount_get_form_contact_id($form); $autodiscount = FALSE; $eid = $form->getVar('_eventId'); $psid = $form->get('priceSetId'); $ps = $form->get('priceSet'); $v = $form->getVar('_values'); $code = trim(CRM_Utils_Request::retrieve('discountcode', 'String', $form, false, null, 'REQUEST')); if (!array_key_exists('discountcode', $form->_submitValues) && ($pid = $form->getVar('_participantId')) && $form->getVar('_action') & CRM_Core_Action::UPDATE) { $code = _cividiscount_get_item_by_track('civicrm_participant', $pid, $contact_id, TRUE); } if (!empty($v['currency'])) { $currency = $v['currency']; } elseif (!empty($v['event']['currency'])) { $currency = $v['event']['currency']; } else { $currency = CRM_Core_Config::singleton()->defaultCurrency; } // If additional participants are not allowed to receive a discount we need // to interrupt the form processing on build and POST. // This is a potential landmine if the form processing ever changes in Civi. if (!_cividiscount_allow_multiple()) { // POST from participant form to confirm page if ($form->getVar('_lastParticipant') == 1) { return; } // On build participant form $keys = array_keys($_GET); foreach ($keys as $key) { if (substr($key, 0, 16) == "_qf_Participant_") { // We can somewhat safely assume we're in the additional participant // registration form. // @todo what is the effect of this? if ($_GET[$key] == 'true') { return; } } } } if (!$new_member && !empty($code)) { echo "Sorry! Discount is not applicable for renewal!"; return; } if (!empty($payids) && !empty($code)) { if (!in_array($selectedProcessorValue, $payids)) { echo "Sorry! Discount is only applicable for Direct Debit!"; return; } } $form->set('_discountInfo', NULL); $dicountCalculater = new CRM_CiviDiscount_DiscountCalculator($pagetype, $eid, $contact_id, $code, FALSE); $discounts = $dicountCalculater->getDiscounts(); if (!empty($code)) { if (empty($discounts)) { $form->set('discountCodeErrorMsg', ts('The discount code you entered is invalid.')); } else { /*gets discounts info, even if a discount code is not applicable for the membership types in the current form */ // Check if a discount code is applicable to any of the membership types in the form $membership_ids_in_form = membership_type_ids_in_form($form); $membership_ids_in_dicount_info = array(); foreach ($discounts as $code => $discount) { $membership_ids_in_dicount_info = $discount['memberships']; } if (count(array_intersect($membership_ids_in_dicount_info, $membership_ids_in_form)) == 0) { $form->set('discountCodeErrorMsg', ts('The discount code you entered is invalid.')); } } } if (empty($discounts)) { // Check if a discount is available if ($pagetype == 'event') { $discounts = _cividiscount_get_discounts(); foreach ($discounts as $code => $discount) { if (isset($discount['events']) && array_key_exists($eid, $discount['events']) && $discount['discount_msg_enabled']) { // Display discount available message CRM_Core_Session::setStatus(html_entity_decode($discount['discount_msg']), '', 'no-popup'); } } } return; } // here we check if discount is configured for events or for membership types. // There are two scenarios: // 1. Discount is configure for the event or membership type, in that case we should apply discount only // if default fee / membership type is configured. ( i.e price set with quick config true ) // 2. Discount is configure at price field level, in this case discount should be applied only for // that particular price set field. // here we need to check if selected price set is quick config $isQuickConfigPriceSet = CRM_CiviDiscount_Utils::checkForQuickConfigPriceSet($psid); $keys = array_keys($discounts); $key = array_shift($keys); // in this case discount is specified for event id or membership type id, so we need to get info of // associated price set fields. For events discount we already have the list, but for memberships we // need to filter at membership type level //retrieve price set field associated with this priceset $priceSetInfo = CRM_CiviDiscount_Utils::getPriceSetsInfo($psid); $originalAmounts = $amounts; //$discount = array_shift($discounts); foreach ($discounts as $done_care => $discount) { if (!empty($dicountCalculater->autoDiscounts) && array_key_exists($done_care, $dicountCalculater->autoDiscounts)) { $autodiscount = TRUE; } else { $autodiscount = FALSE; } $priceFields = isset($discount['pricesets']) ? $discount['pricesets'] : array(); if (empty($priceFields) && (!empty($code) || $autodiscount)) { // apply discount to all the price fields for quickconfig pricesets if ($pagetype == 'event' && $isQuickConfigPriceSet) { $applyToAllLineItems = TRUE; if (!empty($key)) { $discounts[$key]['pricesets'] = array_keys($priceSetInfo); } } else { // filter only valid membership types that have discount foreach ($priceSetInfo as $pfID => $priceFieldValues) { if (!empty($priceFieldValues['membership_type_id']) && in_array($priceFieldValues['membership_type_id'], CRM_Utils_Array::value('memberships', $discount, array()))) { $priceFields[$pfID] = $pfID; } } } } $apcount = _cividiscount_checkEventDiscountMultipleParticipants($pagetype, $form, $discount); if (empty($apcount)) { //this was set to return but that doesn't make sense as there might be another discount continue; } $discountApplied = FALSE; if (!empty($autodiscount) || !empty($code)) { foreach ($amounts as $fee_id => &$fee) { if (!is_array($fee['options'])) { continue; } foreach ($fee['options'] as $option_id => &$option) { if (!empty($applyToAllLineItems) || CRM_Utils_Array::value($option['id'], $priceFields)) { $originalLabel = $originalAmounts[$fee_id]['options'][$option_id]['label']; $originalAmount = CRM_Utils_Rule::cleanMoney($originalAmounts[$fee_id]['options'][$option_id]['amount']); list($amount, $label) = _cividiscount_calc_discount($originalAmount, $originalLabel, $discount, $autodiscount, $currency); $discountAmount = $originalAmounts[$fee_id]['options'][$option_id]['amount'] - $amount; if ($discountAmount > CRM_Utils_Array::value('discount_applied', $option)) { $option['amount'] = $amount; $option['label'] = $label; $option['discount_applied'] = $discountAmount; } $discountApplied = TRUE; } } } } } // this seems to incorrectly set to only the last discount but it seems not to matter in the way it is used if (isset($discountApplied) && $discountApplied) { if (!empty($ps['fields'])) { $ps['fields'] = $amounts; $form->setVar('_priceSet', $ps); } $form->set('_discountInfo', array('discount' => $discount, 'autodiscount' => $autodiscount, 'contact_id' => $contact_id)); } } }