3359 lines
108 KiB
Perl
3359 lines
108 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::ITSMChange::ITSMWorkOrder;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Kernel::System::EventHandler;
|
|
use Kernel::System::VariableCheck qw(:all);
|
|
|
|
use vars qw(@ISA);
|
|
|
|
our @ObjectDependencies = (
|
|
'Kernel::Config',
|
|
'Kernel::System::Cache',
|
|
'Kernel::System::DB',
|
|
'Kernel::System::DynamicField',
|
|
'Kernel::System::DynamicField::Backend',
|
|
'Kernel::System::Encode',
|
|
'Kernel::System::GeneralCatalog',
|
|
'Kernel::System::HTMLUtils',
|
|
'Kernel::System::ITSMChange::ITSMCondition',
|
|
'Kernel::System::ITSMChange::ITSMStateMachine',
|
|
'Kernel::System::LinkObject',
|
|
'Kernel::System::Log',
|
|
'Kernel::System::Main',
|
|
'Kernel::System::User',
|
|
'Kernel::System::VirtualFS',
|
|
);
|
|
|
|
=head1 NAME
|
|
|
|
Kernel::System::ITSMChange::ITSMWorkOrder - workorder lib
|
|
|
|
=head1 PUBLIC INTERFACE
|
|
|
|
=head2 new()
|
|
|
|
create an object
|
|
|
|
use Kernel::System::ObjectManager;
|
|
local $Kernel::OM = Kernel::System::ObjectManager->new();
|
|
my $WorkOrderObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder');
|
|
|
|
=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} = 'ITSMChangeManagement';
|
|
$Self->{CacheTTL} = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::CacheTTL') * 60;
|
|
|
|
@ISA = (
|
|
'Kernel::System::EventHandler',
|
|
);
|
|
|
|
# init of event handler
|
|
$Self->EventHandlerInit(
|
|
Config => 'ITSMWorkOrder::EventModule',
|
|
);
|
|
|
|
# get database type
|
|
$Self->{DBType} = $Kernel::OM->Get('Kernel::System::DB')->{'DB::Type'} || '';
|
|
$Self->{DBType} = lc $Self->{DBType};
|
|
|
|
return $Self;
|
|
}
|
|
|
|
=head2 WorkOrderAdd()
|
|
|
|
Add a new C<workorder>.
|
|
Internally first a minimal C<workorder> is created,
|
|
then WorkOrderUpdate() is called for setting the remaining arguments.
|
|
|
|
my $WorkOrderID = $WorkOrderObject->WorkOrderAdd(
|
|
ChangeID => 123,
|
|
UserID => 1,
|
|
);
|
|
|
|
or
|
|
|
|
my $WorkOrderID = $WorkOrderObject->WorkOrderAdd(
|
|
ChangeID => 123,
|
|
WorkOrderTitle => 'Replacement of mail server', # (optional)
|
|
Instruction => 'Install the the new server', # (optional)
|
|
Report => 'Installed new server without problems', # (optional)
|
|
WorkOrderStateID => 157, # (optional) or WorkOrderState => 'ready'
|
|
WorkOrderState => 'ready', # (optional) or WorkOrderStateID => 157
|
|
WorkOrderTypeID => 161, # (optional) or WorkOrderType => 'pir'
|
|
WorkOrderType => 'ready', # (optional) or WorkOrderTypeID => 161
|
|
WorkOrderAgentID => 8, # (optional)
|
|
PlannedStartTime => '2009-10-12 00:00:01', # (optional)
|
|
PlannedEndTime => '2009-10-15 15:00:00', # (optional)
|
|
ActualStartTime => '2009-10-14 00:00:01', # (optional)
|
|
ActualEndTime => '2009-01-20 00:00:01', # (optional)
|
|
PlannedEffort => 123, # (optional)
|
|
DynamicField_X => 'Sun', # (optional)
|
|
DynamicField_Y => 'Earth', # (optional)
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderAdd {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(ChangeID UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check that not both WorkOrderState and WorkOrderStateID are given
|
|
if ( $Param{WorkOrderState} && $Param{WorkOrderStateID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need either WorkOrderState OR WorkOrderStateID - not both!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# if a State is given, then look up the ID
|
|
if ( $Param{WorkOrderState} ) {
|
|
$Param{WorkOrderStateID} = $Self->WorkOrderStateLookup(
|
|
WorkOrderState => $Param{WorkOrderState},
|
|
);
|
|
|
|
# delete the workorder state otherwise the update fails
|
|
# as both WorkOrderState and WorkOrderStateID exists then
|
|
delete $Param{WorkOrderState};
|
|
}
|
|
|
|
# check that not both WorkOrderType and WorkOrderTypeID are given
|
|
if ( $Param{WorkOrderType} && $Param{WorkOrderTypeID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need either WorkOrderType OR WorkOrderTypeID - not both!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# if Type is given, then look up the ID
|
|
if ( $Param{WorkOrderType} ) {
|
|
$Param{WorkOrderTypeID} = $Self->WorkOrderTypeLookup(
|
|
WorkOrderType => $Param{WorkOrderType},
|
|
);
|
|
|
|
# delete the workorder type otherwise the update fails
|
|
# as both WorkOrderType and WorkOrderTypeID exists then
|
|
delete $Param{WorkOrderType};
|
|
}
|
|
|
|
# get a plain text version of arguments which might contain HTML markup
|
|
ARGUMENT:
|
|
for my $Argument (qw(Instruction Report)) {
|
|
|
|
next ARGUMENT if !exists $Param{$Argument};
|
|
|
|
$Param{"${Argument}Plain"} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
|
|
String => $Param{$Argument},
|
|
);
|
|
|
|
# Even when passed a plain ASCII string,
|
|
# ToAscii() can return a non-utf8 string with chars in the extended range.
|
|
# Upgrade to utf-8 in order to comply to the OTRS-convention.
|
|
utf8::upgrade( $Param{"${Argument}Plain"} );
|
|
}
|
|
|
|
# check the parameters
|
|
return if !$Self->_CheckWorkOrderParams(%Param);
|
|
|
|
# trigger WorkOrderAddPre-Event
|
|
$Self->EventHandler(
|
|
Event => 'WorkOrderAddPre',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# set initial WorkOrderStateID, use default if not passed
|
|
my $WorkOrderStateID = delete $Param{WorkOrderStateID};
|
|
if ( !$WorkOrderStateID ) {
|
|
|
|
# get initial workorder state id
|
|
my $NextStateIDs = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine')->StateTransitionGet(
|
|
StateID => 0,
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::State',
|
|
);
|
|
$WorkOrderStateID = $NextStateIDs->[0];
|
|
}
|
|
|
|
# set default WorkOrderTypeID, use default if not passed
|
|
my $WorkOrderTypeID = delete $Param{WorkOrderTypeID};
|
|
if ( !$WorkOrderTypeID ) {
|
|
|
|
# set config option
|
|
my $ConfigOption = 'ITSMWorkOrder::Type::Default';
|
|
|
|
# get default workorder type from config
|
|
my $DefaultType = $Kernel::OM->Get('Kernel::Config')->Get($ConfigOption);
|
|
|
|
# check if default type exists in general catalog
|
|
my $ItemDataRef = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::Type',
|
|
Name => $DefaultType,
|
|
);
|
|
|
|
# error handling because of invalid config setting
|
|
if ( !$ItemDataRef || ref $ItemDataRef ne 'HASH' || !%{$ItemDataRef} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The default WorkOrderType '$DefaultType' "
|
|
. "in sysconfig option '$ConfigOption' is invalid! Check the general catalog!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set default
|
|
$WorkOrderTypeID = $ItemDataRef->{ItemID};
|
|
}
|
|
|
|
# get a unique workorder number
|
|
my $WorkOrderNumber = $Self->_GetWorkOrderNumber(%Param);
|
|
|
|
# add WorkOrder to database
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
|
|
SQL => 'INSERT INTO change_workorder '
|
|
. '(change_id, workorder_number, workorder_state_id, workorder_type_id, '
|
|
. 'create_time, create_by, change_time, change_by) '
|
|
. 'VALUES (?, ?, ?, ?, current_timestamp, ?, current_timestamp, ?)',
|
|
Bind => [
|
|
\$Param{ChangeID}, \$WorkOrderNumber, \$WorkOrderStateID, \$WorkOrderTypeID,
|
|
\$Param{UserID}, \$Param{UserID},
|
|
],
|
|
);
|
|
|
|
# get WorkOrderID
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id FROM change_workorder WHERE change_id = ? AND workorder_number = ?',
|
|
Bind => [ \$Param{ChangeID}, \$WorkOrderNumber ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
my $WorkOrderID;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$WorkOrderID = $Row[0];
|
|
}
|
|
|
|
# check error
|
|
if ( !$WorkOrderID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "WorkOrderAdd() failed!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# delete cache
|
|
for my $Key (
|
|
'WorkOrderGet::ID::' . $WorkOrderID,
|
|
'WorkOrderList::ChangeID::' . $Param{ChangeID},
|
|
'WorkOrderChangeEffortsGet::ChangeID::' . $Param{ChangeID},
|
|
'WorkOrderChangeTimeGet::ChangeID::' . $Param{ChangeID},
|
|
'ChangeGet::ID::' . $Param{ChangeID},
|
|
)
|
|
{
|
|
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $Key,
|
|
);
|
|
}
|
|
|
|
# trigger WorkOrderAddPost-Event
|
|
# (yes, we want do do this before the WorkOrderUpdate!)
|
|
$Self->EventHandler(
|
|
Event => 'WorkOrderAddPost',
|
|
Data => {
|
|
%Param,
|
|
WorkOrderID => $WorkOrderID,
|
|
WorkOrderNumber => $WorkOrderNumber,
|
|
WorkOrderStateID => $WorkOrderStateID,
|
|
WorkOrderTypeID => $WorkOrderTypeID,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# update WorkOrder with remaining parameters,
|
|
# the already handles params have been deleted from %Param
|
|
my $UpdateSuccess = $Self->WorkOrderUpdate(
|
|
%Param,
|
|
WorkOrderID => $WorkOrderID,
|
|
);
|
|
|
|
# check update error
|
|
if ( !$UpdateSuccess ) {
|
|
|
|
# delete workorder if it could not be updated
|
|
$Self->WorkOrderDelete(
|
|
WorkOrderID => $WorkOrderID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
return $WorkOrderID;
|
|
}
|
|
|
|
=head2 WorkOrderUpdate()
|
|
|
|
Update a C<workorder>.
|
|
Leading and trailing whitespace is removed from C<WorkOrderTitle>.
|
|
Passing undefined values is generally not allowed. An exception
|
|
are the parameters C<PlannedStartTime>, C<PlannedEndTime>, C<ActualStartTime>, and C<ActualEndTime>.
|
|
There passing C<undef> indicates that the C<workorder> time should be cleared.
|
|
Another exception is the WorkOrderAgentID. Pass undef for removing the C<workorder> agent.
|
|
|
|
my $Success = $WorkOrderObject->WorkOrderUpdate(
|
|
WorkOrderID => 4,
|
|
WorkOrderNumber => 5, # (optional)
|
|
WorkOrderTitle => 'Replacement of mail server', # (optional)
|
|
Instruction => 'Install the the new server', # (optional)
|
|
Report => 'Installed new server without problems', # (optional)
|
|
WorkOrderStateID => 157, # (optional) or WorkOrderState => 'ready'
|
|
WorkOrderState => 'ready', # (optional) or WorkOrderStateID => 157
|
|
WorkOrderTypeID => 161, # (optional) or WorkOrderType => 'pir'
|
|
WorkOrderType => 'pir', # (optional) or WorkOrderStateID => 161
|
|
WorkOrderAgentID => 8, # (optional) can be undef for removing the workorder agent
|
|
PlannedStartTime => '2009-10-12 00:00:01', # (optional) 'undef' indicates clearing
|
|
PlannedEndTime => '2009-10-15 15:00:00', # (optional) 'undef' indicates clearing
|
|
ActualStartTime => '2009-10-14 00:00:01', # (optional) 'undef' indicates clearing
|
|
ActualEndTime => '2009-01-20 00:00:01', # (optional) 'undef' indicates clearing
|
|
PlannedEffort => 123, # (optional)
|
|
AccountedTime => 13, # (optional) the value is added to the value in the database
|
|
DynamicField_X => 'Sun', # (optional)
|
|
DynamicField_Y => 'Earth', # (optional)
|
|
NoNumberCalc => 1, # (optional) default 0, if 1 it prevents a recalculation of the workorder numbers
|
|
BypassStateMachine => 1, # (optional) default 0, if 1 the state machine will be bypassed
|
|
UserID => 1,
|
|
);
|
|
|
|
Constraints:
|
|
|
|
C<xxxStartTime> has to be before C<xxxEndTime>. If just one of the parameter pair is passed
|
|
the other time is retrieved from database.
|
|
The C<WorkOrderStateID> is checked against the state machine.
|
|
|
|
=cut
|
|
|
|
sub WorkOrderUpdate {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(WorkOrderID UserID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check that not both WorkOrderState and WorkOrderStateID are given
|
|
if ( $Param{WorkOrderState} && $Param{WorkOrderStateID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need either WorkOrderState OR WorkOrderStateID - not both!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# when the State is given, then look up the ID
|
|
if ( $Param{WorkOrderState} ) {
|
|
$Param{WorkOrderStateID} = $Self->WorkOrderStateLookup(
|
|
WorkOrderState => $Param{WorkOrderState},
|
|
);
|
|
}
|
|
|
|
# check that not both Type and TypeID are given
|
|
if ( $Param{WorkOrderType} && $Param{WorkOrderTypeID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need either WorkOrderType OR WorkOrderTypeID - not both!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# if Type is given, then look up the ID
|
|
if ( $Param{WorkOrderType} ) {
|
|
$Param{WorkOrderTypeID} = $Self->WorkOrderTypeLookup(
|
|
WorkOrderType => $Param{WorkOrderType},
|
|
);
|
|
}
|
|
|
|
# normalize the Title, when it is given
|
|
if ( $Param{WorkOrderTitle} && !ref $Param{WorkOrderTitle} ) {
|
|
|
|
# remove leading whitespace
|
|
$Param{WorkOrderTitle} =~ s{ \A \s+ }{}xms;
|
|
|
|
# remove trailing whitespace
|
|
$Param{WorkOrderTitle} =~ s{ \s+ \z }{}xms;
|
|
}
|
|
|
|
# get a plain text version of arguments which might contain HTML markup
|
|
ARGUMENT:
|
|
for my $Argument (qw(Instruction Report)) {
|
|
|
|
next ARGUMENT if !exists $Param{$Argument};
|
|
|
|
$Param{"${Argument}Plain"} = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
|
|
String => $Param{$Argument},
|
|
);
|
|
|
|
# Even when passed a plain ASCII string,
|
|
# ToAscii() can return a non-utf8 string with chars in the extended range.
|
|
# Upgrade to utf-8 in order to comply to the OTRS-convention.
|
|
utf8::upgrade( $Param{"${Argument}Plain"} );
|
|
}
|
|
|
|
# default values for planned effort and accounted time
|
|
# this avoids superflous history entries
|
|
ARGUMENT:
|
|
for my $Argument (qw(PlannedEffort AccountedTime)) {
|
|
|
|
next ARGUMENT if !exists $Param{$Argument};
|
|
|
|
$Param{$Argument} ||= 0;
|
|
}
|
|
|
|
# check the given parameters
|
|
return if !$Self->_CheckWorkOrderParams(%Param);
|
|
|
|
# check sanity of the new state with the state machine
|
|
if ( $Param{WorkOrderStateID} ) {
|
|
|
|
# get workorder id
|
|
my $WorkOrderID = $Param{WorkOrderID};
|
|
|
|
# do not give WorkOrderPossibleStatesGet() the WorkOrderID
|
|
# if the statemachine should be bypassed.
|
|
# WorkOrderPossibleStatesGet() will then return all workorder states
|
|
if ( $Param{BypassStateMachine} ) {
|
|
$WorkOrderID = undef;
|
|
}
|
|
|
|
# get the list of possible next states
|
|
my $StateList = $Self->WorkOrderPossibleStatesGet(
|
|
WorkOrderID => $WorkOrderID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !grep { $_->{Key} == $Param{WorkOrderStateID} } @{$StateList} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The state $Param{WorkOrderStateID} is not a possible next state!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get old data to be given to _CheckWorkOrderParams() and the post event handler
|
|
my $WorkOrderData = $Self->WorkOrderGet(
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check if the timestamps are correct
|
|
return if !$Self->_CheckTimestamps(
|
|
%Param,
|
|
WorkOrderData => $WorkOrderData,
|
|
);
|
|
|
|
# trigger WorkOrderUpdatePre-Event
|
|
$Self->EventHandler(
|
|
Event => 'WorkOrderUpdatePre',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# set the workorder dynamic fields
|
|
KEY:
|
|
for my $Key ( sort keys %Param ) {
|
|
|
|
next KEY if $Key !~ m{ \A DynamicField_(.*) \z }xms;
|
|
|
|
# save the real name of the dynamic field (without prefix)
|
|
my $DynamicFieldName = $1;
|
|
|
|
# get the dynamic field config
|
|
my $DynamicFieldConfig = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldGet(
|
|
Name => $DynamicFieldName,
|
|
);
|
|
|
|
# write value to dynamic field
|
|
my $Success = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->ValueSet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $Param{WorkOrderID},
|
|
Value => $Param{$Key},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# map update attributes to column names
|
|
my %Attribute = (
|
|
WorkOrderTitle => 'title',
|
|
WorkOrderNumber => 'workorder_number',
|
|
Instruction => 'instruction',
|
|
Report => 'report',
|
|
WorkOrderStateID => 'workorder_state_id',
|
|
WorkOrderTypeID => 'workorder_type_id',
|
|
WorkOrderAgentID => 'workorder_agent_id',
|
|
PlannedStartTime => 'planned_start_time',
|
|
PlannedEndTime => 'planned_end_time',
|
|
ActualStartTime => 'actual_start_time',
|
|
ActualEndTime => 'actual_end_time',
|
|
InstructionPlain => 'instruction_plain',
|
|
ReportPlain => 'report_plain',
|
|
);
|
|
|
|
# build SQL to update workorder
|
|
my $SQL = 'UPDATE change_workorder SET ';
|
|
my @Bind;
|
|
|
|
# define the DefaultTimeStamp
|
|
my $DefaultTimeStamp = '9999-01-01 00:00:00';
|
|
|
|
ATTRIBUTE:
|
|
for my $Attribute ( sort keys %Attribute ) {
|
|
|
|
# preserve the old value, when the column isn't in function parameters
|
|
next ATTRIBUTE if !exists $Param{$Attribute};
|
|
|
|
# attribute is defined
|
|
if ( defined $Param{$Attribute} ) {
|
|
$SQL .= "$Attribute{$Attribute} = ?, ";
|
|
push @Bind, \$Param{$Attribute};
|
|
}
|
|
|
|
# it's ok if the WorkOrderAgentID is not defined
|
|
elsif ( $Attribute eq 'WorkOrderAgentID' ) {
|
|
$SQL .= "$Attribute{$Attribute} = NULL, ";
|
|
}
|
|
|
|
# attribute is not defined and is one of the time parameters
|
|
elsif ( $Attribute =~ m{ \A ( Actual | Planned ) ( Start | End ) Time \z }xms ) {
|
|
$SQL .= "$Attribute{$Attribute} = ?, ";
|
|
push @Bind, \$DefaultTimeStamp;
|
|
}
|
|
}
|
|
|
|
# addition of accounted time
|
|
if ( $Param{AccountedTime} ) {
|
|
|
|
# get current accounted time
|
|
my $CurrentAccountedTime = $WorkOrderData->{AccountedTime} || 0;
|
|
|
|
# add new accouted time to current accounted time
|
|
my $AccountedTime = $CurrentAccountedTime + $Param{AccountedTime};
|
|
|
|
# db quote
|
|
$AccountedTime = $Kernel::OM->Get('Kernel::System::DB')->Quote( $AccountedTime, 'Number' );
|
|
|
|
# build SQL (without binds)
|
|
$SQL .= "accounted_time = $AccountedTime, ";
|
|
}
|
|
|
|
# setting of planned effort
|
|
if ( $Param{PlannedEffort} ) {
|
|
|
|
# db quote
|
|
$Param{PlannedEffort} = $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{PlannedEffort}, 'Number' );
|
|
|
|
# build SQL (without binds)
|
|
$SQL .= "planned_effort = $Param{PlannedEffort}, ";
|
|
}
|
|
|
|
$SQL .= 'change_time = current_timestamp, change_by = ? ';
|
|
push @Bind, \$Param{UserID};
|
|
$SQL .= 'WHERE id = ?';
|
|
push @Bind, \$Param{WorkOrderID};
|
|
|
|
# update workorder
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
|
|
SQL => $SQL,
|
|
Bind => \@Bind,
|
|
);
|
|
|
|
# delete cache
|
|
for my $Key (
|
|
'WorkOrderGet::ID::' . $Param{WorkOrderID},
|
|
'WorkOrderList::ChangeID::' . $WorkOrderData->{ChangeID},
|
|
'WorkOrderChangeEffortsGet::ChangeID::' . $WorkOrderData->{ChangeID},
|
|
'WorkOrderChangeTimeGet::ChangeID::' . $WorkOrderData->{ChangeID},
|
|
'ChangeGet::ID::' . $WorkOrderData->{ChangeID},
|
|
)
|
|
{
|
|
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $Key,
|
|
);
|
|
}
|
|
|
|
# trigger WorkOrderUpdatePost-Event
|
|
$Self->EventHandler(
|
|
Event => 'WorkOrderUpdatePost',
|
|
Data => {
|
|
OldWorkOrderData => $WorkOrderData,
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 WorkOrderGet()
|
|
|
|
Return a C<WorkOrder> as hash reference.
|
|
When the C<workorder> does not exist, a false value is returned.
|
|
The optional option C<LogNo> turns off logging when the C<workorder> does not exist.
|
|
|
|
my $WorkOrderRef = $WorkOrderObject->WorkOrderGet(
|
|
WorkOrderID => 123,
|
|
UserID => 1,
|
|
LogNo => 1, # optional, turns off logging when the workorder does not exist
|
|
);
|
|
|
|
The returned hash reference contains following elements:
|
|
|
|
$WorkOrder{WorkOrderID}
|
|
$WorkOrder{ChangeID}
|
|
$WorkOrder{WorkOrderNumber}
|
|
$WorkOrder{WorkOrderTitle}
|
|
$WorkOrder{Instruction}
|
|
$WorkOrder{InstructionPlain}
|
|
$WorkOrder{Report}
|
|
$WorkOrder{ReportPlain}
|
|
$WorkOrder{WorkOrderStateID}
|
|
$WorkOrder{WorkOrderState} # fetched from the general catalog
|
|
$WorkOrder{WorkOrderStateSignal} # fetched from SysConfig
|
|
$WorkOrder{WorkOrderTypeID}
|
|
$WorkOrder{WorkOrderType} # fetched from the general catalog
|
|
$WorkOrder{WorkOrderAgentID}
|
|
$WorkOrder{PlannedStartTime}
|
|
$WorkOrder{PlannedEndTime}
|
|
$WorkOrder{ActualStartTime}
|
|
$WorkOrder{ActualEndTime}
|
|
$WorkOrder{AccountedTime}
|
|
$WorkOrder{PlannedEffort}
|
|
$WorkOrder{DynamicField_X}
|
|
$WorkOrder{DynamicField_Y}
|
|
$WorkOrder{CreateTime}
|
|
$WorkOrder{CreateBy}
|
|
$WorkOrder{ChangeTime}
|
|
$WorkOrder{ChangeBy}
|
|
|
|
=cut
|
|
|
|
sub WorkOrderGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(WorkOrderID UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
my %WorkOrderData;
|
|
|
|
# check cache
|
|
my $CacheKey = 'WorkOrderGet::ID::' . $Param{WorkOrderID};
|
|
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
|
|
if ($Cache) {
|
|
|
|
# get data from cache
|
|
%WorkOrderData = %{$Cache};
|
|
}
|
|
|
|
else {
|
|
|
|
# get data from database
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT id, change_id, workorder_number, title, '
|
|
. 'instruction, instruction_plain, '
|
|
. 'report, report_plain, '
|
|
. 'workorder_state_id, workorder_type_id, workorder_agent_id, '
|
|
. 'planned_start_time, planned_end_time, actual_start_time, actual_end_time, '
|
|
. 'create_time, create_by, '
|
|
. 'change_time, change_by, '
|
|
. 'planned_effort, accounted_time '
|
|
. 'FROM change_workorder '
|
|
. 'WHERE id = ?',
|
|
Bind => [ \$Param{WorkOrderID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$WorkOrderData{WorkOrderID} = $Row[0];
|
|
$WorkOrderData{ChangeID} = $Row[1];
|
|
$WorkOrderData{WorkOrderNumber} = $Row[2];
|
|
$WorkOrderData{WorkOrderTitle} = defined $Row[3] ? $Row[3] : '';
|
|
$WorkOrderData{Instruction} = defined $Row[4] ? $Row[4] : '';
|
|
$WorkOrderData{InstructionPlain} = defined $Row[5] ? $Row[5] : '';
|
|
$WorkOrderData{Report} = defined $Row[6] ? $Row[6] : '';
|
|
$WorkOrderData{ReportPlain} = defined $Row[7] ? $Row[7] : '';
|
|
$WorkOrderData{WorkOrderStateID} = $Row[8];
|
|
$WorkOrderData{WorkOrderTypeID} = $Row[9];
|
|
$WorkOrderData{WorkOrderAgentID} = $Row[10];
|
|
$WorkOrderData{PlannedStartTime} = $Row[11];
|
|
$WorkOrderData{PlannedEndTime} = $Row[12];
|
|
$WorkOrderData{ActualStartTime} = $Row[13];
|
|
$WorkOrderData{ActualEndTime} = $Row[14];
|
|
$WorkOrderData{CreateTime} = $Row[15];
|
|
$WorkOrderData{CreateBy} = $Row[16];
|
|
$WorkOrderData{ChangeTime} = $Row[17];
|
|
$WorkOrderData{ChangeBy} = $Row[18];
|
|
$WorkOrderData{PlannedEffort} = $Row[19] // '0.00';
|
|
$WorkOrderData{AccountedTime} = $Row[20] // '0.00';
|
|
}
|
|
|
|
# check error
|
|
if ( !%WorkOrderData ) {
|
|
if ( !$Param{LogNo} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "WorkOrderID $Param{WorkOrderID} does not exist!",
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
TIMEFIELD:
|
|
for my $Time (qw(PlannedStartTime PlannedEndTime ActualStartTime ActualEndTime)) {
|
|
|
|
next TIMEFIELD if !$WorkOrderData{$Time};
|
|
|
|
# cleanup time stamps (some databases are using e. g. 2008-02-25 22:03:00.000000)
|
|
$WorkOrderData{$Time}
|
|
=~ s{ \A ( \d\d\d\d - \d\d - \d\d \s \d\d:\d\d:\d\d ) \. .+? \z }{$1}xms;
|
|
|
|
# replace default time values with empty string
|
|
if ( $WorkOrderData{$Time} eq '9999-01-01 00:00:00' ) {
|
|
$WorkOrderData{$Time} = '';
|
|
}
|
|
}
|
|
|
|
ATTRIBUTE:
|
|
for my $Attribute (qw(PlannedEffort AccountedTime)) {
|
|
|
|
next ATTRIBUTE if !$WorkOrderData{$Attribute};
|
|
|
|
# do not show zero values
|
|
if ( $WorkOrderData{$Attribute} eq 0 ) {
|
|
$WorkOrderData{$Attribute} = '';
|
|
next ATTRIBUTE;
|
|
}
|
|
|
|
# convert decimal character from ',' to '.' if neccessary
|
|
$WorkOrderData{$Attribute} =~ s{,}{.}xmsg;
|
|
|
|
# format as decimal number
|
|
$WorkOrderData{$Attribute} = sprintf '%.2f', $WorkOrderData{$Attribute};
|
|
}
|
|
|
|
# get all dynamic fields for the object type ITSMWorkOrder
|
|
my $DynamicFieldList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
ObjectType => 'ITSMWorkOrder',
|
|
);
|
|
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$DynamicFieldList} ) {
|
|
|
|
# validate each dynamic field
|
|
next DYNAMICFIELD if !$DynamicFieldConfig;
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
|
|
next DYNAMICFIELD if !IsHashRefWithData( $DynamicFieldConfig->{Config} );
|
|
|
|
# get the current value for each dynamic field
|
|
my $Value = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->ValueGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $Param{WorkOrderID},
|
|
);
|
|
|
|
# set the dynamic field name and value into the workorder data hash
|
|
$WorkOrderData{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $Value // '';
|
|
}
|
|
|
|
# set cache (workorder data exists at this point, it was checked before)
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
|
Type => 'ITSMChangeManagement',
|
|
Key => $CacheKey,
|
|
Value => \%WorkOrderData,
|
|
TTL => $Self->{CacheTTL},
|
|
);
|
|
}
|
|
|
|
# add the name of the workorder state
|
|
if ( $WorkOrderData{WorkOrderStateID} ) {
|
|
$WorkOrderData{WorkOrderState} = $Self->WorkOrderStateLookup(
|
|
WorkOrderStateID => $WorkOrderData{WorkOrderStateID},
|
|
);
|
|
}
|
|
|
|
# add the workorder state signal
|
|
if ( $WorkOrderData{WorkOrderState} ) {
|
|
|
|
# get all workorder state signals
|
|
my $StateSignal = $Kernel::OM->Get('Kernel::Config')->Get('ITSMWorkOrder::State::Signal');
|
|
|
|
$WorkOrderData{WorkOrderStateSignal} = $StateSignal->{ $WorkOrderData{WorkOrderState} };
|
|
}
|
|
|
|
# add the name of the workorder type
|
|
if ( $WorkOrderData{WorkOrderTypeID} ) {
|
|
$WorkOrderData{WorkOrderType} = $Self->WorkOrderTypeLookup(
|
|
WorkOrderTypeID => $WorkOrderData{WorkOrderTypeID},
|
|
);
|
|
}
|
|
|
|
return \%WorkOrderData;
|
|
}
|
|
|
|
=head2 WorkOrderList()
|
|
|
|
Return a list of all C<workorder> ids of the given change as array reference.
|
|
The C<workorder> ids are ordered by C<workorder> number.
|
|
|
|
my $WorkOrderIDsRef = $WorkOrderObject->WorkOrderList(
|
|
ChangeID => 5,
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderList {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(ChangeID UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
my @WorkOrderIDs;
|
|
|
|
# check cache
|
|
my $CacheKey = 'WorkOrderList::ChangeID::' . $Param{ChangeID};
|
|
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
|
|
if ($Cache) {
|
|
|
|
# get data from cache
|
|
@WorkOrderIDs = @{$Cache};
|
|
}
|
|
|
|
# get data from database
|
|
else {
|
|
|
|
# get workorder ids
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL =>
|
|
'SELECT id FROM change_workorder '
|
|
. 'WHERE change_id = ? '
|
|
. 'ORDER BY workorder_number, id',
|
|
Bind => [ \$Param{ChangeID} ],
|
|
);
|
|
|
|
# fetch the result
|
|
while ( my ($ID) = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
push @WorkOrderIDs, $ID;
|
|
}
|
|
|
|
# set cache
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
|
Type => 'ITSMChangeManagement',
|
|
Key => $CacheKey,
|
|
Value => \@WorkOrderIDs,
|
|
TTL => $Self->{CacheTTL},
|
|
);
|
|
}
|
|
|
|
return \@WorkOrderIDs;
|
|
}
|
|
|
|
=head2 WorkOrderSearch()
|
|
|
|
Returns either a list, as an arrayref, or a count of found C<workorder> ids.
|
|
The count of results is returned when the parameter C<Result => 'COUNT'> is passed.
|
|
|
|
The search criteria are logically AND connected.
|
|
When a list is passed as C<criterium>, the individual members are OR connected.
|
|
When an undef or a reference to an empty array is passed, then the search C<criterium>
|
|
is ignored.
|
|
|
|
my $WorkOrderIDsRef = $WorkOrderObject->WorkOrderSearch(
|
|
ChangeIDs => [ 123, 122 ] # (optional)
|
|
WorkOrderNumber => 12, # (optional)
|
|
|
|
WorkOrderTitle => 'Replacement of mail server', # (optional)
|
|
Instruction => 'Install the the new server', # (optional)
|
|
Report => 'Installed new server without problems', # (optional)
|
|
|
|
# DynamicFields (for workorders)
|
|
# At least one operator must be specified. Operators will be connected with AND,
|
|
# values in an operator with OR.
|
|
# You can also pass more than one argument to an operator: ['value1', 'value2']
|
|
DynamicField_FieldNameX => {
|
|
Equals => 123,
|
|
Like => 'value*', # "equals" operator with wildcard support
|
|
GreaterThan => '2001-01-01 01:01:01',
|
|
GreaterThanEquals => '2001-01-01 01:01:01',
|
|
SmallerThan => '2002-02-02 02:02:02',
|
|
SmallerThanEquals => '2002-02-02 02:02:02',
|
|
}
|
|
|
|
WorkOrderStateIDs => [ 11, 12 ], # (optional)
|
|
WorkOrderStates => [ 'closed', 'canceled' ], # (optional)
|
|
|
|
WorkOrderTypeIDs => [ 21, 22 ], # (optional)
|
|
WorkOrderTypes => [ 'approval', 'workorder' ], # (optional)
|
|
|
|
WorkOrderAgentIDs => [ 1, 2, 3 ], # (optional)
|
|
CreateBy => [ 5, 2, 3 ], # (optional)
|
|
ChangeBy => [ 3, 2, 1 ], # (optional)
|
|
|
|
# search in text fields of change object
|
|
ChangeNumber => 'Number of change', # (optional)
|
|
ChangeTitle => 'Title of change', # (optional)
|
|
ChangeDescription => 'Description of change', # (optional)
|
|
ChangeJustification => 'Justification of change', # (optional)
|
|
|
|
# workorders with planned start time after ...
|
|
PlannedStartTimeNewerDate => '2006-01-09 00:00:01', # (optional)
|
|
# workorders with planned start time before then ....
|
|
PlannedStartTimeOlderDate => '2006-01-19 23:59:59', # (optional)
|
|
|
|
# workorders with planned end time after ...
|
|
PlannedEndTimeNewerDate => '2006-01-09 00:00:01', # (optional)
|
|
# workorders with planned end time before then ....
|
|
PlannedEndTimeOlderDate => '2006-01-19 23:59:59', # (optional)
|
|
|
|
# workorders with actual start time after ...
|
|
ActualStartTimeNewerDate => '2006-01-09 00:00:01', # (optional)
|
|
# workorders with actual start time before then ....
|
|
ActualStartTimeOlderDate => '2006-01-19 23:59:59', # (optional)
|
|
|
|
# workorders with actual end time after ...
|
|
ActualEndTimeNewerDate => '2006-01-09 00:00:01', # (optional)
|
|
# workorders with actual end time before then ....
|
|
ActualEndTimeOlderDate => '2006-01-19 23:59:59', # (optional)
|
|
|
|
# workorders with created time after ...
|
|
CreateTimeNewerDate => '2006-01-09 00:00:01', # (optional)
|
|
# workorders with created time before then ....
|
|
CreateTimeOlderDate => '2006-01-19 23:59:59', # (optional)
|
|
|
|
# workorders with changed time after ...
|
|
ChangeTimeNewerDate => '2006-01-09 00:00:01', # (optional)
|
|
# workorders with changed time before then ....
|
|
ChangeTimeOlderDate => '2006-01-19 23:59:59', # (optional)
|
|
|
|
OrderBy => [ 'ChangeID', 'WorkOrderNumber' ], # (optional)
|
|
# ignored when the result type is 'COUNT'
|
|
# default: [ 'WorkOrderID' ],
|
|
# (WorkOrderID, ChangeID, WorkOrderNumber, WorkOrderTitle
|
|
# WorkOrderStateID, WorkOrderTypeID, WorkOrderAgentID,
|
|
# PlannedStartTime, PlannedEndTime,
|
|
# ActualStartTime, ActualEndTime,
|
|
# CreateTime, CreateBy, ChangeTime, ChangeBy)
|
|
|
|
# Additional information for OrderBy:
|
|
# The OrderByDirection can be specified for each OrderBy attribute.
|
|
# The pairing is made by the array indices.
|
|
|
|
OrderByDirection => [ 'Down', 'Up' ], # (optional)
|
|
# ignored when the result type is 'COUNT'
|
|
# default: [ 'Down' ]
|
|
# (Down | Up)
|
|
|
|
UsingWildcards => 1, # (optional)
|
|
# (0 | 1) default 1
|
|
|
|
Result => 'ARRAY' || 'COUNT', # (optional)
|
|
# default: ARRAY, returns an array of workorder ids
|
|
# COUNT returns a scalar with the number of found workorders
|
|
|
|
Limit => 100, # (optional)
|
|
# ignored when the result type is 'COUNT'
|
|
|
|
MirrorDB => 1, # (optional)
|
|
# (0 | 1) default 0
|
|
# if set to 1 and ITSMChange::ChangeSearch::MirrorDB
|
|
# is activated and a mirror db is configured in
|
|
# Core::MirrorDB::DSN the workorder search will then use
|
|
# the mirror db
|
|
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderSearch {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{UserID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need UserID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# verify that all passed array parameters contain an arrayref
|
|
ARGUMENT:
|
|
for my $Argument (
|
|
qw(
|
|
OrderBy
|
|
OrderByDirection
|
|
WorkOrderStateIDs
|
|
WorkOrderStates
|
|
WorkOrderTypes
|
|
WorkOrderTypeIDs
|
|
ChangeIDs
|
|
WorkOrderAgentIDs
|
|
CreateBy
|
|
ChangeBy
|
|
)
|
|
)
|
|
{
|
|
if ( !defined $Param{$Argument} ) {
|
|
$Param{$Argument} ||= [];
|
|
|
|
next ARGUMENT;
|
|
}
|
|
|
|
if ( ref $Param{$Argument} ne 'ARRAY' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "$Argument must be an array reference!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# define a local database object
|
|
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
|
|
|
|
# get config object
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# if we need to do a workorder search on an external mirror database
|
|
if (
|
|
$Param{MirrorDB}
|
|
&& $ConfigObject->Get('ITSMChange::ChangeSearch::MirrorDB')
|
|
&& $ConfigObject->Get('Core::MirrorDB::DSN')
|
|
&& $ConfigObject->Get('Core::MirrorDB::User')
|
|
&& $ConfigObject->Get('Core::MirrorDB::Password')
|
|
)
|
|
{
|
|
|
|
# create an extra database object for the mirror db
|
|
my $ExtraDatabaseObject = Kernel::System::DB->new(
|
|
DatabaseDSN => $ConfigObject->Get('Core::MirrorDB::DSN'),
|
|
DatabaseUser => $ConfigObject->Get('Core::MirrorDB::User'),
|
|
DatabasePw => $ConfigObject->Get('Core::MirrorDB::Password'),
|
|
);
|
|
|
|
# check error
|
|
if ( !$ExtraDatabaseObject ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Could not create database object for MirrorDB!',
|
|
);
|
|
return;
|
|
}
|
|
$DBObject = $ExtraDatabaseObject;
|
|
}
|
|
|
|
my @SQLWhere; # assemble the conditions used in the WHERE clause
|
|
my @InnerJoinTables; # keep track of the tables that need to be inner joined
|
|
|
|
# check all configured workorder dynamic fields, build lookup hash by name
|
|
my %WorkOrderDynamicFieldName2Config;
|
|
my $WorkOrderDynamicFields = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
ObjectType => 'ITSMWorkOrder',
|
|
);
|
|
for my $DynamicField ( @{$WorkOrderDynamicFields} ) {
|
|
$WorkOrderDynamicFieldName2Config{ $DynamicField->{Name} } = $DynamicField;
|
|
}
|
|
|
|
# define order table
|
|
my %OrderByTable = (
|
|
|
|
# workorder attributes
|
|
ChangeID => 'wo.change_id',
|
|
WorkOrderID => 'wo.id',
|
|
WorkOrderNumber => 'wo.workorder_number',
|
|
WorkOrderTitle => 'wo.title',
|
|
WorkOrderStateID => 'wo.workorder_state_id',
|
|
WorkOrderTypeID => 'wo.workorder_type_id',
|
|
WorkOrderAgentID => 'wo.workorder_agent_id',
|
|
PlannedStartTime => 'wo.planned_start_time',
|
|
PlannedEndTime => 'wo.planned_end_time',
|
|
ActualStartTime => 'wo.actual_start_time',
|
|
ActualEndTime => 'wo.actual_end_time',
|
|
CreateTime => 'wo.create_time',
|
|
CreateBy => 'wo.create_by',
|
|
ChangeTime => 'wo.change_time',
|
|
ChangeBy => 'wo.change_by',
|
|
|
|
# change attributes
|
|
ChangeNumber => 'c.change_number',
|
|
ChangeTitle => 'c.title',
|
|
ChangeStateID => 'c.change_state_id',
|
|
ChangeManagerID => 'c.change_manager_id',
|
|
ChangeBuilderID => 'c.change_builder_id',
|
|
CategoryID => 'c.category_id',
|
|
ImpactID => 'c.impact_id',
|
|
PriorityID => 'c.priority_id',
|
|
RequestedTime => 'c.requested_time',
|
|
);
|
|
|
|
# check if OrderBy contains only unique valid values
|
|
my %OrderBySeen;
|
|
for my $OrderBy ( @{ $Param{OrderBy} } ) {
|
|
|
|
if ( !$OrderBy || !$OrderByTable{$OrderBy} || $OrderBySeen{$OrderBy} ) {
|
|
|
|
# found an error
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "OrderBy contains invalid value '$OrderBy' "
|
|
. 'or the value is used more than once!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# remember the value to check if it appears more than once
|
|
$OrderBySeen{$OrderBy} = 1;
|
|
|
|
# join the change table, when it is needed for the OrderBy-clause
|
|
if ( $OrderByTable{$OrderBy} =~ m{ \A c[.] }xms ) {
|
|
push @InnerJoinTables, 'c';
|
|
}
|
|
}
|
|
|
|
# check if OrderByDirection array contains only 'Up' or 'Down'
|
|
DIRECTION:
|
|
for my $Direction ( @{ $Param{OrderByDirection} } ) {
|
|
|
|
# only 'Up' or 'Down' allowed
|
|
next DIRECTION if $Direction eq 'Up';
|
|
next DIRECTION if $Direction eq 'Down';
|
|
|
|
# found an error
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "OrderByDirection can only contain 'Up' or 'Down'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set default values
|
|
if ( !defined $Param{UsingWildcards} ) {
|
|
$Param{UsingWildcards} = 1;
|
|
}
|
|
|
|
# set the default behaviour for the return type
|
|
my $Result = $Param{Result} || 'ARRAY';
|
|
|
|
# check whether all of the given WorkOrderStateIDs are valid
|
|
return if !$Self->WorkOrderStateIDsCheck( WorkOrderStateIDs => $Param{WorkOrderStateIDs} );
|
|
|
|
# look up and thus check the States
|
|
for my $WorkOrderState ( @{ $Param{WorkOrderStates} } ) {
|
|
|
|
# look up the ID for the name
|
|
my $WorkOrderStateID = $Self->WorkOrderStateLookup(
|
|
WorkOrderState => $WorkOrderState,
|
|
);
|
|
|
|
# check whether the ID was found, whether the name exists
|
|
if ( !$WorkOrderStateID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The workorder state $WorkOrderState is not known!",
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
push @{ $Param{WorkOrderStateIDs} }, $WorkOrderStateID;
|
|
}
|
|
|
|
# check whether the given WorkOrderTypeIDs are all valid
|
|
return if !$Self->_CheckWorkOrderTypeIDs( WorkOrderTypeIDs => $Param{WorkOrderTypeIDs} );
|
|
|
|
# look up and thus check the WorkOrderTypes
|
|
for my $Type ( @{ $Param{WorkOrderTypes} } ) {
|
|
|
|
# get the ID for the name
|
|
my $TypeID = $Self->WorkOrderTypeLookup(
|
|
WorkOrderType => $Type,
|
|
);
|
|
|
|
# check whether the ID was found, whether the name exists
|
|
if ( !$TypeID ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The workorder type '$Type' is not known!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
push @{ $Param{WorkOrderTypeIDs} }, $TypeID;
|
|
}
|
|
|
|
# add string params to the WHERE clause
|
|
my %StringParams = (
|
|
|
|
# in workorder table
|
|
WorkOrderNumber => 'wo.workorder_number',
|
|
WorkOrderTitle => 'wo.title',
|
|
Instruction => 'wo.instruction_plain',
|
|
Report => 'wo.report_plain',
|
|
|
|
# in change table
|
|
ChangeNumber => 'c.change_number',
|
|
ChangeTitle => 'c.title',
|
|
ChangeDescription => 'c.description_plain',
|
|
ChangeJustification => 'c.justification_plain',
|
|
);
|
|
|
|
# add string params to sql-where-array
|
|
STRINGPARAM:
|
|
for my $StringParam ( sort keys %StringParams ) {
|
|
|
|
# check string params for useful values, the string '0' is allowed
|
|
next STRINGPARAM if !exists $Param{$StringParam};
|
|
next STRINGPARAM if !defined $Param{$StringParam};
|
|
next STRINGPARAM if $Param{$StringParam} eq '';
|
|
|
|
# quote
|
|
$Param{$StringParam} = $DBObject->Quote( $Param{$StringParam} );
|
|
|
|
# check if a CLOB field is used in oracle
|
|
# Fix/Workaround for ORA-00932: inconsistent datatypes: expected - got CLOB
|
|
my $ForceLikeSearchForSpecialFields;
|
|
if (
|
|
$Self->{DBType} eq 'oracle'
|
|
&& (
|
|
$StringParam eq 'Instruction'
|
|
|| $StringParam eq 'Report'
|
|
|| $StringParam eq 'ChangeDescription'
|
|
|| $StringParam eq 'ChangeJustification'
|
|
)
|
|
)
|
|
{
|
|
my $ForceLikeSearchForSpecialFields = 1;
|
|
}
|
|
|
|
# wildcards are used (or LIKE search is forced for some special fields on oracle)
|
|
if ( $Param{UsingWildcards} || $ForceLikeSearchForSpecialFields ) {
|
|
|
|
# get like escape string needed for some databases (e.g. oracle)
|
|
my $LikeEscapeString = $DBObject->GetDatabaseFunction('LikeEscapeString');
|
|
|
|
# Quote
|
|
$Param{$StringParam} = $DBObject->Quote( $Param{$StringParam}, 'Like' );
|
|
|
|
# replace * with %
|
|
$Param{$StringParam} =~ s{ \*+ }{%}xmsg;
|
|
|
|
# do not use string params which contain only %
|
|
next STRINGPARAM if $Param{$StringParam} =~ m{ \A %* \z }xms;
|
|
|
|
push @SQLWhere,
|
|
"LOWER($StringParams{$StringParam}) LIKE LOWER('$Param{$StringParam}') $LikeEscapeString";
|
|
}
|
|
|
|
# no wildcards are used
|
|
else {
|
|
push @SQLWhere,
|
|
"LOWER($StringParams{$StringParam}) = LOWER('$Param{$StringParam}')";
|
|
}
|
|
|
|
# join the change table, when it is needed in the WHERE clause
|
|
if ( $StringParams{$StringParam} =~ m{ \A c[.] }xms ) {
|
|
push @InnerJoinTables, 'c';
|
|
}
|
|
}
|
|
|
|
# build sql for dynamic fields
|
|
my $SQLDynamicFieldInnerJoins = ''; # join-statements
|
|
my $SQLDynamicFieldWhere = ''; # where-clause
|
|
my $DynamicFieldJoinCounter = 1;
|
|
|
|
DYNAMICFIELD:
|
|
for my $DynamicField ( @{$WorkOrderDynamicFields} ) {
|
|
|
|
my $SearchParam = $Param{ "DynamicField_" . $DynamicField->{Name} };
|
|
|
|
next DYNAMICFIELD if ( !$SearchParam );
|
|
next DYNAMICFIELD if ( ref $SearchParam ne 'HASH' );
|
|
|
|
my $NeedJoin;
|
|
|
|
for my $Operator ( sort keys %{$SearchParam} ) {
|
|
|
|
my @SearchParams = ( ref $SearchParam->{$Operator} eq 'ARRAY' )
|
|
? @{ $SearchParam->{$Operator} }
|
|
: ( $SearchParam->{$Operator} );
|
|
|
|
my $SQLDynamicFieldWhereSub = '';
|
|
if ($SQLDynamicFieldWhere) {
|
|
$SQLDynamicFieldWhereSub = ' AND (';
|
|
}
|
|
else {
|
|
$SQLDynamicFieldWhereSub = ' (';
|
|
}
|
|
|
|
my $Counter = 0;
|
|
TEXT:
|
|
for my $Text (@SearchParams) {
|
|
next TEXT if ( !defined $Text || $Text eq '' );
|
|
|
|
$Text =~ s/\*/%/gi;
|
|
|
|
# check search attribute, we do not need to search for *
|
|
next TEXT if $Text =~ /^\%{1,3}$/;
|
|
|
|
# validate data type
|
|
my $ValidateSuccess = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->ValueValidate(
|
|
DynamicFieldConfig => $DynamicField,
|
|
Value => $Text,
|
|
UserID => $Param{UserID} || 1,
|
|
);
|
|
if ( !$ValidateSuccess ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"Search not executed due to invalid value '"
|
|
. $Text
|
|
. "' on field '"
|
|
. $DynamicField->{Name}
|
|
. "'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
if ($Counter) {
|
|
$SQLDynamicFieldWhereSub .= ' OR ';
|
|
}
|
|
$SQLDynamicFieldWhereSub
|
|
.= $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->SearchSQLGet(
|
|
DynamicFieldConfig => $DynamicField,
|
|
TableAlias => "dfv$DynamicFieldJoinCounter",
|
|
Operator => $Operator,
|
|
SearchTerm => $Text,
|
|
);
|
|
|
|
$Counter++;
|
|
}
|
|
$SQLDynamicFieldWhereSub .= ') ';
|
|
if ($Counter) {
|
|
$SQLDynamicFieldWhere .= $SQLDynamicFieldWhereSub;
|
|
$NeedJoin = 1;
|
|
}
|
|
}
|
|
|
|
if ($NeedJoin) {
|
|
|
|
if ( $DynamicField->{ObjectType} eq 'ITSMWorkOrder' ) {
|
|
|
|
$SQLDynamicFieldInnerJoins
|
|
.= "INNER JOIN dynamic_field_value dfv$DynamicFieldJoinCounter
|
|
ON (wo.id = dfv$DynamicFieldJoinCounter.object_id
|
|
AND dfv$DynamicFieldJoinCounter.field_id = " .
|
|
$DBObject->Quote( $DynamicField->{ID}, 'Integer' ) . ") ";
|
|
}
|
|
|
|
$DynamicFieldJoinCounter++;
|
|
}
|
|
}
|
|
|
|
# set array params
|
|
my %ArrayParams = (
|
|
ChangeIDs => 'wo.change_id',
|
|
WorkOrderStateIDs => 'wo.workorder_state_id',
|
|
WorkOrderTypeIDs => 'wo.workorder_type_id',
|
|
WorkOrderAgentIDs => 'wo.workorder_agent_id',
|
|
CreateBy => 'wo.create_by',
|
|
ChangeBy => 'wo.change_by',
|
|
);
|
|
|
|
# add array params to sql-where-array
|
|
ARRAYPARAM:
|
|
for my $ArrayParam ( sort keys %ArrayParams ) {
|
|
|
|
# ignore empty lists
|
|
next ARRAYPARAM if !@{ $Param{$ArrayParam} };
|
|
|
|
# quote as integer
|
|
for my $OneParam ( @{ $Param{$ArrayParam} } ) {
|
|
$OneParam = $DBObject->Quote( $OneParam, 'Integer' );
|
|
}
|
|
|
|
# create string
|
|
my $InString = join ', ', @{ $Param{$ArrayParam} };
|
|
|
|
push @SQLWhere, "$ArrayParams{$ArrayParam} IN ($InString)";
|
|
}
|
|
|
|
# check the time params and add them to the WHERE clause of the SELECT-Statement
|
|
my %TimeParams = (
|
|
CreateTimeNewerDate => 'wo.create_time >=',
|
|
CreateTimeOlderDate => 'wo.create_time <=',
|
|
ChangeTimeNewerDate => 'wo.change_time >=',
|
|
ChangeTimeOlderDate => 'wo.change_time <=',
|
|
PlannedStartTimeNewerDate => 'wo.planned_start_time >=',
|
|
PlannedStartTimeOlderDate => 'wo.planned_start_time <=',
|
|
PlannedEndTimeNewerDate => 'wo.planned_end_time >=',
|
|
PlannedEndTimeOlderDate => 'wo.planned_end_time <=',
|
|
ActualStartTimeNewerDate => 'wo.actual_start_time >=',
|
|
ActualStartTimeOlderDate => 'wo.actual_start_time <=',
|
|
ActualEndTimeNewerDate => 'wo.actual_end_time >=',
|
|
ActualEndTimeOlderDate => 'wo.actual_end_time <=',
|
|
);
|
|
TIMEPARAM:
|
|
for my $TimeParam ( sort keys %TimeParams ) {
|
|
|
|
next TIMEPARAM if !$Param{$TimeParam};
|
|
|
|
if ( $Param{$TimeParam} !~ m{ \A \d\d\d\d-\d\d-\d\d \s \d\d:\d\d:\d\d \z }xms ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter $TimeParam has an invalid date format!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# quote
|
|
$Param{$TimeParam} = $DBObject->Quote( $Param{$TimeParam} );
|
|
|
|
push @SQLWhere, "$TimeParams{$TimeParam} '$Param{$TimeParam}'";
|
|
}
|
|
|
|
# delete the OrderBy parameter when the result type is 'COUNT'
|
|
if ( $Result eq 'COUNT' ) {
|
|
$Param{OrderBy} = [];
|
|
}
|
|
|
|
# assemble the ORDER BY clause
|
|
my @SQLOrderBy;
|
|
my $Count = 0;
|
|
for my $OrderBy ( @{ $Param{OrderBy} } ) {
|
|
|
|
# set the default order direction
|
|
my $Direction = 'DESC';
|
|
|
|
# add the given order direction
|
|
if ( $Param{OrderByDirection}->[$Count] ) {
|
|
if ( $Param{OrderByDirection}->[$Count] eq 'Up' ) {
|
|
$Direction = 'ASC';
|
|
}
|
|
elsif ( $Param{OrderByDirection}->[$Count] eq 'Down' ) {
|
|
$Direction = 'DESC';
|
|
}
|
|
}
|
|
|
|
# add SQL
|
|
push @SQLOrderBy, "$OrderByTable{$OrderBy} $Direction";
|
|
}
|
|
continue {
|
|
$Count++;
|
|
}
|
|
|
|
# if there is a possibility that the ordering is not determined
|
|
# we add an descending ordering by id
|
|
if ( !grep { $_ eq 'WorkOrderID' } ( @{ $Param{OrderBy} } ) ) {
|
|
push @SQLOrderBy, "$OrderByTable{WorkOrderID} DESC";
|
|
}
|
|
|
|
# assemble the SQL query
|
|
my $SQL = 'SELECT wo.id FROM change_workorder wo ';
|
|
|
|
# modify SQL when the result type is 'COUNT'.
|
|
# There is no 'GROUP BY' SQL-clause, therefore COUNT(c.id) always give the wanted count
|
|
if ( $Result eq 'COUNT' ) {
|
|
$SQL = 'SELECT COUNT(wo.id) FROM change_workorder wo ';
|
|
@SQLOrderBy = ();
|
|
}
|
|
|
|
# add the joins
|
|
my %LongTableName = (
|
|
c => 'change_item',
|
|
);
|
|
my %TableSeen;
|
|
|
|
INNER_JOIN_TABLE:
|
|
for my $Table (@InnerJoinTables) {
|
|
|
|
# do not join a table twice
|
|
next INNER_JOIN_TABLE if $TableSeen{$Table};
|
|
|
|
$TableSeen{$Table} = 1;
|
|
|
|
if ( !$LongTableName{$Table} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Encountered invalid inner join table '$Table'!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
$SQL .= "INNER JOIN $LongTableName{$Table} $Table ON $Table.id = wo.change_id ";
|
|
}
|
|
|
|
# add the dynamic field inner join statements
|
|
$SQL .= $SQLDynamicFieldInnerJoins;
|
|
|
|
# add the WHERE clause
|
|
if (@SQLWhere) {
|
|
$SQL .= 'WHERE ';
|
|
$SQL .= join ' AND ', map {"( $_ )"} @SQLWhere;
|
|
$SQL .= ' ';
|
|
if ($SQLDynamicFieldWhere) {
|
|
$SQL .= ' AND ' . $SQLDynamicFieldWhere;
|
|
}
|
|
}
|
|
else {
|
|
if ($SQLDynamicFieldWhere) {
|
|
$SQL .= ' WHERE ' . $SQLDynamicFieldWhere;
|
|
}
|
|
}
|
|
|
|
# add the ORDER BY clause
|
|
if (@SQLOrderBy) {
|
|
$SQL .= ' ORDER BY ';
|
|
$SQL .= join ', ', @SQLOrderBy;
|
|
$SQL .= ' ';
|
|
}
|
|
|
|
# ignore the parameter 'Limit' when result type is 'COUNT'
|
|
if ( $Result eq 'COUNT' ) {
|
|
$Param{Limit} = 1;
|
|
}
|
|
|
|
# ask database
|
|
return if !$DBObject->Prepare(
|
|
SQL => $SQL,
|
|
Limit => $Param{Limit},
|
|
);
|
|
|
|
# fetch the result
|
|
my @IDs;
|
|
while ( my @Row = $DBObject->FetchrowArray() ) {
|
|
push @IDs, $Row[0];
|
|
}
|
|
|
|
# return the count as scalar
|
|
return $IDs[0] if $Result eq 'COUNT';
|
|
|
|
return \@IDs;
|
|
}
|
|
|
|
=head2 WorkOrderDelete()
|
|
|
|
Delete a C<workorder>.
|
|
|
|
This function removes all links and attachments to the C<workorder>
|
|
with the passed C<workorder> id.
|
|
After that the C<workorder> is removed.
|
|
|
|
my $Success = $WorkOrderObject->WorkOrderDelete(
|
|
WorkOrderID => 123,
|
|
NoNumberCalc => 1, # (optional) default 0, if 1 it prevents a recalculation of the workorder numbers
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderDelete {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(WorkOrderID UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# trigger WorkOrderDeletePre-Event
|
|
$Self->EventHandler(
|
|
Event => 'WorkOrderDeletePre',
|
|
Data => {
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# get old workorder data to be given to post event handler
|
|
my $WorkOrderData = $Self->WorkOrderGet(
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# delete all links to this workorder
|
|
return if !$Kernel::OM->Get('Kernel::System::LinkObject')->LinkDeleteAll(
|
|
Object => 'ITSMWorkOrder',
|
|
Key => $Param{WorkOrderID},
|
|
UserID => 1,
|
|
);
|
|
|
|
# get the list of workorder attachments and delete them
|
|
my @Attachments = $Self->WorkOrderAttachmentList(
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
);
|
|
|
|
for my $Filename (@Attachments) {
|
|
|
|
my $DeleteSuccess = $Self->WorkOrderAttachmentDelete(
|
|
ChangeID => $WorkOrderData->{ChangeID},
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
Filename => $Filename,
|
|
AttachmentType => 'WorkOrder',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return if !$DeleteSuccess;
|
|
}
|
|
|
|
# get the list of report attachments and delete them
|
|
my @ReportAttachments = $Self->WorkOrderReportAttachmentList(
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
);
|
|
for my $Filename (@ReportAttachments) {
|
|
|
|
my $DeleteSuccess = $Self->WorkOrderAttachmentDelete(
|
|
ChangeID => $WorkOrderData->{ChangeID},
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
Filename => $Filename,
|
|
AttachmentType => 'WorkOrderReport',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return if !$DeleteSuccess;
|
|
}
|
|
|
|
# get all dynamic fields for the object type ITSMWorkOrder
|
|
my $DynamicFieldList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
ObjectType => 'ITSMWorkOrder',
|
|
Valid => 0,
|
|
);
|
|
|
|
# delete dynamicfield values for this workorder
|
|
DYNAMICFIELD:
|
|
for my $DynamicFieldConfig ( @{$DynamicFieldList} ) {
|
|
|
|
next DYNAMICFIELD if !$DynamicFieldConfig;
|
|
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
|
|
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
|
|
next DYNAMICFIELD if !IsHashRefWithData( $DynamicFieldConfig->{Config} );
|
|
|
|
$Kernel::OM->Get('Kernel::System::DynamicField::Backend')->ValueDelete(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $Param{WorkOrderID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# delete the workorder
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
|
|
SQL => 'DELETE FROM change_workorder WHERE id = ? ',
|
|
Bind => [ \$Param{WorkOrderID} ],
|
|
);
|
|
|
|
# delete cache
|
|
for my $Key (
|
|
'WorkOrderGet::ID::' . $Param{WorkOrderID},
|
|
'WorkOrderList::ChangeID::' . $WorkOrderData->{ChangeID},
|
|
'WorkOrderChangeEffortsGet::ChangeID::' . $WorkOrderData->{ChangeID},
|
|
'WorkOrderChangeTimeGet::ChangeID::' . $WorkOrderData->{ChangeID},
|
|
'ChangeGet::ID::' . $WorkOrderData->{ChangeID},
|
|
)
|
|
{
|
|
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Delete(
|
|
Type => $Self->{CacheType},
|
|
Key => $Key,
|
|
);
|
|
}
|
|
|
|
# trigger WorkOrderDeletePost-Event
|
|
$Self->EventHandler(
|
|
Event => 'WorkOrderDeletePost',
|
|
Data => {
|
|
OldWorkOrderData => $WorkOrderData,
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 WorkOrderChangeTimeGet()
|
|
|
|
Returns a list of PlannedStartTime | PlannedEndTime | ActualStartTime | ActualEndTime
|
|
of a change, which would be the respective time of the earliest starting
|
|
C<workorder> (for start times) or the latest ending C<workorder> (for end times).
|
|
|
|
For PlannedStartTime | PlannedEndTime | ActualEndTime an empty string is returned
|
|
if any of the C<workorders> of a change has the respective time not defined.
|
|
|
|
The ActualStartTime is defined when any of the C<workorders> of a change has
|
|
a defined ActualStartTime.
|
|
|
|
Return
|
|
|
|
$Time{PlannedStartTime}
|
|
$Time{PlannedEndTime}
|
|
$Time{ActualStartTime}
|
|
$Time{ActualEndTime}
|
|
|
|
my $TimeRef = $WorkOrderObject->WorkOrderChangeTimeGet(
|
|
ChangeID => 123,
|
|
UserID => 1,
|
|
|
|
# ---------------------------------------------------- #
|
|
|
|
# TODO: (decide this later!)
|
|
Maybe add this new attribute:
|
|
|
|
# These are WorkOrderTypes (Types, not States!)
|
|
# which would be excluded from the calculation
|
|
# of the change start time.
|
|
|
|
ExcludeWorkOrderTypes => [ 'approval', 'pir' ], # (optional)
|
|
|
|
# ---------------------------------------------------- #
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderChangeTimeGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(ChangeID UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# initialize the return time hash
|
|
my %TimeReturn;
|
|
|
|
# check cache
|
|
my $CacheKey = 'WorkOrderChangeTimeGet::ChangeID::' . $Param{ChangeID};
|
|
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
|
|
if ($Cache) {
|
|
|
|
# get data from cache
|
|
%TimeReturn = %{$Cache};
|
|
}
|
|
else {
|
|
|
|
# build sql, using min and max functions
|
|
my $SQL = 'SELECT '
|
|
. 'MIN( planned_start_time ), '
|
|
. 'MAX( planned_end_time ), '
|
|
. 'MIN( actual_start_time ), '
|
|
. 'MAX( actual_end_time ) '
|
|
. 'FROM change_workorder '
|
|
. 'WHERE change_id = ?';
|
|
|
|
# retrieve the requested time
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => $SQL,
|
|
Bind => [ \$Param{ChangeID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$TimeReturn{PlannedStartTime} = $Row[0] || '';
|
|
$TimeReturn{PlannedEndTime} = $Row[1] || '';
|
|
$TimeReturn{ActualStartTime} = $Row[2] || '';
|
|
$TimeReturn{ActualEndTime} = $Row[3] || '';
|
|
}
|
|
|
|
TIMEFIELD:
|
|
for my $Time ( sort keys %TimeReturn ) {
|
|
|
|
next TIMEFIELD if !$TimeReturn{$Time};
|
|
|
|
# cleanup time stamps (some databases are using e. g. 2008-02-25 22:03:00.000000)
|
|
$TimeReturn{$Time}
|
|
=~ s{ \A ( \d\d\d\d - \d\d - \d\d \s \d\d:\d\d:\d\d ) \. .+? \z }{$1}xms;
|
|
|
|
# set empty string if the default time was found
|
|
if ( $TimeReturn{$Time} eq '9999-01-01 00:00:00' ) {
|
|
$TimeReturn{$Time} = '';
|
|
}
|
|
}
|
|
|
|
# check if change has workorders with not yet defined planned_start_time entries
|
|
if ( $TimeReturn{PlannedStartTime} ) {
|
|
|
|
# build SQL
|
|
my $SQL = 'SELECT count(*) '
|
|
. 'FROM change_workorder '
|
|
. "WHERE planned_start_time = '9999-01-01 00:00:00' "
|
|
. 'AND change_id = ?';
|
|
|
|
# retrieve number of not defined entries
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => $SQL,
|
|
Bind => [ \$Param{ChangeID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
my $Count;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$Count = $Row[0];
|
|
}
|
|
|
|
# reset PlannedStartTime
|
|
if ($Count) {
|
|
$TimeReturn{PlannedStartTime} = '';
|
|
}
|
|
}
|
|
|
|
# set cache
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
|
Type => 'ITSMChangeManagement',
|
|
Key => $CacheKey,
|
|
Value => \%TimeReturn,
|
|
TTL => $Self->{CacheTTL},
|
|
);
|
|
}
|
|
|
|
return \%TimeReturn;
|
|
}
|
|
|
|
=head2 WorkOrderStateLookup()
|
|
|
|
This method does a lookup for a C<workorder> state. If a C<workorder> state id is given,
|
|
it returns the name of the C<workorder> state. If a C<workorder> state name is given,
|
|
the appropriate id is returned.
|
|
|
|
my $WorkOrderState = $WorkOrderObject->WorkOrderStateLookup(
|
|
WorkOrderStateID => 157,
|
|
);
|
|
|
|
my $WorkOrderStateID = $WorkOrderObject->WorkOrderStateLookup(
|
|
WorkOrderState => 'ready',
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderStateLookup {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# either WorkOrderStateID or WorkOrderState must be passed
|
|
if ( !$Param{WorkOrderStateID} && !$Param{WorkOrderState} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderStateID or WorkOrderState!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
if ( $Param{WorkOrderStateID} && $Param{WorkOrderState} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderStateID OR WorkOrderState - not both!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get the workorder states from the general catalog
|
|
my $StateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::State',
|
|
);
|
|
|
|
# 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 workorder states from the general catalog.',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if ( $Param{WorkOrderStateID} ) {
|
|
return $StateID2Name{ $Param{WorkOrderStateID} };
|
|
}
|
|
else {
|
|
|
|
# reverse key - value pairs to have the name as keys
|
|
my %StateName2ID = reverse %StateID2Name;
|
|
|
|
return $StateName2ID{ $Param{WorkOrderState} };
|
|
}
|
|
}
|
|
|
|
=head2 WorkOrderPossibleStatesGet()
|
|
|
|
This method returns a list of possible C<workorder> states.
|
|
If C<WorkOrderID> is omitted, the complete list of C<workorder> states is returned.
|
|
If C<WorkOrderID> is given, the list of possible states for the given
|
|
C<workorder> is returned.
|
|
|
|
my $WorkOrderStateList = $WorkOrderObject->WorkOrderPossibleStatesGet(
|
|
WorkOrderID => 123, # (optional)
|
|
UserID => 1,
|
|
);
|
|
|
|
The return value is a reference to an array of hashrefs. The element 'Key' is then
|
|
the StateID and the element 'Value' is the name of the state. The array elements
|
|
are sorted by state id.
|
|
|
|
my $WorkOrderStateList = [
|
|
{
|
|
Key => 156,
|
|
Value => 'accepted',
|
|
},
|
|
{
|
|
Key => 157,
|
|
Value => 'in progress',
|
|
},
|
|
];
|
|
|
|
=cut
|
|
|
|
sub WorkOrderPossibleStatesGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# get workorder state list
|
|
my $StateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::State',
|
|
) || {};
|
|
|
|
# to store an array of hash refs
|
|
my @ArrayHashRef;
|
|
|
|
# if WorkOrderID is given, only use possible next states as defined in state machine
|
|
if ( $Param{WorkOrderID} ) {
|
|
|
|
# get workorder data
|
|
my $WorkOrder = $Self->WorkOrderGet(
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# check for state lock
|
|
my $StateLock = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMCondition')->ConditionMatchStateLock(
|
|
ObjectName => 'ITSMWorkOrder',
|
|
Selector => $Param{WorkOrderID},
|
|
StateID => $WorkOrder->{WorkOrderStateID},
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# set as default state current workorder state
|
|
my @NextStateIDs = ( $WorkOrder->{WorkOrderStateID} );
|
|
|
|
# check if reachable workorder end states should be allowed for locked workorder states
|
|
my $WorkOrderEndStatesAllowed
|
|
= $Kernel::OM->Get('Kernel::Config')->Get('ITSMWorkOrder::StateLock::AllowEndStates');
|
|
|
|
if ($WorkOrderEndStatesAllowed) {
|
|
|
|
# set as default state current state and all possible end states
|
|
my $EndStateIDsRef
|
|
= $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine')->StateTransitionGetEndStates(
|
|
StateID => $WorkOrder->{WorkOrderStateID},
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::State',
|
|
) || [];
|
|
@NextStateIDs = sort ( @{$EndStateIDsRef}, $WorkOrder->{WorkOrderStateID} );
|
|
}
|
|
|
|
# get possible next states if no state lock
|
|
if ( !$StateLock ) {
|
|
|
|
# get the possible next state ids
|
|
my $NextStateIDsRef = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine')->StateTransitionGet(
|
|
StateID => $WorkOrder->{WorkOrderStateID},
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::State',
|
|
) || [];
|
|
|
|
# add current workorder state id to list
|
|
@NextStateIDs = sort ( @{$NextStateIDsRef}, $WorkOrder->{WorkOrderStateID} );
|
|
}
|
|
|
|
# assemble the array of hash refs with only possible next states
|
|
STATEID:
|
|
for my $StateID (@NextStateIDs) {
|
|
|
|
# check state id
|
|
next STATEID if !$StateID;
|
|
|
|
# store id and name in the array
|
|
push @ArrayHashRef, {
|
|
Key => $StateID,
|
|
Value => $StateList->{$StateID},
|
|
};
|
|
}
|
|
|
|
return \@ArrayHashRef;
|
|
}
|
|
|
|
# assemble the array of hash refs with all next states
|
|
for my $StateID ( sort keys %{$StateList} ) {
|
|
push @ArrayHashRef, {
|
|
Key => $StateID,
|
|
Value => $StateList->{$StateID},
|
|
};
|
|
}
|
|
|
|
return \@ArrayHashRef;
|
|
}
|
|
|
|
=head2 WorkOrderTypeLookup()
|
|
|
|
This method does a lookup for a C<workorder> type. If a C<workorder> type id is given,
|
|
it returns the name of the C<workorder> type. If a C<workorder> type name is given,
|
|
the appropriate id is returned.
|
|
|
|
my $WorkOrderType = $WorkOrderObject->WorkOrderTypeLookup(
|
|
WorkOrderTypeID => 157,
|
|
);
|
|
|
|
my $WorkOrderTypeID = $WorkOrderObject->WorkOrderTypeLookup(
|
|
WorkOrderType => 'ready',
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderTypeLookup {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# either WorkOrderTypeID or WorkOrderType must be passed
|
|
if ( !$Param{WorkOrderTypeID} && !$Param{WorkOrderType} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderTypeID or WorkOrderType!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
if ( $Param{WorkOrderTypeID} && $Param{WorkOrderType} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderTypeID OR WorkOrderType - not both!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get workorder type from general catalog
|
|
# mapping of the id to the name
|
|
my %WorkOrderType = %{
|
|
$Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::Type',
|
|
)
|
|
};
|
|
|
|
# check the workorder types hash
|
|
if ( !%WorkOrderType ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Could not retrieve workorder types from the general catalog.',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if ( $Param{WorkOrderTypeID} ) {
|
|
return $WorkOrderType{ $Param{WorkOrderTypeID} };
|
|
}
|
|
else {
|
|
|
|
# reverse key - value pairs to have the name as keys
|
|
my %ReversedWorkOrderType = reverse %WorkOrderType;
|
|
|
|
return $ReversedWorkOrderType{ $Param{WorkOrderType} };
|
|
}
|
|
}
|
|
|
|
=head2 WorkOrderTypeList()
|
|
|
|
This method returns a list of all C<workorder> types.
|
|
|
|
my $WorkOrderTypeList = $WorkOrderObject->WorkOrderTypeList(
|
|
UserID => 1,
|
|
Default => 1, # optional - the default type is selected type (default: 0)
|
|
SelectedID => 123, # optional - this id is selected
|
|
);
|
|
|
|
The return value is a reference to an array of hashrefs. The Element 'Key' is then
|
|
the TypeID and die Element 'Value' is the name of the type. The array elements
|
|
are sorted by type id.
|
|
|
|
my $WorkOrderTypeList = [
|
|
{
|
|
Key => 171,
|
|
Value => 'workorder',
|
|
},
|
|
{
|
|
Key => 172,
|
|
Value => 'backout',
|
|
},
|
|
];
|
|
|
|
=cut
|
|
|
|
sub WorkOrderTypeList {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# what type is selected
|
|
my $SelectedID = $Param{Selected} || 0;
|
|
|
|
if ( $Param{Default} ) {
|
|
|
|
# set config option
|
|
my $ConfigOption = 'ITSMWorkOrder::Type::Default';
|
|
|
|
# get default workorder type from config
|
|
my $DefaultType = $Kernel::OM->Get('Kernel::Config')->Get($ConfigOption);
|
|
|
|
# check if default type exists in general catalog
|
|
my $ItemDataRef = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemGet(
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::Type',
|
|
Name => $DefaultType,
|
|
);
|
|
|
|
# error handling because of invalid config setting
|
|
if ( !$ItemDataRef || ref $ItemDataRef ne 'HASH' || !%{$ItemDataRef} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The default WorkOrderType '$DefaultType' "
|
|
. "in sysconfig option '$ConfigOption' is invalid! Check the general catalog!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# set default
|
|
$SelectedID = $ItemDataRef->{ItemID};
|
|
}
|
|
|
|
# get workorder type list
|
|
my $WorkOrderTypeList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
|
|
Class => 'ITSM::ChangeManagement::WorkOrder::Type',
|
|
) || {};
|
|
|
|
# assemble a an array of hash refs
|
|
my @ArrayHashRef;
|
|
for my $TypeID ( sort keys %{$WorkOrderTypeList} ) {
|
|
my %SelectedInfo;
|
|
|
|
if ( $SelectedID && $SelectedID == $TypeID ) {
|
|
$SelectedInfo{Selected} = 1;
|
|
}
|
|
|
|
push @ArrayHashRef, {
|
|
Key => $TypeID,
|
|
Value => $WorkOrderTypeList->{$TypeID},
|
|
%SelectedInfo,
|
|
};
|
|
}
|
|
|
|
return \@ArrayHashRef;
|
|
}
|
|
|
|
=head2 Permission()
|
|
|
|
Returns whether the agent C<UserID> has permissions of the type C<Type>
|
|
on the C<workorder> C<WorkOrderID>. The parameters are passed on to
|
|
the permission modules that were registered in the permission registry.
|
|
The standard permission registry is B<ITSMWorkOrder::Permission>, but
|
|
that can be overridden with the parameter C<PermissionRegistry>.
|
|
|
|
The registered permission modules are run in the alphabetical order of
|
|
their registry keys.
|
|
Overall permission is granted when a permission module, which has the attribute 'Granted' set,
|
|
grants permission. Overall permission is denied when a permission module, which has the attribute 'Required'
|
|
set, denies permission. Overall permission is also denied when when all permission module were asked
|
|
without coming to an conclusion.
|
|
|
|
Approval is indicated by the return value 1. Denial is indicated by returning an empty list.
|
|
|
|
The optional option C<LogNo> turns off logging when access was denied.
|
|
This is useful when the method is used for checking whether a link or an action should be shown.
|
|
|
|
my $Access = $WorkOrderObject->Permission(
|
|
UserID => 123,
|
|
Type => 'ro', # 'ro' and 'rw' are supported
|
|
Action => 'AgentITSMWorkOrderReport', # optional
|
|
WorkOrderID => 4444,
|
|
PermissionRegistry => 'ITSMWorkOrder::TakePermission',
|
|
# optional with default 'ITSMWorkOrder::Permission'
|
|
LogNo => 1, # optional, turns off logging when access is denied
|
|
);
|
|
|
|
=cut
|
|
|
|
sub Permission {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(Type UserID WorkOrderID)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# the place where the permission modules are registerd can be overridden by a parameter
|
|
my $Registry = $Param{PermissionRegistry} || 'ITSMWorkOrder::Permission';
|
|
|
|
# run the relevant permission modules
|
|
if ( ref $Kernel::OM->Get('Kernel::Config')->Get($Registry) eq 'HASH' ) {
|
|
|
|
my %Modules = %{ $Kernel::OM->Get('Kernel::Config')->Get($Registry) };
|
|
|
|
MODULE:
|
|
for my $Module ( sort keys %Modules ) {
|
|
|
|
# log try of load module
|
|
if ( $Self->{Debug} > 1 ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'debug',
|
|
Message => "Try to load module: $Modules{$Module}->{Module}!",
|
|
);
|
|
}
|
|
|
|
# load module
|
|
next MODULE
|
|
if !$Kernel::OM->Get('Kernel::System::Main')->Require( $Modules{$Module}->{Module} );
|
|
|
|
# create object
|
|
my $ModuleObject = $Modules{$Module}->{Module}->new();
|
|
|
|
# ask for the opinion of the Permission module
|
|
my $Access = $ModuleObject->Run(%Param);
|
|
|
|
# Grant overall permission,
|
|
# when the module granted a sufficient permission.
|
|
if ( $Access && $Modules{$Module}->{Granted} ) {
|
|
if ( $Self->{Debug} > 0 ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'debug',
|
|
Message => "Granted '$Param{Type}' access for "
|
|
. "UserID: $Param{UserID} on "
|
|
. "WorkOrderID '$Param{WorkOrderID}' "
|
|
. "through $Modules{$Module}->{Module} (no more checks)!",
|
|
);
|
|
}
|
|
|
|
# grant permission
|
|
return 1;
|
|
}
|
|
|
|
# Deny overall permission,
|
|
# when the module denied a required permission.
|
|
if ( !$Access && $Modules{$Module}->{Required} ) {
|
|
if ( !$Param{LogNo} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'notice',
|
|
Message => "Denied '$Param{Type}' access for "
|
|
. "UserID: $Param{UserID} on "
|
|
. "WorkOrderID '$Param{WorkOrderID}' "
|
|
. "because $Modules{$Module}->{Module} is required!",
|
|
);
|
|
}
|
|
|
|
# deny permission
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Deny access when neither a 'Granted'-Check nor a 'Required'-Check has reached a conclusion.
|
|
if ( !$Param{LogNo} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'notice',
|
|
Message => "Permission denied (UserID: $Param{UserID} '$Param{Type}' "
|
|
. "on WorkOrderID: $Param{WorkOrderID})!",
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
=head2 WorkOrderStateIDsCheck()
|
|
|
|
Check whether all of the given C<workorder> state ids are valid.
|
|
The method is public as it is used in L<Kernel::System::ITSMChange::ChangeSearch>.
|
|
|
|
my $Ok = $WorkOrderObject->WorkOrderStateIDsCheck(
|
|
WorkOrderStateIDs => [ 25 ],
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderStateIDsCheck {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{WorkOrderStateIDs} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderStateID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
if ( ref $Param{WorkOrderStateIDs} ne 'ARRAY' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'The param WorkOrderStateIDs must be an ARRAY reference!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check if WorkOrderStateIDs belongs to correct general catalog class
|
|
for my $StateID ( @{ $Param{WorkOrderStateIDs} } ) {
|
|
my $State = $Self->WorkOrderStateLookup(
|
|
WorkOrderStateID => $StateID,
|
|
);
|
|
|
|
if ( !$State ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The state id $StateID is not valid!",
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 WorkOrderAttachmentAdd()
|
|
|
|
Add an attachment to the given C<workorder>.
|
|
|
|
my $Success = $WorkOrderObject->WorkOrderAttachmentAdd(
|
|
ChangeID => 123,
|
|
WorkOrderID => 456, # the WorkOrderID becomes part of the file path
|
|
AttachmentType => 'WorkOrder', # ( 'WorkOrder' || 'WorkOrderReport') (optional, default 'WorkOrder' )
|
|
Filename => 'filename',
|
|
Content => 'content',
|
|
ContentType => 'text/plain',
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderAttachmentAdd {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Needed (qw(ChangeID WorkOrderID Filename Content ContentType UserID )) {
|
|
if ( !$Param{$Needed} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Needed!",
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Set attachment type to distinguish between attachments of workorders
|
|
# and those from reports of workorders
|
|
my $AttachmentType = $Param{AttachmentType} || 'WorkOrder';
|
|
|
|
# set event name based on the attachment type
|
|
my $Event;
|
|
if ( $AttachmentType eq 'WorkOrder' ) {
|
|
$Event = 'WorkOrderAttachmentAddPost';
|
|
}
|
|
elsif ( $AttachmentType eq 'WorkOrderReport' ) {
|
|
$Event = 'WorkOrderReportAttachmentAddPost';
|
|
}
|
|
|
|
# write to virtual fs
|
|
my $Success = $Kernel::OM->Get('Kernel::System::VirtualFS')->Write(
|
|
Filename => "$AttachmentType/$Param{WorkOrderID}/$Param{Filename}",
|
|
Mode => 'binary',
|
|
Content => \$Param{Content},
|
|
Preferences => {
|
|
ContentID => $Param{ContentID},
|
|
ContentType => $Param{ContentType},
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
UserID => $Param{UserID},
|
|
},
|
|
);
|
|
|
|
# check for error
|
|
if ($Success) {
|
|
|
|
# trigger AttachmentAdd-Event
|
|
$Self->EventHandler(
|
|
Event => $Event,
|
|
Data => {
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Cannot add attachment for workorder $Param{WorkOrderID}",
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 WorkOrderAttachmentDelete()
|
|
|
|
Delete the given file from the virtual filesystem.
|
|
|
|
my $Success = $WorkOrderObject->WorkOrderAttachmentDelete(
|
|
ChangeID => 12345,
|
|
WorkOrderID => 5123,
|
|
AttachmentType => 'WorkOrder', # ( 'WorkOrder' || 'WorkOrderReport') (optional, default 'WorkOrder' )
|
|
Filename => 'Projectplan.pdf', # identifies the attachment (together with the WorkOrderID)
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderAttachmentDelete {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Needed (qw(ChangeID WorkOrderID Filename UserID)) {
|
|
if ( !$Param{$Needed} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Needed!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Set attachment type to distinguish between attachments of workorders
|
|
# and those from reports of workorders
|
|
my $AttachmentType = $Param{AttachmentType} || 'WorkOrder';
|
|
|
|
# set event name based on the attachment type
|
|
my $Event;
|
|
if ( $AttachmentType eq 'WorkOrder' ) {
|
|
$Event = 'WorkOrderAttachmentDeletePost';
|
|
}
|
|
elsif ( $AttachmentType eq 'WorkOrderReport' ) {
|
|
$Event = 'WorkOrderReportAttachmentDeletePost';
|
|
}
|
|
|
|
# add prefix
|
|
my $Filename = "$AttachmentType/$Param{WorkOrderID}/$Param{Filename}";
|
|
|
|
# delete file
|
|
my $Success = $Kernel::OM->Get('Kernel::System::VirtualFS')->Delete(
|
|
Filename => $Filename,
|
|
);
|
|
|
|
# check for error
|
|
if ($Success) {
|
|
|
|
# trigger AttachmentDeletePost-Event
|
|
$Self->EventHandler(
|
|
Event => $Event,
|
|
Data => {
|
|
%Param,
|
|
},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Cannot delete attachment $Filename!",
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
return $Success;
|
|
}
|
|
|
|
=head2 WorkOrderAttachmentGet()
|
|
|
|
This method returns information about one specific attachment.
|
|
|
|
my $Attachment = $WorkOrderObject->WorkOrderAttachmentGet(
|
|
WorkOrderID => 4,
|
|
AttachmentType => 'WorkOrder', # ( 'WorkOrder' || 'WorkOrderReport') (optional, default 'WorkOrder' )
|
|
Filename => 'test.txt',
|
|
);
|
|
|
|
returns
|
|
|
|
$Attachment = {
|
|
Preferences => {
|
|
AllPreferences => 'test',
|
|
},
|
|
Filename => 'test.txt',
|
|
Content => 'hallo',
|
|
ContentType => 'text/plain',
|
|
Filesize => '123 KBytes',
|
|
Type => 'attachment',
|
|
AttachmentType => 'WorkOrder',
|
|
};
|
|
|
|
=cut
|
|
|
|
sub WorkOrderAttachmentGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(WorkOrderID Filename)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Set attachment type to distinguish between attachments of workorders
|
|
# and those from reports of workorders
|
|
my $AttachmentType = $Param{AttachmentType} || 'WorkOrder';
|
|
|
|
# add prefix
|
|
my $Filename = $AttachmentType . '/' . $Param{WorkOrderID} . '/' . $Param{Filename};
|
|
|
|
# find all attachments of this workorder
|
|
my @Attachments = $Kernel::OM->Get('Kernel::System::VirtualFS')->Find(
|
|
Filename => $Filename,
|
|
Preferences => {
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
},
|
|
);
|
|
|
|
# return error if file does not exist
|
|
if ( !@Attachments ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Message => "No such attachment ($Filename)! May be an attack!!!",
|
|
Priority => 'error',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get data for attachment
|
|
my %AttachmentData = $Kernel::OM->Get('Kernel::System::VirtualFS')->Read(
|
|
Filename => $Filename,
|
|
Mode => 'binary',
|
|
);
|
|
|
|
my $AttachmentInfo = {
|
|
%AttachmentData,
|
|
Filename => $Param{Filename},
|
|
Content => ${ $AttachmentData{Content} },
|
|
ContentType => $AttachmentData{Preferences}->{ContentType},
|
|
Type => 'attachment',
|
|
Filesize => $AttachmentData{Preferences}->{FilesizeRaw},
|
|
AttachmentType => $AttachmentType,
|
|
};
|
|
|
|
return $AttachmentInfo;
|
|
}
|
|
|
|
=head2 WorkOrderAttachmentList()
|
|
|
|
Returns an array with all C<workorder> attachments (not the report attachments) of the given C<workorder>.
|
|
|
|
my @Attachments = $WorkOrderObject->WorkOrderAttachmentList(
|
|
WorkOrderID => 123,
|
|
);
|
|
|
|
returns
|
|
|
|
@Attachments = (
|
|
'filename.txt',
|
|
'other_file.pdf',
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderAttachmentList {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{WorkOrderID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderID!',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
# find all attachments of this workorder
|
|
my @Attachments = $Kernel::OM->Get('Kernel::System::VirtualFS')->Find(
|
|
Preferences => {
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
},
|
|
);
|
|
|
|
# extract only the workorder attachments
|
|
my @WorkOrderAttachments;
|
|
FILENAME:
|
|
for my $Filename (@Attachments) {
|
|
|
|
next FILENAME if $Filename !~ m{ \A WorkOrder / \d+ / }xms;
|
|
|
|
# remove extra information from filename
|
|
$Filename =~ s{ \A WorkOrder / \d+ / }{}xms;
|
|
|
|
push @WorkOrderAttachments, $Filename;
|
|
}
|
|
|
|
return @WorkOrderAttachments;
|
|
}
|
|
|
|
=head2 WorkOrderReportAttachmentList()
|
|
|
|
Returns an array with all report attachments of the given C<workorder>.
|
|
|
|
my @Attachments = $WorkOrderObject->WorkOrderReportAttachmentList(
|
|
WorkOrderID => 123,
|
|
);
|
|
|
|
returns
|
|
|
|
@Attachments = (
|
|
'filename.txt',
|
|
'other_file.pdf',
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderReportAttachmentList {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{WorkOrderID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderID!',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
# find all attachments of this workorder
|
|
my @Attachments = $Kernel::OM->Get('Kernel::System::VirtualFS')->Find(
|
|
Preferences => {
|
|
WorkOrderID => $Param{WorkOrderID},
|
|
},
|
|
);
|
|
|
|
# extract only the report attachments
|
|
my @ReportAttachments;
|
|
FILENAME:
|
|
for my $Filename (@Attachments) {
|
|
|
|
next FILENAME if $Filename !~ m{ \A WorkOrderReport / \d+ / }xms;
|
|
|
|
# remove extra information from filename
|
|
$Filename =~ s{ \A WorkOrderReport / \d+ / }{}xms;
|
|
|
|
push @ReportAttachments, $Filename;
|
|
|
|
}
|
|
|
|
return @ReportAttachments;
|
|
}
|
|
|
|
=head2 WorkOrderAttachmentExists()
|
|
|
|
Checks if a file with a given filename exists.
|
|
|
|
my $Exists = $WorkOrderObject->WorkOrderAttachmentExists(
|
|
Filename => 'test.txt',
|
|
WorkOrderID => 123,
|
|
AttachmentType => 'WorkOrder', # ( 'WorkOrder' || 'WorkOrderReport') (optional, default 'WorkOrder' )
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderAttachmentExists {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Needed (qw(Filename WorkOrderID UserID)) {
|
|
if ( !$Param{$Needed} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Needed!",
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Set attachment type to distinguish between attachments of workorders
|
|
# and those from reports of workorders
|
|
my $AttachmentType = $Param{AttachmentType} || 'WorkOrder';
|
|
|
|
return if !$Kernel::OM->Get('Kernel::System::VirtualFS')->Find(
|
|
Filename => $AttachmentType . '/' . $Param{WorkOrderID} . '/' . $Param{Filename},
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 WorkOrderChangeEffortsGet()
|
|
|
|
returns the combined efforts of the C<workorders> for the given change
|
|
|
|
my $ChangeEfforts = $WorkOrderObject->WorkOrderChangeEffortsGet(
|
|
ChangeID => 123,
|
|
UserID => 1,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub WorkOrderChangeEffortsGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Attribute (qw(ChangeID UserID)) {
|
|
if ( !$Param{$Attribute} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Attribute!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# initialize the return time hash
|
|
my %ChangeEfforts;
|
|
|
|
# check cache
|
|
my $CacheKey = 'WorkOrderChangeEffortsGet::ChangeID::' . $Param{ChangeID};
|
|
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
|
|
Type => $Self->{CacheType},
|
|
Key => $CacheKey,
|
|
);
|
|
|
|
if ($Cache) {
|
|
|
|
# get data from cache
|
|
%ChangeEfforts = %{$Cache};
|
|
|
|
}
|
|
else {
|
|
|
|
# build sql, using min and max functions
|
|
my $SQL = 'SELECT '
|
|
. 'SUM( planned_effort ), SUM( accounted_time ) '
|
|
. 'FROM change_workorder '
|
|
. 'WHERE change_id = ?';
|
|
|
|
# retrieve the requested time
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => $SQL,
|
|
Bind => [ \$Param{ChangeID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$ChangeEfforts{PlannedEffort} = $Row[0] || '';
|
|
$ChangeEfforts{AccountedTime} = $Row[1] || '';
|
|
}
|
|
|
|
ATTRIBUTE:
|
|
for my $Attribute (qw(PlannedEffort AccountedTime)) {
|
|
|
|
next ATTRIBUTE if !$ChangeEfforts{$Attribute};
|
|
|
|
# do not show zero values
|
|
if ( $ChangeEfforts{$Attribute} eq 0 ) {
|
|
$ChangeEfforts{$Attribute} = '';
|
|
next ATTRIBUTE;
|
|
}
|
|
|
|
# convert decimal character from ',' to '.' if neccessary
|
|
$ChangeEfforts{$Attribute} =~ s{,}{.}xmsg;
|
|
|
|
# format as decimal number
|
|
$ChangeEfforts{$Attribute} = sprintf '%.2f', $ChangeEfforts{$Attribute};
|
|
}
|
|
|
|
# set cache
|
|
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
|
Type => 'ITSMChangeManagement',
|
|
Key => $CacheKey,
|
|
Value => \%ChangeEfforts,
|
|
TTL => $Self->{CacheTTL},
|
|
);
|
|
}
|
|
|
|
return \%ChangeEfforts;
|
|
}
|
|
|
|
sub DESTROY {
|
|
my $Self = shift;
|
|
|
|
# execute all transaction events
|
|
$Self->EventHandlerTransaction();
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head1 PRIVATE INTERFACE
|
|
|
|
=head2 _CheckWorkOrderTypeIDs()
|
|
|
|
check whether the given C<workorder> type ids are all valid
|
|
|
|
my $Ok = $WorkOrderObject->_CheckWorkOrderTypeIDs(
|
|
WorkOrderTypeIDs => [ 2, 500 ],
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _CheckWorkOrderTypeIDs {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{WorkOrderTypeIDs} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need WorkOrderTypeIDs!',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if ( ref $Param{WorkOrderTypeIDs} ne 'ARRAY' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'The param WorkOrderTypeIDs must be an ARRAY reference!',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
# check if WorkOrderTypeIDs belongs to correct general catalog class
|
|
for my $TypeID ( @{ $Param{WorkOrderTypeIDs} } ) {
|
|
my $Type = $Self->WorkOrderTypeLookup(
|
|
WorkOrderTypeID => $TypeID,
|
|
);
|
|
|
|
if ( !$Type ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The type id $TypeID is not valid!",
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _GetWorkOrderNumber()
|
|
|
|
Get a new unused C<workorder> number for the given change.
|
|
The highest current C<workorder> number for the given change is
|
|
looked up and incremented by one.
|
|
|
|
my $WorkOrderNumber = $WorkOrderObject->_GetWorkOrderNumber(
|
|
ChangeID => 2,
|
|
);
|
|
|
|
=cut
|
|
|
|
sub _GetWorkOrderNumber {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
if ( !$Param{ChangeID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => 'Need ChangeID!',
|
|
);
|
|
return;
|
|
}
|
|
|
|
# get the largest workorder number
|
|
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
|
|
SQL => 'SELECT MAX(workorder_number) '
|
|
. 'FROM change_workorder '
|
|
. 'WHERE change_id = ?',
|
|
Bind => [ \$Param{ChangeID} ],
|
|
Limit => 1,
|
|
);
|
|
|
|
# fetch the result, default to 0 when there are no workorders yet
|
|
my $WorkOrderNumber;
|
|
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
|
|
$WorkOrderNumber = $Row[0];
|
|
}
|
|
$WorkOrderNumber ||= 0;
|
|
|
|
# increment number to get a non-existent workorder number
|
|
$WorkOrderNumber++;
|
|
|
|
return $WorkOrderNumber;
|
|
}
|
|
|
|
=head2 _CheckWorkOrderParams()
|
|
|
|
Checks the params to WorkOrderAdd() and WorkOrderUpdate().
|
|
There are no required parameters.
|
|
The value for C<WorkOrderAgentID> can be undefined.
|
|
|
|
my $Ok = $WorkOrderObject->_CheckWorkOrderParams(
|
|
ChangeID => 123, # (optional)
|
|
WorkOrderNumber => 5, # (optional)
|
|
WorkOrderTitle => 'Replacement of mail server', # (optional)
|
|
Instruction => 'Install the <b>new</b> server', # (optional)
|
|
InstructionPlain => 'Install the new server', # (optional)
|
|
Report => 'Installed new server <b>without</b> problems', # (optional)
|
|
ReportPlain => 'Installed new server without problems', # (optional)
|
|
WorkOrderStateID => 4, # (optional)
|
|
WorkOrderTypeID => 12, # (optional)
|
|
WorkOrderAgentID => 8, # (optional) undef is allowed
|
|
PlannedStartTime => '2009-10-01 10:33:00', # (optional)
|
|
ActualStartTime => '2009-10-01 10:33:00', # (optional)
|
|
PlannedEndTime => '2009-10-01 10:33:00', # (optional)
|
|
ActualEndTime => '2009-10-01 10:33:00', # (optional)
|
|
DynamicField_X => 'Sun', # (optional)
|
|
DynamicField_Y => 'Earth', # (optional)
|
|
|
|
);
|
|
|
|
These string parameters have length constraints:
|
|
|
|
Parameter | max. length
|
|
-----------------+-----------------
|
|
WorkOrderTitle | 250 characters
|
|
Instruction | 1800000 characters
|
|
InstructionPlain | 1800000 characters
|
|
Report | 1800000 characters
|
|
ReportPlain | 1800000 characters
|
|
DynamicField_X | 3800 characters
|
|
DynamicField_Y | 3800 characters
|
|
|
|
=cut
|
|
|
|
sub _CheckWorkOrderParams {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check the string and id parameters
|
|
ARGUMENT:
|
|
for my $Argument (
|
|
qw(
|
|
WorkOrderTitle
|
|
Instruction
|
|
InstructionPlain
|
|
Report
|
|
ReportPlain
|
|
WorkOrderAgentID
|
|
WorkOrderStateID
|
|
WorkOrderTypeID
|
|
WorkOrderNumber
|
|
ChangeID
|
|
)
|
|
)
|
|
{
|
|
|
|
# params are not required
|
|
next ARGUMENT if !exists $Param{$Argument};
|
|
|
|
# check if param is not defined
|
|
if ( $Argument eq 'WorkOrderAgentID' && !defined $Param{$Argument} ) {
|
|
|
|
# WorkOrderAgentID can be undefined
|
|
}
|
|
elsif ( !defined $Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter '$Argument' must be defined!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check if param is not a reference
|
|
if ( ref $Param{$Argument} ne '' ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter '$Argument' mustn't be a reference!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check the maximum length of title
|
|
if ( $Argument eq 'WorkOrderTitle' && length( $Param{$Argument} ) > 250 ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter '$Argument' must be shorter than 250 characters!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check the maximum length of description and justification
|
|
if (
|
|
$Argument eq 'Instruction'
|
|
|| $Argument eq 'InstructionPlain'
|
|
|| $Argument eq 'Report'
|
|
|| $Argument eq 'ReportPlain'
|
|
)
|
|
{
|
|
if ( length( $Param{$Argument} ) > 1800000 ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter '$Argument' must be shorter than 1800000 characters!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
# check time formats
|
|
OPTION:
|
|
for my $Option (qw(PlannedStartTime PlannedEndTime ActualStartTime ActualEndTime)) {
|
|
|
|
next OPTION if !$Param{$Option};
|
|
|
|
if ( $Param{$Option} !~ m{ \A \d\d\d\d-\d\d-\d\d \s \d\d:\d\d:\d\d \z }xms ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Wrong time format for $Option: '$Param{$Option}'!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check workorder agent
|
|
if ( exists $Param{WorkOrderAgentID} && defined $Param{WorkOrderAgentID} ) {
|
|
|
|
# WorkOrderAgent must be an agent
|
|
my %UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
|
|
UserID => $Param{WorkOrderAgentID},
|
|
Valid => 1,
|
|
);
|
|
|
|
if ( !$UserData{UserID} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The WorkOrderAgent $Param{WorkOrderAgentID} is not a valid user id!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check the workorder dynamic fields
|
|
KEY:
|
|
for my $Key ( sort keys %Param ) {
|
|
|
|
next KEY if $Key !~ m{ \A DynamicField_(.*) \z }xms;
|
|
|
|
# params are not required
|
|
next KEY if !exists $Param{$Key};
|
|
|
|
# check if param is not defined
|
|
if ( !defined $Param{$Key} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter '$Key' must be defined!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check the maximum length of dynamic fields
|
|
if ( length( $Param{$Key} ) > 3800 ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The parameter '$Key' must be shorter than 3800 characters!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
# check if given WorkOrderStateID is valid
|
|
if ( exists $Param{WorkOrderStateID} ) {
|
|
return if !$Self->WorkOrderStateIDsCheck(
|
|
WorkOrderStateIDs => [ $Param{WorkOrderStateID} ],
|
|
);
|
|
}
|
|
|
|
# check if given WorkOrderTypeID is valid
|
|
if ( exists $Param{WorkOrderTypeID} ) {
|
|
return if !$Self->_CheckWorkOrderTypeIDs(
|
|
WorkOrderTypeIDs => [ $Param{WorkOrderTypeID} ],
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
=head2 _CheckTimestamps()
|
|
|
|
Checks the constraints of timestamps: C<xxxStartTime> must be before C<xxxEndTime>
|
|
|
|
my $Ok = $WorkOrderObject->_CheckTimestamps(
|
|
WorkOrderData => $WorkOrderData,
|
|
PlannedStartTime => '2009-10-12 00:00:01', # (optional) or undef
|
|
PlannedEndTime => '2009-10-15 15:00:00', # (optional) or undef
|
|
ActualStartTime => '2009-10-14 00:00:01', # (optional) or undef
|
|
ActualEndTime => '2009-01-20 00:00:01', # (optional) or undef
|
|
);
|
|
|
|
If PlannedStartTime is given, PlannedEndTime has to be given, too - and vice versa.
|
|
If ActualStartTime is given ActualEndTime is optional.
|
|
But if ActualEndTime is given then ActualStartTime has to be given, too.
|
|
WorkOrderData is only passed for improving the performance.
|
|
|
|
=cut
|
|
|
|
sub _CheckTimestamps {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check needed stuff
|
|
for my $Argument (qw(WorkOrderData)) {
|
|
if ( !$Param{$Argument} ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "Need $Argument!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
my $DefaultTimeStamp = '9999-01-01 00:00:00';
|
|
|
|
# check times
|
|
TYPE:
|
|
for my $Type (qw(Actual Planned)) {
|
|
|
|
# check only when a start or a end time is given
|
|
if ( !exists $Param{ $Type . 'StartTime' } && !exists $Param{ $Type . 'EndTime' } ) {
|
|
next TYPE;
|
|
}
|
|
|
|
# for the log messages
|
|
my $TypeLc = lc $Type;
|
|
|
|
my $StartTime = '';
|
|
if ( !exists $Param{ $Type . 'StartTime' } ) {
|
|
|
|
# if a time is not given, get it from the workorder
|
|
$StartTime = $Param{WorkOrderData}->{ $Type . 'StartTime' };
|
|
}
|
|
elsif ( !defined $Param{ $Type . 'StartTime' } ) {
|
|
|
|
# special case for clearing the time
|
|
$StartTime = $DefaultTimeStamp;
|
|
}
|
|
elsif ( !$Param{ $Type . 'StartTime' } ) {
|
|
|
|
# if a time is not given, get it from the workorder
|
|
$StartTime = $Param{WorkOrderData}->{ $Type . 'StartTime' };
|
|
}
|
|
elsif ( $Param{ $Type . 'StartTime' } eq $DefaultTimeStamp ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The value $StartTime is invalid for the $TypeLc start time!",
|
|
);
|
|
return;
|
|
}
|
|
else {
|
|
$StartTime = $Param{ $Type . 'StartTime' };
|
|
}
|
|
|
|
my $EndTime = '';
|
|
if ( !exists $Param{ $Type . 'EndTime' } ) {
|
|
|
|
# if a time is not given, get it from the workorder
|
|
$EndTime = $Param{WorkOrderData}->{ $Type . 'EndTime' };
|
|
}
|
|
elsif ( !defined $Param{ $Type . 'EndTime' } ) {
|
|
|
|
# special case for clearing the time
|
|
$EndTime = $DefaultTimeStamp;
|
|
}
|
|
elsif ( !$Param{ $Type . 'EndTime' } ) {
|
|
|
|
# if a time is not given, get it from the workorder
|
|
$EndTime = $Param{WorkOrderData}->{ $Type . 'EndTime' };
|
|
}
|
|
elsif ( $Param{ $Type . 'EndTime' } eq $DefaultTimeStamp ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "The value $EndTime is invalid for the $TypeLc end time!",
|
|
);
|
|
return;
|
|
}
|
|
else {
|
|
$EndTime = $Param{ $Type . 'EndTime' };
|
|
}
|
|
|
|
# don't check actual start time when the workorder has not ended yet
|
|
if ( $Type eq 'Actual' && $StartTime && !$EndTime ) {
|
|
next TYPE;
|
|
}
|
|
|
|
# the check fails if not both (start and end) times are present
|
|
if ( !$StartTime || !$EndTime ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message => "$Type start time and $TypeLc end time must be given!",
|
|
);
|
|
return;
|
|
}
|
|
|
|
# check the ordering of the times, only in the non-default-case
|
|
if ( $StartTime ne $DefaultTimeStamp && $EndTime ne $DefaultTimeStamp ) {
|
|
|
|
# remove all non-digit characters
|
|
$StartTime =~ s{ \D }{}xmsg;
|
|
$EndTime =~ s{ \D }{}xmsg;
|
|
|
|
# start time must be smaller than end time
|
|
if ( $StartTime >= $EndTime ) {
|
|
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
|
Priority => 'error',
|
|
Message =>
|
|
"The $TypeLc start time '$StartTime' must be before the $TypeLc end time '$EndTime'!",
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
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
|