1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
report.php
См. документацию.
1<?php
2
4
5class CReport
6{
7 protected static $totalCountableAggrFuncs = [
8 'SUM', 'COUNT_DISTINCT', 'AVG', 'MAX', 'MIN'
9 ];
10
11 protected static $alternateColumnPhrases = null;
12
14 'EQUAL' => '=',
15 'GREATER_OR_EQUAL' => '>=',
16 'GREATER' => '>',
17 'LESS' => '<',
18 'LESS_OR_EQUAL' => '<=',
19 'NOT_EQUAL' => '!',
20 'START_WITH' => '>%',
21 'CONTAINS' => '%',
22 'NOT_CONTAINS' => '!%',
23 'BETWEEN' => '><',
24 'NOT_BETWEEN' => '!><'
25 );
26
27 public static function Add($settings)
28 {
29 global $DB, $USER;
30
31 $name = $settings['title'];
32 $description = $settings['description'];
33 $owner = $settings['owner'];
34 unset($settings['title']);
35 unset($settings['description']);
36 unset($settings['owner']);
37
38
39 $fields = array(
40 'TITLE' => $name,
41 'DESCRIPTION' => $description,
42 'OWNER_ID' => $owner,
43 'CREATED_DATE' => date($DB->DateFormatToPHP(CSite::GetDateFormat("FULL")), time()+CTimeZone::GetOffset()),
44 'CREATED_BY' => $USER->GetID()
45 );
46
47 if (isset($settings['mark_default']))
48 {
49 $fields['MARK_DEFAULT'] = $settings['mark_default'];
50 unset($settings['mark_default']);
51 }
52
53 $fields['SETTINGS'] = serialize($settings);
54
55 // pre-events
56 foreach (GetModuleEvents("report", "OnBeforeReportAdd", true) as $arEvent)
57 {
58 if (ExecuteModuleEventEx($arEvent, array(&$fields)) === false)
59 {
60 return false;
61 }
62 }
63
64 // save data
65 $ID = $DB->Add("b_report", $fields, array("SETTINGS", "DESCRIPTION"), "report");
66
67 // clear view params
69
70 // post-events
71 foreach (GetModuleEvents("report", "OnReportAdd", true) as $arEvent)
72 {
74 }
75
76 return $ID;
77 }
78
79 public static function Update($ID, $settings)
80 {
81 global $DB;
82
83 $name = $settings['title'];
84 $description = $settings['description'];
85 unset($settings['title']);
86 unset($settings['description']);
87 unset($settings['owner']);
88
89 $settings = serialize($settings);
90
91 $fields = array(
92 'TITLE' => $name,
93 'DESCRIPTION' => $description,
94 'SETTINGS' => $settings,
95 'MARK_DEFAULT' => false
96 );
97
98 // pre-events
99 foreach (GetModuleEvents("report", "OnBeforeReportUpdate", true) as $arEvent)
100 {
101 if (ExecuteModuleEventEx($arEvent, array($ID, &$fields)) === false)
102 {
103 return false;
104 }
105 }
106
107 // save data
108 $strUpdate = $DB->PrepareUpdate("b_report", $fields, "report");
109 $strSql = "UPDATE b_report SET ".$strUpdate." WHERE ID='".$DB->ForSQL($ID)."'";
110
111 $result = $DB->QueryBind(
112 $strSql, array('SETTINGS' => $settings, 'DESCRIPTION' => $description)
113 );
114
115 // post-events
116 if ($result)
117 {
118 foreach (GetModuleEvents("report", "OnReportUpdate", true) as $arEvent)
119 {
121 }
122 }
123
124 // clear view params
126
127 return $result;
128 }
129
130 public static function Delete($ID)
131 {
132 global $DB;
133
134 $strSql = "DELETE FROM b_report WHERE ID = ".intval($ID);
135
136 // pre-events
137 foreach (GetModuleEvents("report", "OnBeforeReportDelete", true) as $arEvent)
138 {
139 if (ExecuteModuleEventEx($arEvent, array($ID)) === false)
140 {
141 return false;
142 }
143 }
144
145 // save data
146 $DB->Query($strSql);
147
148 // post-events
149 foreach (GetModuleEvents("report", "OnReportDelete", true) as $arEvent)
150 {
151 ExecuteModuleEventEx($arEvent, array($ID));
152 }
153
154 // clear view params
156
157 return true;
158 }
159
160 public static function GetList($owner = '')
161 {
162 global $USER;
163
164 return Bitrix\Report\ReportTable::getList(array(
165 'select' => array('ID', 'TITLE', 'DESCRIPTION','CREATED_DATE'),
166 'filter' => array('=CREATED_BY' => $USER->GetID(), '=OWNER_ID' => $owner)
167 ));
168 }
169
170 public static function setViewParams($id, $templateName, $strParams)
171 {
172 global $USER;
173
174 $result = false;
175 if (empty($templateName)) $templateName = '.default';
176 if (
177 get_class($USER) === 'CUser'
178 && $id !== null && intval($id) >= 0
179 && !empty($templateName)
180 && !empty($strParams)
181 )
182 {
183 $user_id = $USER->GetId();
184 if ($user_id != null)
185 {
186 $result = CUserOptions::SetOption(
187 'report', 'view_params_'.$id.'_'.$templateName, $_SERVER['QUERY_STRING'], false, $user_id
188 );
189 }
190 }
191
192 return $result;
193 }
194
195 public static function getViewParams($id, $templateName)
196 {
197 global $USER;
198
199 $result = '';
200 if (empty($templateName)) $templateName = '.default';
201 if (get_class($USER) === 'CUser' && $id !== null && intval($id) >= 0 && !empty($templateName))
202 {
203 $user_id = $USER->GetId();
204 if ($user_id != null)
205 {
206 $result = CUserOptions::GetOption(
207 'report', 'view_params_'.$id.'_'.$templateName, false, $user_id
208 );
209 }
210
211 }
212
213 return $result;
214 }
215
216 public static function clearViewParams($id)
217 {
218 if ($id !== null && intval($id) >= 0)
219 {
220 $dbRes = CUserOptions::GetList(
221 array("ID" => "ASC"),
222 array('CATEGORY' => 'report', 'NAME_MASK' => 'view_params_'.$id.'_')
223 );
224 if (is_object($dbRes))
225 {
226 while ($row = $dbRes->fetch())
227 {
228 $userId = (int)$row['USER_ID'];
229 if ($userId > 0)
230 {
231 if (mb_strpos($row['NAME'], 'view_params_'.$id.'_') === 0)
232 CUserOptions::DeleteOption('report', $row['NAME'], false, $userId);
233 }
234 }
235 }
236 }
237 }
238
239
240 public static function GetCountInt($owner = '')
241 {
242 global $DB, $USER;
243
244 $strSql = "SELECT COUNT(ID) AS CNT FROM b_report WHERE CREATED_BY='".$DB->ForSql($USER->GetID())."' AND OWNER_ID='".$DB->ForSql($owner)."'";
245
246 $res = $DB->Query($strSql);
247 $row = $res->Fetch();
248
249 return (int) $row['CNT'];
250 }
251
252 public static function generateChains($strChains, $initEntity, $initKey)
253 {
254 $chains = array();
255
256 foreach ($strChains as $k => $v)
257 {
258 if (is_array($v))
259 {
260 // catalog here
261 $key = empty($initKey) ? $k : $initKey . '.' .$k;
262
263 try
264 {
265 $chain = Entity\QueryChain::getChainByDefinition($initEntity, $key);
266 $lastElem = $chain->getLastElement();
267
268 if ($lastElem->getValue() instanceof Entity\ReferenceField || is_array($lastElem->getValue()))
269 {
270 // reference to another entity
271 $v = self::generateChains($v, $initEntity, $key);
272 $v['__CHAIN__'] = $chain;
273 }
274 else
275 {
276 throw new \Bitrix\Main\SystemException('', 100);
277 }
278 }
279 catch (Exception $e)
280 {
281 // try to recognize virtual category
282 if ($e->getCode() == 100)
283 {
284 // `getChainByDefinition` field not found, there is virtual category
285 $v = self::generateChains($v, $initEntity, $initKey);
286 }
287 else
288 {
289 throw $e;
290 }
291 }
292 }
293 else
294 {
295 // normal field
296 // collect chain path FLD1.FLD2.FLD3
297 $key = empty($initKey) ? '' : $initKey . '.';
298
299 $key = $key.$v;
300 $v = Entity\QueryChain::getChainByDefinition($initEntity, $key);
301 }
302
303 // replace key
304 $chains[$key] = $v;
305 }
306
307 return $chains;
308 }
309
310 protected static function initializeAlternateColumnPhrases($helperClass)
311 {
312 if (static::$alternateColumnPhrases === null)
313 {
314 static::$alternateColumnPhrases = [];
315
316 if (is_string($helperClass)
317 && $helperClass !== ''
318 && method_exists($helperClass, 'getAlternatePhrasesOfColumns'))
319 {
320 $phrases = call_user_func([$helperClass, 'getAlternatePhrasesOfColumns']);
321 if (is_array($phrases))
322 {
323 static::$alternateColumnPhrases[$helperClass] = $phrases;
324 }
325 else
326 {
327 static::$alternateColumnPhrases[$helperClass] = [];
328 }
329 }
330 }
331 }
332
333 public static function isAlternateColumnPhraseExists($helperClass, $messageCode)
334 {
335 static::initializeAlternateColumnPhrases($helperClass);
336
337 $result = false;
338
339 if (is_string($helperClass) && $helperClass !== ''
340 && is_array(static::$alternateColumnPhrases[$helperClass])
341 && isset(static::$alternateColumnPhrases[$helperClass][$messageCode]))
342 {
343 $result = true;
344 }
345
346 return $result;
347 }
348
349 public static function getAlternateColumnPhrase($helperClass, $messageCode)
350 {
351 $result = '';
352
353 if (static::isAlternateColumnPhraseExists($helperClass, $messageCode))
354 {
355 $result = static::$alternateColumnPhrases[$helperClass][$messageCode];
356 }
357
358 return $result;
359 }
360
361 public static function generateColumnTree($chains, $initEntity, $helper_class, $level = 0)
362 {
363 $tree = array();
364
365 foreach ($chains as $k => $v)
366 {
367 if ($k == '__CHAIN__')
368 {
369 continue;
370 }
371
372 if ($v instanceof Entity\QueryChain)
373 {
374 // there is single element (chain)
375 $chain = $v;
376 $treeElem = $chain->getLastElement()->getValue();
377 $branch = null;
378 }
379 else
380 {
381 // there is sub tree
382 if (!empty($v['__CHAIN__']))
383 {
384 // real sub-tree
385 $chain = $v['__CHAIN__'];
386 $treeElem = $v['__CHAIN__']->getLastElement()->getValue();
387
388 if (is_array($treeElem))
389 {
390 $treeElem = $treeElem[1];
391 }
392 }
393 else
394 {
395 // virtual category
396 $chain = null;
397 $treeElem = null;
398 }
399
400 $branch = self::generateColumnTree($v, $initEntity, $helper_class, $level+1);
401 }
402
403 $tree[] = array(
404 'fieldName' => $k,
405 'humanTitle' => '', // reserved
406 'fullHumanTitle' => '', // reserved
407 'field' => $treeElem,
408 'chain' => $chain,
409 'branch' => $branch
410 );
411 }
412
413 if ($level == 0)
414 {
415 self::attachLangToColumnTree($tree, $initEntity, $helper_class);
416 }
417
418 return $tree;
419 }
420
421 protected static function attachLangToColumnTree(&$tree, $initEntity, $helperClass, $preTitle = array())
422 {
423 foreach($tree as &$treeElem)
424 {
425 $ownerId = call_user_func(array($helperClass, 'getOwnerId'));
426
427 $humanTitle = '';
428
429 if (!empty($treeElem['field']))
430 {
431 // detect UF
432 $arUF = call_user_func(array($helperClass, 'detectUserField'), $treeElem['field']);
433 if ($arUF['isUF'])
434 {
435 $treeElem['isUF'] = true;
436 $treeElem['ufInfo'] = $arUF['ufInfo'];
437 $humanTitle = $arUF['ufInfo']['LIST_COLUMN_LABEL'];
438 }
439 unset($arUF);
440
441 // first: report-defined lang
442 $rElementTitle = 'REPORT_'.$ownerId.'_'.$treeElem['fieldName'];
443
444 // second: entity-defined lang
445 $eElementTitle = $treeElem['field']->getLangCode();
446
447 // PRCNT hack should not be here
448 if (mb_substr($rElementTitle, -12) === '_PRCNT_FIELD')
449 {
450 $messageCode = mb_substr($rElementTitle, 0, -12).'_FIELD';
451 }
452 else
453 {
454 $messageCode = $rElementTitle;
455 }
456
457 if (static::isAlternateColumnPhraseExists($helperClass, $messageCode))
458 {
459 $elementTitle = $rElementTitle;
460 }
461 else
462 {
463 $elementMessage = GetMessage($messageCode);
464 if (is_string($elementMessage) && $elementMessage !== '')
465 {
466 $elementTitle = $rElementTitle;
467 }
468 else
469 {
470 $elementTitle = $eElementTitle;
471 }
472 }
473
474 unset($messageCode);
475 }
476 else
477 {
478 // virtual field - subtree head
479 $elementName = $treeElem['fieldName'];
480 $elementTitle = 'REPORT_'.$ownerId.'_COLUMN_TREE_'.$elementName;
481 }
482
483 if (!isset($treeElem['isUF']) || !$treeElem['isUF'])
484 {
485 // PRCNT hack should not be here
486 if (mb_substr($elementTitle, -12) === '_PRCNT_FIELD')
487 {
488 $messageCode = mb_substr($elementTitle, 0, -12).'_FIELD';
489 }
490 else
491 {
492 $messageCode = $elementTitle;
493 }
494
495 if (static::isAlternateColumnPhraseExists($helperClass, $messageCode))
496 {
497 $humanTitle = static::getAlternateColumnPhrase($helperClass, $messageCode);
498 }
499 else
500 {
501 $humanTitle = GetMessage($messageCode);
502 }
503
504 unset($messageCode);
505 }
506
507 if (!is_string($humanTitle) || $humanTitle === '')
508 {
509 $humanTitle = $treeElem['fieldName'];
510 }
511
512 if (mb_substr($elementTitle, -12) == '_PRCNT_FIELD')
513 {
514 $humanTitle .= ' (%)';
515 }
516
517 if (empty($treeElem['branch']))
518 {
519 $fullHumanTitle = $humanTitle;
520
521 if (!empty($preTitle))
522 {
523 $fullHumanTitle = join(': ', $preTitle) . ': ' . $fullHumanTitle;
524 }
525
526 $treeElem['humanTitle'] = $humanTitle;
527 $treeElem['fullHumanTitle'] = $fullHumanTitle;
528 }
529 else
530 {
531 $treeElem['humanTitle'] = $humanTitle;
532 $treeElem['fullHumanTitle'] = $humanTitle;
533
534 $sendPreTitle = array($humanTitle);
535
536 self::attachLangToColumnTree($treeElem['branch'], $initEntity, $helperClass, $sendPreTitle);
537 }
538 }
539 }
540
542 public static function fillFilterReferenceColumns(&$filters, &$fieldList, $helperClass)
543 {
544 foreach ($filters as &$filter)
545 {
546 foreach ($filter as &$fElem)
547 {
548 if (is_array($fElem) && $fElem['type'] == 'field')
549 {
550 $field = $fieldList[$fElem['name']];
551
552 if ($field instanceof Entity\ReferenceField)
553 {
554 call_user_func_array(
555 array($helperClass, 'fillFilterReferenceColumn'),
556 array(&$fElem, &$field)
557 );
558 }
559 }
560 }
561 }
562 }
563
572 public static function checkSelectViewElementCyclicDependency($select, $elemIndex)
573 {
574 static $elems = array();
575
576 if (isset($elems[$elemIndex]))
577 {
578 $result = true;
579 }
580 else
581 {
582 $elems[$elemIndex] = true;
583 $elem = $select[$elemIndex];
584 $result = false;
585
586 $prcnt = $elem['prcnt'] ?? '';
587 if ($prcnt !== '' && $prcnt !== 'self_column')
588 {
590 }
591 unset($elems[$elemIndex]);
592 }
593
594 return $result;
595 }
596
608 public static function prepareSelectViewElement($elem, $select, $isInitEntityAggregated, $fList, $fChainList,
609 $helperClassName, Entity\Base $entity)
610 {
611 $selectElem = null;
612 $totalInfo = null;
613 $alias = null;
614
615 $prcnt = $elem['prcnt'] ?? '';
616 if (empty($elem['aggr']) && !mb_strlen($prcnt))
617 {
618 $selectElem = $elem['name'];
619 }
620 else
621 {
622 $expression = '';
623
625 $field = $fList[$elem['name']];
626 $chain = $fChainList[$elem['name']];
627 $sourceAlias = $alias = $chain->getAlias();
628
629 $dataType = call_user_func(array($helperClassName, 'getFieldDataType'), $field);
630
631 // Need pack 1:N aggregations into subquery?
632 $needPack1NAggr = false;
633 if ($chain->hasBackReference() && $elem['aggr'] != 'GROUP_CONCAT')
634 {
635 $confirm = call_user_func_array(
636 array($helperClassName, 'confirmSelectBackReferenceRewrite'),
637 array(&$elem, $chain)
638 );
639
640 if ($confirm)
641 {
642 $needPack1NAggr = true;
643 }
644 }
645
646 if (!empty($elem['aggr']))
647 {
648 $alias = $elem['aggr'] . '_' . $alias;
649
650 if ($dataType == 'boolean')
651 {
652 // sum int for boolean
653 global $DB;
654
656 $trueValue = $field->normalizeValue(true);
657 $localDef = 'CASE WHEN %s = \''.$DB->ForSql($trueValue).'\' THEN 1 ELSE 0 END';
658 }
659 else
660 {
661 $localDef = '%s';
662 }
663
664 if ($elem['aggr'] == 'COUNT_DISTINCT')
665 {
666 $dataType = 'integer';
667 $expression = array(
668 'COUNT(DISTINCT '.$localDef.')', $elem['name']
669 );
670 }
671 else
672 {
673 if ($dataType == 'boolean')
674 {
675 $dataType = 'integer';
676 }
677
678 if ($elem['aggr'] == 'GROUP_CONCAT')
679 {
680 $expression = array(
681 $localDef, $elem['name']
682 );
683 }
684 else
685 {
686 if ($elem['aggr'] === 'AVG')
687 {
688 if (!is_array($totalInfo))
689 {
690 $totalInfo = [];
691 }
692 $totalInfo['average'] = [
693 'type' => 'average',
694 'cnt' => [
695 'alias' => $sourceAlias.'_AVGCNT',
696 'def' => [
697 'data_type' => 'integer',
698 'expression' => ['COUNT(1)']
699 ]
700 ],
701 'sum' => [
702 'alias' => $sourceAlias.'_AVGSUM',
703 'def' => [
704 'data_type' => $dataType,
705 'expression' => ['SUM('.$localDef.')', $elem['name']]
706 ]
707 ]
708 ];
709 }
710 else
711 {
712 if ($elem['aggr'] === 'MIN' || $elem['aggr'] === 'MAX')
713 {
714 $typeMap = ['MIN' => 'minimum', 'MAX' => 'maximum'];
715 $type = $typeMap[$elem['aggr']];
716 if (!is_array($totalInfo))
717 {
718 $totalInfo = [];
719 }
720 $totalInfo[$type] = ['type' => $type];
721 unset($typeMap, $type);
722 }
723 }
724
725 $expression = [$elem['aggr'].'('.$localDef.')', $elem['name']];
726 }
727 }
728
729 // pack 1:N aggregations into subquery
730 if ($needPack1NAggr)
731 {
732 $filter = array();
733 foreach ($entity->GetPrimaryArray() as $primary)
734 {
735 $filter['='.$primary] = new CSQLWhereExpression(
736 '?#', mb_strtolower($entity->getCode()).'.'.$primary
737 );
738 }
739
740 $query = new Entity\Query($entity);
741 $query->addSelect(new Entity\ExpressionField('X', $expression[0], $elem['name']));
742 $query->setFilter($filter);
743 $query->setTableAliasPostfix('_sub');
744
745 $expression = array('('.$query->getQuery().')');
746
747 // double aggregation if init entity aggregated
748 if ($isInitEntityAggregated)
749 {
750 if ($elem['aggr'] == 'COUNT_DISTINCT')
751 {
752 $expression[0] = 'SUM('.$expression[0].')';
753 }
754 else
755 {
756 if ($elem['aggr'] === 'AVG')
757 {
758 $cntQuery = new Entity\Query($entity);
759 $cntQuery->addSelect(new Entity\ExpressionField('CNT', 'COUNT(1)', $elem['name']));
760 $cntQuery->setFilter($filter);
761 $cntQuery->setTableAliasPostfix('_cnt');
762
763 $sumQuery = new Entity\Query($entity);
764 $sumQuery->addSelect(new Entity\ExpressionField(
765 'SUM', 'SUM('.$localDef.')', $elem['name'])
766 );
767 $sumQuery->setFilter($filter);
768 $sumQuery->setTableAliasPostfix('_sum');
769
770 if (!is_array($totalInfo))
771 {
772 $totalInfo = [];
773 }
774 $totalInfo['average'] = [
775 'type' => 'average',
776 'cnt' => [
777 'alias' => $sourceAlias.'_AVGCNT',
778 'def' => [
779 'data_type' => 'integer',
780 'expression' => ['SUM(('.$cntQuery->getQuery().'))']
781 ]
782 ],
783 'sum' => [
784 'alias' => $sourceAlias.'_AVGSUM',
785 'def' => [
786 'data_type' => $dataType,
787 'expression' => ['SUM(('.$sumQuery->getQuery().'))']
788 ]
789 ]
790 ];
791
792 unset($cntQuery, $sumQuery);
793 }
794 $expression[0] = $elem['aggr'].'('.$expression[0].')';
795 }
796 }
797 }
798 }
799
800 if($prcnt !== '')
801 {
802 $alias = $alias.'_PRCNT';
803 $dataType = 'integer';
804
805 if($prcnt === 'self_column')
806 {
807 if(empty($expression))
808 {
809 $expression = array('%s', $elem['name']);
810 }
811 }
812 else
813 {
814 if(empty($expression))
815 {
816 $localDef = '%s';
817 $localMembers = array($elem['name']);
818 }
819 else
820 {
821 $localDef = $expression[0];
822 $localMembers = array_slice($expression, 1);
823 }
824
825 list($remoteAlias, $remoteSelect) = self::prepareSelectViewElement(
826 $select[$prcnt],
827 $select,
828 $isInitEntityAggregated,
829 $fList,
830 $fChainList,
831 $helperClassName,
832 $entity
833 );
834
835 if(is_array($remoteSelect) && !empty($remoteSelect['expression']))
836 {
837 // remote field is expression
838 $remoteDef = $remoteSelect['expression'][0];
839 $remoteMembers = array_slice($remoteSelect['expression'], 1);
840
841 $alias = $alias.'_FROM_'.$remoteAlias;
842 }
843 else
844 {
845 // remote field is usual field
846 $remoteDef = '%s';
847 $remoteMembers = array($remoteSelect);
848
849 $remoteAlias = Entity\QueryChain::getAliasByDefinition($entity, $remoteSelect);
850 $alias = $alias.'_FROM_'.$remoteAlias;
851 }
852
853 // Expression
854 // 'ROUND(STATUS / ID * 100)'
855 // 'ROUND( (EX1(F1, F2)) / (EX2(F3, F1)) * 100)',
856 // F1, F2, F3, F1
857 $exprDef = '('.$localDef.') / ('.$remoteDef.') * 100';
858 $expression = array_merge(array($exprDef), $localMembers, $remoteMembers);
859
860 // Total expression
861 if(!is_array($totalInfo))
862 {
863 $totalInfo = [];
864 }
865 $totalInfo['prcntFromCol'] = [
866 'type' => 'prcntFromCol',
867 'local' => [
868 'alias' => $sourceAlias.'_PRCNTFC',
869 'def' => [
870 'data_type' => $dataType,
871 'expression' => array_merge(array($localDef), $localMembers)
872 ]
873 ],
874 'remote' => [
875 'alias' => $remoteAlias
876 ]
877 ];
878 }
879 }
880
881 $selectElem = array(
882 'data_type' => $dataType,
883 'expression' => $expression
884 );
885 }
886
887 return array($alias, $selectElem, $totalInfo);
888 }
889
890 public static function getFullColumnTitle($view, $viewColumns, $fullHumanTitles)
891 {
892 $title = $fullHumanTitles[$view['fieldName']];
893
894 if (!empty($view['aggr']))
895 {
896 $title .= ' ('.GetMessage('REPORT_SELECT_CALC_VAR_'.$view['aggr']).')';
897 }
898
899 if($view['prcnt'] <> '')
900 {
901 if($view['prcnt'] == 'self_column')
902 {
903 $title .= ' (%)';
904 }
905 else
906 {
907 $byTitle = self::getFullColumnTitle($viewColumns[$view['prcnt']], $viewColumns, $fullHumanTitles);
908 $title .= ' ('.GetMessage('REPORT_PRCNT_FROM_TITLE').' '.$byTitle.')';
909 }
910 }
911
912 return $title;
913 }
914
915 public static function isColumnPercentable($view, $helperClassName)
916 {
917 /*
918 1. any integer
919 2. any float
920 3. boolean with aggr
921 4. any with COUNT_DISTINCT aggr
922 */
923
924 $dataType = call_user_func(array($helperClassName, 'getFieldDataType'), $view['field']);
925
927 if (($dataType === 'integer' || $dataType === 'float')
928 && (!$view['isUF'] || $view['ufInfo']['MULTIPLE'] !== 'Y'))
929 {
930 return true;
931 }
932 elseif ($dataType === 'boolean' && $view['aggr'] === 'SUM'
933 && (!$view['isUF'] || $view['ufInfo']['MULTIPLE'] !== 'Y'))
934 {
935 return true;
936 }
937 elseif ($view['aggr'] === 'COUNT_DISTINCT')
938 {
939 return true;
940 }
941
942 return false;
943 }
944
945 public static function getTotalCountableAggregationFunctions()
946 {
947 return static::$totalCountableAggrFuncs;
948 }
949
950 public static function isTotalCountableAggregationFunction($aggr)
951 {
952 return in_array($aggr, static::getTotalCountableAggregationFunctions(), true);
953 }
954
955 public static function isColumnTotalCountable($view, $helperClassName)
956 {
958 $dataType = call_user_func(array($helperClassName, 'getFieldDataType'), $view['field']);
959
960 if (($dataType === 'integer' || $dataType === 'float')
961 && empty($view['aggr'])
962 && (!$view['isUF'] || $view['ufInfo']['MULTIPLE'] !== 'Y'))
963 {
964 return true;
965 }
966 elseif (static::isTotalCountableAggregationFunction($view['aggr']))
967 {
968 return true;
969 }
970
971 return false;
972 }
973
974 public static function appendHrefSelectElements(&$elem, $fList, $entity, $helper_class, &$select, &$runtime)
975 {
976 // default href assign
977 if (empty($elem['href']))
978 {
979 $href = call_user_func(array($helper_class, 'getDefaultElemHref'), $elem, $fList);
980
981 if (!empty($href))
982 {
983 $elem['href'] = $href;
984 }
985 }
986
987 // user defined or default href
988 if (!empty($elem['href']))
989 {
990 $matches = array();
991 preg_match_all('/#([a-zA-Z0-9_\.:\\\\]+)#/', $elem['href']['pattern'], $matches);
992
993 if (!empty($matches[1]))
994 {
995 foreach ($matches[1] as $match)
996 {
997 // by default get definition from href
998 $fieldDefinition = $match;
999 $fieldAggr = null;
1000
1001 // try to find extended info about href element
1002 if (!empty($elem['href']['elements'][$fieldDefinition]))
1003 {
1004 $fieldDefinition = $elem['href']['elements'][$match]['name'];
1005
1006 if (!empty($elem['href']['elements'][$match]['aggr']))
1007 {
1008 $fieldAggr = $elem['href']['elements'][$match]['aggr'];
1009 }
1010 else
1011 {
1012 // normalize
1013 $elem['href']['elements'][$match]['aggr'] = null;
1014 }
1015 }
1016 else
1017 {
1018 // normalize
1019 $elem['href']['elements'][$fieldDefinition] = array(
1020 'name' => $fieldDefinition,
1021 'aggr' => null
1022 );
1023 }
1024
1025 $fieldAlias = Entity\QueryChain::getAliasByDefinition($entity, $fieldDefinition);
1026
1027 // add to select
1028 if (empty($fieldAggr) && !in_array($fieldDefinition, $select, true))
1029 {
1030 $select[$fieldAlias] = $fieldDefinition;
1031 }
1032 elseif (!empty($fieldAggr))
1033 {
1034 $fieldAlias = $fieldAggr.'_'.$fieldAlias;
1035
1036 // add if not exists
1037 if (!array_key_exists($fieldAlias, $select))
1038 {
1039 // get field object
1040 $chain = Entity\QueryChain::getChainByDefinition($entity, $fieldDefinition);
1041 $field = $chain->getLastElement()->getValue();
1042
1043 // add to select
1044 if ($fieldAggr == 'COUNT_DISTINCT')
1045 {
1046 $runtime[$fieldAlias] = array(
1047 'data_type' => 'integer', // until we don't have group_concat
1048 'expression' => array(
1049 'COUNT(DISTINCT %s)', $fieldDefinition
1050 )
1051 );
1052 }
1053 else
1054 {
1055 $runtime[$fieldAlias] = array(
1056 'data_type' => call_user_func(array($helper_class, 'getFieldDataType') ,$field),
1057 'expression' => array(
1058 $fieldAggr.'(%s)', $fieldDefinition
1059 )
1060 );
1061 }
1062
1063 $select[] = $fieldAlias;
1064 }
1065 }
1066 } // href pattern and elements saved
1067 }
1068 }
1069 }
1070
1071
1072 public static function generateValueUrl($elem, $dataRow, $entity)
1073 {
1074 // create url
1075 $urlParams = array();
1076
1077 foreach ($elem['href']['elements'] as $hrefElem)
1078 {
1079 $alias = Entity\QueryChain::getAliasByDefinition($entity, $hrefElem['name']);
1080
1081 if (!empty($hrefElem['aggr']))
1082 {
1083 $alias = $hrefElem['aggr'].'_'.$alias;
1084 }
1085
1086 $urlParams[$hrefElem['name']] = $dataRow[$alias];
1087 }
1088
1089 return CComponentEngine::MakePathFromTemplate($elem['href']['pattern'], $urlParams);
1090 }
1091
1092
1093 public static function rewriteUserShortName(&$select, &$runtime, $format, $entity, $grc =false)
1094 {
1095 foreach ($select as $k => $def)
1096 {
1097 if (
1098 (is_string($def) && (mb_substr($def, -11) == '.SHORT_NAME' || $def === 'SHORT_NAME'))
1099 || (is_array($def) && count($def['expression']) === 2 && mb_substr($def['expression'][1], -11) == '.SHORT_NAME')
1100 )
1101 {
1102 $definition = is_string($def) ? $def : $def['expression'][1];
1103 $pre = mb_substr($definition, 0, -11);
1104 $_alias = Entity\QueryChain::getAliasByDefinition($entity, $definition);
1105
1106 $expression = self::getFormattedNameExpr($format, $pre);
1107
1108 // show login if names is null
1109 global $DB;
1110 $nNameElements = count($expression) - 1;
1111 if ($nNameElements < 1)
1112 {
1113 $expression = array(
1114 $DB->IsNull('%s', '\' \''),
1115 (empty($pre) ? '' : $pre.'.').'LOGIN'
1116 );
1117 }
1118 else
1119 {
1120 $arConcatNameElements = array($DB->IsNull('%s', '\' \''));
1121 $n = $nNameElements;
1122 while (--$n > 0)
1123 $arConcatNameElements[] = $DB->IsNull('%s', '\' \'');
1124 $strConcatNameElements = call_user_func_array(array($DB, 'concat'), $arConcatNameElements);
1125 $expression[0] = 'CASE WHEN '.$DB->Length('LTRIM(RTRIM('.$strConcatNameElements.'))').'>0 THEN '.$expression[0].' ELSE %s END';
1126 for ($i = 1; $i <= $nNameElements; $i++)
1127 $expression[] = $expression[$i];
1128 $expression[] = (empty($pre) ? '' : $pre.'.').'LOGIN';
1129 }
1130
1131 // modify select
1132 unset($select[$k]);
1133
1134 if (is_string($def))
1135 {
1136 $runtime[$_alias] = array(
1137 'data_type' => 'string',
1138 'expression' => $expression
1139 );
1140 }
1141 else
1142 {
1143 // add aggr
1144 if (mb_substr($def['expression'][0], 0, 14) == 'COUNT(DISTINCT')
1145 {
1146 $_alias = 'COUNT_DISTINCT_'.$_alias;
1147 }
1148 elseif ($grc)
1149 {
1150 $_alias = 'GROUP_CONCAT_'.$_alias;
1151 }
1152
1153 $expression[0] = str_replace('%s', $expression[0], $def['expression'][0]);
1154
1155 $runtime[$_alias] = array(
1156 'data_type' => 'integer',
1157 'expression' => $expression
1158 );
1159 }
1160
1161 $select[] = $_alias;
1162 }
1163 }
1164 }
1165
1166 public static function getUniqueFieldsByTree($tree)
1167 {
1168 $list = array();
1169
1170 foreach ($tree as $treeElem)
1171 {
1172 $fieldDefinition = $treeElem['fieldName'];
1173 $field = $treeElem['field'];
1174 $branch = $treeElem['branch'];
1175
1176 $list[$fieldDefinition] = $field;
1177
1178 if (!empty($branch))
1179 {
1180 $list = array_merge($list, self::getUniqueFieldsByTree($branch));
1181 }
1182 }
1183
1184 return $list;
1185 }
1186
1187 public static function isValidFilterCompareVariation($fDefinition, $fType, $variation, $variations)
1188 {
1189 if (array_key_exists($fDefinition, $variations))
1190 {
1191 $vars = $variations[$fDefinition];
1192 }
1193 else
1194 {
1195 $vars = $variations[$fType];
1196 }
1197
1198 return in_array($variation, $vars, true);
1199 }
1200
1201 public static function addFreshDefaultReports($vReports, $ownerId)
1202 {
1203 foreach ($vReports as &$dReport)
1204 {
1205 $dReport['settings']['mark_default'] = $dReport['mark_default'] ?? 0;
1206 $dReport['settings']['title'] = $dReport['title'] ?? '';
1207 $dReport['settings']['description'] = $dReport['description'] ?? '';
1208 $dReport['settings']['owner'] = $ownerId;
1209
1210 self::Add($dReport['settings']);
1211 }
1212 unset($dReport);
1213 }
1214
1215 public static function sqlizeFilter($filter)
1216 {
1217 $newFilter = [];
1218
1219 foreach ($filter as $fId => $filterInfo)
1220 {
1221 $iFilterItems = [];
1222
1223 foreach ($filterInfo as $key => $subFilter)
1224 {
1225 // collect only fields and subfilters
1226 if ($key === 'LOGIC')
1227 {
1228 continue;
1229 }
1230
1231 if ($subFilter['type'] == 'field')
1232 {
1233
1234 $compare = self::$iBlockCompareVariations[$subFilter['compare']];
1235 $name = $subFilter['name'];
1236 $value = $subFilter['value'];
1237
1238 switch ($compare)
1239 {
1240 case '!':
1241 case '!%':
1242 $iFilterItems[] = [
1243 'LOGIC' => 'OR',
1244 $compare.$name => $value,
1245 '='.$name => false
1246 ];
1247 break;
1249 case '>%':
1250 $compare = '';
1251 $value = $value.'%';
1252 default:
1253 $iFilterItems[] = [$compare.$name => $value];
1254 }
1255 }
1256 else if ($subFilter['type'] == 'filter')
1257 {
1258 // hold link to another filter
1259 $iFilterItems[] = 'FILTER_'.$subFilter['name'];
1260 }
1261 }
1262
1263 if (!empty($iFilterItems))
1264 {
1265 $iFilterItems['LOGIC'] = $filterInfo['LOGIC'];
1266 $newFilter[$fId] = $iFilterItems;
1267 }
1268 }
1269
1270 return $newFilter;
1271 }
1272
1273 public static function makeSingleFilter($filter)
1274 {
1275 $filter = self::sqlizeFilter($filter);
1276
1277 // the 0s element should be in the end
1278 $filter = array_reverse($filter, true);
1279
1280 foreach ($filter as &$filterInfo)
1281 {
1282 foreach ($filterInfo as $key => $subFilter)
1283 {
1284 if ($key !== 'LOGIC' && is_string($subFilter))
1285 {
1286 $sfId = mb_substr($subFilter, 7);
1287
1288 if (array_key_exists($sfId, $filter))
1289 {
1290 $filterInfo[$key] = &$filter[$sfId];
1291 }
1292 }
1293 }
1294 }
1295
1296 return array_key_exists(0, $filter) ? $filter[0] : array();
1297 }
1298
1299 public static function collectFullHumanTitles($tree)
1300 {
1301 $fullHumanTitles = array();
1302
1303 foreach ($tree as $treeElem)
1304 {
1305 //$fullHumanTitles[$treeElem['fieldName']] = $treeElem['fullHumanTitle'];
1306 $fullHumanTitle = $treeElem['fullHumanTitle'];
1307 if (mb_substr($treeElem['fieldName'], -11) == '.SHORT_NAME') // hack for ticket 0037576
1308 {
1309 $pos = mb_strrpos($fullHumanTitle, ':');
1310 if ($pos !== false)
1311 {
1312 $fullHumanTitle = mb_substr($fullHumanTitle, 0, $pos);
1313 }
1314 }
1315 $fullHumanTitles[$treeElem['fieldName']] = $fullHumanTitle;
1316 unset($fullHumanTitle);
1317
1318 if (!empty($treeElem['branch']))
1319 {
1320 $fullHumanTitles = array_merge($fullHumanTitles, self::collectFullHumanTitles($treeElem['branch']));
1321 }
1322 }
1323
1324 return $fullHumanTitles;
1325 }
1326
1327 public static function getFormattedNameExpr($format, $defPrefix)
1328 {
1329 global $DB;
1330
1331 $values = array(
1332 '#NAME#' => array($DB->IsNull('%s', '\' \''), 'NAME'),
1333 '#NAME_SHORT#' => array($DB->Concat("UPPER(".$DB->Substr($DB->IsNull('%s', '\' \''), 1, 1).")", "'.'"), 'NAME'),
1334 '#SECOND_NAME#' => array($DB->IsNull('%s', '\' \''), 'SECOND_NAME'),
1335 '#SECOND_NAME_SHORT#' => array($DB->Concat("UPPER(".$DB->Substr($DB->IsNull('%s', '\' \''), 1, 1).")", "'.'"), 'SECOND_NAME'),
1336 '#LAST_NAME#' => array($DB->IsNull('%s', '\' \''), 'LAST_NAME'),
1337 '#LAST_NAME_SHORT#' => array($DB->Concat("UPPER(".$DB->Substr($DB->IsNull('%s', '\' \''), 1, 1).")", "'.'"), 'LAST_NAME')
1338 );
1339
1340 if (empty($format))
1341 {
1342 $format = '#LAST_NAME# #NAME_SHORT#';
1343 }
1344
1345 $sql_fields = array(null);
1346
1347 $matches = preg_split(
1348 '/('.join('|', array_keys($values)).')/',
1349 str_replace('%', '%%', $format),
1350 -1, PREG_SPLIT_DELIM_CAPTURE
1351 );
1352
1353 $expression = array();
1354
1355 foreach ($matches as $match)
1356 {
1357 if (array_key_exists($match, $values))
1358 {
1359 $expression[] = $values[$match][0];
1360 $sql_fields[] = (empty($defPrefix) ? '' : $defPrefix.'.').$values[$match][1];
1361 }
1362 elseif ($match !== '')
1363 {
1364 $expression[] = "'".$match."'";
1365 }
1366 }
1367
1368 $expression = call_user_func_array(array($DB, 'Concat'), $expression);
1369
1370 $sql_fields[0] = $expression;
1371
1372 return $sql_fields;
1373 }
1374
1375}
1376
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
Определения report.php:1380
Определения report.php:1378
Определения report.php:6
static isAlternateColumnPhraseExists($helperClass, $messageCode)
Определения report.php:333
static Update($ID, $settings)
Определения report.php:79
static clearViewParams($id)
Определения report.php:216
static Delete($ID)
Определения report.php:130
static fillFilterReferenceColumns(&$filters, &$fieldList, $helperClass)
Определения report.php:542
static $alternateColumnPhrases
Определения report.php:11
static $iBlockCompareVariations
Определения report.php:13
static collectFullHumanTitles($tree)
Определения report.php:1299
static generateColumnTree($chains, $initEntity, $helper_class, $level=0)
Определения report.php:361
static getViewParams($id, $templateName)
Определения report.php:195
static rewriteUserShortName(&$select, &$runtime, $format, $entity, $grc=false)
Определения report.php:1093
static generateValueUrl($elem, $dataRow, $entity)
Определения report.php:1072
static GetList($owner='')
Определения report.php:160
static addFreshDefaultReports($vReports, $ownerId)
Определения report.php:1201
static Add($settings)
Определения report.php:27
static getFormattedNameExpr($format, $defPrefix)
Определения report.php:1327
static makeSingleFilter($filter)
Определения report.php:1273
static $totalCountableAggrFuncs
Определения report.php:7
static generateChains($strChains, $initEntity, $initKey)
Определения report.php:252
static setViewParams($id, $templateName, $strParams)
Определения report.php:170
static GetCountInt($owner='')
Определения report.php:240
static checkSelectViewElementCyclicDependency($select, $elemIndex)
Определения report.php:572
static getAlternateColumnPhrase($helperClass, $messageCode)
Определения report.php:349
static attachLangToColumnTree(&$tree, $initEntity, $helperClass, $preTitle=array())
Определения report.php:421
static isValidFilterCompareVariation($fDefinition, $fType, $variation, $variations)
Определения report.php:1187
static getUniqueFieldsByTree($tree)
Определения report.php:1166
static initializeAlternateColumnPhrases($helperClass)
Определения report.php:310
static sqlizeFilter($filter)
Определения report.php:1215
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
if($ajaxMode) $ID
Определения get_user.php:27
$entity
if(Loader::includeModule( 'bitrix24')) elseif(Loader::includeModule('intranet') &&CIntranetUtils::getPortalZone() !=='ru') $description
Определения .description.php:24
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
global $DB
Определения cron_frame.php:29
global $USER
Определения csv_new_run.php:40
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
GetMessage($name, $aReplace=null)
Определения tools.php:3397
$name
Определения menu_edit.php:35
Определения ufield.php:9
$settings
Определения product_settings.php:43
return false
Определения prolog_main_admin.php:185
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$matches
Определения index.php:22
$k
Определения template_pdf.php:567
foreach($arTemplatesList as $templ) if(mb_strpos($templ["NAME"] $def
Определения template_copy.php:264
$n
Определения update_log.php:107
$dbRes
Определения yandex_detail.php:168
$fields
Определения yandex_run.php:501