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