21 private const ENTITY_CATALOG_IBLOCK =
'CATALOG_IBLOCK';
22 private const ENTITY_VAT =
'VAT';
27 public const FIELD_ALLOWED_ALL = self::FIELD_ALLOWED_SELECT|self::FIELD_ALLOWED_FILTER|self::FIELD_ALLOWED_ORDER;
29 private const FIELD_PATTERN_OLD_STORE =
'/^CATALOG_STORE_AMOUNT_([0-9]+)$/';
30 private const FIELD_PATTERN_OLD_PRICE_ROW =
'/^CATALOG_GROUP_([0-9]+)$/';
31 private const FIELD_PATTERN_OLD_PRICE =
'/^CATALOG_([A-Z][A-Z_]+)+_([0-9]+)$/';
32 private const FIELD_PATTERN_OLD_PRODUCT =
'/^CATALOG_([A-Z][A-Z_]+)$/';
33 private const FIELD_PATTERN_FLAT_ENTITY =
'/^([A-Z][A-Z_]+)$/';
34 private const FIELD_PATTERN_SEPARATE_ENTITY =
'/^([A-Z][A-Z_]+)_([1-9][0-9]*)$/';
35 private const FIELD_PATTERN_PRODUCT_USER_FIELD =
'/^PRODUCT_(UF_[A-Z0-9_]+)$/';
37 private const ENTITY_TYPE_FLAT = 0x0001;
38 private const ENTITY_TYPE_SEPARATE = 0x0002;
40 private const FIELD_TYPE_INT =
'int';
41 private const FIELD_TYPE_FLOAT =
'float';
42 private const FIELD_TYPE_CHAR =
'char';
43 private const FIELD_TYPE_STRING =
'string';
45 private static array $entityDescription = [];
47 private static array $entityFields = [];
49 private static array $options = [];
78 $query = self::prepareQuery($parameters, $options);
97 $field = strtoupper($field);
103 self::initEntityDescription();
104 self::initEntityFields();
108 if (preg_match(self::FIELD_PATTERN_OLD_STORE, $field, $prepared))
112 if (preg_match(self::FIELD_PATTERN_OLD_PRICE_ROW, $field, $prepared))
116 if (preg_match(self::FIELD_PATTERN_OLD_PRICE, $field, $prepared))
120 if (preg_match(self::FIELD_PATTERN_OLD_PRODUCT, $field, $prepared))
124 if (preg_match(self::FIELD_PATTERN_SEPARATE_ENTITY, $field, $prepared))
126 if (self::searchFieldEntity($prepared[1], self::ENTITY_TYPE_SEPARATE))
131 if (preg_match(self::FIELD_PATTERN_FLAT_ENTITY, $field, $prepared))
133 if (self::searchFieldEntity($prepared[1], self::ENTITY_TYPE_FLAT))
138 if (preg_match(self::FIELD_PATTERN_PRODUCT_USER_FIELD, $field, $prepared))
152 self::initEntityDescription();
153 self::initEntityFields();
155 $prepareField = self::parseField($field);
156 if (!self::checkPreparedField($prepareField))
160 if (!self::checkAllowedAction($prepareField[
'ALLOWED'], self::FIELD_ALLOWED_FILTER))
165 $description = self::getFieldDescription($prepareField[
'ENTITY'], $prepareField[
'FIELD']);
185 return self::isEntityFilterField(
188 self::ENTITY_PRODUCT =>
true,
189 self::ENTITY_PRODUCT_USER_FIELD =>
true,
190 self::ENTITY_OLD_PRODUCT =>
true,
191 self::ENTITY_OLD_PRICE =>
true,
192 self::ENTITY_PRICE =>
true,
193 self::ENTITY_FLAT_PRICE =>
true,
194 self::ENTITY_WARENHOUSE =>
true,
195 self::ENTITY_FLAT_WAREHNOUSE =>
true,
196 self::ENTITY_FLAT_BARCODE =>
true,
197 self::ENTITY_OLD_STORE =>
true,
208 return self::isEntityFilterField(
211 self::ENTITY_PRODUCT =>
true,
212 self::ENTITY_PRODUCT_USER_FIELD =>
true,
213 self::ENTITY_OLD_PRODUCT =>
true,
224 return self::isEntityFilterField(
227 self::ENTITY_OLD_PRICE =>
true,
228 self::ENTITY_PRICE =>
true,
229 self::ENTITY_FLAT_PRICE =>
true,
240 return self::isEntityFilterField(
243 self::ENTITY_WARENHOUSE =>
true,
244 self::ENTITY_FLAT_WAREHNOUSE =>
true,
245 self::ENTITY_OLD_STORE =>
true,
260 self::initEntityDescription();
261 self::initEntityFields();
263 if (empty(
$order) || empty($options[
'QUANTITY']))
268 foreach (array_keys(
$order) as $field)
270 $prepareField = self::parseField($field);
271 if (!self::checkPreparedField($prepareField))
275 switch ($prepareField[
'ENTITY'])
277 case self::ENTITY_OLD_PRICE:
279 $prepareField[
'FIELD'] ===
'PRICE'
280 || $prepareField[
'FIELD'] ===
'PRICE_SCALE'
281 || $prepareField[
'FIELD'] ===
'CURRENCY'
284 $filterFieldDescription = [
285 'ENTITY' => $prepareField[
'ENTITY'],
286 'FIELD' =>
'SHOP_QUANTITY',
287 'ENTITY_ID' => $prepareField[
'ENTITY_ID'],
289 $filterField = self::getField($filterFieldDescription, []);
290 if (!empty($filterField))
292 $filterField = $filterField[
'ALIAS'];
293 if (!isset(
$result[$filterField]))
295 $result[$filterField] = $options[
'QUANTITY'];
298 unset($filterField, $filterFieldDescription);
301 case self::ENTITY_PRICE:
302 case self::ENTITY_FLAT_PRICE:
304 $prepareField[
'FIELD'] ===
'PRICE'
305 || $prepareField[
'FIELD'] ===
'SCALED_PRICE'
306 || $prepareField[
'FIELD'] ===
'CURRENCY'
309 $filterFieldDescription = [
310 'ENTITY' => $prepareField[
'ENTITY'],
311 'FIELD' =>
'QUANTITY_RANGE_FILTER',
312 'ENTITY_ID' => $prepareField[
'ENTITY_ID'],
314 $filterField = self::getField($filterFieldDescription, []);
315 if (!empty($filterField))
317 $filterField = $filterField[
'ALIAS'];
318 if (!isset(
$result[$filterField]))
320 $result[$filterField] = $options[
'QUANTITY'];
323 unset($filterField, $filterFieldDescription);
339 self::initEntityDescription();
340 self::initEntityFields();
342 $prepareField = self::parseField($field);
343 if (!self::checkPreparedField($prepareField))
347 if (!self::checkAllowedAction($prepareField[
'ALLOWED'], $useMode))
353 switch ($prepareField[
'ENTITY'])
355 case self::ENTITY_OLD_PRODUCT:
356 $newEntity = self::ENTITY_PRODUCT;
358 case self::ENTITY_OLD_PRICE:
359 $newEntity = self::ENTITY_PRICE;
361 case self::ENTITY_OLD_STORE:
362 $newEntity = self::ENTITY_WARENHOUSE;
365 if ($newEntity ===
null)
370 $description = self::getFieldDescription($prepareField[
'ENTITY'], $prepareField[
'FIELD']);
376 if ($useMode === self::FIELD_ALLOWED_ORDER)
389 'ENTITY' => $newEntity,
390 'ENTITY_ID' => $prepareField[
'ENTITY_ID'],
391 'FIELD' =>
$description[
'NEW_ID'] ?? $prepareField[
'FIELD'],
393 unset($newEntity, $prepareField);
395 $description = self::getFieldDescription($newField[
'ENTITY'], $newField[
'FIELD']);
401 return str_replace(
'#ENTITY_ID#', $newField[
'ENTITY_ID'],
$description[
'ALIAS']);
417 foreach (
$select as $index => $field)
420 $result[$index] = $newField ===
null ? $field : $newField;
422 unset($newField, $index, $field);
440 foreach (
$filter as $field => $value)
442 if (is_object($value))
446 elseif (is_numeric($field))
456 $filterItem = \CIBlock::MkOperationFilter($field);
457 if ($filterItem[
'FIELD'] ===
'SUBQUERY')
461 && isset($value[
'FIELD'])
462 && isset($value[
'FILTER'])
463 && is_array($value[
'FILTER'])
473 if ($newField !==
null)
475 $result[$filterItem[
'PREFIX'] . $newField] = $value;
486 unset($filed, $value);
505 if ($newField ===
null)
529 self::initEntityDescription();
530 self::initEntityFields();
532 if (!isset(self::$entityFields[
$entity]))
539 isset($options[
'ENTITY_ID'])
540 && (is_string($options[
'ENTITY_ID']) || is_int($options[
'ENTITY_ID']))
543 $entityId = (string)$options[
'ENTITY_ID'];
547 foreach (self::$entityFields[
$entity] as $field)
559 private static function initEntityDescription(): void
561 if (!empty(self::$entityDescription))
566 self::$entityDescription = [
567 self::ENTITY_PRODUCT => [
568 'NAME' =>
'b_catalog_product',
570 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.ID = #ELEMENT#.ID)'
572 self::ENTITY_PRODUCT_USER_FIELD => [
574 'HANDLER' => [__CLASS__,
'handleProductUserFields'],
576 self::ENTITY_PRICE => [
577 'NAME' =>
'b_catalog_price',
578 'ALIAS' =>
'PRC_#ENTITY_ID#',
579 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID and #ALIAS#.CATALOG_GROUP_ID = #ENTITY_ID##JOIN_MODIFY#)'
581 self::ENTITY_WARENHOUSE => [
582 'NAME' =>
'b_catalog_store_product',
583 'ALIAS' =>
'WHS_#ENTITY_ID#',
584 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID and #ALIAS#.STORE_ID = #ENTITY_ID#)'
586 self::ENTITY_FLAT_PRICE => [
587 'NAME' =>
'b_catalog_price',
589 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID#JOIN_MODIFY#)'
591 self::ENTITY_FLAT_WAREHNOUSE => [
592 'NAME' =>
'b_catalog_store_product',
594 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID)'
596 self::ENTITY_FLAT_BARCODE => [
597 'NAME' =>
'b_catalog_store_barcode',
599 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID)'
601 self::ENTITY_OLD_PRODUCT => [
602 'NAME' =>
'b_catalog_product',
604 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.ID = #ELEMENT#.ID)'
606 self::ENTITY_OLD_PRICE => [
607 'NAME' =>
'b_catalog_price',
608 'ALIAS' =>
'CAT_P#ENTITY_ID#',
609 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID and #ALIAS#.CATALOG_GROUP_ID = #ENTITY_ID##JOIN_MODIFY#)'
611 self::ENTITY_OLD_STORE => [
612 'NAME' =>
'b_catalog_store_product',
613 'ALIAS' =>
'CAT_SP#ENTITY_ID#',
614 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.PRODUCT_ID = #ELEMENT#.ID and #ALIAS#.STORE_ID = #ENTITY_ID#)'
616 self::ENTITY_CATALOG_IBLOCK => [
617 'NAME' =>
'b_catalog_iblock',
619 'JOIN' =>
'left join #NAME# as #ALIAS# on ((CAT_PR.VAT_ID IS NULL or CAT_PR.VAT_ID = 0) and #ALIAS#.IBLOCK_ID = #ELEMENT#.IBLOCK_ID)'
621 self::ENTITY_VAT => [
622 'NAME' =>
'b_catalog_vat',
623 'ALIAS' =>
'CAT_VAT',
624 'JOIN' =>
'left join #NAME# as #ALIAS# on (#ALIAS#.ID = CASE WHEN (CAT_PR.VAT_ID IS NULL OR CAT_PR.VAT_ID = 0) THEN CAT_IB.VAT_ID ELSE CAT_PR.VAT_ID END)',
625 'RELATION' => [self::ENTITY_CATALOG_IBLOCK]
633 private static function initEntityFields(): void
635 if (!empty(self::$entityFields))
640 self::$entityFields = [
641 self::ENTITY_PRODUCT => self::getProductFields(),
642 self::ENTITY_PRODUCT_USER_FIELD => self::getProductUserFields(),
643 self::ENTITY_PRICE => self::getPriceFields(),
644 self::ENTITY_WARENHOUSE => self::getWarenhouseFields(),
645 self::ENTITY_FLAT_PRICE => self::getFlatPriceFields(),
646 self::ENTITY_FLAT_WAREHNOUSE => self::getFlatWarenhouseFields(),
647 self::ENTITY_FLAT_BARCODE => self::getFlatBarcodeFields(),
648 self::ENTITY_OLD_PRODUCT => self::getOldProductFields(),
649 self::ENTITY_OLD_PRICE => self::getOldPriceFields(),
650 self::ENTITY_OLD_STORE => self::getOldStoreFields(),
651 self::ENTITY_VAT => self::getVatFields(),
658 private static function getProductFields():
array
664 'TYPE' => self::FIELD_TYPE_INT,
665 'ALLOWED' => self::FIELD_ALLOWED_ALL,
668 'NAME' =>
'AVAILABLE',
669 'ALIAS' =>
'AVAILABLE',
670 'TYPE' => self::FIELD_TYPE_CHAR,
671 'ALLOWED' => self::FIELD_ALLOWED_ALL,
672 'ORDER_DEFAULT' =>
'DESC',
677 'TYPE' => self::FIELD_TYPE_CHAR,
678 'ALLOWED' => self::FIELD_ALLOWED_ALL,
681 'NAME' =>
'QUANTITY',
682 'ALIAS' =>
'QUANTITY',
683 'TYPE' => self::FIELD_TYPE_FLOAT,
684 'ALLOWED' => self::FIELD_ALLOWED_ALL,
686 'QUANTITY_RESERVED' => [
687 'NAME' =>
'QUANTITY_RESERVED',
688 'ALIAS' =>
'QUANTITY_RESERVED',
689 'TYPE' => self::FIELD_TYPE_FLOAT,
690 'ALLOWED' => self::FIELD_ALLOWED_ALL,
692 'QUANTITY_TRACE' => [
693 'NAME' =>
'QUANTITY_TRACE',
694 'ALIAS' =>
'QUANTITY_TRACE',
695 'TYPE' => self::FIELD_TYPE_CHAR,
696 'ALLOWED' => self::FIELD_ALLOWED_ALL,
697 'SELECT_EXPRESSION' => [__CLASS__,
'selectQuantityTrace'],
698 'FILTER_PREPARE_VALUE_EXPRESSION' => [__CLASS__,
'prepareFilterQuantityTrace'],
700 'QUANTITY_TRACE_RAW' => [
701 'NAME' =>
'QUANTITY_TRACE',
702 'ALIAS' =>
'QUANTITY_TRACE_RAW',
703 'TYPE' => self::FIELD_TYPE_CHAR,
704 'ALLOWED' => self::FIELD_ALLOWED_ALL,
707 'NAME' =>
'CAN_BUY_ZERO',
708 'ALIAS' =>
'CAN_BUY_ZERO',
709 'TYPE' => self::FIELD_TYPE_CHAR,
710 'ALLOWED' => self::FIELD_ALLOWED_ALL,
711 'SELECT_EXPRESSION' => [__CLASS__,
'selectCanBuyZero'],
712 'FILTER_PREPARE_VALUE_EXPRESSION' => [__CLASS__,
'prepareFilterCanBuyZero'],
714 'CAN_BUY_ZERO_RAW' => [
715 'NAME' =>
'CAN_BUY_ZERO',
716 'ALIAS' =>
'CAN_BUY_ZERO_RAW',
717 'TYPE' => self::FIELD_TYPE_CHAR,
718 'ALLOWED' => self::FIELD_ALLOWED_ALL,
721 'NAME' =>
'SUBSCRIBE',
722 'ALIAS' =>
'SUBSCRIBE',
723 'TYPE' => self::FIELD_TYPE_CHAR,
724 'ALLOWED' => self::FIELD_ALLOWED_ALL,
725 'SELECT_EXPRESSION' => [__CLASS__,
'selectSubscribe'],
726 'FILTER_PREPARE_VALUE_EXPRESSION' => [__CLASS__,
'prepareFilterSubscribe'],
729 'NAME' =>
'SUBSCRIBE',
730 'ALIAS' =>
'SUBSCRIBE_RAW',
731 'TYPE' => self::FIELD_TYPE_CHAR,
732 'ALLOWED' => self::FIELD_ALLOWED_ALL,
737 'TYPE' => self::FIELD_TYPE_INT,
738 'ALLOWED' => self::FIELD_ALLOWED_ALL,
741 'NAME' =>
'VAT_INCLUDED',
742 'ALIAS' =>
'VAT_INCLUDED',
743 'TYPE' => self::FIELD_TYPE_CHAR,
744 'ALLOWED' => self::FIELD_ALLOWED_ALL,
746 'PURCHASING_PRICE' => [
747 'NAME' =>
'PURCHASING_PRICE',
748 'ALIAS' =>
'PURCHASING_PRICE',
749 'TYPE' => self::FIELD_TYPE_FLOAT,
750 'ALLOWED' => self::FIELD_ALLOWED_ALL,
752 'PURCHASING_CURRENCY' => [
753 'NAME' =>
'PURCHASING_CURRENCY',
754 'ALIAS' =>
'PURCHASING_CURRENCY',
755 'TYPE' => self::FIELD_TYPE_CHAR,
756 'ALLOWED' => self::FIELD_ALLOWED_ALL,
759 'NAME' =>
'BARCODE_MULTI',
760 'ALIAS' =>
'BARCODE_MULTI',
761 'TYPE' => self::FIELD_TYPE_CHAR,
762 'ALLOWED' => self::FIELD_ALLOWED_ALL,
767 'TYPE' => self::FIELD_TYPE_FLOAT,
768 'ALLOWED' => self::FIELD_ALLOWED_ALL,
773 'TYPE' => self::FIELD_TYPE_FLOAT,
774 'ALLOWED' => self::FIELD_ALLOWED_ALL,
779 'TYPE' => self::FIELD_TYPE_FLOAT,
780 'ALLOWED' => self::FIELD_ALLOWED_ALL,
785 'TYPE' => self::FIELD_TYPE_FLOAT,
786 'ALLOWED' => self::FIELD_ALLOWED_ALL,
790 'ALIAS' =>
'MEASURE',
791 'TYPE' => self::FIELD_TYPE_INT,
792 'ALLOWED' => self::FIELD_ALLOWED_ALL,
795 'NAME' =>
'PRICE_TYPE',
796 'ALIAS' =>
'PAYMENT_TYPE',
797 'TYPE' => self::FIELD_TYPE_CHAR,
798 'ALLOWED' => self::FIELD_ALLOWED_ALL,
800 'RECUR_SCHEME_LENGTH' => [
801 'NAME' =>
'RECUR_SCHEME_LENGTH',
802 'ALIAS' =>
'RECUR_SCHEME_LENGTH',
803 'TYPE' => self::FIELD_TYPE_INT,
804 'ALLOWED' => self::FIELD_ALLOWED_ALL,
806 'RECUR_SCHEME_TYPE' => [
807 'NAME' =>
'RECUR_SCHEME_TYPE',
808 'ALIAS' =>
'RECUR_SCHEME_TYPE',
809 'TYPE' => self::FIELD_TYPE_CHAR,
810 'ALLOWED' => self::FIELD_ALLOWED_ALL,
812 'TRIAL_PRICE_ID' => [
813 'NAME' =>
'TRIAL_PRICE_ID',
814 'ALIAS' =>
'TRIAL_PRICE_ID',
815 'TYPE' => self::FIELD_TYPE_INT,
816 'ALLOWED' => self::FIELD_ALLOWED_ALL,
819 'NAME' =>
'WITHOUT_ORDER',
820 'ALIAS' =>
'WITHOUT_ORDER',
821 'TYPE' => self::FIELD_TYPE_CHAR,
822 'ALLOWED' => self::FIELD_ALLOWED_ALL,
830 private static function getProductUserFields():
array
834 $iterator = Main\UserFieldTable::getList([
845 '=ENTITY_ID' => Bitrix\Catalog\ProductTable::getUfId(),
857 if (!self::isValidProductUserField($row))
862 'NAME' => $row[
'FIELD_NAME'],
863 'ALIAS' =>
'PRODUCT_'.$row[
'FIELD_NAME'],
864 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
867 $result[$row[
'FIELD_NAME']] = $item;
880 private static function getPriceFields():
array
885 'ALIAS' =>
'PRICE_#ENTITY_ID#',
886 'TYPE' => self::FIELD_TYPE_FLOAT,
887 'ALLOWED' => self::FIELD_ALLOWED_ALL,
888 'ORDER_NULLABLE' =>
true,
891 'NAME' =>
'CURRENCY',
892 'ALIAS' =>
'CURRENCY_#ENTITY_ID#',
893 'TYPE' => self::FIELD_TYPE_CHAR,
894 'ALLOWED' => self::FIELD_ALLOWED_ALL,
895 'ORDER_NULLABLE' =>
true,
898 'NAME' =>
'QUANTITY_FROM',
899 'ALIAS' =>
'QUANTITY_FROM_#ENTITY_ID#',
900 'TYPE' => self::FIELD_TYPE_INT,
901 'ALLOWED' => self::FIELD_ALLOWED_ALL,
902 'ORDER_NULLABLE' =>
true,
905 'NAME' =>
'QUANTITY_TO',
906 'ALIAS' =>
'QUANTITY_TO_#ENTITY_ID#',
907 'TYPE' => self::FIELD_TYPE_INT,
908 'ALLOWED' => self::FIELD_ALLOWED_ALL,
909 'ORDER_NULLABLE' =>
true,
912 'NAME' =>
'PRICE_SCALE',
913 'ALIAS' =>
'SCALED_PRICE_#ENTITY_ID#',
914 'TYPE' => self::FIELD_TYPE_FLOAT,
915 'ALLOWED' => self::FIELD_ALLOWED_ALL,
916 'ORDER_NULLABLE' =>
true,
919 'NAME' =>
'EXTRA_ID',
920 'ALIAS' =>
'EXTRA_ID_#ENTITY_ID#',
921 'TYPE' => self::FIELD_TYPE_INT,
922 'ALLOWED' => self::FIELD_ALLOWED_ALL,
924 'DEFAULT_PRICE_FILTER' => [
927 'ALIAS' =>
'DEFAULT_PRICE_FILTER_#ENTITY_ID#',
929 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
930 'JOIN_MODIFY_EXPRESSION' => [__CLASS__,
'priceParametersFilter'],
932 'QUANTITY_RANGE_FILTER' => [
935 'ALIAS' =>
'QUANTITY_RANGE_FILTER_#ENTITY_ID#',
937 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
938 'JOIN_MODIFY_EXPRESSION' => [__CLASS__,
'priceParametersFilter'],
940 'CURRENCY_FOR_SCALE' => [
943 'ALIAS' =>
'CURRENCY_FOR_SCALE_#ENTITY_ID#',
945 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
946 'FILTER_MODIFY_EXPRESSION' => [__CLASS__,
'filterModifierCurrencyScale'],
954 private static function getFlatPriceFields():
array
958 'NAME' =>
'CATALOG_GROUP_ID',
959 'ALIAS' =>
'PRICE_TYPE',
960 'TYPE' => self::FIELD_TYPE_INT,
961 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
966 'TYPE' => self::FIELD_TYPE_FLOAT,
967 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
968 'ORDER_NULLABLE' =>
true,
971 'NAME' =>
'CURRENCY',
972 'ALIAS' =>
'CURRENCY',
973 'TYPE' => self::FIELD_TYPE_CHAR,
974 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
975 'ORDER_NULLABLE' =>
true,
978 'NAME' =>
'QUANTITY_FROM',
979 'ALIAS' =>
'QUANTITY_FROM',
980 'TYPE' => self::FIELD_TYPE_INT,
981 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
982 'ORDER_NULLABLE' =>
true,
985 'NAME' =>
'QUANTITY_TO',
986 'ALIAS' =>
'QUANTITY_TO',
987 'TYPE' => self::FIELD_TYPE_INT,
988 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
989 'ORDER_NULLABLE' =>
true,
992 'NAME' =>
'PRICE_SCALE',
993 'ALIAS' =>
'SCALED_PRICE',
994 'TYPE' => self::FIELD_TYPE_FLOAT,
995 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
996 'ORDER_NULLABLE' =>
true,
999 'NAME' =>
'EXTRA_ID',
1000 'ALIAS' =>
'EXTRA_ID',
1001 'TYPE' => self::FIELD_TYPE_INT,
1002 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1004 'DEFAULT_PRICE_FILTER' => [
1007 'ALIAS' =>
'DEFAULT_PRICE_FILTER',
1009 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1010 'JOIN_MODIFY_EXPRESSION' => [__CLASS__,
'priceParametersFilter'],
1012 'QUANTITY_RANGE_FILTER' => [
1015 'ALIAS' =>
'QUANTITY_RANGE_FILTER',
1017 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1018 'JOIN_MODIFY_EXPRESSION' => [__CLASS__,
'priceParametersFilter'],
1020 'CURRENCY_FOR_SCALE' => [
1023 'ALIAS' =>
'CURRENCY_FOR_SCALE',
1025 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1026 'FILTER_MODIFY_EXPRESSION' => [__CLASS__,
'filterModifierCurrencyScale'],
1034 private static function getWarenhouseFields():
array
1039 'ALIAS' =>
'STORE_AMOUNT_#ENTITY_ID#',
1040 'TYPE' => self::FIELD_TYPE_FLOAT,
1041 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1042 'ORDER_NULLABLE' =>
true,
1050 private static function getFlatWarenhouseFields():
array
1054 'NAME' =>
'STORE_ID',
1055 'ALIAS' =>
'STORE_NUMBER',
1056 'TYPE' => self::FIELD_TYPE_INT,
1057 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1061 'ALIAS' =>
'STORE_AMOUNT',
1062 'TYPE' => self::FIELD_TYPE_FLOAT,
1063 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1064 'ORDER_NULLABLE' =>
true,
1072 private static function getFlatBarcodeFields():
array
1075 'PRODUCT_BARCODE' => [
1076 'NAME' =>
'BARCODE',
1077 'ALIAS' =>
'PRODUCT_BARCODE',
1078 'TYPE' => self::FIELD_TYPE_STRING,
1079 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1081 'PRODUCT_BARCODE_STORE' => [
1082 'NAME' =>
'STORE_ID',
1083 'ALIAS' =>
'PRODUCT_BARCODE_STORE',
1084 'TYPE' => self::FIELD_TYPE_INT,
1085 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1087 'PRODUCT_BARCODE_ORDER' => [
1088 'NAME' =>
'ORDER_ID',
1089 'ALIAS' =>
'PRODUCT_BARCODE_ORDER',
1090 'TYPE' => self::FIELD_TYPE_INT,
1091 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1099 private static function getOldProductFields():
array
1103 'NAME' =>
'QUANTITY',
1104 'ALIAS' =>
'CATALOG_QUANTITY',
1105 'TYPE' => self::FIELD_TYPE_FLOAT,
1106 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1108 'QUANTITY_TRACE' => [
1109 'NAME' =>
'QUANTITY_TRACE',
1110 'ALIAS' =>
'CATALOG_QUANTITY_TRACE',
1111 'TYPE' => self::FIELD_TYPE_CHAR,
1112 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1113 'SELECT_EXPRESSION' => [__CLASS__,
'selectQuantityTrace'],
1115 'QUANTITY_TRACE_ORIG' => [
1116 'NAME' =>
'QUANTITY_TRACE',
1117 'ALIAS' =>
'CATALOG_QUANTITY_TRACE_ORIG',
1118 'TYPE' => self::FIELD_TYPE_CHAR,
1119 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1120 'NEW_ID' =>
'QUANTITY_TRACE_RAW',
1124 'ALIAS' =>
'CATALOG_WEIGHT',
1125 'TYPE' => self::FIELD_TYPE_FLOAT,
1126 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1130 'ALIAS' =>
'CATALOG_VAT_ID',
1131 'TYPE' => self::FIELD_TYPE_INT,
1132 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1135 'NAME' =>
'VAT_INCLUDED',
1136 'ALIAS' =>
'CATALOG_VAT_INCLUDED',
1137 'TYPE' => self::FIELD_TYPE_CHAR,
1138 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1141 'NAME' =>
'CAN_BUY_ZERO',
1142 'ALIAS' =>
'CATALOG_CAN_BUY_ZERO',
1143 'TYPE' => self::FIELD_TYPE_CHAR,
1144 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1145 'SELECT_EXPRESSION' => [__CLASS__,
'selectCanBuyZero'],
1147 'CAN_BUY_ZERO_ORIG' => [
1148 'NAME' =>
'CAN_BUY_ZERO',
1149 'ALIAS' =>
'CATALOG_CAN_BUY_ZERO_ORIG',
1150 'TYPE' => self::FIELD_TYPE_CHAR,
1151 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1152 'NEW_ID' =>
'CAN_BUY_ZERO_RAW',
1154 'PURCHASING_PRICE' => [
1155 'NAME' =>
'PURCHASING_PRICE',
1156 'ALIAS' =>
'CATALOG_PURCHASING_PRICE',
1157 'TYPE' => self::FIELD_TYPE_FLOAT,
1158 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1160 'PURCHASING_CURRENCY' => [
1161 'NAME' =>
'PURCHASING_CURRENCY',
1162 'ALIAS' =>
'CATALOG_PURCHASING_CURRENCY',
1163 'TYPE' => self::FIELD_TYPE_CHAR,
1164 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1166 'QUANTITY_RESERVED' => [
1167 'NAME' =>
'QUANTITY_RESERVED',
1168 'ALIAS' =>
'CATALOG_QUANTITY_RESERVED',
1169 'TYPE' => self::FIELD_TYPE_FLOAT,
1170 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1173 'NAME' =>
'SUBSCRIBE',
1174 'ALIAS' =>
'CATALOG_SUBSCRIBE',
1175 'TYPE' => self::FIELD_TYPE_CHAR,
1176 'ALLOWED' => self::FIELD_ALLOWED_SELECT|self::FIELD_ALLOWED_FILTER,
1177 'SELECT_EXPRESSION' => [__CLASS__,
'selectSubscribe'],
1178 'FILTER_PREPARE_VALUE_EXPRESSION' => [__CLASS__,
'prepareFilterSubscribe'],
1180 'SUBSCRIBE_ORIG' => [
1181 'NAME' =>
'SUBSCRIBE',
1182 'ALIAS' =>
'CATALOG_SUBSCRIBE_ORIG',
1183 'TYPE' => self::FIELD_TYPE_CHAR,
1184 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1185 'NEW_ID' =>
'SUBSCRIBE_RAW',
1189 'ALIAS' =>
'CATALOG_WIDTH',
1190 'TYPE' => self::FIELD_TYPE_FLOAT,
1191 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1195 'ALIAS' =>
'CATALOG_LENGTH',
1196 'TYPE' => self::FIELD_TYPE_FLOAT,
1197 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1201 'ALIAS' =>
'CATALOG_HEIGHT',
1202 'TYPE' => self::FIELD_TYPE_FLOAT,
1203 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1206 'NAME' =>
'MEASURE',
1207 'ALIAS' =>
'CATALOG_MEASURE',
1208 'TYPE' => self::FIELD_TYPE_INT,
1209 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1213 'ALIAS' =>
'CATALOG_TYPE',
1214 'TYPE' => self::FIELD_TYPE_INT,
1215 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1218 'NAME' =>
'AVAILABLE',
1219 'ALIAS' =>
'CATALOG_AVAILABLE',
1220 'TYPE' => self::FIELD_TYPE_CHAR,
1221 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1222 'ORDER_DEFAULT' =>
'DESC',
1226 'ALIAS' =>
'CATALOG_BUNDLE',
1227 'TYPE' => self::FIELD_TYPE_CHAR,
1228 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1231 'NAME' =>
'PRICE_TYPE',
1232 'ALIAS' =>
'CATALOG_PRICE_TYPE',
1233 'TYPE' => self::FIELD_TYPE_CHAR,
1234 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1235 'NEW_ID' =>
'PAYMENT_TYPE',
1237 'RECUR_SCHEME_LENGTH' => [
1238 'NAME' =>
'RECUR_SCHEME_LENGTH',
1239 'ALIAS' =>
'CATALOG_RECUR_SCHEME_LENGTH',
1240 'TYPE' => self::FIELD_TYPE_INT,
1241 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1243 'RECUR_SCHEME_TYPE' => [
1244 'NAME' =>
'RECUR_SCHEME_TYPE',
1245 'ALIAS' =>
'CATALOG_RECUR_SCHEME_TYPE',
1246 'TYPE' => self::FIELD_TYPE_CHAR,
1247 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1249 'TRIAL_PRICE_ID' => [
1250 'NAME' =>
'TRIAL_PRICE_ID',
1251 'ALIAS' =>
'CATALOG_TRIAL_PRICE_ID',
1252 'TYPE' => self::FIELD_TYPE_INT,
1253 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1255 'WITHOUT_ORDER' => [
1256 'NAME' =>
'WITHOUT_ORDER',
1257 'ALIAS' =>
'CATALOG_WITHOUT_ORDER',
1258 'TYPE' => self::FIELD_TYPE_CHAR,
1259 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1261 'SELECT_BEST_PRICE' => [
1262 'NAME' =>
'SELECT_BEST_PRICE',
1263 'ALIAS' =>
'CATALOG_SELECT_BEST_PRICE',
1264 'TYPE' => self::FIELD_TYPE_CHAR,
1265 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1267 'NEGATIVE_AMOUNT_TRACE' => [
1268 'NAME' =>
'NEGATIVE_AMOUNT_TRACE',
1269 'ALIAS' =>
'CATALOG_NEGATIVE_AMOUNT_TRACE',
1270 'TYPE' => self::FIELD_TYPE_CHAR,
1271 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1272 'SELECT_EXPRESSION' => [__CLASS__,
'selectNegativeAmountTrace'],
1274 'NEGATIVE_AMOUNT_TRACE_ORIG' => [
1275 'NAME' =>
'NEGATIVE_AMOUNT_TRACE',
1276 'ALIAS' =>
'CATALOG_NEGATIVE_AMOUNT_TRACE_ORIG',
1277 'TYPE' => self::FIELD_TYPE_CHAR,
1278 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1286 private static function getOldPriceFields():
array
1291 'ALIAS' =>
'CATALOG_PRICE_ID_#ENTITY_ID#',
1292 'TYPE' => self::FIELD_TYPE_INT,
1293 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1296 'NAME' =>
'PRODUCT_ID',
1298 'TYPE' => self::FIELD_TYPE_INT,
1299 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1301 'CATALOG_GROUP_ID' => [
1302 'NAME' =>
'CATALOG_GROUP_ID',
1303 'ALIAS' =>
'CATALOG_GROUP_ID_#ENTITY_ID#',
1304 'TYPE' => self::FIELD_TYPE_INT,
1305 'ALLOWED' => self::FIELD_ALLOWED_SELECT|self::FIELD_ALLOWED_FILTER,
1309 'ALIAS' =>
'CATALOG_PRICE_#ENTITY_ID#',
1310 'TYPE' => self::FIELD_TYPE_FLOAT,
1311 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1312 'ORDER_TRANSFORM' =>
'PRICE_SCALE',
1315 'NAME' =>
'CURRENCY',
1316 'ALIAS' =>
'CATALOG_CURRENCY_#ENTITY_ID#',
1317 'TYPE' => self::FIELD_TYPE_CHAR,
1318 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1319 'ORDER_NULLABLE' =>
true,
1322 'NAME' =>
'PRICE_SCALE',
1324 'TYPE' => self::FIELD_TYPE_FLOAT,
1325 'ALLOWED' => self::FIELD_ALLOWED_ORDER|self::FIELD_ALLOWED_FILTER,
1326 'ORDER_NULLABLE' =>
true,
1327 'NEW_ID' =>
'SCALED_PRICE',
1329 'QUANTITY_FROM' => [
1330 'NAME' =>
'QUANTITY_FROM',
1331 'ALIAS' =>
'CATALOG_QUANTITY_FROM_#ENTITY_ID#',
1332 'TYPE' => self::FIELD_TYPE_INT,
1333 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1336 'NAME' =>
'QUANTITY_TO',
1337 'ALIAS' =>
'CATALOG_QUANTITY_TO_#ENTITY_ID#',
1338 'TYPE' => self::FIELD_TYPE_INT,
1339 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1342 'NAME' =>
'EXTRA_ID',
1343 'ALIAS' =>
'CATALOG_EXTRA_ID_#ENTITY_ID#',
1344 'TYPE' => self::FIELD_TYPE_INT,
1345 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1347 'CATALOG_GROUP_NAME' => [
1349 'ALIAS' =>
'CATALOG_GROUP_NAME_#ENTITY_ID#',
1350 'TYPE' => self::FIELD_TYPE_STRING,
1351 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1352 'SELECT_EXPRESSION' => [__CLASS__,
'selectPriceTypeName'],
1354 'CATALOG_CAN_ACCESS' => [
1356 'ALIAS' =>
'CATALOG_CAN_ACCESS_#ENTITY_ID#',
1357 'TYPE' => self::FIELD_TYPE_CHAR,
1358 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1359 'SELECT_EXPRESSION' => [__CLASS__,
'selectPriceTypeAllowedView'],
1361 'CATALOG_CAN_BUY' => [
1363 'ALIAS' =>
'CATALOG_CAN_BUY_#ENTITY_ID#',
1364 'TYPE' => self::FIELD_TYPE_CHAR,
1365 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1366 'SELECT_EXPRESSION' => [__CLASS__,
'selectPriceTypeAllowedBuy'],
1368 'CURRENCY_SCALE' => [
1371 'ALIAS' =>
'CATALOG_CURRENCY_SCALE_#ENTITY_ID#',
1373 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1374 'FILTER_MODIFY_EXPRESSION' => [__CLASS__,
'filterModifierCurrencyScale'],
1375 'NEW_ID' =>
'CURRENCY_FOR_SCALE',
1377 'SHOP_QUANTITY' => [
1380 'ALIAS' =>
'CATALOG_SHOP_QUANTITY_#ENTITY_ID#',
1382 'ALLOWED' => self::FIELD_ALLOWED_FILTER,
1383 'JOIN_MODIFY_EXPRESSION' => [__CLASS__,
'priceParametersFilter'],
1384 'NEW_ID' =>
'DEFAULT_PRICE_FILTER',
1392 private static function getOldStoreFields():
array
1397 'ALIAS' =>
'CATALOG_STORE_AMOUNT_#ENTITY_ID#',
1398 'TYPE' => self::FIELD_TYPE_FLOAT,
1399 'ALLOWED' => self::FIELD_ALLOWED_ALL,
1400 'ORDER_NULLABLE' =>
true,
1408 private static function getVatFields():
array
1413 'ALIAS' =>
'CATALOG_VAT',
1414 'TYPE' => self::FIELD_TYPE_FLOAT,
1415 'ALLOWED' => self::FIELD_ALLOWED_SELECT,
1426 if ($userField[
'USER_TYPE_ID'] ===
Main\
UserField\Types\FileType::USER_TYPE_ID)
1439 private static function getFieldAllowed(
string $entity,
string $field): int
1441 if (!isset(self::$entityFields[
$entity][$field]))
1443 if (!isset(self::$entityFields[
$entity][$field][
'ALLOWED']))
1445 return (self::$entityFields[
$entity][$field][
'ALLOWED']);
1452 private static function getFieldsAllowedToSelect(
string $entity): ?
array
1456 return (isset($field[
'ALLOWED']) && ($field[
'ALLOWED'] & self::FIELD_ALLOWED_SELECT > 0));
1468 private static function parseField(
string $field): ?
array
1475 $field = strtoupper($field);
1481 $compatible =
false;
1484 if (preg_match(self::FIELD_PATTERN_OLD_STORE, $field, $prepared))
1487 $entity = self::ENTITY_OLD_STORE;
1488 $field =
'STORE_AMOUNT';
1492 elseif (preg_match(self::FIELD_PATTERN_OLD_PRICE_ROW, $field, $prepared))
1495 $entity = self::ENTITY_OLD_PRICE;
1500 elseif (preg_match(self::FIELD_PATTERN_OLD_PRICE, $field, $prepared))
1503 $entity = self::ENTITY_OLD_PRICE;
1504 $field = $prepared[1];
1508 elseif (preg_match(self::FIELD_PATTERN_OLD_PRODUCT, $field, $prepared))
1511 $entity = self::ENTITY_OLD_PRODUCT;
1512 $field = $prepared[1];
1515 elseif (preg_match(self::FIELD_PATTERN_PRODUCT_USER_FIELD, $field, $prepared))
1517 $entity = self::ENTITY_PRODUCT_USER_FIELD;
1518 $field = $prepared[1];
1521 elseif (preg_match(self::FIELD_PATTERN_SEPARATE_ENTITY, $field, $prepared))
1523 $entity = self::searchFieldEntity($prepared[1], self::ENTITY_TYPE_SEPARATE);
1526 $field = $prepared[1];
1531 elseif (preg_match(self::FIELD_PATTERN_FLAT_ENTITY, $field, $prepared))
1533 $entity = self::searchFieldEntity($prepared[1], self::ENTITY_TYPE_FLAT);
1536 $field = $prepared[1];
1543 $allowed = self::getFieldAllowed(
$entity, $field);
1544 if (empty($allowed))
1559 'ALLOWED' => $allowed,
1560 'COMPATIBLE' => $compatible
1569 private static function searchFieldEntity(
string $field,
int $type): ?string
1575 case self::ENTITY_TYPE_FLAT:
1576 if (isset(self::$entityFields[self::ENTITY_PRODUCT][$field]))
1577 $result = self::ENTITY_PRODUCT;
1578 elseif (isset(self::$entityFields[self::ENTITY_FLAT_PRICE][$field]))
1579 $result = self::ENTITY_FLAT_PRICE;
1580 elseif (isset(self::$entityFields[self::ENTITY_FLAT_WAREHNOUSE][$field]))
1581 $result = self::ENTITY_FLAT_WAREHNOUSE;
1582 elseif (isset(self::$entityFields[self::ENTITY_FLAT_BARCODE][$field]))
1583 $result = self::ENTITY_FLAT_BARCODE;
1585 case self::ENTITY_TYPE_SEPARATE:
1586 if (isset(self::$entityFields[self::ENTITY_WARENHOUSE][$field]))
1587 $result = self::ENTITY_WARENHOUSE;
1588 elseif (isset(self::$entityFields[self::ENTITY_PRICE][$field]))
1601 private static function checkAllowedAction(
int $allowed,
int $action): bool
1603 return ($allowed &
$action) > 0;
1611 private static function isEntityFilterField(
string $field,
array $entityList): bool
1613 if (is_numeric($field))
1620 self::initEntityDescription();
1621 self::initEntityFields();
1623 $filterItem = \CIBlock::MkOperationFilter($field);
1624 $prepareField = self::parseField($filterItem[
'FIELD']);
1626 self::checkPreparedField($prepareField)
1627 && self::checkAllowedAction($prepareField[
'ALLOWED'], self::FIELD_ALLOWED_FILTER)
1630 if (isset($entityList[$prepareField[
'ENTITY']]))
1633 unset($prepareField, $filterItem);
1642 private static function getEntityIndex(
array $field): string
1644 return $field[
'ENTITY'].
':'.$field[
'ENTITY_ID'];
1651 private static function getEntityRow(
string $entity): ?
array
1653 if (!isset(self::$entityDescription[
$entity]))
1658 return self::$entityDescription[
$entity];
1665 private static function isExternalEntity(
string $entity): bool
1667 return isset(self::$entityDescription[
$entity][
'EXTERNAL']);
1678 $row = self::getEntityRow(
$entity[
'ENTITY']);
1683 if (self::isExternalEntity(
$entity[
'ENTITY']))
1688 $row[
'ALIAS'] = str_replace(
'#ENTITY_ID#',
$entity[
'ENTITY_ID'], $row[
'ALIAS']);
1691 '#NAME#' => $row[
'NAME'],
1692 '#ALIAS#' => $row[
'ALIAS'],
1693 '#ENTITY_ID#' =>
$entity[
'ENTITY_ID']
1695 $additionalAliases = self::getOption(
'ALIASES');
1696 if (!empty($additionalAliases))
1697 $joinTemplates = $joinTemplates + $additionalAliases;
1698 unset($additionalAliases);
1699 $row[
'JOIN'] = str_replace(
1700 array_keys($joinTemplates),
1701 array_values($joinTemplates),
1704 unset($joinTemplates);
1713 private static function getFieldIndex(
array $field): string
1715 return $field[
'ENTITY'].
':'.$field[
'ENTITY_ID'].
':'.$field[
'FIELD'];
1722 private static function isPhantomField(
array $field): bool
1724 return isset($field[
'PHANTOM']);
1732 private static function getFieldDescription(
string $entity,
string $field): ?
array
1734 if (empty(self::$entityFields[
$entity][$field]))
1736 return self::$entityFields[
$entity][$field];
1744 private static function getField(
array $queryItem,
array $options): ?
array
1751 'ENTITY_DESCRIPTION' =>
true
1754 $field = self::getFieldDescription($queryItem[
'ENTITY'], $queryItem[
'FIELD']);
1760 $entity = self::getEntityDescription($queryItem);
1766 $fantomField = self::isPhantomField($field);
1768 if ($field[
'ALIAS'] !==
null)
1770 $field[
'ALIAS'] = str_replace(
'#ENTITY_ID#', $queryItem[
'ENTITY_ID'], $field[
'ALIAS']);
1774 $field[
'FULL_NAME'] =
$entity[
'ALIAS'].
'.'.$field[
'NAME'];
1778 self::checkAllowedAction($field[
'ALLOWED'], self::FIELD_ALLOWED_SELECT)
1779 && isset($options[
'select'])
1784 $field[
'SELECT'] =
'#FULL_NAME#';
1785 if (isset($field[
'SELECT_EXPRESSION']) && is_callable($field[
'SELECT_EXPRESSION']))
1787 call_user_func_array(
1788 $field[
'SELECT_EXPRESSION'],
1789 [&$queryItem, &
$entity, &$field]
1792 $field[
'SELECT'] = str_replace(
'#FULL_NAME#', $field[
'FULL_NAME'], $field[
'SELECT']);
1797 self::checkAllowedAction($field[
'ALLOWED'], self::FIELD_ALLOWED_FILTER)
1798 && isset($options[
'filter'])
1801 if (isset($field[
'FILTER_PREPARE_VALUE_EXPRESSION']) && is_callable($field[
'FILTER_PREPARE_VALUE_EXPRESSION']))
1803 call_user_func_array(
1804 $field[
'FILTER_PREPARE_VALUE_EXPRESSION'],
1805 [&$queryItem, &
$entity, &$field]
1811 $field[
'FILTER'] = \CIBlock::FilterCreate(
1813 $queryItem[
'VALUES'],
1814 self::getFilterType($field[
'TYPE']),
1815 $queryItem[
'OPERATION']
1817 $field[
'FILTER'] = str_replace(
'#FULL_NAME#', $field[
'FULL_NAME'], $field[
'FILTER']);
1822 self::checkAllowedAction($field[
'ALLOWED'], self::FIELD_ALLOWED_ORDER)
1823 && isset($options[
'order'])
1828 $field[
'ORDER'] = \CIBlock::_Order(
1830 $queryItem[
'ORDER'],
1831 $field[
'ORDER_DEFAULT'] ??
'ASC',
1832 isset($field[
'ORDER_NULLABLE'])
1834 $field[
'ORDER'] = str_replace(
'#FULL_NAME#', $field[
'FULL_NAME'], $field[
'ORDER']);
1838 if (isset($field[
'JOIN_MODIFY_EXPRESSION']) && is_callable($field[
'JOIN_MODIFY_EXPRESSION']))
1840 call_user_func_array(
1841 $field[
'JOIN_MODIFY_EXPRESSION'],
1842 [&$queryItem, &
$entity, &$field]
1845 if (isset($field[
'JOIN_MODIFY']))
1847 $field[
'JOIN_MODIFY'] = str_replace(
'#TABLE#',
$entity[
'ALIAS'], $field[
'JOIN_MODIFY']);
1848 $entity[
'JOIN_MODIFY'] = $field[
'JOIN_MODIFY'];
1851 unset($fantomField);
1853 $field[
'ENTITY_DESCRIPTION'] =
$entity;
1856 $result = array_intersect_key($field, $whiteList);
1857 unset($field, $whiteList);
1866 private static function getFilterType(
string $fieldType): string
1868 return match ($fieldType)
1870 self::FIELD_TYPE_INT, self::FIELD_TYPE_FLOAT =>
'number',
1871 self::FIELD_TYPE_CHAR =>
'string_equal',
1872 default =>
'string',
1880 private static function checkPreparedField(?
array $field): bool
1886 if ($field[
'ENTITY_ID'] === 0)
1889 $field[
'ENTITY'] != self::ENTITY_PRODUCT
1890 && $field[
'ENTITY'] != self::ENTITY_PRODUCT_USER_FIELD
1891 && $field[
'ENTITY'] != self::ENTITY_FLAT_PRICE
1892 && $field[
'ENTITY'] != self::ENTITY_FLAT_WAREHNOUSE
1893 && $field[
'ENTITY'] != self::ENTITY_FLAT_BARCODE
1894 && $field[
'ENTITY'] != self::ENTITY_OLD_PRODUCT
1908 private static function getFieldSignature(
array $field):
array
1911 'ENTITY' => $field[
'ENTITY'],
1912 'FIELD' => $field[
'FIELD'],
1913 'ENTITY_ID' => $field[
'ENTITY_ID']
1921 private static function prepareSelectedCompatibleFields(
array &$parameters): void
1923 if ($parameters[
'compatible_mode'] && !empty($parameters[
'compatible_entities']))
1925 foreach ($parameters[
'compatible_entities'] as
$entity)
1927 $list = self::getFieldsAllowedToSelect(
$entity[
'ENTITY']);
1930 foreach ($list as $fieldId)
1933 'ENTITY' =>
$entity[
'ENTITY'],
1934 'FIELD' => $fieldId,
1935 'ENTITY_ID' =>
$entity[
'ENTITY_ID']
1937 $parameters[
'select'][self::getFieldIndex($field)] = $field;
1939 unset($field, $fieldId, $list);
1943 unset($parameters[
'compatible_mode'], $parameters[
'compatible_entities']);
1951 private static function fillCompatibleEntities(
array &
$result,
array $field): void
1953 if (!$field[
'COMPATIBLE'])
1955 $result[
'compatible_mode'] =
true;
1956 $result[
'compatible_entities'][self::getEntityIndex($field)] = [
1957 'ENTITY' => $field[
'ENTITY'],
1958 'ENTITY_ID' => $field[
'ENTITY_ID']
1962 'ENTITY' => self::ENTITY_OLD_PRODUCT,
1967 'ENTITY' => self::ENTITY_VAT,
1980 private static function prepareQuery(
array $parameters,
array $options): ?
array
1982 self::initEntityDescription();
1983 self::initEntityFields();
1985 self::setOptions($options);
1988 'compatible_mode' =>
false,
1989 'compatible_entities' => [],
1995 if (!empty($parameters[
'select']) && is_array($parameters[
'select']))
1997 foreach ($parameters[
'select'] as $field)
1999 $prepareField = self::parseField($field);
2000 if (!self::checkPreparedField($prepareField))
2004 if (!self::checkAllowedAction($prepareField[
'ALLOWED'], self::FIELD_ALLOWED_SELECT))
2009 self::fillCompatibleEntities(
$result, $prepareField);
2010 $result[
'select'][self::getFieldIndex($prepareField)] = self::getFieldSignature($prepareField);
2012 unset($prepareField, $field);
2015 if (!empty($parameters[
'filter']) && is_array($parameters[
'filter']))
2017 foreach (array_keys($parameters[
'filter']) as
$key)
2020 $prepareField = self::parseField(
$filter[
'FIELD']);
2022 if (!self::checkPreparedField($prepareField))
2026 if (!self::checkAllowedAction($prepareField[
'ALLOWED'], self::FIELD_ALLOWED_FILTER))
2031 self::fillCompatibleEntities(
$result, $prepareField);
2032 $prepareField = self::getFieldSignature($prepareField);
2033 $prepareField[
'OPERATION'] =
$filter[
'OPERATION'];
2034 $prepareField[
'PREFIX'] =
$filter[
'PREFIX'];
2035 $prepareField[
'VALUES'] = $parameters[
'filter'][
$key];
2036 $result[
'filter'][] = $prepareField;
2041 if (!empty($parameters[
'order']) && is_array($parameters[
'order']))
2043 foreach ($parameters[
'order'] as $index => $value)
2045 if (empty($value) || !is_array($value))
2051 $field = key($value);
2053 $prepareField = self::parseField($field);
2054 if (!self::checkPreparedField($prepareField))
2058 if (!self::checkAllowedAction($prepareField[
'ALLOWED'], self::FIELD_ALLOWED_ORDER))
2063 self::orderTransformField($prepareField);
2065 self::fillCompatibleEntities(
$result, $prepareField);
2066 $fieldIndex = self::getFieldIndex($prepareField);
2068 $prepareField = self::getFieldSignature($prepareField);
2069 $result[
'select'][$fieldIndex] = $prepareField;
2071 $prepareField[
'INDEX'] = $index;
2072 $prepareField[
'ORDER'] =
$order;
2073 $result[
'order'][$fieldIndex] = $prepareField;
2075 unset(
$order, $field, $index, $value);
2078 self::prepareSelectedCompatibleFields(
$result);
2081 self::clearOptions();
2088 private static function clearOptions(): void
2090 self::$options = [];
2097 private static function setOptions(
array $options): void
2101 if (!isset($options[
'ALIASES']))
2103 $options[
'ALIASES'] = [];
2105 if (!isset($options[
'ALIASES'][
'#ELEMENT#']))
2107 $options[
'ALIASES'][
'#ELEMENT#'] =
'BE';
2109 if (!isset($options[
'ALIASES'][
'#ELEMENT_JOIN#']))
2111 $options[
'ALIASES'][
'#ELEMENT_JOIN#'] =
'BE.ID';
2114 if (!isset($options[
'USER']))
2116 $options[
'USER'] = [];
2118 if (!isset($options[
'USER'][
'ID']))
2120 $options[
'USER'][
'ID'] = (\CCatalog::IsUserExists() ?
$USER->GetID() : 0);
2122 $options[
'USER'][
'ID'] = (int)$options[
'USER'][
'ID'];
2124 self::$options = $options;
2131 private static function getOption(
string $index): mixed
2133 if (!isset(self::$options[$index]))
2135 return self::$options[$index];
2142 private static function build(
array $parameters): ?
array
2150 'join_modify' => [],
2153 if (!empty($parameters[
'select']))
2155 self::buildSelect(
$result, $parameters[
'select']);
2156 if (!empty(
$result[
'select']))
2161 if (!empty($parameters[
'filter']))
2163 self::buildFilter(
$result, $parameters[
'filter']);
2169 if (!empty($parameters[
'order']))
2171 self::buildOrder(
$result, $parameters[
'order']);
2186 unset(
$result[
'join_modify']);
2198 foreach ($list as $item)
2200 $field = self::getField($item, [
'select' =>
true]);
2204 if (isset($field[
'SELECT']))
2205 $result[
'select'][] = $field[
'SELECT'].
' as '.$field[
'ALIAS'];
2207 $item[
'ENTITY_DESCRIPTION'] = $field[
'ENTITY_DESCRIPTION'];
2208 self::addJoin(
$result, $item);
2220 self::filterModify($list);
2222 $packedFilter = self::getPackedFilter($list);
2224 self::buildInternalFilter(
$result, $packedFilter[
'INTERNAL']);
2225 self::buildExternalFilter(
$result, $packedFilter[
'EXTERNAL']);
2235 foreach ($list as $item)
2237 $field = self::getField($item, [
'order' =>
true]);
2241 if (isset($field[
'ORDER']))
2242 $result[
'order'][$item[
'INDEX']] = $field[
'ORDER'];
2244 $item[
'ENTITY_DESCRIPTION'] = $field[
'ENTITY_DESCRIPTION'];
2245 self::addJoin(
$result, $item);
2247 unset($field, $item);
2254 private static function buildJoin(
array &
$result): void
2256 foreach (array_keys(
$result[
'join']) as $index)
2258 $modifier = (isset(
$result[
'join_modify'][$index])
2259 ?
' '.implode(
' ',
$result[
'join_modify'][$index])
2262 $result[
'join'][$index] = str_replace(
'#JOIN_MODIFY#', $modifier,
$result[
'join'][$index]);
2264 unset($modifier, $index);
2271 private static function filterModify(
array &$list): void
2273 foreach (array_keys($list) as $index)
2275 $item = $list[$index];
2276 $field = self::getFieldDescription($item[
'ENTITY'], $item[
'FIELD']);
2282 $entity = self::getEntityDescription($item);
2288 if (isset($field[
'FILTER_MODIFY_EXPRESSION']) && is_callable($field[
'FILTER_MODIFY_EXPRESSION']))
2290 call_user_func_array(
2291 $field[
'FILTER_MODIFY_EXPRESSION'],
2292 [&$list, $index,
$entity, $field]
2296 unset($field,
$entity, $item, $index);
2309 if (self::isExternalEntity(
$entity))
2315 $index = self::getEntityIndex($item);
2324 $result[
'INTERNAL'][] = $item;
2337 foreach ($list as $item)
2339 $field = self::getField($item, [
'filter' =>
true]);
2345 if (isset($field[
'FILTER']))
2347 $result[
'filter'][] = $field[
'FILTER'];
2350 $item[
'ENTITY_DESCRIPTION'] = $field[
'ENTITY_DESCRIPTION'];
2351 self::addJoin(
$result, $item);
2363 $index = self::getEntityIndex(
$entity);
2366 if (!isset(
$result[
'join'][$index]))
2372 if (!is_array($item))
2379 $ownerIndex = self::getEntityIndex($item);
2380 if (isset(
$result[
'join'][$ownerIndex]))
2382 $owner = self::getEntityDescription($item);
2385 $result[
'join'][$ownerIndex] = $owner[
'JOIN'];
2387 unset($owner, $ownerIndex, $parent, $item);
2393 if (!isset(
$result[
'join_modify'][$index]))
2394 $result[
'join_modify'][$index] = [];
2408 $row = self::getEntityRow(
$entity);
2409 if (isset($row[
'HANDLER']) && is_callable($row[
'HANDLER']))
2411 call_user_func_array(
2429 private static function orderTransformField(
array &$item): void
2431 $field = self::getFieldDescription($item[
'ENTITY'], $item[
'FIELD']);
2434 if (isset($field[
'ORDER_TRANSFORM']))
2435 $item[
'FIELD'] = $field[
'ORDER_TRANSFORM'];
2449 $field[
'SELECT'] = self::getReplaceSqlFunction(Main\Config\Option::get(
'catalog',
'default_quantity_trace'));
2462 $field[
'SELECT'] = self::getReplaceSqlFunction(Main\Config\Option::get(
'catalog',
'default_can_buy_zero'));
2473 private static function selectNegativeAmountTrace(
array &$parameters,
array &
$entity,
array &$field): void
2475 $field[
'SELECT'] = self::getReplaceSqlFunction(Main\Config\Option::get(
'catalog',
'allow_negative_amount'));
2488 $field[
'SELECT'] = self::getReplaceSqlFunction(Main\Config\Option::get(
'catalog',
'default_subscribe'));
2497 private static function getReplaceSqlFunction(
string $defaultValue): string
2499 return 'CASE WHEN #FULL_NAME# = \'' . ProductTable::STATUS_DEFAULT .
'\' THEN \
'' .
$defaultValue .
'\' ELSE #FULL_NAME# END
';
2508 private static function selectPriceTypeName(array &$parameters, array &$entity, array &$field): void
2511 $id = $parameters['ENTITY_ID
'];
2512 $fullPriceTypeList = GroupTable::getTypeList();
2513 if (!empty($fullPriceTypeList[$id]))
2515 $result = $fullPriceTypeList[$id]['NAME_LANG
'] ?? $fullPriceTypeList[$id]['NAME
'];
2516 $connection = Main\Application::getInstance()->getConnection();
2517 $sqlHelper = $connection->getSqlHelper();
2518 $result = $sqlHelper->forSql($result);
2519 unset($sqlHelper, $connection);
2521 unset($fullPriceTypeList, $id);
2522 $field['SELECT
'] = '\
''.$result.
'\'';
2532 private static function selectPriceTypeAllowedView(
array &$parameters,
array &
$entity,
array &$field): void
2534 $parameters[
'ACCESS'] = GroupAccessTable::ACCESS_VIEW;
2535 $field[
'SELECT'] = self::getPriceTypeAccess($parameters);
2544 private static function selectPriceTypeAllowedBuy(
array &$parameters,
array &
$entity,
array &$field): void
2546 $parameters[
'ACCESS'] = GroupAccessTable::ACCESS_BUY;
2547 $field[
'SELECT'] = self::getPriceTypeAccess($parameters);
2554 private static function getPriceTypeAccess(
array $parameters): string
2558 $user = self::getOption(
'USER');
2561 if (empty(
$user[
'GROUPS']) || !is_array(
$user[
'GROUPS']))
2562 $user[
'GROUPS'] = self::getUserGroups(
$user[
'ID']);
2566 '=CATALOG_GROUP_ID' => $parameters[
'ENTITY_ID'],
2567 '@GROUP_ID' =>
$user[
'GROUPS'],
2568 '=ACCESS' => $parameters[
'ACCESS']
2579 return '\''.$result.
'\'';
2588 private static function prepareFilterQuantityTrace(
array &$parameters,
array &
$entity,
array &$field): void
2590 $parameters[
'VALUES'] = self::addDefaultValue(
2591 $parameters[
'VALUES'],
2592 Main\Config\Option::get(
'catalog',
'default_quantity_trace')
2604 $parameters[
'VALUES'] = self::addDefaultValue(
2605 $parameters[
'VALUES'],
2606 Main\Config\Option::get(
'catalog',
'default_can_buy_zero')
2618 $parameters[
'VALUES'] = self::addDefaultValue(
2619 $parameters[
'VALUES'],
2620 Main\Config\Option::get(
'catalog',
'default_subscribe')
2631 private static function addDefaultValue(mixed $values,
string $defaultValue): mixed
2633 if (!is_array($values))
2637 $values = [$values, ProductTable::STATUS_DEFAULT];
2644 $values[] = ProductTable::STATUS_DEFAULT;
2645 $values = array_unique($values);
2659 if (empty($parameters[
'VALUES']))
2661 if (!is_string($parameters[
'VALUES']) && !is_int($parameters[
'VALUES']))
2663 $value = (int)$parameters[
'VALUES'];
2667 $field[
'JOIN_MODIFY'] =
' and '.($parameters[
'OPERATION'] ==
'N' ?
'not' :
'').
2668 ' ((#TABLE#.QUANTITY_FROM <= '.
$value.
' or #TABLE#.QUANTITY_FROM IS NULL)'.
2669 ' and (#TABLE#.QUANTITY_TO >= '.$value.
' or #TABLE#.QUANTITY_TO IS NULL))';
2681 $activeItem =
$filter[$filterKey];
2683 if ($activeItem[
'FIELD'] !==
'CURRENCY_FOR_SCALE' && $activeItem[
'FIELD'] !==
'CURRENCY_SCALE')
2687 if ($activeItem[
'OPERATION'] !==
'E' && $activeItem[
'OPERATION'] !==
'I')
2692 $value = $activeItem[
'VALUES'];
2693 if (!is_string($value))
2698 $currencyId = Currency\CurrencyManager::checkCurrencyID($value);
2699 if ($currencyId ===
false)
2704 $currency = \CCurrency::GetByID($currencyId);
2710 if (
$currency[
'CURRENT_BASE_RATE'] <= 0)
2715 foreach (array_keys(
$filter) as $index)
2717 if ($index == $filterKey)
2721 $filterItem =
$filter[$index];
2723 $filterItem[
'ENTITY'] != $activeItem[
'ENTITY']
2724 || $filterItem[
'ENTITY_ID'] != $activeItem[
'ENTITY_ID']
2729 if ($filterItem[
'FIELD'] !=
'PRICE')
2733 if (is_array(
$filter[$index][
'VALUES']))
2736 foreach (
$filter[$index][
'VALUES'] as $oldPrice)
2738 $newPrices[] = (float)$oldPrice *
$currency[
'CURRENT_BASE_RATE'];
2740 $filter[$index][
'VALUES'] = $newPrices;
2741 unset($oldPrice, $newPrices);
2747 $filter[$index][
'FIELD'] = $activeItem[
'FIELD'] ===
'CURRENCY_FOR_SCALE'
2759 private static function getUserGroups(
int $userId):
array
2761 return Main\UserTable::getUserGroupIds(
$userId);
2771 if (empty(
$data[
'filter']))
2775 $aliases = self::getOption(
'ALIASES');
2776 if (empty($aliases[
'#ELEMENT_JOIN#']))
2781 $userFieldManager =
new CUserTypeSQL;
2783 foreach (
$data[
'filter'] as $entityIndex =>
$rows)
2785 $userFieldManager->SetEntity(ProductTable::getUfId(), $aliases[
'#ELEMENT_JOIN#']);
2786 $userFieldManager->SetSelect([]);
2788 foreach (
$rows as $row)
2790 $rawFilter[$row[
'PREFIX'].$row[
'FIELD']] = $row[
'VALUES'];
2792 $userFieldManager->SetFilter($rawFilter);
2793 $userFieldManager->SetOrder([]);
2795 $filter = $userFieldManager->GetFilter();
2799 $join = $userFieldManager->GetJoin($aliases[
'#ELEMENT_JOIN#']);
2802 $result[
'join'][$entityIndex] = $join;
2807 unset($userFieldManager);