include unit test
[civicrm-core.git] / CRM / Price / BAO / PriceField.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2016
32 * $Id$
33 *
34 */
35
36 /**
37 * Business objects for managing price fields.
38 *
39 */
40 class CRM_Price_BAO_PriceField extends CRM_Price_DAO_PriceField {
41
42 protected $_options;
43
44 /**
45 * Takes an associative array and creates a price field object.
46 *
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
49 * pairs
50 *
51 * @param array $params
52 * (reference) an assoc array of name/value pairs.
53 *
54 * @return CRM_Price_BAO_PriceField
55 */
56 public static function add(&$params) {
57 $priceFieldBAO = new CRM_Price_BAO_PriceField();
58
59 $priceFieldBAO->copyValues($params);
60
61 if ($id = CRM_Utils_Array::value('id', $params)) {
62 $priceFieldBAO->id = $id;
63 }
64
65 $priceFieldBAO->save();
66 return $priceFieldBAO;
67 }
68
69 /**
70 * Takes an associative array and creates a price field object.
71 *
72 * This function is invoked from within the web form layer and also from the api layer
73 *
74 * @param array $params
75 * (reference) an assoc array of name/value pairs.
76 *
77 * @return CRM_Price_DAO_PriceField
78 */
79 public static function create(&$params) {
80 if (empty($params['id']) && empty($params['name'])) {
81 $params['name'] = strtolower(CRM_Utils_String::munge($params['label'], '_', 242));
82 }
83 $transaction = new CRM_Core_Transaction();
84
85 $priceField = self::add($params);
86
87 if (is_a($priceField, 'CRM_Core_Error')) {
88 $transaction->rollback();
89 return $priceField;
90 }
91
92 $optionsIds = array();
93 $maxIndex = CRM_Price_Form_Field::NUM_OPTION;
94
95 if ($priceField->html_type == 'Text') {
96 $maxIndex = 1;
97
98 $fieldValue = new CRM_Price_DAO_PriceFieldValue();
99 $fieldValue->price_field_id = $priceField->id;
100
101 // update previous field values( if any )
102 if ($fieldValue->find(TRUE)) {
103 $optionsIds['id'] = $fieldValue->id;
104 }
105 }
106 $defaultArray = array();
107 //html type would be empty in update scenario not sure what would happen ...
108 if (!empty($params['html_type']) && $params['html_type'] == 'CheckBox' && isset($params['default_checkbox_option'])) {
109 $tempArray = array_keys($params['default_checkbox_option']);
110 foreach ($tempArray as $v) {
111 if ($params['option_amount'][$v]) {
112 $defaultArray[$v] = 1;
113 }
114 }
115 }
116 else {
117 if (!empty($params['default_option'])) {
118 $defaultArray[$params['default_option']] = 1;
119 }
120 }
121
122 for ($index = 1; $index <= $maxIndex; $index++) {
123 if (array_key_exists('option_amount', $params) &&
124 array_key_exists($index, $params['option_amount']) &&
125 (CRM_Utils_Array::value($index, CRM_Utils_Array::value('option_label', $params)) || !empty($params['is_quick_config'])) &&
126 !CRM_Utils_System::isNull($params['option_amount'][$index])
127 ) {
128 $options = array(
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],
138 'is_active' => 1,
139 'is_default' => CRM_Utils_Array::value($params['option_weight'][$index], $defaultArray) ? $defaultArray[$params['option_weight'][$index]] : 0,
140 'membership_num_terms' => NULL,
141 'non_deductible_amount' => CRM_Utils_Array::value('non_deductible_amount', $params),
142 );
143
144 if ($options['membership_type_id']) {
145 $options['membership_num_terms'] = CRM_Utils_Array::value($index, CRM_Utils_Array::value('membership_num_terms', $params), 1);
146 }
147
148 if (CRM_Utils_Array::value($index, CRM_Utils_Array::value('option_financial_type_id', $params))) {
149 $options['financial_type_id'] = $params['option_financial_type_id'][$index];
150 }
151 elseif (!empty($params['financial_type_id'])) {
152 $options['financial_type_id'] = $params['financial_type_id'];
153 }
154
155 if ($opIds = CRM_Utils_Array::value('option_id', $params)) {
156 if ($opId = CRM_Utils_Array::value($index, $opIds)) {
157 $optionsIds['id'] = $opId;
158 }
159 else {
160 $optionsIds['id'] = NULL;
161 }
162 }
163 CRM_Price_BAO_PriceFieldValue::create($options, $optionsIds);
164 }
165 }
166
167 $transaction->commit();
168 return $priceField;
169 }
170
171 /**
172 * Fetch object based on array of properties.
173 *
174 * @param array $params
175 * (reference ) an assoc array of name/value pairs.
176 * @param array $defaults
177 * (reference ) an assoc array to hold the flattened values.
178 *
179 * @return CRM_Price_DAO_PriceField
180 */
181 public static function retrieve(&$params, &$defaults) {
182 return CRM_Core_DAO::commonRetrieve('CRM_Price_DAO_PriceField', $params, $defaults);
183 }
184
185 /**
186 * Update the is_active flag in the db.
187 *
188 * @param int $id
189 * Id of the database record.
190 * @param bool $is_active
191 * Value we want to set the is_active field.
192 *
193 * @return Object
194 * DAO object on success, null otherwise.
195 */
196 public static function setIsActive($id, $is_active) {
197 return CRM_Core_DAO::setFieldValue('CRM_Price_DAO_PriceField', $id, 'is_active', $is_active);
198 }
199
200 /**
201 * Freeze form if the event is full.
202 *
203 * @param $element
204 * @param $fieldOptions
205 *
206 * @return null
207 */
208 public static function freezeIfEnabled(&$element, $fieldOptions) {
209 if (!empty($fieldOptions['is_full'])) {
210 $element->freeze();
211 }
212 return NULL;
213 }
214
215 /**
216 * Get the field title.
217 *
218 * @param int $id
219 * Id of field.
220 *
221 * @return string
222 * name
223 *
224 */
225 public static function getTitle($id) {
226 return CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceField', $id, 'label');
227 }
228
229 /**
230 * This function for building custom fields.
231 *
232 * @param CRM_Core_Form $qf
233 * Form object (reference).
234 * @param string $elementName
235 * Name of the custom field.
236 * @param int $fieldId
237 * @param bool $inactiveNeeded
238 * @param bool $useRequired
239 * True if required else false.
240 * @param string $label
241 * Label for custom field.
242 *
243 * @param null $fieldOptions
244 * @param array $freezeOptions
245 *
246 * @return null
247 */
248 public static function addQuickFormElement(
249 &$qf,
250 $elementName,
251 $fieldId,
252 $inactiveNeeded,
253 $useRequired = TRUE,
254 $label = NULL,
255 $fieldOptions = NULL,
256 $freezeOptions = array()
257 ) {
258
259 $field = new CRM_Price_DAO_PriceField();
260 $field->id = $fieldId;
261 if (!$field->find(TRUE)) {
262 /* FIXME: failure! */
263 return NULL;
264 }
265
266 $is_pay_later = 0;
267 if (isset($qf->_mode) && empty($qf->_mode)) {
268 $is_pay_later = 1;
269 }
270 elseif (isset($qf->_values)) {
271 $is_pay_later = CRM_Utils_Array::value('is_pay_later', $qf->_values);
272 }
273
274 $otherAmount = $qf->get('values');
275 $config = CRM_Core_Config::singleton();
276 $currencySymbol = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_Currency', $config->defaultCurrency, 'symbol', 'name');
277 $qf->assign('currencySymbol', $currencySymbol);
278 // get currency name for price field and option attributes
279 $currencyName = $config->defaultCurrency;
280
281 if (!isset($label)) {
282 $label = (!empty($qf->_membershipBlock) && $field->name == 'contribution_amount') ? ts('Additional Contribution') : $field->label;
283 }
284
285 if ($field->name == 'contribution_amount') {
286 $qf->_contributionAmount = 1;
287 }
288
289 if (isset($qf->_online) && $qf->_online) {
290 $useRequired = FALSE;
291 }
292
293 $customOption = $fieldOptions;
294 if (!is_array($customOption)) {
295 $customOption = CRM_Price_BAO_PriceField::getOptions($field->id, $inactiveNeeded);
296 }
297
298 //use value field.
299 $valueFieldName = 'amount';
300 $seperator = '|';
301 $invoiceSettings = Civi::settings()->get('contribution_invoice_settings');
302 $taxTerm = CRM_Utils_Array::value('tax_term', $invoiceSettings);
303 $displayOpt = CRM_Utils_Array::value('tax_display_settings', $invoiceSettings);
304 $invoicing = CRM_Utils_Array::value('invoicing', $invoiceSettings);
305 switch ($field->html_type) {
306 case 'Text':
307 $optionKey = key($customOption);
308 $count = CRM_Utils_Array::value('count', $customOption[$optionKey], '');
309 $max_value = CRM_Utils_Array::value('max_value', $customOption[$optionKey], '');
310 $taxAmount = CRM_Utils_Array::value('tax_amount', $customOption[$optionKey]);
311 if (isset($taxAmount) && $displayOpt && $invoicing) {
312 $qf->assign('displayOpt', $displayOpt);
313 $qf->assign('taxTerm', $taxTerm);
314 $qf->assign('invoicing', $invoicing);
315 }
316 $priceVal = implode($seperator, array(
317 $customOption[$optionKey][$valueFieldName] + $taxAmount,
318 $count,
319 $max_value,
320 ));
321
322 $extra = array();
323 if (!empty($qf->_membershipBlock) && !empty($qf->_quickConfig) && $field->name == 'other_amount' && empty($qf->_contributionAmount)) {
324 $useRequired = 0;
325 }
326 elseif (!empty($fieldOptions[$optionKey]['label'])) {
327 //check for label.
328 $label = $fieldOptions[$optionKey]['label'];
329 if (!empty($qf->_quickConfig) && !empty($qf->_contributionAmount) && strtolower($fieldOptions[$optionKey]['name']) == 'other_amount') {
330 $label .= ' ' . $currencySymbol;
331 $qf->assign('priceset', $elementName);
332 $extra = array('onclick' => 'useAmountOther();');
333 }
334 }
335
336 $element = &$qf->add('text', $elementName, $label,
337 array_merge($extra,
338 array(
339 'price' => json_encode(array($optionKey, $priceVal)),
340 'size' => '4',
341 )
342 ),
343 $useRequired && $field->is_required
344 );
345 if ($is_pay_later) {
346 $qf->add('text', 'txt-' . $elementName, $label, array('size' => '4'));
347 }
348
349 // CRM-6902 - Add "max" option for a price set field
350 if (in_array($optionKey, $freezeOptions)) {
351 self::freezeIfEnabled($element, $fieldOptions[$optionKey]);
352 // CRM-14696 - Improve display for sold out price set options
353 $element->setLabel($label . '&nbsp;<span class="sold-out-option">' . ts('Sold out') . '</span>');
354 }
355
356 //CRM-10117
357 if (!empty($qf->_quickConfig)) {
358 $message = ts('Please enter a valid amount.');
359 $type = 'money';
360 }
361 else {
362 $message = ts('%1 must be a number (with or without decimal point).', array(1 => $label));
363 $type = 'numeric';
364 }
365 // integers will have numeric rule applied to them.
366 $qf->addRule($elementName, $message, $type);
367 break;
368
369 case 'Radio':
370 $choice = array();
371
372 if (!empty($qf->_quickConfig) && !empty($qf->_contributionAmount)) {
373 $qf->assign('contriPriceset', $elementName);
374 }
375
376 foreach ($customOption as $opId => $opt) {
377 $taxAmount = CRM_Utils_Array::value('tax_amount', $opt);
378 if ($field->is_display_amounts) {
379 $opt['label'] = !empty($opt['label']) ? $opt['label'] . '<span class="crm-price-amount-label-separator">&nbsp;-&nbsp;</span>' : '';
380 $preHelpText = $postHelpText = '';
381 if (isset($opt['help_pre'])) {
382 $preHelpText = '<span class="crm-price-amount-help-pre description">' . $opt['help_pre'] . '</span>: ';
383 }
384 if (isset($opt['help_post'])) {
385 $postHelpText = ': <span class="crm-price-amount-help-post description">' . $opt['help_post'] . '</span>';
386 }
387 if (isset($taxAmount) && $invoicing) {
388 if ($displayOpt == 'Do_not_show') {
389 $opt['label'] = '<span class="crm-price-amount-label">' . $opt['label'] . '</span>' . '<span class="crm-price-amount-amount">' . CRM_Utils_Money::format($opt[$valueFieldName] + $taxAmount) . '</span>';
390 }
391 elseif ($displayOpt == 'Inclusive') {
392 $opt['label'] = '<span class="crm-price-amount-label">' . $opt['label'] . '</span>' . '<span class="crm-price-amount-amount">' . CRM_Utils_Money::format($opt[$valueFieldName] + $taxAmount) . '</span>';
393 $opt['label'] .= '<span class="crm-price-amount-tax"> (includes ' . $taxTerm . ' of ' . CRM_Utils_Money::format($opt['tax_amount']) . ')</span>';
394 }
395 else {
396 $opt['label'] = '<span class="crm-price-amount-label">' . $opt['label'] . '</span>' . '<span class="crm-price-amount-amount">' . CRM_Utils_Money::format($opt[$valueFieldName]) . '</span>';
397 $opt['label'] .= '<span class="crm-price-amount-tax"> + ' . CRM_Utils_Money::format($opt['tax_amount']) . ' ' . $taxTerm . '</span>';
398 }
399 }
400 else {
401 $opt['label'] = '<span class="crm-price-amount-label">' . $opt['label'] . '</span>' . '<span class="crm-price-amount-amount">' . CRM_Utils_Money::format($opt[$valueFieldName]) . '</span>';
402 }
403 $opt['label'] = $preHelpText . $opt['label'] . $postHelpText;
404 }
405 $count = CRM_Utils_Array::value('count', $opt, '');
406 $max_value = CRM_Utils_Array::value('max_value', $opt, '');
407 $priceVal = implode($seperator, array($opt[$valueFieldName] + $taxAmount, $count, $max_value));
408 $extra = array(
409 'price' => json_encode(array($elementName, $priceVal)),
410 'data-amount' => $opt[$valueFieldName],
411 'data-currency' => $currencyName,
412 'data-price-field-values' => json_encode($customOption),
413 );
414 if (!empty($qf->_quickConfig) && $field->name == 'contribution_amount') {
415 $extra += array('onclick' => 'clearAmountOther();');
416 }
417 elseif (!empty($qf->_quickConfig) && $field->name == 'membership_amount') {
418 $extra += array(
419 'onclick' => "return showHideAutoRenew({$opt['membership_type_id']});",
420 'membership-type' => $opt['membership_type_id'],
421 );
422 $qf->assign('membershipFieldID', $field->id);
423 }
424
425 $choice[$opId] = $qf->createElement('radio', NULL, '', $opt['label'], $opt['id'], $extra);
426 if ($is_pay_later) {
427 $qf->add('text', 'txt-' . $elementName, $label, array('size' => '4'));
428 }
429
430 // CRM-6902 - Add "max" option for a price set field
431 if (in_array($opId, $freezeOptions)) {
432 self::freezeIfEnabled($choice[$opId], $customOption[$opId]);
433 // CRM-14696 - Improve display for sold out price set options
434 $choice[$opId]->setText('<span class="sold-out-option">' . $choice[$opId]->getText() . '&nbsp;(' . ts('Sold out') . ')</span>');
435 }
436 }
437 if (!empty($qf->_membershipBlock) && $field->name == 'contribution_amount') {
438 $choice[] = $qf->createElement('radio', NULL, '', ts('No thank you'), '-1',
439 array(
440 'price' => json_encode(array($elementName, '0|0')),
441 'data-currency' => $currencyName,
442 'onclick' => 'clearAmountOther();',
443 )
444 );
445 }
446
447 if (!$field->is_required) {
448 // add "none" option
449 if (!empty($otherAmount['is_allow_other_amount']) && $field->name == 'contribution_amount') {
450 $none = ts('Other Amount');
451 }
452 elseif (!empty($qf->_membershipBlock) && empty($qf->_membershipBlock['is_required']) && $field->name == 'membership_amount') {
453 $none = ts('No thank you');
454 }
455 else {
456 $none = ts('- none -');
457 }
458
459 $choice[] = $qf->createElement('radio', NULL, '', $none, '0',
460 array('price' => json_encode(array($elementName, '0')))
461 );
462 }
463
464 $element = &$qf->addGroup($choice, $elementName, $label);
465
466 // make contribution field required for quick config when membership block is enabled
467 if (($field->name == 'membership_amount' || $field->name == 'contribution_amount')
468 && !empty($qf->_membershipBlock) && !$field->is_required
469 ) {
470 $useRequired = $field->is_required = TRUE;
471 }
472
473 if ($useRequired && $field->is_required) {
474 $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required');
475 }
476 break;
477
478 case 'Select':
479 $selectOption = $allowedOptions = $priceVal = array();
480
481 foreach ($customOption as $opt) {
482 $taxAmount = CRM_Utils_Array::value('tax_amount', $opt);
483 $count = CRM_Utils_Array::value('count', $opt, '');
484 $max_value = CRM_Utils_Array::value('max_value', $opt, '');
485
486 if ($field->is_display_amounts) {
487 $opt['label'] .= '&nbsp;-&nbsp;';
488 if (isset($taxAmount) && $invoicing) {
489 $opt['label'] = $opt['label'] . self::getTaxLabel($opt, $valueFieldName, $displayOpt, $taxTerm);
490 }
491 else {
492 $opt['label'] = $opt['label'] . CRM_Utils_Money::format($opt[$valueFieldName]);
493 }
494 }
495
496 $priceVal[$opt['id']] = implode($seperator, array($opt[$valueFieldName] + $taxAmount, $count, $max_value));
497
498 if (!in_array($opt['id'], $freezeOptions)) {
499 $allowedOptions[] = $opt['id'];
500 }
501 // CRM-14696 - Improve display for sold out price set options
502 else {
503 $opt['id'] = 'crm_disabled_opt-' . $opt['id'];
504 $opt['label'] = $opt['label'] . ' (' . ts('Sold out') . ')';
505 }
506
507 $selectOption[$opt['id']] = $opt['label'];
508
509 if ($is_pay_later) {
510 $qf->add('text', 'txt-' . $elementName, $label, array('size' => '4'));
511 }
512 }
513
514 $element = &$qf->add('select', $elementName, $label,
515 array(
516 '' => ts('- select -'),
517 ) + $selectOption,
518 $useRequired && $field->is_required,
519 array('price' => json_encode($priceVal), 'class' => 'crm-select2', 'data-price-field-values' => json_encode($customOption))
520 );
521
522 // CRM-6902 - Add "max" option for a price set field
523 $button = substr($qf->controller->getButtonName(), -4);
524 if (!empty($freezeOptions) && $button != 'skip') {
525 $qf->addRule($elementName, ts('Sorry, this option is currently sold out.'), 'regex', "/" . implode('|', $allowedOptions) . "/");
526 }
527 break;
528
529 case 'CheckBox':
530
531 $check = array();
532 foreach ($customOption as $opId => $opt) {
533 $taxAmount = CRM_Utils_Array::value('tax_amount', $opt);
534 $count = CRM_Utils_Array::value('count', $opt, '');
535 $max_value = CRM_Utils_Array::value('max_value', $opt, '');
536
537 if ($field->is_display_amounts) {
538 $preHelpText = $postHelpText = '';
539 if (isset($opt['help_pre'])) {
540 $preHelpText = '<span class="crm-price-amount-help-pre description">' . $opt['help_pre'] . '</span>: ';
541 }
542 if (isset($opt['help_post'])) {
543 $postHelpText = ': <span class="crm-price-amount-help-post description">' . $opt['help_post'] . '</span>';
544 }
545 $opt['label'] = '<span class="crm-price-amount-label">' . $opt['label'] . '</span>&nbsp;-&nbsp;';
546 if (isset($taxAmount) && $invoicing) {
547 $opt['label'] .= self::getTaxLabel($opt, $valueFieldName, $displayOpt, $taxTerm);
548 }
549 else {
550 $opt['label'] .= CRM_Utils_Money::format($opt[$valueFieldName]);
551 }
552 $opt['label'] = $preHelpText . $opt['label'] . $postHelpText;
553 }
554 $priceVal = implode($seperator, array($opt[$valueFieldName] + $taxAmount, $count, $max_value));
555 $check[$opId] = &$qf->createElement('checkbox', $opt['id'], NULL, $opt['label'],
556 array(
557 'price' => json_encode(array($opt['id'], $priceVal)),
558 'data-amount' => $opt[$valueFieldName],
559 'data-currency' => $currencyName,
560 )
561 );
562 if ($is_pay_later) {
563 $txtcheck[$opId] =& $qf->createElement('text', $opId, $opt['label'], array('size' => '4'));
564 $qf->addGroup($txtcheck, 'txt-' . $elementName, $label);
565 }
566 // CRM-6902 - Add "max" option for a price set field
567 if (in_array($opId, $freezeOptions)) {
568 self::freezeIfEnabled($check[$opId], $customOption[$opId]);
569 // CRM-14696 - Improve display for sold out price set options
570 $check[$opId]->setText('<span class="sold-out-option">' . $check[$opId]->getText() . '&nbsp;(' . ts('Sold out') . ')</span>');
571 }
572 }
573 $element = &$qf->addGroup($check, $elementName, $label);
574 if ($useRequired && $field->is_required) {
575 $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required');
576 }
577 break;
578 }
579 if (isset($qf->_online) && $qf->_online) {
580 $element->freeze();
581 }
582 }
583
584 /**
585 * Retrieve a list of options for the specified field.
586 *
587 * @param int $fieldId
588 * Price field ID.
589 * @param bool $inactiveNeeded
590 * Include inactive options.
591 * @param bool $reset
592 * Discard stored values.
593 *
594 * @return array
595 * array of options
596 */
597 public static function getOptions($fieldId, $inactiveNeeded = FALSE, $reset = FALSE) {
598 static $options = array();
599 if ($reset) {
600 $options = array();
601 // This would happen if the function was only called to clear the cache.
602 if (empty($fieldId)) {
603 return array();
604 }
605 }
606
607 if (empty($options[$fieldId])) {
608 $values = array();
609 CRM_Price_BAO_PriceFieldValue::getValues($fieldId, $values, 'weight', !$inactiveNeeded);
610 $options[$fieldId] = $values;
611 $taxRates = CRM_Core_PseudoConstant::getTaxRates();
612
613 // ToDo - Code for Hook Invoke
614
615 foreach ($options[$fieldId] as $priceFieldId => $priceFieldValues) {
616 if (isset($priceFieldValues['financial_type_id']) && array_key_exists($priceFieldValues['financial_type_id'], $taxRates)) {
617 $options[$fieldId][$priceFieldId]['tax_rate'] = $taxRates[$priceFieldValues['financial_type_id']];
618 $taxAmount = CRM_Contribute_BAO_Contribution_Utils::calculateTaxAmount($priceFieldValues['amount'], $options[$fieldId][$priceFieldId]['tax_rate']);
619 $options[$fieldId][$priceFieldId]['tax_amount'] = $taxAmount['tax_amount'];
620 }
621 }
622 }
623
624 return $options[$fieldId];
625 }
626
627 /**
628 * @param $optionLabel
629 * @param int $fid
630 *
631 * @return mixed
632 */
633 public static function getOptionId($optionLabel, $fid) {
634 if (!$optionLabel || !$fid) {
635 return;
636 }
637
638 $optionGroupName = "civicrm_price_field.amount.{$fid}";
639
640 $query = "
641 SELECT
642 option_value.id as id
643 FROM
644 civicrm_option_value option_value,
645 civicrm_option_group option_group
646 WHERE
647 option_group.name = %1
648 AND option_group.id = option_value.option_group_id
649 AND option_value.label = %2";
650
651 $dao = CRM_Core_DAO::executeQuery($query, array(
652 1 => array($optionGroupName, 'String'),
653 2 => array($optionLabel, 'String'),
654 ));
655
656 while ($dao->fetch()) {
657 return $dao->id;
658 }
659 }
660
661 /**
662 * Delete the price set field.
663 *
664 * @param int $id
665 * Field Id.
666 *
667 * @return bool
668 *
669 */
670 public static function deleteField($id) {
671 $field = new CRM_Price_DAO_PriceField();
672 $field->id = $id;
673
674 if ($field->find(TRUE)) {
675 // delete the options for this field
676 CRM_Price_BAO_PriceFieldValue::deleteValues($id);
677
678 // reorder the weight before delete
679 $fieldValues = array('price_set_id' => $field->price_set_id);
680
681 CRM_Utils_Weight::delWeight('CRM_Price_DAO_PriceField', $field->id, $fieldValues);
682
683 // now delete the field
684 return $field->delete();
685 }
686
687 return NULL;
688 }
689
690 /**
691 * @return array
692 */
693 public static function &htmlTypes() {
694 static $htmlTypes = NULL;
695 if (!$htmlTypes) {
696 $htmlTypes = array(
697 'Text' => ts('Text / Numeric Quantity'),
698 'Select' => ts('Select'),
699 'Radio' => ts('Radio'),
700 'CheckBox' => ts('CheckBox'),
701 );
702 }
703 return $htmlTypes;
704 }
705
706 /**
707 * Validate the priceset.
708 *
709 * @param int $priceSetId
710 * , array $fields.
711 *
712 * retrun the error string
713 *
714 * @param $fields
715 * @param $error
716 * @param bool $allowNoneSelection
717 *
718 */
719 public static function priceSetValidation($priceSetId, $fields, &$error, $allowNoneSelection = FALSE) {
720 // check for at least one positive
721 // amount price field should be selected.
722 $priceField = new CRM_Price_DAO_PriceField();
723 $priceField->price_set_id = $priceSetId;
724 $priceField->find();
725
726 $priceFields = array();
727
728 if ($allowNoneSelection) {
729 $noneSelectedPriceFields = array();
730 }
731
732 while ($priceField->fetch()) {
733 $key = "price_{$priceField->id}";
734
735 if ($allowNoneSelection) {
736 if (array_key_exists($key, $fields)) {
737 if ($fields[$key] == 0 && !$priceField->is_required) {
738 $noneSelectedPriceFields[] = $priceField->id;
739 }
740 }
741 }
742
743 if (!empty($fields[$key])) {
744 $priceFields[$priceField->id] = $fields[$key];
745 }
746 }
747
748 if (!empty($priceFields)) {
749 // we should has to have positive amount.
750 $sql = "
751 SELECT id, html_type
752 FROM civicrm_price_field
753 WHERE id IN (" . implode(',', array_keys($priceFields)) . ')';
754 $fieldDAO = CRM_Core_DAO::executeQuery($sql);
755 $htmlTypes = array();
756 while ($fieldDAO->fetch()) {
757 $htmlTypes[$fieldDAO->id] = $fieldDAO->html_type;
758 }
759
760 $selectedAmounts = array();
761
762 foreach ($htmlTypes as $fieldId => $type) {
763 $options = array();
764 CRM_Price_BAO_PriceFieldValue::getValues($fieldId, $options);
765
766 if (empty($options)) {
767 continue;
768 }
769
770 if ($type == 'Text') {
771 foreach ($options as $opId => $option) {
772 $selectedAmounts[$opId] = $priceFields[$fieldId] * $option['amount'];
773 break;
774 }
775 }
776 elseif (is_array($fields["price_{$fieldId}"])) {
777 foreach (array_keys($fields["price_{$fieldId}"]) as $opId) {
778 $selectedAmounts[$opId] = $options[$opId]['amount'];
779 }
780 }
781 elseif (in_array($fields["price_{$fieldId}"], array_keys($options))) {
782 $selectedAmounts[$fields["price_{$fieldId}"]] = $options[$fields["price_{$fieldId}"]]['amount'];
783 }
784 }
785
786 list($componentName) = explode(':', $fields['_qf_default']);
787 // now we have all selected amount in hand.
788 $totalAmount = array_sum($selectedAmounts);
789 if ($totalAmount < 0) {
790 $error['_qf_default'] = ts('%1 amount can not be less than zero. Please select the options accordingly.', array(1 => $componentName));
791 }
792 }
793 else {
794 if ($allowNoneSelection) {
795 if (empty($noneSelectedPriceFields)) {
796 $error['_qf_default'] = ts('Please select at least one option from price set.');
797 }
798 }
799 else {
800 $error['_qf_default'] = ts('Please select at least one option from price set.');
801 }
802 }
803 }
804
805 /**
806 * Generate the label for price fields based on tax display setting option on CiviContribute Component Settings page.
807 *
808 * @param array $opt
809 * @param string $valueFieldName
810 * Amount.
811 * @param string $displayOpt
812 * Tax display setting option.
813 *
814 * @param string $taxTerm
815 *
816 * @return string
817 * Tax label for custom field.
818 */
819 public static function getTaxLabel($opt, $valueFieldName, $displayOpt, $taxTerm) {
820 if ($displayOpt == 'Do_not_show') {
821 $label = CRM_Utils_Money::format($opt[$valueFieldName] + $opt['tax_amount']);
822 }
823 elseif ($displayOpt == 'Inclusive') {
824 $label = CRM_Utils_Money::format($opt[$valueFieldName] + $opt['tax_amount']);
825 $label .= '<span class="crm-price-amount-tax"> (includes ' . $taxTerm . ' of ' . CRM_Utils_Money::format($opt['tax_amount']) . ')</span>';
826 }
827 else {
828 $label = CRM_Utils_Money::format($opt[$valueFieldName]);
829 $label .= '<span class="crm-price-amount-tax"> + ' . CRM_Utils_Money::format($opt['tax_amount']) . ' ' . $taxTerm . '</span>';
830 }
831
832 return $label;
833 }
834
835 }