Files
scripts/Perl OTRS/Kernel/Modules/AgentITSMChangeTimeSlot.pm
2024-10-14 00:08:40 +02:00

440 lines
14 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::Modules::AgentITSMChangeTimeSlot;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
our $ObjectManagerDisabled = 1;
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {%Param};
bless( $Self, $Type );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# get needed object
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# get needed ChangeID
my $ChangeID = $ParamObject->GetParam( Param => 'ChangeID' );
# check needed stuff
if ( !$ChangeID ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('No ChangeID is given!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# get needed objects
my $ChangeObject = $Kernel::OM->Get('Kernel::System::ITSMChange');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get config of frontend module
$Self->{Config} = $ConfigObject->Get("ITSMChange::Frontend::$Self->{Action}");
# check permissions
my $Access = $ChangeObject->Permission(
Type => $Self->{Config}->{Permission},
Action => $Self->{Action},
ChangeID => $ChangeID,
UserID => $Self->{UserID},
);
# error screen
if ( !$Access ) {
return $LayoutObject->NoPermission(
Message =>
$LayoutObject->{LanguageObject}->Translate( 'You need %s permissions!', $Self->{Config}->{Permission} ),
WithHeader => 'yes',
);
}
# get change data
my $Change = $ChangeObject->ChangeGet(
ChangeID => $ChangeID,
UserID => $Self->{UserID},
);
# check if change is found
if ( !$Change ) {
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}->Translate( 'Change "%s" not found in database!', $ChangeID ),
Comment => Translatable('Please contact the administrator.'),
);
}
# Moving is possible only when there are workorders.
if ( !$Change->{WorkOrderCount} ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('The change can\'t be moved, as it has no workorders.'),
Comment => Translatable('Add a workorder first.'),
);
}
# Moving is allowed only when there the change has not started yet.
if ( $Change->{ActualStartTime} ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Can\'t move a change which already has started!'),
Comment => Translatable('Please move the individual workorders instead.'),
);
}
# store needed parameters in %GetParam to make it reloadable
my %GetParam;
$GetParam{MoveTimeType} = $ParamObject->GetParam(
Param => 'MoveTimeType',
) || 'PlannedStartTime';
# store time related fields in %GetParam
for my $TimePart (qw(Year Month Day Hour Minute)) {
my $ParamName = 'MoveTime' . $TimePart;
$GetParam{$ParamName} = $ParamObject->GetParam(
Param => $ParamName,
);
}
# Remember the reason why saving was not attempted.
my %ValidationErrors;
# get time object
# move time slot of change
if ( $Self->{Subaction} eq 'MoveTimeSlot' ) {
# check validity of the time type
my $MoveTimeType = $GetParam{MoveTimeType};
if ( !defined $MoveTimeType )
{
$ValidationErrors{MoveTimeInvalid} = 'ServerError';
}
else {
# check the completeness of the time parameter list,
# only hour and minute are allowed to be '0'
if (
!$GetParam{MoveTimeYear}
|| !$GetParam{MoveTimeMonth}
|| !$GetParam{MoveTimeDay}
|| !defined $GetParam{MoveTimeHour}
|| !defined $GetParam{MoveTimeMinute}
)
{
$ValidationErrors{MoveTimeInvalid} = 'ServerError';
}
}
# get the system time from the input, if it can't be determined we have a validation error
my $PlannedSystemTime;
if ( !%ValidationErrors ) {
# transform change planned time, time stamp based on user time zone
%GetParam = $LayoutObject->TransformDateSelection(
%GetParam,
Prefix => 'MoveTime',
);
# format as timestamp
my $PlannedTime = sprintf '%04d-%02d-%02d %02d:%02d:00',
$GetParam{MoveTimeYear},
$GetParam{MoveTimeMonth},
$GetParam{MoveTimeDay},
$GetParam{MoveTimeHour},
$GetParam{MoveTimeMinute};
# sanity check of the assembled timestamp
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $PlannedTime,
}
);
$PlannedSystemTime = $DateTimeObject->ToEpoch();
if ( !$PlannedSystemTime ) {
$ValidationErrors{MoveTimeInvalid} = 'ServerError';
}
}
# move time slot only when there are no validation errors
if ( !%ValidationErrors ) {
# Determine the difference in seconds
my $CurrentPlannedTime = $Change->{$MoveTimeType};
# Even when there are workorders, a change might still miss a planned time.
# In that case moving the time slot is not possible.
if ( !$CurrentPlannedTime ) {
# show error message
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}
->Translate( 'The current %s could not be determined.', $MoveTimeType ),
Comment => $LayoutObject->{LanguageObject}
->Translate( 'The %s of all workorders has to be defined.', $MoveTimeType ),
);
}
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $CurrentPlannedTime,
TimeZone => $ConfigObject->Get('TimeZoneUser'),
}
);
my $CurrentPlannedSystemTime = $DateTimeObject->ToEpoch();
my $DiffSeconds = $PlannedSystemTime - $CurrentPlannedSystemTime;
my $MoveError = $Self->_MoveWorkOrders(
DiffSeconds => $DiffSeconds,
WorkOrderIDs => $Change->{WorkOrderIDs},
ChangeNumber => $Change->{ChangeNumber},
);
if ($MoveError) {
return $MoveError;
}
# load new URL in parent window and close popup
return $LayoutObject->PopupClose(
URL => "Action=AgentITSMChangeZoom;ChangeID=$ChangeID",
);
}
}
elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) {
# get planned start time or planned end time from the change
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Change->{ $GetParam{MoveTimeType} },
}
);
my $SystemTime = $DateTimeObject->ToEpoch();
# set the parameter hash for the answers
# the seconds are ignored
my $DateTimeSettings = $DateTimeObject->Get();
# get config for the number of years which should be selectable
my $TimePeriod = $ConfigObject->Get('ITSMWorkOrder::TimePeriod');
my $StartYear = $DateTimeSettings->{Year} - $TimePeriod->{YearPeriodPast};
my $EndYear = $DateTimeSettings->{Year} + $TimePeriod->{YearPeriodFuture};
# assemble the data that will be returned
my $JSON = $LayoutObject->BuildSelectionJSON(
[
{
Name => 'MoveTimeMinute',
Data => [ map { sprintf '%02d', $_ } ( 0 .. 59 ) ],
SelectedID => $DateTimeSettings->{Minute},
},
{
Name => 'MoveTimeHour',
Data => [ map { sprintf '%02d', $_ } ( 0 .. 23 ) ],
SelectedID => $DateTimeSettings->{Hour},
},
{
Name => 'MoveTimeDay',
Data => { map { $_ => sprintf '%02d', $_ } ( 1 .. 31 ) },
SelectedID => int $DateTimeSettings->{Day},
},
{
Name => 'MoveTimeMonth',
Data => { map { $_ => sprintf '%02d', $_ } ( 1 .. 12 ) },
SelectedID => int $DateTimeSettings->{Month},
},
{
Name => 'MoveTimeYear',
Data => [ $StartYear .. $EndYear ],
SelectedID => $GetParam{MoveTimeYear},
},
],
);
return $LayoutObject->Attachment(
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
Content => $JSON,
Type => 'inline',
NoCache => 1,
);
}
else {
# no subaction,
# get planned start time or planned end time from the change
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Change->{ $GetParam{MoveTimeType} }
}
);
my $SystemTime = $DateTimeObject->ToEpoch();
# set the parameter hash for BuildDateSelection()
# the seconds are ignored
my $DateTimeSettings = $DateTimeObject->Get();
$GetParam{MoveTimeMinute} = $DateTimeSettings->{Minute};
$GetParam{MoveTimeHour} = $DateTimeSettings->{Hour};
$GetParam{MoveTimeDay} = $DateTimeSettings->{Day};
$GetParam{MoveTimeMonth} = $DateTimeSettings->{Month};
$GetParam{MoveTimeYear} = $DateTimeSettings->{Year};
}
# build drop-down with time types
my $MoveTimeTypeSelectionString = $LayoutObject->BuildSelection(
Data => [
{
Key => 'PlannedStartTime',
Value => Translatable('Planned Start Time')
},
{
Key => 'PlannedEndTime',
Value => Translatable('Planned End Time')
},
],
Name => 'MoveTimeType',
SelectedID => $GetParam{MoveTimeType},
Class => 'Modernize',
);
# time period that can be selected from the GUI
my %TimePeriod = %{ $ConfigObject->Get('ITSMWorkOrder::TimePeriod') };
# add selection for the time
my $MoveTimeSelectionString = $LayoutObject->BuildDateSelection(
%GetParam,
Format => 'DateInputFormatLong',
Prefix => 'MoveTime',
Validate => 1,
MoveTimeClass => $ValidationErrors{MoveTimeInvalid} || '',
%TimePeriod,
);
# output header
my $Output = $LayoutObject->Header(
Title => Translatable('Move Time Slot'),
Type => 'Small',
);
# start template output
$Output .= $LayoutObject->Output(
TemplateFile => 'AgentITSMChangeTimeSlot',
Data => {
%{$Change},
%ValidationErrors,
MoveTimeTypeSelectionString => $MoveTimeTypeSelectionString,
MoveTimeSelectionString => $MoveTimeSelectionString,
},
);
# add footer
$Output .= $LayoutObject->Footer( Type => 'Small' );
return $Output;
}
# the actual moving is done here
sub _MoveWorkOrders {
my ( $Self, %Param ) = @_;
my @CollectedUpdateParams; # an array of params for WorkOrderUpdate()
my %WorkOrderID2Number; # used only for error messages
# get work order object
my $WorkOrderObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# determine the new times
WORKORDERID:
for my $WorkOrderID ( @{ $Param{WorkOrderIDs} } ) {
my $WorkOrder = $WorkOrderObject->WorkOrderGet(
WorkOrderID => $WorkOrderID,
UserID => $Self->{UserID},
);
next WORKORDERID if !$WorkOrder;
next WORKORDERID if ref $WorkOrder ne 'HASH';
next WORKORDERID if !%{$WorkOrder};
$WorkOrderID2Number{$WorkOrderID} = $WorkOrder->{WorkOrderNumber};
my %UpdateParams;
TYPE:
for my $Type (qw(PlannedStartTime PlannedEndTime)) {
next TYPE if !$WorkOrder->{$Type};
# get datetime object
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $WorkOrder->{$Type},
}
);
next TYPE if !$DateTimeObject;
# add the number of seconds that the time slot should be moved
$DateTimeObject->Add( Seconds => $Param{DiffSeconds} );
$UpdateParams{$Type} = $DateTimeObject->ToString();
}
next WORKORDERID if !%UpdateParams;
# remember the workorder that should be moved
$UpdateParams{WorkOrderID} = $WorkOrderID;
push @CollectedUpdateParams, \%UpdateParams;
}
# do the updating
UPDATEPARAMS:
for my $UpdateParams (@CollectedUpdateParams) {
# no number calculation necessary because the workorder order doesn't change
my $UpdateOk = $WorkOrderObject->WorkOrderUpdate(
%{$UpdateParams},
NoNumberCalc => 1,
UserID => $Self->{UserID},
);
if ( !$UpdateOk ) {
# show error message
my $Number = join '-',
$Param{ChangeNumber},
$WorkOrderID2Number{ $UpdateParams->{WorkOrderID} };
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}
->Translate( 'Was not able to move time slot for workorder #%s!', $Number ),
Comment => Translatable('Please contact the administrator.'),
);
}
}
# moving was successful
return;
}
1;