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::State;
10
11use strict;
12use warnings;
13
14our @ObjectDependencies = (
15    'Kernel::Config',
16    'Kernel::System::Cache',
17    'Kernel::System::DB',
18    'Kernel::System::Log',
19    'Kernel::System::SysConfig',
20    'Kernel::System::Valid',
21);
22
23=head1 NAME
24
25Kernel::System::State - state lib
26
27=head1 DESCRIPTION
28
29All ticket state functions.
30
31=head1 PUBLIC INTERFACE
32
33=head2 new()
34
35create an object
36
37    my $StateObject = $Kernel::OM->Get('Kernel::System::State');
38
39=cut
40
41sub new {
42    my ( $Type, %Param ) = @_;
43
44    # allocate new hash for object
45    my $Self = {};
46    bless( $Self, $Type );
47
48    $Self->{CacheType} = 'State';
49    $Self->{CacheTTL}  = 60 * 60 * 24 * 20;
50
51    return $Self;
52}
53
54=head2 StateAdd()
55
56add new states
57
58    my $ID = $StateObject->StateAdd(
59        Name    => 'New State',
60        Comment => 'some comment',
61        ValidID => 1,
62        TypeID  => 1,
63        UserID  => 123,
64    );
65
66=cut
67
68sub StateAdd {
69    my ( $Self, %Param ) = @_;
70
71    # check needed stuff
72    for (qw(Name ValidID TypeID UserID)) {
73        if ( !$Param{$_} ) {
74            $Kernel::OM->Get('Kernel::System::Log')->Log(
75                Priority => 'error',
76                Message  => "Need $_!"
77            );
78            return;
79        }
80    }
81
82    # get database object
83    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
84
85    # store data
86    return if !$DBObject->Do(
87        SQL => 'INSERT INTO ticket_state (name, valid_id, type_id, comments,'
88            . ' create_time, create_by, change_time, change_by)'
89            . ' VALUES (?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
90        Bind => [
91            \$Param{Name}, \$Param{ValidID}, \$Param{TypeID}, \$Param{Comment},
92            \$Param{UserID}, \$Param{UserID},
93        ],
94    );
95
96    # get new state id
97    return if !$DBObject->Prepare(
98        SQL   => 'SELECT id FROM ticket_state WHERE name = ?',
99        Bind  => [ \$Param{Name} ],
100        Limit => 1,
101    );
102
103    # fetch the result
104    my $ID;
105    while ( my @Row = $DBObject->FetchrowArray() ) {
106        $ID = $Row[0];
107    }
108
109    return if !$ID;
110
111    # delete cache
112    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
113        Type => $Self->{CacheType},
114    );
115
116    return $ID;
117}
118
119=head2 StateGet()
120
121get state attributes
122
123    my %State = $StateObject->StateGet(
124        Name  => 'New State',
125    );
126
127    my %State = $StateObject->StateGet(
128        ID    => 123,
129    );
130
131returns
132
133    my %State = (
134        Name       => "new",
135        ID         => 1,
136        TypeName   => "new",
137        TypeID     => 1,
138        ValidID    => 1,
139        CreateTime => "2010-11-29 11:04:04",
140        ChangeTime => "2010-11-29 11:04:04",
141        Comment    => "New ticket created by customer.",
142    );
143
144=cut
145
146sub StateGet {
147    my ( $Self, %Param ) = @_;
148
149    # check needed stuff
150    if ( !$Param{ID} && !$Param{Name} ) {
151        $Kernel::OM->Get('Kernel::System::Log')->Log(
152            Priority => 'error',
153            Message  => "Need ID or Name!"
154        );
155        return;
156    }
157
158    # check cache
159    my $CacheKey;
160    if ( $Param{Name} ) {
161        $CacheKey = 'StateGet::Name::' . $Param{Name};
162    }
163    else {
164        $CacheKey = 'StateGet::ID::' . $Param{ID};
165    }
166    my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
167        Type => $Self->{CacheType},
168        Key  => $CacheKey,
169    );
170    return %{$Cache} if $Cache;
171
172    # get database object
173    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
174
175    # sql
176    my @Bind;
177    my $SQL = 'SELECT ts.id, ts.name, ts.valid_id, ts.comments, ts.type_id, tst.name,'
178        . ' ts.change_time, ts.create_time'
179        . ' FROM ticket_state ts, ticket_state_type tst WHERE ts.type_id = tst.id AND ';
180    if ( $Param{Name} ) {
181        $SQL .= ' ts.name = ?';
182        push @Bind, \$Param{Name};
183    }
184    else {
185        $SQL .= ' ts.id = ?';
186        push @Bind, \$Param{ID};
187    }
188
189    return if !$DBObject->Prepare(
190        SQL   => $SQL,
191        Bind  => \@Bind,
192        Limit => 1,
193    );
194
195    # fetch the result
196    my %Data;
197    while ( my @Data = $DBObject->FetchrowArray() ) {
198        %Data = (
199            ID         => $Data[0],
200            Name       => $Data[1],
201            Comment    => $Data[3],
202            ValidID    => $Data[2],
203            TypeID     => $Data[4],
204            TypeName   => $Data[5],
205            ChangeTime => $Data[6],
206            CreateTime => $Data[7],
207        );
208    }
209
210    # set cache
211    $Kernel::OM->Get('Kernel::System::Cache')->Set(
212        Type  => $Self->{CacheType},
213        TTL   => $Self->{CacheTTL},
214        Key   => $CacheKey,
215        Value => \%Data,
216    );
217
218    # no data found...
219    if ( !%Data ) {
220        my $Error = $Param{Name} ? "State '$Param{Name}'" : "StateID '$Param{ID}'";
221        $Kernel::OM->Get('Kernel::System::Log')->Log(
222            Priority => 'error',
223            Message  => $Error . " not found!",
224        );
225        return;
226    }
227
228    return %Data;
229}
230
231=head2 StateUpdate()
232
233update state attributes
234
235    $StateObject->StateUpdate(
236        ID             => 123,
237        Name           => 'New State',
238        Comment        => 'some comment',
239        ValidID        => 1,
240        TypeID         => 1,
241        UserID         => 123,
242    );
243
244=cut
245
246sub StateUpdate {
247    my ( $Self, %Param ) = @_;
248
249    # check needed stuff
250    for (qw(ID Name ValidID TypeID UserID)) {
251        if ( !$Param{$_} ) {
252            $Kernel::OM->Get('Kernel::System::Log')->Log(
253                Priority => 'error',
254                Message  => "Need $_!"
255            );
256            return;
257        }
258    }
259
260    # get database object
261    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
262
263    # sql
264    return if !$DBObject->Do(
265        SQL => 'UPDATE ticket_state SET name = ?, comments = ?, type_id = ?, '
266            . ' valid_id = ?, change_time = current_timestamp, change_by = ? '
267            . ' WHERE id = ?',
268        Bind => [
269            \$Param{Name}, \$Param{Comment}, \$Param{TypeID}, \$Param{ValidID},
270            \$Param{UserID}, \$Param{ID},
271        ],
272    );
273
274    # delete cache
275    $Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
276        Type => $Self->{CacheType},
277    );
278
279    return 1;
280}
281
282=head2 StateGetStatesByType()
283
284get list of states for a type or a list of state types.
285
286Get all states with state type open and new:
287(available: new, open, closed, pending reminder, pending auto, removed, merged)
288
289    my @List = $StateObject->StateGetStatesByType(
290        StateType => ['open', 'new'],
291        Result    => 'ID', # HASH|ID|Name
292    );
293
294Get all state types used by config option named like
295Ticket::ViewableStateType for "Viewable" state types.
296
297    my %List = $StateObject->StateGetStatesByType(
298        Type   => 'Viewable',
299        Result => 'HASH', # HASH|ID|Name
300    );
301
302=cut
303
304sub StateGetStatesByType {
305    my ( $Self, %Param ) = @_;
306
307    # check needed stuff
308    if ( !$Param{Result} ) {
309        $Kernel::OM->Get('Kernel::System::Log')->Log(
310            Priority => 'error',
311            Message  => 'Need Result!'
312        );
313        return;
314    }
315
316    if ( !$Param{Type} && !$Param{StateType} ) {
317        $Kernel::OM->Get('Kernel::System::Log')->Log(
318            Priority => 'error',
319            Message  => 'Need Type or StateType!'
320        );
321        return;
322    }
323
324    # cache key
325    my $CacheKey = 'StateGetStatesByType::';
326    if ( $Param{Type} ) {
327        $CacheKey .= 'Type::' . $Param{Type};
328    }
329    if ( $Param{StateType} ) {
330
331        my @StateType;
332        if ( ref $Param{StateType} eq 'ARRAY' ) {
333            @StateType = @{ $Param{StateType} };
334        }
335        else {
336            push @StateType, $Param{StateType};
337        }
338        $CacheKey .= 'StateType::' . join ':', sort @StateType;
339    }
340
341    # check cache
342    my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
343        Type => $Self->{CacheType},
344        Key  => $CacheKey,
345    );
346    if ($Cache) {
347        if ( $Param{Result} eq 'Name' ) {
348            return @{ $Cache->{Name} };
349        }
350        elsif ( $Param{Result} eq 'HASH' ) {
351            return %{ $Cache->{HASH} };
352        }
353        return @{ $Cache->{ID} };
354    }
355
356    # sql
357    my @StateType;
358    my @Name;
359    my @ID;
360    my %Data;
361    if ( $Param{Type} ) {
362
363        # get config object
364        my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
365
366        if ( $ConfigObject->Get( 'Ticket::' . $Param{Type} . 'StateType' ) ) {
367            @StateType = @{ $ConfigObject->Get( 'Ticket::' . $Param{Type} . 'StateType' ) };
368        }
369        else {
370            $Kernel::OM->Get('Kernel::System::Log')->Log(
371                Priority => 'error',
372                Message  => "Type 'Ticket::$Param{Type}StateType' not found in Kernel/Config.pm!",
373            );
374            die;
375        }
376    }
377    else {
378        if ( ref $Param{StateType} eq 'ARRAY' ) {
379            @StateType = @{ $Param{StateType} };
380        }
381        else {
382            push @StateType, $Param{StateType};
383        }
384    }
385
386    # get database object
387    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
388
389    @StateType = map { $DBObject->Quote($_) } @StateType;
390
391    my $SQL = ''
392        . 'SELECT ts.id, ts.name, tst.name'
393        . ' FROM ticket_state ts, ticket_state_type tst'
394        . ' WHERE tst.id = ts.type_id'
395        . " AND tst.name IN ('${\(join '\', \'', sort @StateType)}' )"
396        . " AND ts.valid_id IN ( ${\(join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet())} )";
397
398    return if !$DBObject->Prepare( SQL => $SQL );
399
400    # fetch the result
401    while ( my @Data = $DBObject->FetchrowArray() ) {
402        push @Name, $Data[1];
403        push @ID,   $Data[0];
404        $Data{ $Data[0] } = $Data[1];
405    }
406
407    # set runtime cache
408    my $All = {
409        Name => \@Name,
410        ID   => \@ID,
411        HASH => \%Data,
412    };
413
414    # set permanent cache
415    $Kernel::OM->Get('Kernel::System::Cache')->Set(
416        Type  => $Self->{CacheType},
417        TTL   => $Self->{CacheTTL},
418        Key   => $CacheKey,
419        Value => $All,
420    );
421
422    if ( $Param{Result} eq 'Name' ) {
423        return @Name;
424    }
425    elsif ( $Param{Result} eq 'HASH' ) {
426        return %Data;
427    }
428
429    return @ID;
430}
431
432=head2 StateList()
433
434get state list as a hash of ID, Name pairs
435
436    my %List = $StateObject->StateList(
437        UserID => 123,
438    );
439
440    my %List = $StateObject->StateList(
441        UserID => 123,
442        Valid  => 1, # is default
443    );
444
445    my %List = $StateObject->StateList(
446        UserID => 123,
447        Valid  => 0,
448    );
449
450returns
451
452    my %List = (
453        1 => "new",
454        2 => "closed successful",
455        3 => "closed unsuccessful",
456        4 => "open",
457        5 => "removed",
458        6 => "pending reminder",
459        7 => "pending auto close+",
460        8 => "pending auto close-",
461        9 => "merged",
462    );
463
464=cut
465
466sub StateList {
467    my ( $Self, %Param ) = @_;
468
469    # check needed stuff
470    if ( !$Param{UserID} ) {
471        $Kernel::OM->Get('Kernel::System::Log')->Log(
472            Priority => 'error',
473            Message  => 'UserID!'
474        );
475        return;
476    }
477
478    my $Valid = 1;
479    if ( !$Param{Valid} && defined( $Param{Valid} ) ) {
480        $Valid = 0;
481    }
482
483    # check cache
484    my $CacheKey = 'StateList::' . $Valid;
485    my $Cache    = $Kernel::OM->Get('Kernel::System::Cache')->Get(
486        Type => $Self->{CacheType},
487        Key  => $CacheKey,
488    );
489    return %{$Cache} if $Cache;
490
491    # sql
492    my $SQL = 'SELECT id, name FROM ticket_state';
493    if ($Valid) {
494        $SQL
495            .= " WHERE valid_id IN ( ${\(join ', ', $Kernel::OM->Get('Kernel::System::Valid')->ValidIDsGet())} )";
496    }
497
498    # get database object
499    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
500
501    return if !$DBObject->Prepare( SQL => $SQL );
502
503    # fetch the result
504    my %Data;
505    while ( my @Row = $DBObject->FetchrowArray() ) {
506        $Data{ $Row[0] } = $Row[1];
507    }
508
509    # set cache
510    $Kernel::OM->Get('Kernel::System::Cache')->Set(
511        Type  => $Self->{CacheType},
512        TTL   => $Self->{CacheTTL},
513        Key   => $CacheKey,
514        Value => \%Data,
515    );
516
517    return %Data;
518}
519
520=head2 StateLookup()
521
522returns the id or the name of a state
523
524    my $StateID = $StateObject->StateLookup(
525        State => 'closed successful',
526    );
527
528    my $State = $StateObject->StateLookup(
529        StateID => 2,
530    );
531
532=cut
533
534sub StateLookup {
535    my ( $Self, %Param ) = @_;
536
537    # check needed stuff
538    if ( !$Param{State} && !$Param{StateID} ) {
539        $Kernel::OM->Get('Kernel::System::Log')->Log(
540            Priority => 'error',
541            Message  => 'Need State or StateID!'
542        );
543        return;
544    }
545
546    # get (already cached) state list
547    my %StateList = $Self->StateList(
548        Valid  => 0,
549        UserID => 1,
550    );
551
552    my $Key;
553    my $Value;
554    my $ReturnData;
555    if ( $Param{StateID} ) {
556        $Key        = 'StateID';
557        $Value      = $Param{StateID};
558        $ReturnData = $StateList{ $Param{StateID} };
559    }
560    else {
561        $Key   = 'State';
562        $Value = $Param{State};
563        my %StateListReverse = reverse %StateList;
564        $ReturnData = $StateListReverse{ $Param{State} };
565    }
566
567    # check if data exists
568    if ( !defined $ReturnData ) {
569        $Kernel::OM->Get('Kernel::System::Log')->Log(
570            Priority => 'error',
571            Message  => "No $Key for $Value found!",
572        );
573        return;
574    }
575
576    return $ReturnData;
577}
578
579=head2 StateTypeList()
580
581get state type list as a hash of ID, Name pairs
582
583    my %ListType = $StateObject->StateTypeList(
584        UserID => 123,
585    );
586
587returns
588
589    my %ListType = (
590        1 => "new",
591        2 => "open",
592        3 => "closed",
593        4 => "pending reminder",
594        5 => "pending auto",
595        6 => "removed",
596        7 => "merged",
597    );
598
599=cut
600
601sub StateTypeList {
602    my ( $Self, %Param ) = @_;
603
604    # check needed stuff
605    if ( !$Param{UserID} ) {
606        $Kernel::OM->Get('Kernel::System::Log')->Log(
607            Priority => 'error',
608            Message  => 'UserID!'
609        );
610        return;
611    }
612
613    # check cache
614    my $CacheKey = 'StateTypeList';
615    my $Cache    = $Kernel::OM->Get('Kernel::System::Cache')->Get(
616        Type => $Self->{CacheType},
617        Key  => $CacheKey,
618    );
619    return %{$Cache} if $Cache;
620
621    # get database object
622    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
623
624    # sql
625    return if !$DBObject->Prepare(
626        SQL => 'SELECT id, name FROM ticket_state_type',
627    );
628
629    # fetch the result
630    my %Data;
631    while ( my @Row = $DBObject->FetchrowArray() ) {
632        $Data{ $Row[0] } = $Row[1];
633    }
634
635    # set cache
636    $Kernel::OM->Get('Kernel::System::Cache')->Set(
637        Type  => $Self->{CacheType},
638        TTL   => $Self->{CacheTTL},
639        Key   => $CacheKey,
640        Value => \%Data,
641    );
642
643    return %Data;
644}
645
646=head2 StateTypeLookup()
647
648returns the id or the name of a state type
649
650    my $StateTypeID = $StateObject->StateTypeLookup(
651        StateType => 'pending auto',
652    );
653
654or
655
656    my $StateType = $StateObject->StateTypeLookup(
657        StateTypeID => 1,
658    );
659
660=cut
661
662sub StateTypeLookup {
663    my ( $Self, %Param ) = @_;
664
665    # check needed stuff
666    if ( !$Param{StateType} && !$Param{StateTypeID} ) {
667        $Kernel::OM->Get('Kernel::System::Log')->Log(
668            StateType => 'error',
669            Message   => 'Need StateType or StateTypeID!',
670        );
671        return;
672    }
673
674    # get (already cached) state type list
675    my %StateTypeList = $Self->StateTypeList(
676        UserID => 1,
677    );
678
679    my $Key;
680    my $Value;
681    my $ReturnData;
682    if ( $Param{StateTypeID} ) {
683        $Key        = 'StateTypeID';
684        $Value      = $Param{StateTypeID};
685        $ReturnData = $StateTypeList{ $Param{StateTypeID} };
686    }
687    else {
688        $Key   = 'StateType';
689        $Value = $Param{StateType};
690        my %StateTypeListReverse = reverse %StateTypeList;
691        $ReturnData = $StateTypeListReverse{ $Param{StateType} };
692    }
693
694    # check if data exists
695    if ( !defined $ReturnData ) {
696        $Kernel::OM->Get('Kernel::System::Log')->Log(
697            Priority => 'error',
698            Message  => "No $Key for $Value found!",
699        );
700        return;
701    }
702
703    return $ReturnData;
704}
705
7061;
707
708=head1 TERMS AND CONDITIONS
709
710This software is part of the OTRS project (L<https://otrs.org/>).
711
712This software comes with ABSOLUTELY NO WARRANTY. For details, see
713the enclosed file COPYING for license information (GPL). If you
714did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
715
716=cut
717