1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9package Kernel::System::Stats::Dynamic::Ticket; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(:all); 15use Kernel::Language qw(Translatable); 16 17our @ObjectDependencies = ( 18 'Kernel::Config', 19 'Kernel::System::DB', 20 'Kernel::System::DynamicField', 21 'Kernel::System::DynamicField::Backend', 22 'Kernel::System::Lock', 23 'Kernel::System::Log', 24 'Kernel::System::Priority', 25 'Kernel::System::Queue', 26 'Kernel::System::Service', 27 'Kernel::System::SLA', 28 'Kernel::System::State', 29 'Kernel::System::Ticket', 30 'Kernel::System::DateTime', 31 'Kernel::System::Type', 32 'Kernel::System::User', 33); 34 35sub new { 36 my ( $Type, %Param ) = @_; 37 38 # allocate new hash for object 39 my $Self = {}; 40 bless( $Self, $Type ); 41 42 # get the dynamic fields for ticket object 43 $Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 44 Valid => 1, 45 ObjectType => ['Ticket'], 46 ); 47 48 return $Self; 49} 50 51sub GetObjectName { 52 my ( $Self, %Param ) = @_; 53 54 return 'TicketAccumulation'; 55} 56 57sub GetObjectBehaviours { 58 my ( $Self, %Param ) = @_; 59 60 my %Behaviours = ( 61 ProvidesDashboardWidget => 1, 62 ); 63 64 return %Behaviours; 65} 66 67sub GetObjectAttributes { 68 my ( $Self, %Param ) = @_; 69 70 # get needed objects 71 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 72 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 73 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 74 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 75 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 76 my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); 77 my $LockObject = $Kernel::OM->Get('Kernel::System::Lock'); 78 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 79 80 my $ValidAgent = 0; 81 if ( 82 defined $ConfigObject->Get('Stats::UseInvalidAgentInStats') 83 && ( $ConfigObject->Get('Stats::UseInvalidAgentInStats') == 0 ) 84 ) 85 { 86 $ValidAgent = 1; 87 } 88 89 # Get user list without the out of office message, because of the caching in the statistics 90 # and not meaningful with a date selection. 91 my %UserList = $UserObject->UserList( 92 Type => 'Long', 93 Valid => $ValidAgent, 94 NoOutOfOffice => 1, 95 ); 96 97 # get state list 98 my %StateList = $StateObject->StateList( 99 UserID => 1, 100 ); 101 102 # get state type list 103 my %StateTypeList = $StateObject->StateTypeList( 104 UserID => 1, 105 ); 106 107 # get queue list 108 my %QueueList = $QueueObject->GetAllQueues(); 109 110 # get priority list 111 my %PriorityList = $PriorityObject->PriorityList( 112 UserID => 1, 113 ); 114 115 # get lock list 116 my %LockList = $LockObject->LockList( 117 UserID => 1, 118 ); 119 120 # get current time to fix bug#3830 121 my $Date = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d' ); 122 my $Today = sprintf "%s 23:59:59", $Date; 123 124 my @ObjectAttributes = ( 125 { 126 Name => Translatable('Queue'), 127 UseAsXvalue => 1, 128 UseAsValueSeries => 1, 129 UseAsRestriction => 1, 130 Element => 'QueueIDs', 131 Block => 'MultiSelectField', 132 Translation => 0, 133 TreeView => 1, 134 Values => \%QueueList, 135 }, 136 { 137 Name => Translatable('State'), 138 UseAsXvalue => 1, 139 UseAsValueSeries => 1, 140 UseAsRestriction => 1, 141 Element => 'StateIDs', 142 Block => 'MultiSelectField', 143 Values => \%StateList, 144 }, 145 { 146 Name => Translatable('State Type'), 147 UseAsXvalue => 1, 148 UseAsValueSeries => 1, 149 UseAsRestriction => 1, 150 Element => 'StateTypeIDs', 151 Block => 'MultiSelectField', 152 Values => \%StateTypeList, 153 }, 154 { 155 Name => Translatable('Priority'), 156 UseAsXvalue => 1, 157 UseAsValueSeries => 1, 158 UseAsRestriction => 1, 159 Element => 'PriorityIDs', 160 Block => 'MultiSelectField', 161 Values => \%PriorityList, 162 }, 163 { 164 Name => Translatable('Created in Queue'), 165 UseAsXvalue => 1, 166 UseAsValueSeries => 1, 167 UseAsRestriction => 1, 168 Element => 'CreatedQueueIDs', 169 Block => 'MultiSelectField', 170 Translation => 0, 171 TreeView => 1, 172 Values => \%QueueList, 173 }, 174 { 175 Name => Translatable('Created Priority'), 176 UseAsXvalue => 1, 177 UseAsValueSeries => 1, 178 UseAsRestriction => 1, 179 Element => 'CreatedPriorityIDs', 180 Block => 'MultiSelectField', 181 Values => \%PriorityList, 182 }, 183 { 184 Name => Translatable('Created State'), 185 UseAsXvalue => 1, 186 UseAsValueSeries => 1, 187 UseAsRestriction => 1, 188 Element => 'CreatedStateIDs', 189 Block => 'MultiSelectField', 190 Values => \%StateList, 191 }, 192 { 193 Name => Translatable('Lock'), 194 UseAsXvalue => 1, 195 UseAsValueSeries => 1, 196 UseAsRestriction => 1, 197 Element => 'LockIDs', 198 Block => 'MultiSelectField', 199 Values => \%LockList, 200 }, 201 { 202 Name => Translatable('Title'), 203 UseAsXvalue => 0, 204 UseAsValueSeries => 0, 205 UseAsRestriction => 1, 206 Element => 'Title', 207 Block => 'InputField', 208 }, 209 { 210 Name => Translatable('From'), 211 UseAsXvalue => 0, 212 UseAsValueSeries => 0, 213 UseAsRestriction => 1, 214 Element => 'MIMEBase_From', 215 Block => 'InputField', 216 }, 217 { 218 Name => Translatable('To'), 219 UseAsXvalue => 0, 220 UseAsValueSeries => 0, 221 UseAsRestriction => 1, 222 Element => 'MIMEBase_To', 223 Block => 'InputField', 224 }, 225 { 226 Name => Translatable('Cc'), 227 UseAsXvalue => 0, 228 UseAsValueSeries => 0, 229 UseAsRestriction => 1, 230 Element => 'MIMEBase_Cc', 231 Block => 'InputField', 232 }, 233 { 234 Name => Translatable('Subject'), 235 UseAsXvalue => 0, 236 UseAsValueSeries => 0, 237 UseAsRestriction => 1, 238 Element => 'MIMEBase_Subject', 239 Block => 'InputField', 240 }, 241 { 242 Name => Translatable('Text'), 243 UseAsXvalue => 0, 244 UseAsValueSeries => 0, 245 UseAsRestriction => 1, 246 Element => 'MIMEBase_Body', 247 Block => 'InputField', 248 }, 249 { 250 Name => Translatable('Create Time'), 251 UseAsXvalue => 1, 252 UseAsValueSeries => 1, 253 UseAsRestriction => 1, 254 Element => 'CreateTime', 255 TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong', 256 Block => 'Time', 257 TimeStop => $Today, 258 Values => { 259 TimeStart => 'TicketCreateTimeNewerDate', 260 TimeStop => 'TicketCreateTimeOlderDate', 261 }, 262 }, 263 { 264 Name => Translatable('Last changed times'), 265 UseAsXvalue => 1, 266 UseAsValueSeries => 1, 267 UseAsRestriction => 1, 268 Element => 'LastChangeTime', 269 TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong', 270 Block => 'Time', 271 TimeStop => $Today, 272 Values => { 273 TimeStart => 'TicketLastChangeTimeNewerDate', 274 TimeStop => 'TicketLastChangeTimeOlderDate', 275 }, 276 }, 277 { 278 Name => Translatable('Change times'), 279 UseAsXvalue => 1, 280 UseAsValueSeries => 1, 281 UseAsRestriction => 1, 282 Element => 'ChangeTime', 283 TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong', 284 Block => 'Time', 285 TimeStop => $Today, 286 Values => { 287 TimeStart => 'TicketChangeTimeNewerDate', 288 TimeStop => 'TicketChangeTimeOlderDate', 289 }, 290 }, 291 { 292 Name => Translatable('Pending until time'), 293 UseAsXvalue => 1, 294 UseAsValueSeries => 1, 295 UseAsRestriction => 1, 296 Element => 'PendingUntilTime', 297 TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong', 298 Block => 'Time', 299 TimeStop => $Today, 300 Values => { 301 TimeStart => 'TicketPendingTimeNewerDate', 302 TimeStop => 'TicketPendingTimeOlderDate', 303 }, 304 }, 305 { 306 Name => Translatable('Close Time'), 307 UseAsXvalue => 1, 308 UseAsValueSeries => 1, 309 UseAsRestriction => 1, 310 Element => 'CloseTime2', 311 TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong', 312 Block => 'Time', 313 TimeStop => $Today, 314 Values => { 315 TimeStart => 'TicketCloseTimeNewerDate', 316 TimeStop => 'TicketCloseTimeOlderDate', 317 }, 318 }, 319 { 320 Name => Translatable('Escalation'), 321 UseAsXvalue => 1, 322 UseAsValueSeries => 1, 323 UseAsRestriction => 1, 324 Element => 'EscalationTime', 325 TimePeriodFormat => 'DateInputFormatLong', # 'DateInputFormat', 326 Block => 'Time', 327 TimeStop => $Today, 328 Values => { 329 TimeStart => 'TicketEscalationTimeNewerDate', 330 TimeStop => 'TicketEscalationTimeOlderDate', 331 }, 332 }, 333 { 334 Name => Translatable('Escalation - First Response Time'), 335 UseAsXvalue => 1, 336 UseAsValueSeries => 1, 337 UseAsRestriction => 1, 338 Element => 'EscalationResponseTime', 339 TimePeriodFormat => 'DateInputFormatLong', # 'DateInputFormat', 340 Block => 'Time', 341 TimeStop => $Today, 342 Values => { 343 TimeStart => 'TicketEscalationResponseTimeNewerDate', 344 TimeStop => 'TicketEscalationResponseTimeOlderDate', 345 }, 346 }, 347 { 348 Name => Translatable('Escalation - Update Time'), 349 UseAsXvalue => 1, 350 UseAsValueSeries => 1, 351 UseAsRestriction => 1, 352 Element => 'EscalationUpdateTime', 353 TimePeriodFormat => 'DateInputFormatLong', # 'DateInputFormat', 354 Block => 'Time', 355 TimeStop => $Today, 356 Values => { 357 TimeStart => 'TicketEscalationUpdateTimeNewerDate', 358 TimeStop => 'TicketEscalationUpdateTimeOlderDate', 359 }, 360 }, 361 { 362 Name => Translatable('Escalation - Solution Time'), 363 UseAsXvalue => 1, 364 UseAsValueSeries => 1, 365 UseAsRestriction => 1, 366 Element => 'EscalationSolutionTime', 367 TimePeriodFormat => 'DateInputFormatLong', # 'DateInputFormat', 368 Block => 'Time', 369 TimeStop => $Today, 370 Values => { 371 TimeStart => 'TicketEscalationSolutionTimeNewerDate', 372 TimeStop => 'TicketEscalationSolutionTimeOlderDate', 373 }, 374 }, 375 ); 376 377 if ( $ConfigObject->Get('Ticket::Service') ) { 378 379 # get service list 380 my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList( 381 KeepChildren => $ConfigObject->Get('Ticket::Service::KeepChildren'), 382 UserID => 1, 383 ); 384 385 # get sla list 386 my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList( 387 UserID => 1, 388 ); 389 390 my @ObjectAttributeAdd = ( 391 { 392 Name => Translatable('Service'), 393 UseAsXvalue => 1, 394 UseAsValueSeries => 1, 395 UseAsRestriction => 1, 396 Element => 'ServiceIDs', 397 Block => 'MultiSelectField', 398 Translation => 0, 399 TreeView => 1, 400 Values => \%Service, 401 }, 402 { 403 Name => Translatable('SLA'), 404 UseAsXvalue => 1, 405 UseAsValueSeries => 1, 406 UseAsRestriction => 1, 407 Element => 'SLAIDs', 408 Block => 'MultiSelectField', 409 Translation => 0, 410 Values => \%SLA, 411 }, 412 ); 413 414 unshift @ObjectAttributes, @ObjectAttributeAdd; 415 } 416 417 if ( $ConfigObject->Get('Ticket::Type') ) { 418 419 # get ticket type list 420 my %Type = $Kernel::OM->Get('Kernel::System::Type')->TypeList( 421 UserID => 1, 422 ); 423 424 my %ObjectAttribute1 = ( 425 Name => Translatable('Type'), 426 UseAsXvalue => 1, 427 UseAsValueSeries => 1, 428 UseAsRestriction => 1, 429 Element => 'TypeIDs', 430 Block => 'MultiSelectField', 431 Translation => 0, 432 Values => \%Type, 433 ); 434 435 unshift @ObjectAttributes, \%ObjectAttribute1; 436 } 437 438 if ( $ConfigObject->Get('Stats::UseAgentElementInStats') ) { 439 440 my @ObjectAttributeAdd = ( 441 { 442 Name => Translatable('Agent/Owner'), 443 UseAsXvalue => 1, 444 UseAsValueSeries => 1, 445 UseAsRestriction => 1, 446 Element => 'OwnerIDs', 447 Block => 'MultiSelectField', 448 Translation => 0, 449 Values => \%UserList, 450 }, 451 { 452 Name => Translatable('Created by Agent/Owner'), 453 UseAsXvalue => 1, 454 UseAsValueSeries => 1, 455 UseAsRestriction => 1, 456 Element => 'CreatedUserIDs', 457 Block => 'MultiSelectField', 458 Translation => 0, 459 Values => \%UserList, 460 }, 461 { 462 Name => Translatable('Responsible'), 463 UseAsXvalue => 1, 464 UseAsValueSeries => 1, 465 UseAsRestriction => 1, 466 Element => 'ResponsibleIDs', 467 Block => 'MultiSelectField', 468 Translation => 0, 469 Values => \%UserList, 470 }, 471 ); 472 473 push @ObjectAttributes, @ObjectAttributeAdd; 474 } 475 476 if ( $ConfigObject->Get('Stats::CustomerIDAsMultiSelect') ) { 477 478 # Get all CustomerIDs which are related to a ticket. 479 $DBObject->Prepare( 480 SQL => "SELECT DISTINCT customer_id FROM ticket", 481 ); 482 483 # fetch the result 484 my %CustomerID; 485 while ( my @Row = $DBObject->FetchrowArray() ) { 486 if ( $Row[0] ) { 487 $CustomerID{ $Row[0] } = $Row[0]; 488 } 489 } 490 491 my %ObjectAttribute = ( 492 Name => Translatable('Customer ID'), 493 UseAsXvalue => 1, 494 UseAsValueSeries => 1, 495 UseAsRestriction => 1, 496 Element => 'CustomerID', 497 Block => 'MultiSelectField', 498 Values => \%CustomerID, 499 ); 500 501 push @ObjectAttributes, \%ObjectAttribute; 502 } 503 else { 504 505 my @CustomerIDAttributes = ( 506 { 507 Name => Translatable('CustomerID (complex search)'), 508 UseAsXvalue => 0, 509 UseAsValueSeries => 0, 510 UseAsRestriction => 1, 511 Element => 'CustomerID', 512 Block => 'InputField', 513 }, 514 { 515 Name => Translatable('CustomerID (exact match)'), 516 UseAsXvalue => 0, 517 UseAsValueSeries => 0, 518 UseAsRestriction => 1, 519 Element => 'CustomerIDRaw', 520 Block => 'InputField', 521 }, 522 ); 523 524 push @ObjectAttributes, @CustomerIDAttributes; 525 } 526 527 if ( $ConfigObject->Get('Stats::CustomerUserLoginsAsMultiSelect') ) { 528 529 # Get all CustomerUserLogins which are related to a tiket. 530 $DBObject->Prepare( 531 SQL => "SELECT DISTINCT customer_user_id FROM ticket", 532 ); 533 534 # fetch the result 535 my %CustomerUserIDs; 536 while ( my @Row = $DBObject->FetchrowArray() ) { 537 if ( $Row[0] ) { 538 $CustomerUserIDs{ $Row[0] } = $Row[0]; 539 } 540 } 541 542 my %ObjectAttribute = ( 543 Name => Translatable('Assigned to Customer User Login'), 544 UseAsXvalue => 1, 545 UseAsValueSeries => 1, 546 UseAsRestriction => 1, 547 Element => 'CustomerUserLoginRaw', 548 Block => 'MultiSelectField', 549 Values => \%CustomerUserIDs, 550 ); 551 552 push @ObjectAttributes, \%ObjectAttribute; 553 } 554 else { 555 556 my @CustomerUserAttributes = ( 557 { 558 Name => Translatable('Assigned to Customer User Login (complex search)'), 559 UseAsXvalue => 0, 560 UseAsValueSeries => 0, 561 UseAsRestriction => 1, 562 Element => 'CustomerUserLogin', 563 Block => 'InputField', 564 }, 565 { 566 Name => Translatable('Assigned to Customer User Login (exact match)'), 567 UseAsXvalue => 0, 568 UseAsValueSeries => 0, 569 UseAsRestriction => 1, 570 Element => 'CustomerUserLoginRaw', 571 Block => 'InputField', 572 CSSClass => 'CustomerAutoCompleteSimple', 573 HTMLDataAttributes => { 574 'customer-search-type' => 'CustomerUser', 575 }, 576 }, 577 ); 578 579 push @ObjectAttributes, @CustomerUserAttributes; 580 } 581 582 # Add always the field for the customer user login accessible tickets as auto complete field. 583 my %ObjectAttribute = ( 584 Name => Translatable('Accessible to Customer User Login (exact match)'), 585 UseAsXvalue => 0, 586 UseAsValueSeries => 0, 587 UseAsRestriction => 1, 588 Element => 'CustomerUserID', 589 Block => 'InputField', 590 CSSClass => 'CustomerAutoCompleteSimple', 591 HTMLDataAttributes => { 592 'customer-search-type' => 'CustomerUser', 593 }, 594 ); 595 push @ObjectAttributes, \%ObjectAttribute; 596 597 if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { 598 599 my %ObjectAttribute = ( 600 Name => Translatable('Archive Search'), 601 UseAsXvalue => 0, 602 UseAsValueSeries => 0, 603 UseAsRestriction => 1, 604 Element => 'SearchInArchive', 605 Block => 'SelectField', 606 Translation => 1, 607 Values => { 608 ArchivedTickets => Translatable('Archived tickets'), 609 NotArchivedTickets => Translatable('Unarchived tickets'), 610 AllTickets => Translatable('All tickets'), 611 }, 612 ); 613 614 push @ObjectAttributes, \%ObjectAttribute; 615 } 616 617 # get dynamic field backend object 618 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 619 620 # cycle trough the activated Dynamic Fields for this screen 621 DYNAMICFIELD: 622 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 623 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 624 625 # skip all fields not designed to be supported by statistics 626 my $IsStatsCondition = $DynamicFieldBackendObject->HasBehavior( 627 DynamicFieldConfig => $DynamicFieldConfig, 628 Behavior => 'IsStatsCondition', 629 ); 630 631 next DYNAMICFIELD if !$IsStatsCondition; 632 633 my $PossibleValuesFilter; 634 635 my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( 636 DynamicFieldConfig => $DynamicFieldConfig, 637 Behavior => 'IsACLReducible', 638 ); 639 640 if ($IsACLReducible) { 641 642 # get PossibleValues 643 my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( 644 DynamicFieldConfig => $DynamicFieldConfig, 645 ); 646 647 # convert possible values key => value to key => key for ACLs using a Hash slice 648 my %AclData = %{ $PossibleValues || {} }; 649 @AclData{ keys %AclData } = keys %AclData; 650 651 # set possible values filter from ACLs 652 my $ACL = $TicketObject->TicketAcl( 653 Action => 'AgentStats', 654 Type => 'DynamicField_' . $DynamicFieldConfig->{Name}, 655 ReturnType => 'Ticket', 656 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 657 Data => \%AclData || {}, 658 UserID => 1, 659 ); 660 if ($ACL) { 661 my %Filter = $TicketObject->TicketAclData(); 662 663 # convert Filer key => key back to key => value using map 664 %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } keys %Filter; 665 } 666 } 667 668 # get field html 669 my $DynamicFieldStatsParameter = $DynamicFieldBackendObject->StatsFieldParameterBuild( 670 DynamicFieldConfig => $DynamicFieldConfig, 671 PossibleValuesFilter => $PossibleValuesFilter, 672 ); 673 674 if ( IsHashRefWithData($DynamicFieldStatsParameter) ) { 675 676 # backward compatibility 677 if ( !$DynamicFieldStatsParameter->{Block} ) { 678 $DynamicFieldStatsParameter->{Block} = 'InputField'; 679 if ( IsHashRefWithData( $DynamicFieldStatsParameter->{Values} ) ) { 680 $DynamicFieldStatsParameter->{Block} = 'MultiSelectField'; 681 } 682 } 683 684 if ( $DynamicFieldStatsParameter->{Block} eq 'Time' ) { 685 686 # create object attributes (date/time fields) 687 my $TimePeriodFormat = $DynamicFieldStatsParameter->{TimePeriodFormat} || 'DateInputFormatLong'; 688 689 my %ObjectAttribute = ( 690 Name => $DynamicFieldStatsParameter->{Name}, 691 UseAsXvalue => 1, 692 UseAsValueSeries => 1, 693 UseAsRestriction => 1, 694 Element => $DynamicFieldStatsParameter->{Element}, 695 TimePeriodFormat => $TimePeriodFormat, 696 Block => $DynamicFieldStatsParameter->{Block}, 697 TimePeriodFormat => $TimePeriodFormat, 698 Values => { 699 TimeStart => 700 $DynamicFieldStatsParameter->{Element} 701 . '_GreaterThanEquals', 702 TimeStop => 703 $DynamicFieldStatsParameter->{Element} 704 . '_SmallerThanEquals', 705 }, 706 ); 707 push @ObjectAttributes, \%ObjectAttribute; 708 } 709 elsif ( $DynamicFieldStatsParameter->{Block} eq 'MultiSelectField' ) { 710 711 # create object attributes (multiple values) 712 my %ObjectAttribute = ( 713 Name => $DynamicFieldStatsParameter->{Name}, 714 UseAsXvalue => 1, 715 UseAsValueSeries => 1, 716 UseAsRestriction => 1, 717 Element => $DynamicFieldStatsParameter->{Element}, 718 Block => $DynamicFieldStatsParameter->{Block}, 719 Values => $DynamicFieldStatsParameter->{Values}, 720 Translation => $DynamicFieldStatsParameter->{TranslatableValues} || 0, 721 IsDynamicField => 1, 722 ShowAsTree => $DynamicFieldConfig->{Config}->{TreeView} || 0, 723 ); 724 push @ObjectAttributes, \%ObjectAttribute; 725 } 726 else { 727 728 # create object attributes (text fields) 729 my %ObjectAttribute = ( 730 Name => $DynamicFieldStatsParameter->{Name}, 731 UseAsXvalue => 0, 732 UseAsValueSeries => 0, 733 UseAsRestriction => 1, 734 Element => $DynamicFieldStatsParameter->{Element}, 735 Block => $DynamicFieldStatsParameter->{Block}, 736 ); 737 push @ObjectAttributes, \%ObjectAttribute; 738 } 739 } 740 } 741 742 return @ObjectAttributes; 743} 744 745sub GetStatElementPreview { 746 my ( $Self, %Param ) = @_; 747 748 return int rand 50; 749} 750 751sub GetStatElement { 752 my ( $Self, %Param ) = @_; 753 754 # get dynamic field backend object 755 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 756 my $DBObject = $Kernel::OM->Get('Kernel::System::DB'); 757 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 758 759 # escape search attributes for ticket search 760 my %AttributesToEscape = ( 761 'CustomerID' => 1, 762 'Title' => 1, 763 ); 764 765 # Map the CustomerID search parameter to CustomerIDRaw search parameter for the 766 # exact search match, if the 'Stats::CustomerIDAsMultiSelect' is active. 767 if ( $ConfigObject->Get('Stats::CustomerIDAsMultiSelect') ) { 768 $Param{CustomerIDRaw} = $Param{CustomerID}; 769 } 770 771 for my $ParameterName ( sort keys %Param ) { 772 if ( 773 $ParameterName =~ m{ \A DynamicField_ ( [a-zA-Z\d]+ ) (?: _ ( [a-zA-Z\d]+ ) )? \z }xms 774 ) 775 { 776 my $FieldName = $1; 777 my $Operator = $2; 778 779 # loop over the dynamic fields configured 780 DYNAMICFIELD: 781 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 782 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 783 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 784 785 # skip all fields that do not match with current field name 786 # without the 'DynamicField_' prefix 787 next DYNAMICFIELD if $DynamicFieldConfig->{Name} ne $FieldName; 788 789 # skip all fields not designed to be supported by statistics 790 my $IsStatsCondition = $DynamicFieldBackendObject->HasBehavior( 791 DynamicFieldConfig => $DynamicFieldConfig, 792 Behavior => 'IsStatsCondition', 793 ); 794 795 next DYNAMICFIELD if !$IsStatsCondition; 796 797 # get new search parameter 798 my $DynamicFieldStatsSearchParameter = $DynamicFieldBackendObject->StatsSearchFieldParameterBuild( 799 DynamicFieldConfig => $DynamicFieldConfig, 800 Value => $Param{$ParameterName}, 801 Operator => $Operator, 802 ); 803 804 # add new search parameter 805 if ( !IsHashRefWithData( $Param{"DynamicField_$FieldName"} ) ) { 806 $Param{"DynamicField_$FieldName"} = 807 $DynamicFieldStatsSearchParameter; 808 } 809 810 # extend search parameter 811 elsif ( IsHashRefWithData($DynamicFieldStatsSearchParameter) ) { 812 $Param{"DynamicField_$FieldName"} = { 813 %{ $Param{"DynamicField_$FieldName"} }, 814 %{$DynamicFieldStatsSearchParameter}, 815 }; 816 } 817 } 818 } 819 elsif ( $AttributesToEscape{$ParameterName} ) { 820 if ( ref $Param{$ParameterName} ) { 821 if ( ref $Param{$ParameterName} eq 'ARRAY' ) { 822 $Param{$ParameterName} = [ 823 map { $DBObject->QueryStringEscape( QueryString => $_ ) } 824 @{ $Param{$ParameterName} } 825 ]; 826 } 827 } 828 else { 829 $Param{$ParameterName} = $DBObject->QueryStringEscape( QueryString => $Param{$ParameterName} ); 830 } 831 } 832 } 833 834 if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { 835 $Param{SearchInArchive} ||= ''; 836 if ( $Param{SearchInArchive} eq 'AllTickets' ) { 837 $Param{ArchiveFlags} = [ 'y', 'n' ]; 838 } 839 elsif ( $Param{SearchInArchive} eq 'ArchivedTickets' ) { 840 $Param{ArchiveFlags} = ['y']; 841 } 842 else { 843 $Param{ArchiveFlags} = ['n']; 844 } 845 } 846 847 # search tickets 848 return $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch( 849 UserID => 1, 850 Result => 'COUNT', 851 Permission => 'ro', 852 Limit => 100_000_000, 853 %Param, 854 ) || 0; 855} 856 857sub ExportWrapper { 858 my ( $Self, %Param ) = @_; 859 860 # get needed objects 861 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 862 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 863 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 864 my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); 865 866 # wrap ids to used spelling 867 for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) { 868 ELEMENT: 869 for my $Element ( @{ $Param{$Use} } ) { 870 next ELEMENT if !$Element || !$Element->{SelectedValues}; 871 my $ElementName = $Element->{Element}; 872 my $Values = $Element->{SelectedValues}; 873 874 if ( $ElementName eq 'QueueIDs' || $ElementName eq 'CreatedQueueIDs' ) { 875 ID: 876 for my $ID ( @{$Values} ) { 877 next ID if !$ID; 878 $ID->{Content} = $QueueObject->QueueLookup( QueueID => $ID->{Content} ); 879 } 880 } 881 elsif ( $ElementName eq 'StateIDs' || $ElementName eq 'CreatedStateIDs' ) { 882 my %StateList = $StateObject->StateList( UserID => 1 ); 883 ID: 884 for my $ID ( @{$Values} ) { 885 next ID if !$ID; 886 $ID->{Content} = $StateList{ $ID->{Content} }; 887 } 888 } 889 elsif ( $ElementName eq 'PriorityIDs' || $ElementName eq 'CreatedPriorityIDs' ) { 890 my %PriorityList = $PriorityObject->PriorityList( UserID => 1 ); 891 ID: 892 for my $ID ( @{$Values} ) { 893 next ID if !$ID; 894 $ID->{Content} = $PriorityList{ $ID->{Content} }; 895 } 896 } 897 elsif ( 898 $ElementName eq 'OwnerIDs' 899 || $ElementName eq 'CreatedUserIDs' 900 || $ElementName eq 'ResponsibleIDs' 901 ) 902 { 903 ID: 904 for my $ID ( @{$Values} ) { 905 next ID if !$ID; 906 $ID->{Content} = $UserObject->UserLookup( UserID => $ID->{Content} ); 907 } 908 } 909 910 # locks and statustype don't have to wrap because they are never different 911 } 912 } 913 914 return \%Param; 915} 916 917sub ImportWrapper { 918 my ( $Self, %Param ) = @_; 919 920 # get needed objects 921 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 922 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 923 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 924 my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); 925 926 # wrap used spelling to ids 927 for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) { 928 ELEMENT: 929 for my $Element ( @{ $Param{$Use} } ) { 930 next ELEMENT if !$Element || !$Element->{SelectedValues}; 931 my $ElementName = $Element->{Element}; 932 my $Values = $Element->{SelectedValues}; 933 934 if ( $ElementName eq 'QueueIDs' || $ElementName eq 'CreatedQueueIDs' ) { 935 ID: 936 for my $ID ( @{$Values} ) { 937 next ID if !$ID; 938 if ( $QueueObject->QueueLookup( Queue => $ID->{Content} ) ) { 939 $ID->{Content} = $QueueObject->QueueLookup( Queue => $ID->{Content} ); 940 } 941 else { 942 $Kernel::OM->Get('Kernel::System::Log')->Log( 943 Priority => 'error', 944 Message => "Import: Can' find the queue $ID->{Content}!" 945 ); 946 $ID = undef; 947 } 948 } 949 } 950 elsif ( $ElementName eq 'StateIDs' || $ElementName eq 'CreatedStateIDs' ) { 951 ID: 952 for my $ID ( @{$Values} ) { 953 next ID if !$ID; 954 955 my %State = $StateObject->StateGet( 956 Name => $ID->{Content}, 957 Cache => 1, 958 ); 959 if ( $State{ID} ) { 960 $ID->{Content} = $State{ID}; 961 } 962 else { 963 $Kernel::OM->Get('Kernel::System::Log')->Log( 964 Priority => 'error', 965 Message => "Import: Can' find state $ID->{Content}!" 966 ); 967 $ID = undef; 968 } 969 } 970 } 971 elsif ( $ElementName eq 'PriorityIDs' || $ElementName eq 'CreatedPriorityIDs' ) { 972 my %PriorityList = $PriorityObject->PriorityList( UserID => 1 ); 973 my %PriorityIDs; 974 for my $Key ( sort keys %PriorityList ) { 975 $PriorityIDs{ $PriorityList{$Key} } = $Key; 976 } 977 ID: 978 for my $ID ( @{$Values} ) { 979 next ID if !$ID; 980 981 if ( $PriorityIDs{ $ID->{Content} } ) { 982 $ID->{Content} = $PriorityIDs{ $ID->{Content} }; 983 } 984 else { 985 $Kernel::OM->Get('Kernel::System::Log')->Log( 986 Priority => 'error', 987 Message => "Import: Can' find priority $ID->{Content}!" 988 ); 989 $ID = undef; 990 } 991 } 992 } 993 elsif ( 994 $ElementName eq 'OwnerIDs' 995 || $ElementName eq 'CreatedUserIDs' 996 || $ElementName eq 'ResponsibleIDs' 997 ) 998 { 999 ID: 1000 for my $ID ( @{$Values} ) { 1001 next ID if !$ID; 1002 1003 if ( $UserObject->UserLookup( UserLogin => $ID->{Content} ) ) { 1004 $ID->{Content} = $UserObject->UserLookup( 1005 UserLogin => $ID->{Content} 1006 ); 1007 } 1008 else { 1009 $Kernel::OM->Get('Kernel::System::Log')->Log( 1010 Priority => 'error', 1011 Message => "Import: Can' find user $ID->{Content}!" 1012 ); 1013 $ID = undef; 1014 } 1015 } 1016 } 1017 1018 # locks and status type don't have to wrap because they are never different 1019 } 1020 } 1021 return \%Param; 1022} 1023 10241; 1025