12class IblockMoneyProperty
14 public const USER_TYPE =
'Money';
15 public const SEPARATOR =
'|';
22 public static function getUserTypeDescription()
24 $className = get_called_class();
26 'PROPERTY_TYPE' =>
'S',
27 'USER_TYPE' => self::USER_TYPE,
29 'GetPublicEditHTML' => [$className,
'getPublicEditHTML'],
30 'GetPublicViewHTML' => [$className,
'getPublicViewHTML'],
31 'GetPropertyFieldHtml' => [$className,
'getPropertyFieldHtml'],
32 'GetAdminListViewHTML' => [$className,
'getAdminListViewHTML'],
33 'GetUIEntityEditorProperty' => [$className,
'GetUIEntityEditorProperty'],
34 'getFormattedValue' => [$className,
'getSeparatedValues'],
35 'CheckFields' => [$className,
'checkFields'],
36 'GetLength' => [$className,
'getLength'],
37 'ConvertToDB' => [$className,
'convertToDB'],
38 'ConvertFromDB' => [$className,
'convertFromDB'],
39 'AddFilterFields' => [$className,
'addFilterFields'],
51 public static function getPublicEditHTML($property, $value, $controlSettings)
53 return self::getPropertyFieldHtml($property, $value, $controlSettings);
64 public static function getPublicViewHTML($property, $value, $controlSettings)
66 return self::getAdminListViewHTML($property, $value, $controlSettings);
77 public static function getPropertyFieldHtml($property, $value, $controlSettings)
79 $randString = mb_strtolower(\
Bitrix\Main\Security\Random::getString(6));
81 $explode = (is_string($value[
'VALUE']) ? explode(self::SEPARATOR, $value[
'VALUE']) : []);
82 $currentValue = (isset($explode[0]) && $explode[0] !==
'' ? $explode[0] :
'');
83 $currentCurrency = ($explode[1] ??
'');
85 $html =
'<input type="text" style="width: auto;" value="'.htmlspecialcharsbx($currentValue).
86 '" id="input-'.$randString.
'">';
87 $html .=
'<input type="hidden" id="hidden-'.$randString.
'" name="'.
88 htmlspecialcharsbx($controlSettings[
'VALUE']).
'" value="'.htmlspecialcharsbx($value[
"VALUE"]).
'">';
89 $listCurrency = self::getListCurrency();
92 if($property[
'MULTIPLE'] ==
'Y')
93 $html .=
'<input type="hidden" data-id="'.$randString.
'">';
94 $html .=
'<select id="selector-'.$randString.
'" style="width: auto; margin: 0 5px;">';
95 foreach($listCurrency as $currency)
97 $selected = ($currentCurrency == $currency[
'CURRENCY'] ||
98 (!$currentCurrency && $currency[
'BASE'] ==
'Y')) ?
'selected' :
'';
99 $html .=
'<option '.$selected.
' value="'.$currency[
'CURRENCY'].
'">'.
100 htmlspecialcharsbx($currency[
'NAME']).
'</option>';
102 $html .=
'</select>';
103 $html .=
'<span id="error-'.$randString.
'" style="color: red;"></span>';
104 $html .= self::getJsHandlerSelector($randString, $listCurrency);
118 public static function getAdminListViewHTML($property, $value, $controlSettings)
120 $explode = is_string($value[
'VALUE']) ? explode(self::SEPARATOR, $value[
'VALUE']) : [];
121 $currentValue = (isset($explode[0]) && $explode[0] !==
'' ? $explode[0] :
'');
122 $currentCurrency = $explode[1] ??
'';
124 if (!$currentCurrency)
125 return is_numeric($currentValue) ? $currentValue :
'';
127 if (CurrencyManager::isCurrencyExist($currentCurrency))
129 if(!empty($controlSettings[
'MODE']))
131 switch($controlSettings[
'MODE'])
134 return $value[
'VALUE'];
135 case 'ELEMENT_TEMPLATE':
137 return $currentValue;
141 list($currentValue, $currentCurrency, $decimalsValue) = array_values(self::getSeparatedValues($value[
'VALUE']));
142 $currentValue = $currentValue.
'.'.$decimalsValue;
144 return \CCurrencyLang::CurrencyFormat($currentValue, $currentCurrency,
true);
157 public static function checkFields($property, $value)
160 if(empty($value[
'VALUE']))
return $result;
161 $explode = (is_string($value[
'VALUE']) ? explode(self::SEPARATOR, $value[
'VALUE']) : []);
162 $currentValue = (isset($explode[0]) && $explode[0] !==
'' ? $explode[0] :
'');
163 $currentCurrency = ($explode[1] ??
'');
165 if(!$currentCurrency)
166 return is_numeric($currentValue) ? $result : array(
Loc::getMessage(
'CIMP_FORMAT_ERROR'));
168 if(CurrencyManager::isCurrencyExist($currentCurrency))
170 $format = \CCurrencyLang::GetFormatDescription($currentCurrency);
171 $decPoint = $format[
'DEC_POINT'];
172 $thousandsSep = $format[
'THOUSANDS_SEP'];
173 $decimals = $format[
'DECIMALS'];
174 $regExp =
'/^\d{1,3}(('.$thousandsSep.
'){0,1}\d{3})*(\\'.$decPoint.
'\d{1,'.$decimals.
'})?$/';
175 if($thousandsSep && $decPoint)
179 $regExp =
'/^\d{1,3}(('.$thousandsSep.
'){0,1}\d{3})*(\\'.$decPoint.
'\d{1,'.$decimals.
'})?$/';
183 $regExp =
'/^\d{1,3}(('.$thousandsSep.
'){0,1}\d{3})*$/';
186 elseif($thousandsSep && !$decPoint)
188 $regExp =
'/^\d{1,3}(('.$thousandsSep.
'){0,1}\d{3})*$/';
190 elseif(!$thousandsSep && $decPoint)
194 $regExp =
'/^[0-9]*(\\'.$decPoint.
'\d{1,'.$decimals.
'})?$/';
198 $regExp =
'/^[0-9]*$/';
201 elseif(!$thousandsSep && !$decPoint)
203 $regExp =
'/^[0-9]*$/';
205 if(!preg_match($regExp, $currentValue))
225 public static function getLength($property, $value)
227 return mb_strlen(trim($value[
'VALUE'],
"\n\r\t"));
237 public static function convertToDB($property, $value)
249 public static function convertFromDB($property, $value)
254 public static function getSeparatedValues($value)
256 $explode = is_string($value) ? explode(self::SEPARATOR, $value) : [];
257 $currentValue = (isset($explode[0]) && $explode[0] !==
'' ? $explode[0] :
'');
258 $currentCurrency = $explode[1] ??
'';
259 $format = \CCurrencyLang::GetFormatDescription($currentCurrency);
260 $explode = explode($format[
'DEC_POINT'], $currentValue);
261 $currentValue = ($explode[0] !==
'' ? $explode[0] :
'');
262 $decimalsValue = $explode[1] ??
'';
264 'AMOUNT' => $currentValue,
265 'CURRENCY' => $currentCurrency,
266 'DECIMALS' => $decimalsValue
270 public static function getUnitedValue($amount,
string $currency): string
275 :
$amount.self::SEPARATOR.$currency
288 public static function addFilterFields($property, $controlSettings, &$filter, &$filtered)
292 if(isset($_REQUEST[$controlSettings[
'VALUE']]))
294 $value = $_REQUEST[$controlSettings[
'VALUE']];
296 elseif(isset($controlSettings[
"FILTER_ID"]))
298 $filterOption = new \Bitrix\Main\UI\Filter\Options($controlSettings[
"FILTER_ID"]);
299 $filterData = $filterOption->getFilter();
300 if(!empty($filterData[$controlSettings[
'VALUE']]))
301 $value = $filterData[$controlSettings[
'VALUE']];
306 $explode = explode(self::SEPARATOR, $value);
307 if(empty($explode[1]))
309 $listCurrency = self::getListCurrency();
312 $filter[$controlSettings[
'VALUE']] = array();
313 foreach($listCurrency as $currencyType => $currency)
315 $filter[$controlSettings[
'VALUE']][] = $value.self::SEPARATOR.$currencyType;
323 protected static function getListCurrency(): array
325 return Editor::getListCurrency();
328 protected static function getJsHandlerSelector($randString, array $listCurrency)
333 if (!window.BX && top.BX)
335 BX.ready(
function() {
337 if(!
BX.HandlerMoneyField) {
338 BX.HandlerMoneyField = function(params) {
339 this.randomString = params.randomString;
340 this.listCurrency = params.listCurrency;
341 this.defaultSeparator = params.defaultSeparator;
342 setTimeout(BX.proxy(this.setDefaultParams, this), 300);
344 BX.HandlerMoneyField.prototype.setDefaultParams =
function() {
345 this.input =
BX(
'input-' + this.randomString);
346 this.hidden =
BX(
'hidden-' + this.randomString);
347 this.selector =
BX(
'selector-' + this.randomString);
348 this.error =
BX(
'error-' + this.randomString);
351 BX.HandlerMoneyField.prototype.init =
function() {
352 if (!this.input || !this.selector || !this.hidden)
return;
353 this.currentCurrency = this.selector.value;
354 this.currentFormat =
null;
356 'currentCurrency': this.currentCurrency,
357 'decPoint': this.listCurrency[this.currentCurrency].DEC_POINT,
358 'thousandsSep': this.listCurrency[this.currentCurrency].SEPARATOR
360 this.availableKey = [8,9,13,36,37,39,38,40,46,18,17,16,188,190,86,65,112,113,114,115,116,117,118,
361 119,120,121,122,123,67,45,34,33,35];
362 this.availableSymbol = [];
363 this.changeCurrency();
364 BX.bind(this.selector,
'change',
BX.proxy(
this.changeCurrency,
this));
365 BX.bind(this.input,
'keydown',
BX.proxy(
function(event) {this.onkeydown(event)},
this));
366 BX.bind(this.input,
'keyup',
BX.proxy(
this.onkeyup,
this));
367 BX.bind(this.input,
'blur',
BX.proxy(
this.onblur,
this));
368 BX.bind(this.input,
'focus',
BX.proxy(
this.onfocus,
this));
369 BX.addCustomEvent(window,
'onAddNewRowBeforeInner',
BX.proxy(
function(htmlObject) {
370 var regExp = new RegExp(
'data-id=".+?"',
'g'), oldId;
371 var match = htmlObject.html.match(regExp);
372 if(match) match = match[0].match(/
"([^"]*)
"/i);
373 if(match) oldId = match[1];
374 if(this.randomString == oldId)
376 var newId = BX.util.getRandomString(6).toLowerCase();
377 htmlObject.html = htmlObject.html.replace(new RegExp(oldId, 'g'), newId);
381 BX.HandlerMoneyField.prototype.changeCurrency = function() {
382 this.currentCurrency = (BX.proxy_context && BX.proxy_context.value) ?
383 BX.proxy_context.value : this.selector.value;
384 this.currentFormat = this.listCurrency[this.currentCurrency].FORMAT;
385 this.decPoint = this.listCurrency[this.currentCurrency].DEC_POINT;
386 this.thousandsSep = this.listCurrency[this.currentCurrency].SEPARATOR;
387 this.decimals = this.listCurrency[this.currentCurrency].DECIMALS;
390 var regExp = this.oldFormat.thousandsSep ? '\\'+this.oldFormat.thousandsSep : '';
391 this.realValue = this.realValue.replace(new RegExp(regExp, 'g'),'');
392 this.input.value = this.oldFormat.decPoint ?
393 this.realValue.replace(this.oldFormat.decPoint,this.decPoint) : this.realValue;
396 'currentCurrency': this.currentCurrency,
397 'decPoint': this.decPoint,
398 'thousandsSep': this.thousandsSep
401 this.setVisibleValue(true);
402 this.setHiddenValue();
404 for(var i = 1; i <= this.decimals; i++)
406 this.availableSymbol.push(this.thousandsSep);
407 this.availableSymbol.push(this.decPoint);
409 this.isDecimalsNull = (!parseInt(this.decimals));
410 if(this.thousandsSep && this.decPoint)
412 if (this.isDecimalsNull)
414 this.regExp = '^\\d{1,3}('+this.thousandsSep+'?\\d{3})*$';
415 this.exampleValue = '6'+this.thousandsSep+'456';
419 this.regExp = '^\\d{1,3}('+this.thousandsSep+
420 '?\\d{3})*(\\'+this.decPoint+'\\d{1,'+this.decimals+'})?$';
421 this.exampleValue = '6'+this.thousandsSep+'456'+this.decPoint+decimals;
424 else if(this.thousandsSep && !this.decPoint)
426 this.regExp = '^\\d{1,3}('+this.thousandsSep+'?\\d{3})*$';
427 this.exampleValue = '6'+this.thousandsSep+'456';
429 else if(!this.thousandsSep && this.decPoint)
431 if (this.isDecimalsNull)
433 this.regExp = '^[0-9]*$';
434 this.exampleValue = '6456';
438 this.regExp = '^[0-9]*(\\'+this.decPoint+'\\d{1,'+this.decimals+'})?$';
439 this.exampleValue = '6456'+this.decPoint+decimals;
442 else if(!this.thousandsSep && !this.decPoint)
444 this.regExp = '^[0-9]*$';
445 this.exampleValue = '6456';
447 this.checkFormatValue();
449 BX.HandlerMoneyField.prototype.onkeydown = function(event) {
451 if (!BX.util.in_array(event.key, this.availableSymbol)
452 && !BX.util.in_array(event.keyCode,this.availableKey)) {
453 if (isNaN(parseInt(event.key))) {
454 this.setTextError(BX.message('CIMP_INPUT_FORMAT_ERROR')
455 .replace('#example#', this.exampleValue));
456 return BX.PreventDefault(event);
460 BX.HandlerMoneyField.prototype.onkeyup = function() {
462 this.setHiddenValue();
463 this.setVisibleValue(true);
465 BX.HandlerMoneyField.prototype.onblur = function() {
467 this.setHiddenValue();
468 this.setVisibleValue(true);
470 this.checkFormatValue();
472 BX.HandlerMoneyField.prototype.onfocus = function() {
473 this.input.value = this.realValue;
475 BX.HandlerMoneyField.prototype.getTemplateValue = function() {
476 return this.currentFormat.replace(new RegExp('(^|[^&])#'), '$1' + this.realValue);
478 BX.HandlerMoneyField.prototype.checkFormatValue = function() {
479 if(!this.realValue) return;
480 var regExp = new RegExp(this.regExp);
481 if(!this.realValue.match(regExp))
483 this.setTextError(BX.message('CIMP_INPUT_FORMAT_ERROR')
484 .replace('#example#', this.exampleValue));
491 BX.HandlerMoneyField.prototype.setTextError = function(text) {
492 this.error.innerHTML = text ? text : '';
494 BX.HandlerMoneyField.prototype.setRealValue = function() {
495 this.realValue = this.getFormatValue();
497 BX.HandlerMoneyField.prototype.setVisibleValue = function(useTemplate) {
498 this.input.value = this.input.value ? !useTemplate ?
499 this.getTemplateValue() : this.getFormatValue(): '';
501 BX.HandlerMoneyField.prototype.setHiddenValue = function() {
502 if (this.input.value)
504 var regExp = this.thousandsSep ? '\\'+this.thousandsSep : '';
505 this.hidden.value = this.realValue.replace(new RegExp(regExp, 'g'), '') +
506 this.defaultSeparator + this.currentCurrency;
510 this.hidden.value = '';
513 BX.HandlerMoneyField.prototype.getFormatValue = function() {
514 var baseValue = this.input.value;
515 var valueLength = baseValue.length;
516 var formatValue = "";
518 if(this.thousandsSep == ',' || this.thousandsSep == '.')
519 regExp = new RegExp('['+this.decPoint+']');
521 regExp = new RegExp('['+this.decPoint+',.]');
522 var decPointPosition = baseValue.match(regExp);
523 decPointPosition = decPointPosition == null ? baseValue.length : decPointPosition.index;
525 for(var i = 0; i < baseValue.length; i++)
527 var symbolPosition = baseValue.length-1-i;
528 var symbol = baseValue.charAt(symbolPosition);
529 var isDigit = ('0123456789'.indexOf(symbol) >= 0);
530 if(isDigit) countDigit++;
531 if(symbolPosition == decPointPosition) countDigit = 0;
532 if(symbolPosition >= decPointPosition)
534 if(this.decPoint == '.' && symbol == ',')
535 symbol = this.decPoint;
536 if(this.decPoint == ',' && symbol == '.')
537 symbol = this.decPoint;
538 if(isDigit || (symbolPosition == decPointPosition && symbol == this.decPoint))
539 formatValue = symbol + formatValue;
541 if(valueLength > symbolPosition) valueLength--;
543 if(symbolPosition < decPointPosition)
546 formatValue = symbol+formatValue;
548 if(valueLength > symbolPosition) valueLength--;
549 if(isDigit && countDigit % 3 == 0 && countDigit !== 0 && symbolPosition !== 0)
551 formatValue = this.thousandsSep + formatValue;
552 if(valueLength >= symbolPosition) valueLength++;
556 if(this.decimals > 0)
558 decPointPosition = formatValue.match(new RegExp('['+this.decPoint+']'));
559 decPointPosition = decPointPosition == null ? formatValue.length : decPointPosition.index;
560 while(formatValue.length-1-decPointPosition > this.decimals)
562 if(valueLength >= formatValue.length-1) valueLength--;
563 formatValue = formatValue.substr(0, formatValue.length - 1);
570 var handlerMoneyField = new BX.HandlerMoneyField({
571 randomString: '<?=$randString?>',
572 defaultSeparator: '<?=self::SEPARATOR?>',
573 listCurrency: <?=Json::encode($listCurrency)?>
576 CIMP_INPUT_FORMAT_ERROR: '<?=GetMessageJS('CIMP_INPUT_FORMAT_ERROR')?>'
581 $script = ob_get_contents();
586 public static function GetUIEntityEditorProperty($settings, $value)
588 if (method_exists(BaseForm::class, 'getAdditionalMoneyValues'))
591 'type' => $settings['MULTIPLE'] === 'Y' ? 'multimoney' : 'money',
static getMessage($code, $replace=null, $language=null)