Files
scripts/Perl OTRS/Kernel/System/ProcessManagement/Process.pm
2024-10-14 00:08:40 +02:00

823 lines
26 KiB
Perl

# --
# 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::ProcessManagement::Process;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Log',
'Kernel::System::ProcessManagement::Activity',
'Kernel::System::ProcessManagement::ActivityDialog',
'Kernel::System::ProcessManagement::Transition',
'Kernel::System::ProcessManagement::TransitionAction',
'Kernel::System::Ticket',
);
=head1 NAME
Kernel::System::ProcessManagement::Process - process lib
=head1 DESCRIPTION
All ProcessManagement Process functions.
=head1 PUBLIC INTERFACE
=head2 new()
Don't use the constructor directly, use the ObjectManager instead:
my $ProcessObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# 0=off; 1=on;
$Self->{Debug} = $Param{Debug} || 0;
return $Self;
}
=head2 ProcessGet()
Get process info
my $Process = $ProcessObject->ProcessGet(
ProcessEntityID => 'P1',
);
Returns:
$Process = {
'Name' => 'Process1',
'CreateBy' => '1',
'CreateTime' => '16-02-2012 13:37:00',
'ChangeBy' => '1',
'ChangeTime' => '17-02-2012 13:37:00',
'State' => 'Active',
'StartActivityDialog' => 'AD1',
'StartActivity' => 'A1',
'Path' => {
'A2' => {
'T3' => {
ActivityEntityID => 'A4',
},
},
'A1' => {
'T1' => {
ActivityEntityID => 'A2',
},
'T2' => {
ActivityEntityID => 'A3',
},
},
},
};
=cut
sub ProcessGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(ProcessEntityID)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $Process = $Kernel::OM->Get('Kernel::Config')->Get('Process');
if ( !IsHashRefWithData($Process) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Process config!',
);
return;
}
if ( !IsHashRefWithData( $Process->{ $Param{ProcessEntityID} } ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No Data for Process '$Param{ProcessEntityID}' found!",
);
return;
}
return $Process->{ $Param{ProcessEntityID} };
}
=head2 ProcessList()
Get a list of all Processes
my $ProcessList = $ProcessObject->ProcessList(
ProcessState => ['Active'], # Active, FadeAway, Inactive
Interface => ['AgentInterface'], # optional, ['AgentInterface'] or ['CustomerInterface'] or ['AgentInterface', 'CustomerInterface'] or 'all'
Silent => 1 # optional, 1 || 0, default 0, if set to 1 does not log errors if there are no processes configured
);
Returns:
$ProcessList = {
'P1' => 'Process 1',
'P2' => 'Process 2',
'P3' => '',
};
=cut
sub ProcessList {
my ( $Self, %Param ) = @_;
for my $Needed (qw(ProcessState)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
if ( !IsArrayRefWithData( $Param{ProcessState} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need at least one ProcessState!',
);
return;
}
my $Processes = $Kernel::OM->Get('Kernel::Config')->Get('Process');
if ( !IsHashRefWithData($Processes) ) {
if ( !$Param{Silent} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need Process config!',
);
}
return;
}
# get only processes with the requested ProcessState(s)
my %ProcessList;
for my $ProcessEntityID ( sort keys %{$Processes} ) {
if ( grep { $_ eq $Processes->{$ProcessEntityID}->{State} } @{ $Param{ProcessState} } ) {
$ProcessList{$ProcessEntityID} = $Processes->{$ProcessEntityID}->{Name} || '';
}
}
# set Interface parameter to 'all' by default if parameter was not set
if ( !defined $Param{Interface} ) {
$Param{Interface} = 'all';
}
# if Interface is 'all' return all processes without interface restrictions
if ( $Param{Interface} eq 'all' ) {
return \%ProcessList;
}
# get activity dialog object
my $ActivityDialogObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog');
# otherwise return only processes where the initial activity dialog matches given interface
my %ReducedProcessList;
PROCESS:
for my $ProcessEntityID ( sort keys %ProcessList ) {
# get process start point for each process we already got
my $Start = $Self->ProcessStartpointGet( ProcessEntityID => $ProcessEntityID );
# skip processes if they does not have a valid start point
next PROCESS if !IsHashRefWithData($Start);
next PROCESS if !IsStringWithData( $Start->{ActivityDialog} );
# try to get the start ActivityDialog for the given interface
my $ActivityDialog = $ActivityDialogObject->ActivityDialogGet(
ActivityDialogEntityID => $Start->{ActivityDialog},
Interface => $Param{Interface},
Silent => 1,
);
# skip process if first activity dialog could not be got for the given interface
next PROCESS if !IsHashRefWithData($ActivityDialog);
$ReducedProcessList{$ProcessEntityID} = $ProcessList{$ProcessEntityID};
}
return \%ReducedProcessList;
}
=head2 ProcessStartpointGet()
Get process startpoint
my $Start = $ProcessObject->ProcessStartpointGet(
ProcessEntityID => 'P1',
);
Returns:
$Start = {
Activity => 'A1',
ActivityDialog => 'AD1',
};
=cut
sub ProcessStartpointGet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(ProcessEntityID)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my $Process = $Self->ProcessGet( ProcessEntityID => $Param{ProcessEntityID} );
# include FadeAway processes so they will be listed as available processes to continue working
# with them
if ( $Process->{State} ne 'Active' && $Process->{State} ne 'FadeAway' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't get 'StartActivity' for Process '$Param{ProcessEntityID}', State"
. " is '$Process->{State}'!",
);
return;
}
if ( !$Process->{StartActivity} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No 'StartActivity' for Process '$Param{ProcessEntityID}' found!",
);
return;
}
if ( !$Process->{StartActivity} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No 'StartActivity' for Process '$Param{ProcessEntityID}' found!",
);
return;
}
if ( !$Process->{StartActivityDialog} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "No 'StartActivityDialog' for Process '$Param{ProcessEntityID}' found!",
);
return;
}
return {
Activity => $Process->{StartActivity},
ActivityDialog => $Process->{StartActivityDialog}
};
}
=head2 ProcessTransition()
Check valid Transitions and Change Ticket's Activity
if a Transition was positively checked
my $ProcessTransition = $ProcessObject->ProcessTransition(
ProcessEntityID => 'P1',
ActivityEntityID => 'A1',
TicketID => 123,
UserID => 123,
CheckOnly => 1, # optional
Data => { # optional
Queue => 'Raw',
DynamicField1 => 'Value',
Subject => 'Testsubject',
#...
},
);
Returns:
$Success = 1; # undef # if "CheckOnly" is NOT set
1 if Transition was executed and Ticket->ActivityEntityID updated
undef if no Transition matched or check failed otherwise
$ProcessTransition = { # if option "CheckOnly" is set
'T1' => {
ActivityEntityID => 'A1',
TransitionAction => [
'TA1',
'TA2',
'TA3',
],
},
};
=cut
sub ProcessTransition {
my ( $Self, %Param ) = @_;
for my $Needed (qw(ProcessEntityID ActivityEntityID TicketID UserID)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my %Data;
# Get Ticket object
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
# Get Ticket Data
%Data = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
UserID => $Param{UserID},
);
# Check if we got a Ticket
if ( !IsHashRefWithData( \%Data ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Invalid TicketID: $Param{TicketID}!",
);
return;
}
# If we have Data lay it over the current %Data
# to check Ticket + Additional Data
if ( $Param{Data} ) {
%Data = ( %Data, %{ $Param{Data} } );
}
my $Process = $Self->ProcessGet( ProcessEntityID => $Param{ProcessEntityID} );
if ( !$Process->{State} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't process Transition for Process '$Param{ProcessEntityID}', can't"
. " get State out of the config!",
);
return;
}
if ( $Process->{State} eq 'Inactive' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't process Transition for Process '$Param{ProcessEntityID}',"
. " ProcessState is '$Process->{State}'!",
);
return;
}
# We need the Processes Config
# and the Config for the process the Ticket is in
# and the Process has to have the 'Path' Hash set
if (
!IsHashRefWithData($Process)
|| !IsHashRefWithData( $Process->{Path} )
)
{
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Path for ProcessEntityID $Param{ProcessEntityID}!",
);
return;
}
# Check if our ActivitySet has a path configured
# if it hasn't we got nothing to do -> print debuglog if desired and return
if ( !IsHashRefWithData( $Process->{Path}->{ $Param{ActivityEntityID} } ) ) {
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => 'No Path configured for Process with ID: '
. "$Param{ProcessEntityID} and ActivityEntityID: $Param{ActivityEntityID}!",
);
}
return;
}
# %Transitions Hash for easier reading
# contains all possible Transitions for the current Activity
my %Transitions = %{ $Process->{Path}->{ $Param{ActivityEntityID} } };
# get transition object
my $TransitionObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Transition');
# Handle all possible TransitionEntityID's for the Process->Path's->ActivityEntityID down to
# Transition.pm's TransitionCheck for validation
# will return undef if nothing matched or the first matching TransitionEntityID
my $TransitionEntityID = $TransitionObject->TransitionCheck(
TransitionEntityID => [ sort { $a cmp $b } keys %Transitions ],
Data => \%Data,
);
# if we didn't get a TransitionEntityID
# no check was successful -> return nothing
if ( !$TransitionEntityID || !length $TransitionEntityID ) {
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => 'No Transition matched for TicketID: '
. "$Param{TicketID} ProcessEntityID: $Param{ProcessEntityID} "
. "ActivityEntityID: $Param{ActivityEntityID}!",
);
}
return;
}
# get activity object
my $ActivityObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity');
# If we have a Transition without valid FutureActivitySet we have to complain
if (
!IsHashRefWithData( $Transitions{$TransitionEntityID} )
|| !$Transitions{$TransitionEntityID}->{ActivityEntityID}
|| !IsHashRefWithData(
$ActivityObject->ActivityGet(
Interface => 'all',
ActivityEntityID => $Transitions{$TransitionEntityID}->{ActivityEntityID}
)
)
)
{
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Target Activity for Process with "
. "ProcessEntityID: $Param{ProcessEntityID} ActivityEntityID:"
. " $Param{ActivityEntityID} TransitionEntityID: $TransitionEntityID!",
);
return;
}
# If we should just check what Transition matched
# return a hash containing
# { TransitionEntityID => FutureActivityEntityID }
if ( $Param{CheckOnly} ) {
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Transition with ID $TransitionEntityID matched for "
. "TicketID: $Param{TicketID} ProcessEntityID: $Param{ProcessEntityID} "
. "ActivityEntityID: $Param{ActivityEntityID}!",
);
}
return { $TransitionEntityID => $Transitions{$TransitionEntityID} };
}
# Set the new ActivityEntityID on the Ticket
my $Success = $Self->ProcessTicketActivitySet(
ProcessEntityID => $Param{ProcessEntityID},
ActivityEntityID => $Transitions{$TransitionEntityID}->{ActivityEntityID},
TicketID => $Param{TicketID},
UserID => $Param{UserID},
);
if ( !$Success ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Failed setting ActivityEntityID of Ticket: $Param{TicketID} to "
. $Transitions{$TransitionEntityID}->{ActivityEntityID}
. " after successful Transition: $TransitionEntityID!",
);
return;
}
# if we don't have Transition Actions on that transition,
# return 1 for successful transition
if (
!$Transitions{$TransitionEntityID}->{TransitionAction}
|| (
ref $Transitions{$TransitionEntityID}->{TransitionAction} eq 'ARRAY'
&& !@{ $Transitions{$TransitionEntityID}->{TransitionAction} }
)
)
{
return 1;
}
# if we have Transition Action and it isn't an array return
if ( !IsArrayRefWithData( $Transitions{$TransitionEntityID}->{TransitionAction} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Defective Process configuration: 'TrasitionAction' must be an array in "
. "Process: $Param{ProcessEntityID} -> Path -> "
. "ActivityEntityID: $Param{ActivityEntityID} -> Transition: $TransitionEntityID!",
);
return;
}
# get transition action object
my $TransitionActionObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::TransitionAction');
my $TransitionActions = $TransitionActionObject->TransitionActionList(
TransitionActionEntityID => $Transitions{$TransitionEntityID}->{TransitionAction},
);
if ( !IsArrayRefWithData($TransitionActions) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Getting TransitionActionList for Process: $Param{ProcessEntityID},"
. " Transition: $TransitionEntityID failed.",
);
return;
}
for my $TransitionAction ( @{$TransitionActions} ) {
# Refresh ticket data, as transition actions could already had modified the ticket
# e.g TicketServiceSet -> TicketSLASet, SLA needs to already have a Service,
# see bug#12147.
%Data = $TicketObject->TicketGet(
TicketID => $Param{TicketID},
DynamicFields => 1,
UserID => $Param{UserID},
);
my $TransitionActionModuleObject = $TransitionAction->{Module}->new();
# Transition actions could replace configuration tags with actual ticket values,
# copying the configuration prevents unwanted results if same Transition action is called
# for multiple tickets, see bug#12179
my %Config = %{ $TransitionAction->{Config} || {} };
my $Success = $TransitionActionModuleObject->Run(
UserID => $Param{UserID},
Ticket => \%Data,
ProcessEntityID => $Param{ProcessEntityID},
ActivityEntityID => $Param{ActivityEntityID},
TransitionEntityID => $TransitionEntityID,
TransitionActionEntityID => $TransitionAction->{TransitionActionEntityID},
Config => \%Config,
);
}
return 1;
}
=head2 ProcessTicketActivitySet()
Set Ticket's ActivityEntityID
my $Success = $ProcessObject->ProcessTicketActivitySet(
ProcessEntityID => 'P1',
ActivityEntityID => 'A1',
TicketID => 123,
UserID => 123,
);
Returns:
$Success = 1; # undef
1 if setting the Activity was executed
undef if setting failed
=cut
sub ProcessTicketActivitySet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(ProcessEntityID ActivityEntityID TicketID UserID)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# get activity object
my $ActivityObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity');
# check on valid ActivityEntityID
my $Success = $ActivityObject->ActivityGet(
Interface => 'all',
ActivityEntityID => $Param{ActivityEntityID},
);
if ( !$Success ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Process->ProcessTicketActivitySet called on "
. "non existing ActivityEntityID: $Param{ActivityEntityID}!",
);
return;
}
# Check on valid State
my $Process = $Self->ProcessGet( ProcessEntityID => $Param{ProcessEntityID} );
if ( !$Process->{State} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't set ActivitySet for Process '$Param{ProcessEntityID}', cat get"
. " State out of the config!",
);
return;
}
if ( $Process->{State} eq 'Inactive' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't set ActivitySet for Process '$Param{ProcessEntityID}', State is"
. " '$Process->{State}'!",
);
return;
}
# Get DynamicField Name that's used for storing the ActivityEntityID per ticket
my $DynamicFieldTicketActivityEntityID
= $Kernel::OM->Get('Kernel::Config')->Get('Process::DynamicFieldProcessManagementActivityID');
if ( !$DynamicFieldTicketActivityEntityID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need DynamicFieldProcessManagementActivityID config "
. "for storing of ActivityEntityID on TicketID: $Param{TicketID}!",
);
return;
}
# get dynamic field objects
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# get the dynamic fields for this screen
my $DynamicFieldList = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => 'Ticket',
);
# Grep the Field out of the config of all Ticket DynamicFields
my @DynamicFieldConfig = grep { $_->{Name} eq $DynamicFieldTicketActivityEntityID } @{$DynamicFieldList};
# if the DynamicField isn't there, return 0 and log
if ( !IsHashRefWithData( $DynamicFieldConfig[0] ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "DynamicField: $DynamicFieldTicketActivityEntityID not configured!",
);
return;
}
# If Ticket Update to the new ActivityEntityID was successful return 1
if (
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldConfig[0],
ObjectID => $Param{TicketID},
Value => $Param{ActivityEntityID},
UserID => $Param{UserID}
)
)
{
return 1;
}
return;
}
=head2 ProcessTicketProcessSet()
Set Ticket's ProcessEntityID
my $Success = $ProcessObject->ProcessTicketProcessSet(
ProcessEntityID => 'P1',
TicketID => 123,
UserID => 123,
);
Returns:
$Success = 1; # undef
1 if setting the Activity was executed
undef if setting failed
=cut
sub ProcessTicketProcessSet {
my ( $Self, %Param ) = @_;
for my $Needed (qw(ProcessEntityID TicketID UserID)) {
if ( !defined $Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# Check on valid ProcessEntityID
my $Process = $Self->ProcessGet( ProcessEntityID => $Param{ProcessEntityID} );
if ( !$Process->{State} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't set Process '$Param{ProcessEntityID}' for TicketID"
. " '$Param{TicketID}', cat get State out of the config!",
);
return;
}
if ( $Process->{State} ne 'Active' ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Can't set Process '$Param{ProcessEntityID}' for TicketID"
. " '$Param{TicketID}', State is '$Process->{State}'!",
);
return;
}
# Get DynamicField Name that's used for storing the ActivityEntityID per ticket
my $DynamicFieldTicketProcessID
= $Kernel::OM->Get('Kernel::Config')->Get('Process::DynamicFieldProcessManagementProcessID');
if ( !$DynamicFieldTicketProcessID ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need DynamicFieldProcessManagementProcessID config "
. "for storing of ProcesID on TicketID: $Param{TicketID}!",
);
return;
}
# get dynamic field objects
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# get the dynamic fields for this screen
my $DynamicFieldList = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => 'Ticket',
);
# Grep the Field out of the config of all Ticket DynamicFields
my @DynamicFieldConfig = grep { $_->{Name} eq $DynamicFieldTicketProcessID } @{$DynamicFieldList};
# if the DynamicField isn't there, return 0 and log
if ( !IsHashRefWithData( $DynamicFieldConfig[0] ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "DynamicField: $DynamicFieldTicketProcessID not configured!",
);
return;
}
# If Ticket Update to the new ActivityEntityID was successful return 1
if (
$DynamicFieldBackendObject->ValueSet(
DynamicFieldConfig => $DynamicFieldConfig[0],
ObjectID => $Param{TicketID},
Value => $Param{ProcessEntityID},
UserID => $Param{UserID},
)
)
{
return 1;
}
return;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
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<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut