3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2013
37 * Business objects for managing price fields.
40 class CRM_Price_BAO_Field
extends CRM_Price_DAO_Field
{
45 * takes an associative array and creates a price field object
47 * the function extract all the params it needs to initialize the create a
48 * price field object. the params array could contain additional unused name/value
51 * @param array $params (reference ) an assoc array of name/value pairs
52 * @param array $ids the array that holds all the db ids
54 * @return object CRM_Price_BAO_Field object
58 static function &add(&$params) {
59 $priceFieldBAO = new CRM_Price_BAO_Field();
61 $priceFieldBAO->copyValues($params);
63 if ($id = CRM_Utils_Array
::value('id', $params)) {
64 $priceFieldBAO->id
= $id;
67 $priceFieldBAO->save();
68 return $priceFieldBAO;
72 * takes an associative array and creates a price field object
74 * This function is invoked from within the web form layer and also from the api layer
76 * @param array $params (reference) an assoc array of name/value pairs
78 * @return object CRM_Price_DAO_Field object
82 static function create(&$params) {
83 $transaction = new CRM_Core_Transaction();
85 $priceField = self
::add($params);
87 if (is_a($priceField, 'CRM_Core_Error')) {
88 $transaction->rollback();
92 $options = $optionsIds = array();
93 $maxIndex = CRM_Price_Form_Field
::NUM_OPTION
;
95 if ($priceField->html_type
== 'Text') {
98 $fieldValue = new CRM_Price_DAO_FieldValue();
99 $fieldValue->price_field_id
= $priceField->id
;
101 // update previous field values( if any )
102 if ($fieldValue->find(TRUE)) {
103 $optionsIds['id'] = $fieldValue->id
;
106 $defaultArray = array();
107 if ($params['html_type'] == 'CheckBox' && isset($params['default_checkbox_option'])) {
108 $tempArray = array_keys($params['default_checkbox_option']);
109 foreach ($tempArray as $v) {
110 if ($params['option_amount'][$v]) {
111 $defaultArray[$v] = 1;
116 if (CRM_Utils_Array
::value('default_option', $params)) {
117 $defaultArray[$params['default_option']] = 1;
121 for ($index = 1; $index <= $maxIndex; $index++
) {
122 if (array_key_exists('option_amount', $params) &&
123 array_key_exists($index, $params['option_amount']) &&
124 (CRM_Utils_Array
::value($index, CRM_Utils_Array
::value('option_label', $params)) ||
125 CRM_Utils_Array
::value('is_quick_config', $params)) &&
126 !CRM_Utils_System
::isNull($params['option_amount'][$index])
129 'price_field_id' => $priceField->id
,
130 'label' => trim($params['option_label'][$index]),
131 'name' => CRM_Utils_String
::munge($params['option_label'][$index], '_', 64),
132 'amount' => CRM_Utils_Rule
::cleanMoney(trim($params['option_amount'][$index])),
133 'count' => CRM_Utils_Array
::value($index, CRM_Utils_Array
::value('option_count', $params), NULL),
134 'max_value' => CRM_Utils_Array
::value($index, CRM_Utils_Array
::value('option_max_value', $params), NULL),
135 'description' => CRM_Utils_Array
::value($index, CRM_Utils_Array
::value('option_description', $params), NULL),
136 'membership_type_id' => CRM_Utils_Array
::value($index, CRM_Utils_Array
::value('membership_type_id', $params), NULL),
137 'weight' => $params['option_weight'][$index],
139 'is_default' => CRM_Utils_Array
::value($params['option_weight'][$index], $defaultArray) ?
$defaultArray[$params['option_weight'][$index]] : 0,
140 'membership_num_terms' => NULL,
143 if ($options['membership_type_id']) {
144 $options['membership_num_terms'] = CRM_Utils_Array
::value($index, CRM_Utils_Array
::value('membership_num_terms', $params), 1);
147 if (CRM_Utils_Array
::value( $index, CRM_Utils_Array
::value('option_financial_type_id', $params))) {
148 $options['financial_type_id'] = $params['option_financial_type_id'][$index];
149 } elseif (CRM_Utils_Array
::value( 'financial_type_id', $params )) {
150 $options['financial_type_id'] = $params['financial_type_id'];
153 if ($opIds = CRM_Utils_Array
::value('option_id', $params)) {
154 if ($opId = CRM_Utils_Array
::value($index, $opIds)) {
155 $optionsIds['id'] = $opId;
157 $optionsIds['id'] = NULL;
160 CRM_Price_BAO_FieldValue
::create($options, $optionsIds);
164 $transaction->commit();
169 * Takes a bunch of params that are needed to match certain criteria and
170 * retrieves the relevant objects. Typically the valid params are only
171 * contact_id. We'll tweak this function to be more full featured over a period
172 * of time. This is the inverse function of create. It also stores all the retrieved
173 * values in the default array
175 * @param array $params (reference ) an assoc array of name/value pairs
176 * @param array $defaults (reference ) an assoc array to hold the flattened values
178 * @return object CRM_Price_DAO_Field object
182 static function retrieve(&$params, &$defaults) {
183 return CRM_Core_DAO
::commonRetrieve('CRM_Price_DAO_Field', $params, $defaults);
187 * update the is_active flag in the db
189 * @param int $id Id of the database record
190 * @param boolean $is_active Value we want to set the is_active field
192 * @return Object DAO object on sucess, null otherwise
197 static function setIsActive($id, $is_active) {
198 return CRM_Core_DAO
::setFieldValue('CRM_Price_DAO_Field', $id, 'is_active', $is_active);
202 * Get the field title.
204 * @param int $id id of field.
206 * @return string name
212 public static function getTitle($id) {
213 return CRM_Core_DAO
::getFieldValue('CRM_Price_DAO_Field', $id, 'label');
217 * This function for building custom fields
219 * @param object $qf form object (reference)
220 * @param string $elementName name of the custom field
221 * @param boolean $inactiveNeeded
222 * @param boolean $useRequired true if required else false
223 * @param boolean $search true if used for search else false
224 * @param string $label label for custom field
229 public static function addQuickFormElement(&$qf,
235 $fieldOptions = NULL,
236 $feezeOptions = array()
239 $field = new CRM_Price_DAO_Field();
240 $field->id
= $fieldId;
241 if (!$field->find(TRUE)) {
242 /* FIXME: failure! */
247 if (isset($qf->_mode
) && empty($qf->_mode
)) {
250 elseif (isset($qf->_values
)) {
251 $is_pay_later = CRM_Utils_Array
::value( 'is_pay_later', $qf->_values
);
254 $otherAmount = $qf->get('values');
255 $config = CRM_Core_Config
::singleton();
256 $currencySymbol = CRM_Core_DAO
::getFieldValue('CRM_Financial_DAO_Currency',$config->defaultCurrency
,'symbol','name');
257 $qf->assign('currencySymbol', $currencySymbol);
258 // get currency name for price field and option attributes
259 $currencyName = $config->defaultCurrency
;
261 if (!isset($label)) {
262 $label = (property_exists($qf,'_membershipBlock') && CRM_Utils_Array
::value('is_separate_payment', $qf->_membershipBlock
) && $field->name
== 'contribution_amount' && !CRM_Utils_Array
::value('is_allow_other_amount', $otherAmount)) ?
ts('Additional Contribution') : $field->label
;
265 if ($field->name
== 'contribution_amount') {
266 $qf->_contributionAmount
= 1;
269 if (isset($qf->_online
) && $qf->_online
) {
270 $useRequired = FALSE;
273 $customOption = $fieldOptions;
274 if (!is_array($customOption)) {
275 $customOption = CRM_Price_BAO_Field
::getOptions($field->id
, $inactiveNeeded);
279 $valueFieldName = 'amount';
281 switch ($field->html_type
) {
283 $optionKey = key($customOption);
284 $count = CRM_Utils_Array
::value('count', $customOption[$optionKey], '');
285 $max_value = CRM_Utils_Array
::value('max_value', $customOption[$optionKey], '');
286 $priceVal = implode($seperator, array($customOption[$optionKey][$valueFieldName], $count, $max_value));
289 if (property_exists($qf,'_quickConfig') && $qf->_quickConfig
&& property_exists($qf,'_contributionAmount') && $qf->_contributionAmount
) {
290 foreach($fieldOptions as &$fieldOption) {
291 if ($fieldOption['name'] == 'other_amount') {
292 $fieldOption['label'] = $fieldOption['label'] . ' ' . $currencySymbol;
295 $qf->assign('priceset', $elementName);
296 $extra = array('onclick' => 'useAmountOther();');
300 // if seperate membership payment is used with quick config priceset then change the other amount label
301 if (property_exists($qf,'_membershipBlock') && CRM_Utils_Array
::value('is_separate_payment', $qf->_membershipBlock
) && $qf->_quickConfig
&& $field->name
== 'other_amount' && !property_exists($qf,'_contributionAmount')) {
302 $label = ts('Additional Contribution');
305 elseif (CRM_Utils_Array
::value('label', $fieldOptions[$optionKey])) { //check for label.
306 $label = $fieldOptions[$optionKey]['label'];
309 $element = &$qf->add('text', $elementName, $label,
311 array('price' => json_encode(array($optionKey, $priceVal)),
315 $useRequired && $field->is_required
318 $qf->add( 'text', 'txt-'.$elementName, $label, array( 'size' => '4'));
322 if (in_array($optionKey, $feezeOptions)) {
327 if (property_exists($qf, '_quickConfig') && $qf->_quickConfig
) {
328 $message = ts('Please enter a valid amount.');
332 $message = ts('%1 must be an integer (whole number).', array(1 => $label));
333 $type = 'positiveInteger';
335 // integers will have numeric rule applied to them.
336 $qf->addRule($elementName, $message, $type);
342 if (property_exists($qf, '_quickConfig') && $qf->_quickConfig
&& property_exists($qf,'_contributionAmount') && $qf->_contributionAmount
) {
343 $qf->assign('contriPriceset', $elementName);
346 foreach ($customOption as $opId => $opt) {
347 if ($field->is_display_amounts
) {
348 $opt['label'] = CRM_Utils_Array
::value('label', $opt) ?
$opt['label'] : '';
349 $opt['label'] = '<span class="crm-price-amount-amount">' . CRM_Utils_Money
::format($opt[$valueFieldName]) . '</span> <span class="crm-price-amount-label">' . $opt['label'] . '</span>';
351 $count = CRM_Utils_Array
::value('count', $opt, '');
352 $max_value = CRM_Utils_Array
::value('max_value', $opt, '');
353 $priceVal = implode($seperator, array($opt[$valueFieldName], $count, $max_value));
354 $extra = array('price' => json_encode(array($elementName, $priceVal)),
355 'data-amount' => $opt[$valueFieldName],
356 'data-currency' => $currencyName,
358 if (property_exists($qf, '_quickConfig') && $qf->_quickConfig
&& $field->name
== 'contribution_amount') {
359 $extra +
= array('onclick' => 'clearAmountOther();');
361 elseif (property_exists($qf, '_quickConfig') && $qf->_quickConfig
&& $field->name
== 'membership_amount') {
363 'onclick' => "return showHideAutoRenew({$opt['membership_type_id']});",
364 'membership-type' => $opt['membership_type_id'],
366 $qf->assign('membershipFieldID',$field->id
);
369 $choice[$opId] = $qf->createElement('radio', NULL, '', $opt['label'], $opt['id'], $extra);
372 $qf->add( 'text', 'txt-'.$elementName, $label, array( 'size' => '4'));
376 if (in_array($opId, $feezeOptions)) {
377 $choice[$opId]->freeze();
380 if (property_exists($qf, '_membershipBlock') && CRM_Utils_Array
::value('is_separate_payment', $qf->_membershipBlock
) && $field->name
== 'contribution_amount') {
381 $choice[] = $qf->createElement('radio', NULL, '', ts('No thank you'), '-1',
383 'onclick' => 'clearAmountOther();',
388 if (!$field->is_required
) {
390 if (CRM_Utils_Array
::value('is_allow_other_amount', $otherAmount) && $field->name
== 'contribution_amount') {
391 $none = ts('Other Amount');
393 elseif (property_exists($qf, '_membershipBlock') && !CRM_Utils_Array
::value('is_required', $qf->_membershipBlock
) && $field->name
== 'membership_amount') {
394 $none = ts('No thank you');
397 $none = ts('- none -');
400 $choice[] = $qf->createElement('radio', NULL, '', $none, '0',
401 array('price' => json_encode(array($elementName, '0')))
405 $element = &$qf->addGroup($choice, $elementName, $label);
407 // make contribution field required for quick config when membership block is enabled
408 if (($field->name
== 'contribution_amount' ||
$field->name
== 'membership_amount') && property_exists($qf, '_membershipBlock') && !empty($qf->_membershipBlock
) && !$field->is_required
) {
409 $useRequired = $field->is_required
= TRUE;
412 if ($useRequired && $field->is_required
) {
413 $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required');
418 $selectOption = $allowedOptions = $priceVal = array();
420 foreach ($customOption as $opt) {
421 $count = CRM_Utils_Array
::value('count', $opt, '');
422 $max_value = CRM_Utils_Array
::value('max_value', $opt, '');
423 $priceVal[$opt['id']] = implode($seperator, array($opt[$valueFieldName], $count, $max_value));
425 if ($field->is_display_amounts
) {
426 $opt['label'] .= ' - ';
427 $opt['label'] .= CRM_Utils_Money
::format($opt[$valueFieldName]);
429 $selectOption[$opt['id']] = $opt['label'];
431 if (!in_array($opt['id'], $feezeOptions)) {
432 $allowedOptions[] = $opt['id'];
435 $qf->add( 'text', 'txt-'.$elementName, $label, array( 'size' => '4'));
438 $element = &$qf->add('select', $elementName, $label,
440 '' => ts('- select -')) +
$selectOption,
441 $useRequired && $field->is_required
,
442 array('price' => json_encode($priceVal))
446 $button = substr($qf->controller
->getButtonName(), -4);
447 if (!empty($feezeOptions) && $button != 'skip') {
448 $qf->addRule($elementName, ts('Sorry, this option is currently sold out.'), 'regex', "/" . implode('|', $allowedOptions) . "/");
455 foreach ($customOption as $opId => $opt) {
456 $count = CRM_Utils_Array
::value('count', $opt, '');
457 $max_value = CRM_Utils_Array
::value('max_value', $opt, '');
458 $priceVal = implode($seperator, array($opt[$valueFieldName], $count, $max_value));
460 if ($field->is_display_amounts
) {
461 $opt['label'] .= ' - ';
462 $opt['label'] .= CRM_Utils_Money
::format($opt[$valueFieldName]);
464 $check[$opId] = &$qf->createElement('checkbox', $opt['id'], NULL, $opt['label'],
465 array('price' => json_encode(array($opt['id'], $priceVal)),
466 'data-amount' => $opt[$valueFieldName],
467 'data-currency' => $currencyName,
471 $txtcheck[$opId] =& $qf->createElement( 'text', $opId, $opt['label'], array( 'size' => '4' ) );
472 $qf->addGroup($txtcheck, 'txt-'.$elementName, $label);
475 if (in_array($opId, $feezeOptions)) {
476 $check[$opId]->freeze();
479 $element = &$qf->addGroup($check, $elementName, $label);
480 if ($useRequired && $field->is_required
) {
481 $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required');
485 if (isset($qf->_online
) && $qf->_online
) {
491 * Retrieve a list of options for the specified field
493 * @param int $fieldId price field ID
494 * @param bool $inactiveNeeded include inactive options
495 * @param bool $reset ignore stored values\
497 * @return array array of options
499 public static function getOptions($fieldId, $inactiveNeeded = FALSE, $reset = FALSE) {
500 static $options = array();
502 if ($reset ||
empty($options[$fieldId])) {
504 CRM_Price_BAO_FieldValue
::getValues($fieldId, $values, 'weight', !$inactiveNeeded);
505 $options[$fieldId] = $values;
508 return $options[$fieldId];
511 public static function getOptionId($optionLabel, $fid) {
512 if (!$optionLabel ||
!$fid) {
516 $optionGroupName = "civicrm_price_field.amount.{$fid}";
520 option_value.id as id
522 civicrm_option_value option_value,
523 civicrm_option_group option_group
525 option_group.name = %1
526 AND option_group.id = option_value.option_group_id
527 AND option_value.label = %2";
529 $dao = CRM_Core_DAO
::executeQuery($query, array(1 => array($optionGroupName, 'String'), 2 => array($optionLabel, 'String')));
531 while ($dao->fetch()) {
537 * Delete the price set field.
539 * @param int $id Field Id
547 public static function deleteField($id) {
548 $field = new CRM_Price_DAO_Field();
551 if ($field->find(TRUE)) {
552 // delete the options for this field
553 CRM_Price_BAO_FieldValue
::deleteValues($id);
555 // reorder the weight before delete
556 $fieldValues = array('price_set_id' => $field->price_set_id
);
558 CRM_Utils_Weight
::delWeight('CRM_Price_DAO_Field', $field->id
, $fieldValues);
560 // now delete the field
561 return $field->delete();
567 static function &htmlTypes() {
568 static $htmlTypes = NULL;
571 'Text' => ts('Text / Numeric Quantity'),
572 'Select' => ts('Select'),
573 'Radio' => ts('Radio'),
574 'CheckBox' => ts('CheckBox'),
581 * Validate the priceset
583 * @param int $priceSetId, array $fields
585 * retrun the error string
592 public static function priceSetValidation($priceSetId, $fields, &$error, $allowNoneSelection = FALSE) {
593 // check for at least one positive
594 // amount price field should be selected.
595 $priceField = new CRM_Price_DAO_Field();
596 $priceField->price_set_id
= $priceSetId;
599 $priceFields = array();
601 if ($allowNoneSelection) {
602 $noneSelectedPriceFields = array();
605 while ($priceField->fetch()) {
606 $key = "price_{$priceField->id}";
608 if ($allowNoneSelection) {
609 if (array_key_exists($key, $fields)) {
610 if ($fields[$key] == 0 && !$priceField->is_required
) {
611 $noneSelectedPriceFields[] = $priceField->id
;
616 if (CRM_Utils_Array
::value($key, $fields)) {
617 $priceFields[$priceField->id
] = $fields[$key];
621 if (!empty($priceFields)) {
622 // we should has to have positive amount.
625 FROM civicrm_price_field
626 WHERE id IN (" . implode(',', array_keys($priceFields)) . ')';
627 $fieldDAO = CRM_Core_DAO
::executeQuery($sql);
628 $htmlTypes = array();
629 while ($fieldDAO->fetch()) {
630 $htmlTypes[$fieldDAO->id
] = $fieldDAO->html_type
;
633 $selectedAmounts = array();
635 foreach ($htmlTypes as $fieldId => $type) {
637 CRM_Price_BAO_FieldValue
::getValues($fieldId, $options);
639 if (empty($options)) {
643 if ($type == 'Text') {
644 foreach ($options as $opId => $option) {
645 $selectedAmounts[$opId] = $priceFields[$fieldId] * $option['amount'];
649 elseif (is_array($fields["price_{$fieldId}"])) {
650 foreach (array_keys($fields["price_{$fieldId}"]) as $opId) {
651 $selectedAmounts[$opId] = $options[$opId]['amount'];
654 elseif (in_array($fields["price_{$fieldId}"], array_keys($options))) {
655 $selectedAmounts[$fields["price_{$fieldId}"]] = $options[$fields["price_{$fieldId}"]]['amount'];
659 list($componentName) = explode(':', $fields['_qf_default']);
660 // now we have all selected amount in hand.
661 $totalAmount = array_sum($selectedAmounts);
662 if ($totalAmount < 0) {
663 $error['_qf_default'] = ts('%1 amount can not be less than zero. Please select the options accordingly.', array(1 => $componentName));
667 if ($allowNoneSelection) {
668 if (empty($noneSelectedPriceFields)) {
669 $error['_qf_default'] = ts('Please select at least one option from price set.');
672 $error['_qf_default'] = ts('Please select at least one option from price set.');