# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::ITSMChange::ITSMStateMachine;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Cache',
'Kernel::System::DB',
'Kernel::System::GeneralCatalog',
'Kernel::System::Log',
);
=head1 NAME
Kernel::System::ITSMChange::ITSMStateMachine - state machine lib
=head1 PUBLIC INTERFACE
=cut
=head2 new()
Create an object.
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
my $StateMachineObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# set the debug flag
$Self->{Debug} = $Param{Debug} || 0;
# get the cache type and TTL (in seconds)
$Self->{CacheType} = 'ITSMStateMachine';
$Self->{CacheTTL} = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::CacheTTL') * 60;
return $Self;
}
=head2 StateTransitionAdd()
Add a new state transition. Returns the transition id on success.
my $TransitionID = $StateMachineObject->StateTransitionAdd(
StateID => 1, # id within the given class, or 0 to indicate the start state
NextStateID => 2, # id within the given class, or 0 to indicate an end state
Class => 'ITSM::ChangeManagement::Change::State', # the name of a general catalog class
);
=cut
sub StateTransitionAdd {
my ( $Self, %Param ) = @_;
# check if StateID and NextStateID are given (they can be 0)
for my $Argument (qw(StateID NextStateID)) {
if ( !defined $Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check that class is given
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# check that not both StateID and NextStateID are zero
if ( !$Param{StateID} && !$Param{NextStateID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "StateID and NextStateID can't both be zero!",
);
return;
}
# define lookup hash for state name
my %StateID2State;
# check if StateID and NextStateID belong to the given class
ARGUMENT:
for my $Argument (qw(StateID NextStateID)) {
$StateID2State{ $Param{$Argument} } = $Self->StateLookup(
StateID => $Param{$Argument},
Class => $Param{Class},
);
# dont check zero values
next ARGUMENT if !$Param{$Argument};
# get class
my $DataRef = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
ItemID => $Param{$Argument},
);
# check if id belongs to given class
if ( !$DataRef || !%{$DataRef} || $DataRef->{Class} ne $Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Argument $Param{$Argument} is not in the class $Param{Class}!",
);
return;
}
}
# check if a state transition with the StateID and NextStateID exists already
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id FROM change_state_machine '
. 'WHERE state_id = ? '
. 'AND next_state_id = ?',
Bind => [ \$Param{StateID}, \$Param{NextStateID} ],
Limit => 1,
);
# fetch the result
my $TransitionID;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$TransitionID = $Row[0];
}
# do not insert this transition twice
return $TransitionID if $TransitionID;
# check if StateID is a start state (=0) and another start state already exists
if ( !$Param{StateID} ) {
# count the number of exsting start states in the given class
# ( the state_id 0 indicates that the next_state_id is a start state )
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT count(s.id) '
. 'FROM change_state_machine s, general_catalog g '
. 'WHERE g.general_catalog_class = ? '
. 'AND s.next_state_id = g.id '
. 'AND s.state_id = 0',
Bind => [ \$Param{Class} ],
Limit => 1,
);
# fetch the result
my $Count;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$Count = $Row[0];
}
# if there is already a start state
if ($Count) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not add state '$StateID2State{$Param{NextStateID}}' "
. "(ID: $Param{NextStateID}) as start state. "
. "There is already a start state defined for class '$Param{Class}'!",
);
return;
}
}
# prevent setting an end state transition, if other state transistions exist already
if ( $Param{StateID} && !$Param{NextStateID} ) {
# check if other state transistions exist for the given StateID
my $NextStateIDs = $Self->StateTransitionGet(
StateID => $Param{StateID},
Class => $Param{Class},
);
# check if any next states are defined for this start state
if ( $NextStateIDs && @{$NextStateIDs} && scalar @{$NextStateIDs} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not set state "
. "'$StateID2State{$Param{StateID}}' (ID: $Param{StateID}) as end state, "
. "because other following states exist, which must be deleted first!",
);
return;
}
}
# prevent the adding of other next states if an end state is already defined
# for this start state
elsif ( $Param{StateID} && $Param{NextStateID} ) {
# check if other state transistions exist for the given StateID
my $NextStateIDs = $Self->StateTransitionGet(
StateID => $Param{StateID},
Class => $Param{Class},
);
# check if there is an end state (=0) defined for this start state
if ( $NextStateIDs && @{$NextStateIDs} && !$NextStateIDs->[0] ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "State '$StateID2State{$Param{StateID}}' "
. "(ID: $Param{StateID}) is defined as an end state, "
. "it must be deleted first, before new following states can be added!",
);
return;
}
}
# add state transition to database
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'INSERT INTO change_state_machine '
. '(state_id, next_state_id) '
. 'VALUES (?, ?)',
Bind => [
\$Param{StateID}, \$Param{NextStateID},
],
);
# get TransitionID
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id FROM change_state_machine '
. 'WHERE state_id = ? '
. 'AND next_state_id = ?',
Bind => [ \$Param{StateID}, \$Param{NextStateID} ],
Limit => 1,
);
# fetch the result
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$TransitionID = $Row[0];
}
# check if state transition could be added
if ( !$TransitionID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "StateTransitionAdd() failed!",
);
return;
}
# cleanup statemachine cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => $Self->{CacheType},
);
return $TransitionID;
}
=head2 StateTransitionDelete()
Delete a state transition. Returns true on success.
my $Success = $StateMachineObject->StateTransitionDelete(
StateID => 1, # id within the given class, or 0 to indicate the start state
NextStateID => 2, # id within the given class, or 0 to indicate an end state
);
=cut
sub StateTransitionDelete {
my ( $Self, %Param ) = @_;
# check if StateID and NextStateID are given (they can be 0)
for my $Argument (qw(StateID NextStateID)) {
if ( !defined $Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# delete state transition from database
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'DELETE FROM change_state_machine '
. 'WHERE state_id = ? AND next_state_id = ?',
Bind => [
\$Param{StateID}, \$Param{NextStateID},
],
);
# cleanup statemachine cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => $Self->{CacheType},
);
return 1;
}
=head2 StateTransitionDeleteAll()
Delete all state transitions of a class. Returns true on success.
my $Success = $StateMachineObject->StateTransitionDeleteAll(
Class => 'ITSM::ChangeManagement::Change::State', # the name of a general catalog class
);
=cut
sub StateTransitionDeleteAll {
my ( $Self, %Param ) = @_;
# check needed parameter
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# find all state ids and next_state ids which belong to the given class
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT id '
. 'FROM general_catalog '
. 'WHERE general_catalog_class = ?',
Bind => [ \$Param{Class} ],
);
my @IDs;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @IDs, $Row[0];
}
# return if no state transitions exist for the given class
return 1 if !@IDs;
# build id string
my $IDString = join ', ', @IDs;
# delete state transition from database
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => "DELETE FROM change_state_machine "
. "WHERE state_id IN ( $IDString ) "
. "OR next_state_id IN ( $IDString )",
);
# cleanup statemachine cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => $Self->{CacheType},
);
return 1;
}
=head2 StateTransitionGet()
Get a state transition for a given state id.
Returns an array reference of the next state ids.
my $NextStateIDsRef = $StateMachineObject->StateTransitionGet(
StateID => 1, # id within the given class, or 0 to indicate the start state
Class => 'ITSM::ChangeManagement::Change::State', # the name of a general catalog class
);
=cut
sub StateTransitionGet {
my ( $Self, %Param ) = @_;
# check if StateID are given (they can be 0)
for my $Argument (qw(StateID)) {
if ( !defined $Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check that class is given
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# check the cache
my $CacheKey = 'StateTransitionGet::StateID::' . $Param{StateID} . '::Class::' . $Param{Class};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return $Cache if $Cache;
# check if StateID belongs to the given class, but only if state id is not a start state (=0)
if ( $Param{StateID} ) {
# get class of given StateID
my $DataRef = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
ItemID => $Param{StateID},
);
# check if StateID belongs to given class
if ( !$DataRef || !%{$DataRef} || $DataRef->{Class} ne $Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "StateID $Param{StateID} is not in the class '$Param{Class}'!",
);
return;
}
}
# find all state ids and next_state ids which belong to the given class
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT DISTINCT s.next_state_id '
. 'FROM change_state_machine s '
. 'LEFT OUTER JOIN general_catalog g '
. 'ON ( (s.state_id = g.id ) OR (s.next_state_id = g.id) ) '
. 'WHERE s.state_id = ? AND g.general_catalog_class = ?',
Bind => [ \$Param{StateID}, \$Param{Class} ],
);
my @NextStateIDs;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @NextStateIDs, $Row[0];
}
# if the start state was requested and more than one start state was found
if ( !$Param{StateID} ) {
if ( !@NextStateIDs ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not get initial state for '$Param{Class}' "
. "No initial state was found!",
);
return;
}
if ( scalar @NextStateIDs > 1 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not get initial state for '$Param{Class}' "
. "More than one initial state was found!",
);
return;
}
}
# save values in cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => $CacheKey,
Value => \@NextStateIDs,
TTL => $Self->{CacheTTL},
);
return \@NextStateIDs;
}
=head2 StateTransitionGetEndStates()
Get a state transition for a given state id, but only show the possible next end states.
Returns an array reference of the next end state ids.
my $NextStateIDsRef = $StateMachineObject->StateTransitionGetEndStates(
StateID => 1, # id within the given class, or 0 to indicate the start state
Class => 'ITSM::ChangeManagement::Change::State', # the name of a general catalog class
);
=cut
sub StateTransitionGetEndStates {
my ( $Self, %Param ) = @_;
# check if StateID are given (they can be 0)
for my $Argument (qw(StateID)) {
if ( !defined $Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check that class is given
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# check the cache
my $CacheKey = 'StateTransitionGetEndStates::StateID::' . $Param{StateID} . '::Class::' . $Param{Class};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return $Cache if $Cache;
# check if StateID belongs to the given class, but only if state id is not a start state (=0)
if ( $Param{StateID} ) {
# get class of given StateID
my $DataRef = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
ItemID => $Param{StateID},
);
# check if StateID belongs to given class
if ( !$DataRef || !%{$DataRef} || $DataRef->{Class} ne $Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "StateID $Param{StateID} is not in the class '$Param{Class}'!",
);
return;
}
}
# find all state ids and next_state ids which belong to the given class
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT DISTINCT s.next_state_id '
. 'FROM change_state_machine s '
. 'LEFT OUTER JOIN general_catalog g '
. 'ON ( (s.state_id = g.id ) OR (s.next_state_id = g.id) ) '
. 'WHERE s.state_id = ? AND g.general_catalog_class = ?',
Bind => [ \$Param{StateID}, \$Param{Class} ],
);
my @NextStateIDs;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @NextStateIDs, $Row[0];
}
# if the start state was requested and more than one start state was found
if ( !$Param{StateID} ) {
if ( !@NextStateIDs ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not get initial state for '$Param{Class}' "
. "No initial state was found!",
);
return;
}
if ( scalar @NextStateIDs > 1 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not get initial state for '$Param{Class}' "
. "More than one initial state was found!",
);
return;
}
}
# build next state ids string
my $NextStateIDsString = join ', ', @NextStateIDs;
# find all next state ids which are end states
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT DISTINCT s.state_id '
. 'FROM change_state_machine s '
. "WHERE s.state_id IN ( $NextStateIDsString ) "
. 'AND s.next_state_id = 0',
);
my @NextEndStateIDs;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @NextEndStateIDs, $Row[0];
}
# save values in cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => $CacheKey,
Value => \@NextEndStateIDs,
TTL => $Self->{CacheTTL},
);
return \@NextEndStateIDs;
}
=head2 StateTransitionList()
Return a state transition list hash-array reference.
The hash key is the StateID, the hash value is an array reference of NextStateIDs.
my $StateTransitionsRef = $StateMachineObject->StateTransitionList(
Class => 'ITSM::ChangeManagement::Change::State', # the name of a general catalog class
);
Return example:
$StateTransitionsRef = {
0 => [ 1 ],
1 => [ 2, 3, 4 ],
2 => [ 5 ],
3 => [ 6, 7 ],
4 => [ 0 ],
5 => [ 0 ],
6 => [ 0 ],
7 => [ 0 ],
};
=cut
sub StateTransitionList {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# check the cache
my $CacheKey = 'StateTransitionList::Class::' . $Param{Class};
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
Type => $Self->{CacheType},
Key => $CacheKey,
);
return $Cache if $Cache;
# get state transitions
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT DISTINCT s.id , s.state_id , s.next_state_id , g.general_catalog_class '
. 'FROM change_state_machine s '
. 'LEFT OUTER JOIN general_catalog g '
. 'ON ( (s.state_id = g.id ) OR (s.next_state_id = g.id) ) '
. 'WHERE g.general_catalog_class = ?',
Bind => [ \$Param{Class} ],
);
my %StateTransition;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @{ $StateTransition{ $Row[1] } }, $Row[2];
}
# save values in cache
$Kernel::OM->Get('Kernel::System::Cache')->Set(
Type => $Self->{CacheType},
Key => $CacheKey,
Value => \%StateTransition,
TTL => $Self->{CacheTTL},
);
return \%StateTransition;
}
=head2 StateTransitionUpdate()
Update the next state of an existing new state transition.
Returns the transition id on success.
my $UpdateSuccess = $StateMachineObject->StateTransitionUpdate(
StateID => 1, # id within the given class, or 0 to indicate the start state
NextStateID => 2, # id within the given class, or 0 to indicate an end state
NewNextStateID => 3, # id within the given class, or 0 to indicate an end state
Class => 'ITSM::ChangeManagement::Change::State', # the name of a general catalog class
);
=cut
sub StateTransitionUpdate {
my ( $Self, %Param ) = @_;
# check if StateID, NextStateID and NewNextStateID are given (they can be 0)
for my $Argument (qw(StateID NextStateID NewNextStateID)) {
if ( !defined $Param{$Argument} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Argument!",
);
return;
}
}
# check that class is given
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# check that not both StateID and NextStateID are zero
if ( !$Param{StateID} && !$Param{NextStateID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "StateID and NextStateID can't both be zero!",
);
return;
}
# check that not both StateID and NewNextStateID are zero
if ( !$Param{StateID} && !$Param{NewNextStateID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "StateID and NewNextStateID can't both be zero!",
);
return;
}
# define lookup hash for state name
my %StateID2State;
# check if StateID, NextStateID and NewNextStateID belong to the given class
ARGUMENT:
for my $Argument (qw(StateID NextStateID NewNextStateID)) {
$StateID2State{ $Param{$Argument} } = $Self->StateLookup(
StateID => $Param{$Argument},
Class => $Param{Class},
);
# dont check zero values
next ARGUMENT if !$Param{$Argument};
# get class
my $DataRef = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
ItemID => $Param{$Argument},
);
# check if id belongs to given class
if ( !$DataRef || !%{$DataRef} || $DataRef->{Class} ne $Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "$Argument $Param{$Argument} is not in the class '$Param{Class}'!",
);
return;
}
}
# do not update if the new next state is the same
return 1 if $Param{NextStateID} == $Param{NewNextStateID};
# get the existing state transition id that should be updated
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT DISTINCT s.id '
. 'FROM change_state_machine s '
. 'LEFT OUTER JOIN general_catalog g '
. 'ON ( (s.state_id = g.id ) OR (s.next_state_id = g.id) ) '
. 'WHERE s.state_id = ? AND s.next_state_id = ? '
. 'AND g.general_catalog_class = ?',
Bind => [ \$Param{StateID}, \$Param{NextStateID}, \$Param{Class} ],
Limit => 1,
);
my $TransitionID;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$TransitionID = $Row[0];
}
# check that the state transition that should be updated exists
if ( !$TransitionID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not update state transition! A state transition with "
. "StateID $Param{StateID} and NextStateID $Param{NextStateID} does not exist!",
);
return;
}
# prevent setting an end state transition, if other state transistions exist already
if ( !$Param{NewNextStateID} ) {
# check if other state transistions exist for the given StateID
my $NextStateIDs = $Self->StateTransitionGet(
StateID => $Param{StateID},
Class => $Param{Class},
);
# The old state transition, which is verified to exist, does not count in this check
if ( $NextStateIDs && scalar @{$NextStateIDs} > 1 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can not set state "
. "'$StateID2State{$Param{StateID}}' (ID: $Param{StateID}) as end state, "
. "because other following states exist, which must be deleted first!",
);
return;
}
}
# update state transition
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => 'UPDATE change_state_machine '
. 'SET next_state_id = ? '
. 'WHERE id = ?',
Bind => [ \$Param{NewNextStateID}, \$TransitionID ],
);
# cleanup statemachine cache
$Kernel::OM->Get('Kernel::System::Cache')->CleanUp(
Type => $Self->{CacheType},
);
return 1;
}
=head2 StateLookup()
This method does a lookup for a state. If a state id is given,
it returns the name of the state. If a state name is given,
the appropriate id is returned.
my $State = $StateMachineObject->StateLookup(
StateID => 1234,
Class => 'ITSM::ChangeManagement::Change::State',
);
my $StateID = $StateMachineObject->StateLookup(
State => 'accepted',
Class => 'ITSM::ChangeManagement::Change::State',
);
=cut
sub StateLookup {
my ( $Self, %Param ) = @_;
# check Class parameter
if ( !$Param{Class} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Class!',
);
return;
}
# The StateID '0' is a special case. Depending on the context it
# can indicate '*START*' or '*END*'. So return '0' in that case,
# and do not flood the error log.
if (
defined $Param{StateID}
&& $Param{StateID} eq '0'
&& !defined $Param{State}
)
{
return '0';
}
# The State '0' is a special case. Depending on the context it
# can indicate '*START*' or '*END*'. So return '0' in that case,
# and do not flood the error log.
if (
defined $Param{State}
&& $Param{State} eq '0'
&& !defined $Param{StateID}
)
{
return '0';
}
# either StateID or State must be passed
if ( !$Param{StateID} && !$Param{State} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need StateID or State!',
);
return;
}
# only one parameter State or StateID is allowed
if ( $Param{StateID} && $Param{State} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need StateID OR State - not both!',
);
return;
}
# get the change states from the general catalog
my $StateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => $Param{Class},
);
# convert state list into a lookup hash
my %StateID2Name;
if ( $StateList && ref $StateList eq 'HASH' && %{$StateList} ) {
%StateID2Name = %{$StateList};
}
# check the state hash
if ( !%StateID2Name ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Could not retrieve change states from the general catalog.',
);
return;
}
if ( $Param{StateID} ) {
return $StateID2Name{ $Param{StateID} };
}
else {
# reverse key - value pairs to have the name as keys
my %StateName2ID = reverse %StateID2Name;
return $StateName2ID{ $Param{State} };
}
}
=head2 StateList()
This method returns a list of states for a catalog class.
my $StateList = $StateMachineObject->StateList(
Class => 'ITSM::ChangeManagement::Change::State',
UserID => 1,
);
The return value is a reference to an array of hashrefs. The element 'Key' is then
the state id and the element 'Value' is the name of the state. The array elements
are sorted by state id.
my $StateList = [
{
Key => 156,
Value => 'approved',
},
{
Key => 157,
Value => 'in progress',
},
];
=cut
sub StateList {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Attribute (qw(Class UserID)) {
if ( !$Param{$Attribute} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Attribute!",
);
return;
}
}
# get change state list
my $StateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => $Param{Class},
) || {};
# to store an array of hash refs
my @ArrayHashRef;
# assemble the array of hash refs with all states
for my $StateID ( sort keys %{$StateList} ) {
push @ArrayHashRef, {
Key => $StateID,
Value => $StateList->{$StateID},
};
}
return \@ArrayHashRef;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L.
=cut