# -- # 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::Console::Command::Admin::ITSM::Change::Check; use strict; use warnings; use parent qw( Kernel::System::Console::BaseCommand Kernel::System::EventHandler); our @ObjectDependencies = ( 'Kernel::Config', 'Kernel::System::DateTime', 'Kernel::System::PID', 'Kernel::System::ITSMChange', 'Kernel::System::ITSMChange::ITSMWorkOrder', 'Kernel::System::ITSMChange::History', ); sub Configure { my ( $Self, %Param ) = @_; $Self->Description('Check if ITSM changes have reached specific times.'); $Self->AddOption( Name => 'force-pid', Description => "Start even if another process is still registered in the database.", Required => 0, HasValue => 0, ); return; } sub PreRun { my ( $Self, %Param ) = @_; # get PID object my $PIDObject = $Kernel::OM->Get('Kernel::System::PID'); # create PID lock my $PIDCreated = $PIDObject->PIDCreate( Name => $Self->Name(), Force => $Self->GetOption('force-pid'), TTL => 60 * 60 * 2, ); if ( !$PIDCreated ) { my $Error = "Unable to register the process in the database. Is another instance still running?\n"; $Error .= "You can use --force-pid to override this check.\n"; die $Error; } # init of event handler $Self->EventHandlerInit( Config => 'ITSMChangeCronjob::EventModule', ); # get time object my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); $Self->{SystemTime} = $DateTimeObject->ToEpoch(); $Self->{Now} = $DateTimeObject->ToString(); return; } sub Run { my ( $Self, %Param ) = @_; $Self->Print("Checking ITSM changes...\n"); # get change object my $ChangeObject = $Kernel::OM->Get('Kernel::System::ITSMChange'); # notifications for changes' plannedXXXtime events for my $Type (qw(StartTime EndTime)) { # get changes with PlannedStartTime older than now my $PlannedChangeIDs = $ChangeObject->ChangeSearch( "Planned${Type}OlderDate" => $Self->{Now}, MirrorDB => 1, UserID => 1, ) || []; CHANGEID: for my $ChangeID ( @{$PlannedChangeIDs} ) { # get change data my $Change = $ChangeObject->ChangeGet( ChangeID => $ChangeID, UserID => 1, ); # skip change if there is already an actualXXXtime set or notification was sent next CHANGEID if $Change->{"Actual$Type"}; my $LastNotificationSentDate = $Self->ChangeNotificationSent( ChangeID => $ChangeID, Type => "Planned${Type}", ); my $AlreadySentWithinPeriod = $Self->SentWithinPeriod( LastNotificationSentDate => $LastNotificationSentDate, ); next CHANGEID if $AlreadySentWithinPeriod; # trigger ChangePlannedStartTimeReachedPost-Event $Self->EventHandler( Event => "ChangePlanned${Type}ReachedPost", Data => { ChangeID => $ChangeID, }, UserID => 1, ); } # get changes with actualxxxtime my $ActualChangeIDs = $ChangeObject->ChangeSearch( "Actual${Type}OlderDate" => $Self->{Now}, MirrorDB => 1, UserID => 1, ) || []; ACTUALCHANGEID: for my $ChangeID ( @{$ActualChangeIDs} ) { # get change data my $Change = $ChangeObject->ChangeGet( ChangeID => $ChangeID, UserID => 1, ); my $LastNotificationSentDate = $Self->ChangeNotificationSent( ChangeID => $ChangeID, Type => "Actual${Type}", ); next ACTUALCHANGEID if $LastNotificationSentDate; # trigger Event $Self->EventHandler( Event => "ChangeActual${Type}ReachedPost", Data => { ChangeID => $ChangeID, }, UserID => 1, ); } } # get changes with RequestedTimeOlderDate my $RequestedTimeChangeIDs = $ChangeObject->ChangeSearch( RequestedTimeOlderDate => $Self->{Now}, MirrorDB => 1, UserID => 1, ) || []; CHANGEID: for my $ChangeID ( @{$RequestedTimeChangeIDs} ) { # get change data my $Change = $ChangeObject->ChangeGet( ChangeID => $ChangeID, UserID => 1, ); my $LastNotificationSentDate = $Self->ChangeNotificationSent( ChangeID => $ChangeID, Type => "RequestedTime", ); next CHANGEID if $LastNotificationSentDate; # trigger Event $Self->EventHandler( Event => "ChangeRequestedTimeReachedPost", Data => { ChangeID => $ChangeID, }, UserID => 1, ); } # notifications for workorders' plannedXXXtime events for my $Type (qw(StartTime EndTime)) { my $WorkOrderObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder'); # get workorders with PlannedStartTime older than now my $PlannedWorkOrderIDs = $WorkOrderObject->WorkOrderSearch( "Planned${Type}OlderDate" => $Self->{Now}, MirrorDB => 1, UserID => 1, ) || []; WORKORDERID: for my $WorkOrderID ( @{$PlannedWorkOrderIDs} ) { # get workorder data my $WorkOrder = $WorkOrderObject->WorkOrderGet( WorkOrderID => $WorkOrderID, UserID => 1, ); # skip workorder if there is already an actualXXXtime set or notification was sent next WORKORDERID if $WorkOrder->{"Actual$Type"}; my $LastNotificationSentDate = $Self->WorkOrderNotificationSent( WorkOrderID => $WorkOrderID, Type => "Planned${Type}", ); my $AlreadySentWithinPeriod = $Self->SentWithinPeriod( LastNotificationSentDate => $LastNotificationSentDate, ); next WORKORDERID if $AlreadySentWithinPeriod; # trigger WorkOrderPlannedStartTimeReachedPost-Event $Self->EventHandler( Event => "WorkOrderPlanned${Type}ReachedPost", Data => { WorkOrderID => $WorkOrderID, ChangeID => $WorkOrder->{ChangeID}, }, UserID => 1, ); } # get workorders with actualxxxtime my $ActualWorkOrderIDs = $WorkOrderObject->WorkOrderSearch( "Actual${Type}OlderDate" => $Self->{Now}, MirrorDB => 1, UserID => 1, ) || []; WORKORDERID: for my $WorkOrderID ( @{$ActualWorkOrderIDs} ) { # get workorder data my $WorkOrder = $WorkOrderObject->WorkOrderGet( WorkOrderID => $WorkOrderID, UserID => 1, ); my $LastNotificationSentDate = $Self->WorkOrderNotificationSent( WorkOrderID => $WorkOrderID, Type => "Actual${Type}", ); next WORKORDERID if $LastNotificationSentDate; # trigger Event $Self->EventHandler( Event => "WorkOrderActual${Type}ReachedPost", Data => { WorkOrderID => $WorkOrderID, ChangeID => $WorkOrder->{ChangeID}, }, UserID => 1, ); } } $Self->Print("Done.\n"); return $Self->ExitCodeOk(); } # check if a notification was already sent for the given change sub ChangeNotificationSent { my ( $Self, %Param ) = @_; # check needed stuff for my $Needed (qw(ChangeID Type)) { return if !$Param{$Needed}; } # get history entries my $History = $Kernel::OM->Get('Kernel::System::ITSMChange::History')->ChangeHistoryGet( ChangeID => $Param{ChangeID}, UserID => 1, ); # search for notifications sent earlier for my $HistoryEntry ( reverse @{$History} ) { if ( $HistoryEntry->{HistoryType} eq 'Change' . $Param{Type} . 'Reached' && $HistoryEntry->{ContentNew} =~ m{ Notification \s Sent $ }xms ) { return $HistoryEntry->{CreateTime}; } } return; } # check if a notification was already sent for the given workorder sub WorkOrderNotificationSent { my ( $Self, %Param ) = @_; # check needed stuff for my $Needed (qw(WorkOrderID Type)) { return if !$Param{$Needed}; } # get history entries my $History = $Kernel::OM->Get('Kernel::System::ITSMChange::History')->WorkOrderHistoryGet( WorkOrderID => $Param{WorkOrderID}, UserID => 1, ); # search for notifications sent earlier for my $HistoryEntry ( reverse @{$History} ) { if ( $HistoryEntry->{HistoryType} eq 'WorkOrder' . $Param{Type} . 'Reached' && $HistoryEntry->{ContentNew} =~ m{ Notification \s Sent }xms ) { return $HistoryEntry->{CreateTime}; } } return; } sub SentWithinPeriod { my ( $Self, %Param ) = @_; return if !$Param{LastNotificationSentDate}; # get SysConfig option my $Config = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::TimeReachedNotifications'); # if notifications should be sent only once return 1 if $Config->{Frequency} eq 'once'; # get epoche seconds of send time my $SentEpoche = $Kernel::OM->Create( 'Kernel::System::DateTime', ObjectParams => { String => $Param{LastNotificationSentDate}, } )->ToEpoch(); # calc diff my $EpocheSinceSent = $Self->{SystemTime} - $SentEpoche; my $HoursSinceSent = int( $EpocheSinceSent / ( 60 * 60 ) ); if ( $HoursSinceSent >= $Config->{Hours} ) { return; } return 1; } sub PostRun { my ( $Self, %Param ) = @_; # delete pid lock $Kernel::OM->Get('Kernel::System::PID')->PIDDelete( Name => $Self->Name() ); return; } 1; =head1 TERMS AND CONDITIONS This software is part of the OTRS project (L). This software comes with ABSOLUTELY NO WARRANTY. For details, see the enclosed file COPYING for license information (GPL). If you did not receive this file, see L. =cut