652 lines
22 KiB
Perl
652 lines
22 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::CustomerITSMChangeSchedule;
|
|
|
|
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 layout object
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
# store last screen
|
|
if (
|
|
!$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
|
|
SessionID => $Self->{SessionID},
|
|
Key => 'LastChangeView',
|
|
Value => $Self->{RequestedURL},
|
|
)
|
|
)
|
|
{
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Title => Translatable('Error'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerError();
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# check needed CustomerID
|
|
if ( !$Self->{UserCustomerID} ) {
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Title => Translatable('Error'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerError(
|
|
Message => Translatable('Need CustomerID!'),
|
|
);
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
return $Output;
|
|
}
|
|
|
|
# get config of frontend module
|
|
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get("ITSMChange::Frontend::$Self->{Action}");
|
|
|
|
# get param object
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
|
|
# get sorting parameters
|
|
my $SortBy = $ParamObject->GetParam( Param => 'SortBy' )
|
|
|| $Self->{Config}->{'SortBy::Default'}
|
|
|| 'PlannedStartTime';
|
|
|
|
# get ordering parameters
|
|
my $OrderBy = $ParamObject->GetParam( Param => 'OrderBy' )
|
|
|| $Self->{Config}->{'Order::Default'}
|
|
|| 'Up';
|
|
|
|
my @SortByArray = ($SortBy);
|
|
my @OrderByArray = ($OrderBy);
|
|
|
|
# investigate refresh
|
|
my $Refresh = $Self->{UserRefreshTime} ? 60 * $Self->{UserRefreshTime} : undef;
|
|
|
|
# starting with page ...
|
|
my $Output = $LayoutObject->CustomerHeader(
|
|
Refresh => $Refresh,
|
|
Title => '',
|
|
);
|
|
|
|
# build NavigationBar
|
|
$Output .= $LayoutObject->CustomerNavigationBar();
|
|
|
|
$LayoutObject->Print( Output => \$Output );
|
|
$Output = '';
|
|
|
|
# find out which columns should be shown
|
|
my @ShowColumns;
|
|
if ( $Self->{Config}->{ShowColumns} ) {
|
|
|
|
# get all possible columns from config
|
|
my %PossibleColumn = %{ $Self->{Config}->{ShowColumns} };
|
|
|
|
# get the column names that should be shown
|
|
COLUMNNAME:
|
|
for my $Name ( sort keys %PossibleColumn ) {
|
|
next COLUMNNAME if !$PossibleColumn{$Name};
|
|
push @ShowColumns, $Name;
|
|
}
|
|
}
|
|
|
|
# to store the filters
|
|
my %Filters;
|
|
|
|
# get change object
|
|
my $ChangeObject = $Kernel::OM->Get('Kernel::System::ITSMChange');
|
|
|
|
# set other filters based on change state
|
|
if ( $Self->{Config}->{'Filter::ChangeStates'} ) {
|
|
|
|
# define position of the filter in the frontend
|
|
my $PrioCounter = 1000;
|
|
|
|
# get all change states that should be used as filters
|
|
CHANGESTATE:
|
|
for my $ChangeState ( @{ $Self->{Config}->{'Filter::ChangeStates'} } ) {
|
|
|
|
# do not use empty change states
|
|
next CHANGESTATE if !$ChangeState;
|
|
|
|
# check if state is valid by looking up the state id
|
|
my $ChangeStateID = $ChangeObject->ChangeStateLookup(
|
|
ChangeState => $ChangeState,
|
|
);
|
|
|
|
# do not use invalid change states
|
|
next CHANGESTATE if !$ChangeStateID;
|
|
|
|
# increase the PrioCounter
|
|
$PrioCounter++;
|
|
|
|
# add filter for the current change state
|
|
$Filters{$ChangeState} = {
|
|
Name => $ChangeState,
|
|
Prio => $PrioCounter,
|
|
Search => {
|
|
ChangeStates => [$ChangeState],
|
|
OrderBy => \@SortByArray,
|
|
OrderByDirection => \@OrderByArray,
|
|
Limit => 1000,
|
|
UserID => 1,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
# get filter and view params
|
|
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || 'All';
|
|
$Self->{View} = $ParamObject->GetParam( Param => 'View' ) || '';
|
|
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
|
$Self->{PageShown} = $Self->{UserShowTickets} || 1;
|
|
|
|
# if only one filter exists
|
|
if ( scalar keys %Filters == 1 ) {
|
|
|
|
# get the name of the only filter
|
|
my ($FilterName) = keys %Filters;
|
|
|
|
# activate this filter
|
|
$Self->{Filter} = $FilterName;
|
|
}
|
|
else {
|
|
|
|
# add default filter
|
|
$Filters{All} = {
|
|
Name => 'All',
|
|
Prio => 1000,
|
|
Search => {
|
|
ChangeStates => $Self->{Config}->{'Filter::ChangeStates'},
|
|
OrderBy => \@SortByArray,
|
|
OrderByDirection => \@OrderByArray,
|
|
Limit => 1000,
|
|
UserID => 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
# check if filter is valid
|
|
if ( !$Filters{ $Self->{Filter} } ) {
|
|
$LayoutObject->FatalError(
|
|
Message => $LayoutObject->{LanguageObject}->Translate( 'Invalid Filter: %s!', $Self->{Filter} ),
|
|
);
|
|
}
|
|
|
|
# search changes which match the selected filter
|
|
my $ChangeIDsRef = $ChangeObject->ChangeSearch(
|
|
%{ $Filters{ $Self->{Filter} }->{Search} },
|
|
);
|
|
my @ChangeIDs = @{$ChangeIDsRef};
|
|
|
|
my %CustomerUserServices;
|
|
my %CountByChangeState;
|
|
|
|
# get needed objects
|
|
my $LinkObject = $Kernel::OM->Get('Kernel::System::LinkObject');
|
|
my $ServiceObject = $Kernel::OM->Get('Kernel::System::Service');
|
|
|
|
# if configured, get only changes which have workorders that are linked with a service
|
|
if ( $Self->{Config}->{ShowOnlyChangesWithAllowedServices} ) {
|
|
|
|
# get all services the customer user is allowed to use
|
|
%CustomerUserServices = $ServiceObject->CustomerUserServiceMemberList(
|
|
CustomerUserLogin => $Self->{UserID},
|
|
Result => 'HASH',
|
|
DefaultServices => 1,
|
|
);
|
|
|
|
my %UniqueChangeIDs;
|
|
CHANGEID:
|
|
for my $ChangeID (@ChangeIDs) {
|
|
|
|
# get change data
|
|
my $Change = $ChangeObject->ChangeGet(
|
|
UserID => $Self->{UserID},
|
|
ChangeID => $ChangeID,
|
|
);
|
|
|
|
# get workorder ids
|
|
my @WorkOrderIDs = @{ $Change->{WorkOrderIDs} };
|
|
|
|
# don't show changes with no workorders (as they can not be linked with a service)
|
|
next CHANGEID if !@WorkOrderIDs;
|
|
|
|
WORKORDERID:
|
|
for my $WorkOrderID (@WorkOrderIDs) {
|
|
|
|
# get the list of linked services
|
|
my %LinkKeyList = $LinkObject->LinkKeyList(
|
|
Object1 => 'ITSMWorkOrder',
|
|
Key1 => $WorkOrderID,
|
|
Object2 => 'Service',
|
|
State => 'Valid',
|
|
UserID => 1,
|
|
);
|
|
|
|
# workorder has no linked service
|
|
next WORKORDERID if !%LinkKeyList;
|
|
|
|
SERVICEID:
|
|
for my $ServiceID ( sort keys %LinkKeyList ) {
|
|
|
|
# only use services where the customer is allowed to use the service
|
|
next SERVICEID if !$CustomerUserServices{$ServiceID};
|
|
|
|
# add change id to list of visible changes for the customer
|
|
$UniqueChangeIDs{$ChangeID}++;
|
|
|
|
# count the visible changes per state
|
|
$CountByChangeState{ $Change->{ChangeState} }++;
|
|
}
|
|
}
|
|
}
|
|
|
|
@ChangeIDs = keys %UniqueChangeIDs;
|
|
|
|
# add the all count
|
|
$CountByChangeState{All} = scalar @ChangeIDs;
|
|
}
|
|
|
|
# display all navbar filters
|
|
my %NavBarFilter;
|
|
my $Counter;
|
|
my $AllChanges;
|
|
my $AllChangesTotal;
|
|
|
|
# store the number of filters
|
|
my $NumberOfFilters = keys %Filters;
|
|
|
|
# array to sort the filters by its priority
|
|
my @NavBarFilters = sort { $Filters{$a}->{Prio} <=> $Filters{$b}->{Prio} } keys %Filters;
|
|
|
|
FILTER:
|
|
for my $FilterName (@NavBarFilters) {
|
|
$Counter++;
|
|
|
|
# due to service restrictions for customer users
|
|
# we need to count differently if the feature "ShowOnlyChangesWithAllowedServices"
|
|
# is activated
|
|
my $Count;
|
|
if ( $Self->{Config}->{ShowOnlyChangesWithAllowedServices} ) {
|
|
$Count = $CountByChangeState{$FilterName} || 0;
|
|
}
|
|
else {
|
|
|
|
# count the number of changes for each filter
|
|
$Count = $ChangeObject->ChangeSearch(
|
|
%{ $Filters{$FilterName}->{Search} },
|
|
Result => 'COUNT',
|
|
);
|
|
}
|
|
|
|
if ( $FilterName ne 'All' && !$Count ) {
|
|
next FILTER;
|
|
}
|
|
|
|
my $ClassLI = '';
|
|
my $ClassA = '';
|
|
if ( $FilterName eq $Self->{Filter} ) {
|
|
$ClassA = 'Selected';
|
|
$AllChanges = $Count || 0;
|
|
}
|
|
if ( $NumberOfFilters == $Counter ) {
|
|
$ClassLI = 'Last';
|
|
}
|
|
if ( $FilterName eq 'All' ) {
|
|
$AllChangesTotal = $Count;
|
|
}
|
|
|
|
# set counter string (emty string if Count is undefined)
|
|
my $CountStrg;
|
|
if ( defined $Count ) {
|
|
$CountStrg = '(' . $Count . ')';
|
|
}
|
|
|
|
# display the navbar filter
|
|
$NavBarFilter{ $Filters{$FilterName}->{Prio} } = {
|
|
CountStrg => $CountStrg,
|
|
Filter => $FilterName,
|
|
ClassLI => $ClassLI,
|
|
ClassA => $ClassA,
|
|
%{ $Filters{$FilterName} },
|
|
};
|
|
}
|
|
|
|
# set meta-link for pagination
|
|
my $Link = 'SortBy=' . $LayoutObject->Ascii2Html( Text => $SortBy )
|
|
. ';OrderBy=' . $LayoutObject->Ascii2Html( Text => $OrderBy )
|
|
. ';Filter=' . $LayoutObject->Ascii2Html( Text => $Self->{Filter} )
|
|
. ';Subaction=' . $LayoutObject->Ascii2Html( Text => $Self->{Subaction} )
|
|
. ';';
|
|
|
|
# create pagination
|
|
my %PageNav = $LayoutObject->PageNavBar(
|
|
Limit => 10000,
|
|
StartHit => $Self->{StartHit},
|
|
PageShown => $Self->{PageShown},
|
|
AllHits => scalar(@ChangeIDs), #$AllChanges,
|
|
Action => 'Action=CustomerITSMChangeSchedule',
|
|
Link => $Link,
|
|
IDPrefix => 'CustomerITSMChangeSchedule',
|
|
);
|
|
|
|
# show changes is data if any
|
|
if ( scalar @ChangeIDs ) {
|
|
|
|
# show header filter
|
|
for my $Key ( sort keys %NavBarFilter ) {
|
|
$LayoutObject->Block(
|
|
Name => 'FilterHeader',
|
|
Data => {
|
|
%{ $NavBarFilter{$Key} },
|
|
},
|
|
);
|
|
}
|
|
|
|
if ( scalar @ShowColumns ) {
|
|
|
|
# set headers
|
|
for my $ColumnName (@ShowColumns) {
|
|
|
|
# create needed veriables
|
|
my $CSS = '';
|
|
my $SetOrderBy;
|
|
|
|
# remove ID if necesary
|
|
if ($SortBy) {
|
|
$SortBy = ( $SortBy eq 'PriorityID' )
|
|
? 'Priority'
|
|
: ( $SortBy eq 'CategoryID' ) ? 'Category'
|
|
: ( $SortBy eq 'ChangeBuilderID' ) ? 'ChangeBuilder'
|
|
: ( $SortBy eq 'ChangeManagerID' ) ? 'ChangeManager'
|
|
: ( $SortBy eq 'ChangeStateID' ) ? 'ChangeState'
|
|
: ( $SortBy eq 'ImpactID' ) ? 'Impact'
|
|
: ( $SortBy eq 'WorkOrderAgentID' ) ? 'WorkOrderAgent'
|
|
: ( $SortBy eq 'WorkOrderStateID' ) ? 'WorkOrderState'
|
|
: ( $SortBy eq 'WorkOrderTypeID' ) ? 'WorkOrderType'
|
|
: $SortBy;
|
|
}
|
|
|
|
# set the correct Set CSS class and order by link
|
|
if ( $SortBy && ( $SortBy eq $ColumnName ) ) {
|
|
if ( $OrderBy && ( $OrderBy eq 'Up' ) ) {
|
|
$SetOrderBy = 'Down';
|
|
$CSS .= ' SortDescending';
|
|
}
|
|
else {
|
|
$SetOrderBy = 'Up';
|
|
$CSS .= ' SortAscending';
|
|
}
|
|
}
|
|
else {
|
|
$SetOrderBy = 'Up';
|
|
}
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'Record' . $ColumnName . 'Header',
|
|
Data => {
|
|
CSS => $CSS,
|
|
OrderBy => $SetOrderBy,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# define incident signals, needed for services
|
|
my %InciSignals = (
|
|
operational => 'greenled',
|
|
warning => 'yellowled',
|
|
incident => 'redled',
|
|
);
|
|
|
|
# show changes's
|
|
$Counter = 0;
|
|
|
|
ID:
|
|
for my $ChangeID (@ChangeIDs) {
|
|
$Counter++;
|
|
if (
|
|
$Counter >= $Self->{StartHit}
|
|
&& $Counter < ( $Self->{PageShown} + $Self->{StartHit} )
|
|
)
|
|
{
|
|
|
|
# to store all data
|
|
my %Data;
|
|
|
|
# to store data of sub-elements
|
|
my %SubElementData;
|
|
|
|
my $Change = $ChangeObject->ChangeGet(
|
|
ChangeID => $ChangeID,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
next ID if !$Change;
|
|
|
|
# add change data,
|
|
# ( let workorder data overwrite
|
|
# some change attributes, i.e. PlannedStartTime, etc... )
|
|
%Data = ( %{$Change}, %Data );
|
|
|
|
# get user data for needed user types
|
|
USERTYPE:
|
|
for my $UserType (qw(ChangeBuilder ChangeManager WorkOrderAgent)) {
|
|
|
|
# check if UserType attribute exists either in change or workorder
|
|
if ( !$Change->{ $UserType . 'ID' } && !$Data{ $UserType . 'ID' } ) {
|
|
next USERTYPE;
|
|
}
|
|
|
|
# get user data
|
|
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
|
|
UserID => $Change->{ $UserType . 'ID' } || $Data{ $UserType . 'ID' },
|
|
Cached => 1,
|
|
);
|
|
|
|
# set user data
|
|
$Data{ $UserType . 'UserLogin' } = $User{UserLogin};
|
|
$Data{ $UserType . 'UserFirstname' } = $User{UserFirstname};
|
|
$Data{ $UserType . 'UserLastname' } = $User{UserLastname};
|
|
$Data{ $UserType . 'UserFullname' } = $User{UserFullname};
|
|
}
|
|
|
|
# to store the linked service data
|
|
my $LinkListWithData = {};
|
|
|
|
my @WorkOrderIDs;
|
|
|
|
# store the combined linked services data from all workorders of this change
|
|
@WorkOrderIDs = @{ $Change->{WorkOrderIDs} };
|
|
|
|
# store the combined linked services data
|
|
for my $WorkOrderID (@WorkOrderIDs) {
|
|
|
|
# get linked objects of this workorder
|
|
my $LinkListWithDataWorkOrder = $LinkObject->LinkListWithData(
|
|
Object => 'ITSMWorkOrder',
|
|
Key => $WorkOrderID,
|
|
State => 'Valid',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
OBJECT:
|
|
for my $Object ( sort keys %{$LinkListWithDataWorkOrder} ) {
|
|
|
|
# only show linked services of workorder
|
|
if ( $Object ne 'Service' ) {
|
|
next OBJECT;
|
|
}
|
|
|
|
LINKTYPE:
|
|
for my $LinkType ( sort keys %{ $LinkListWithDataWorkOrder->{$Object} } ) {
|
|
|
|
DIRECTION:
|
|
for my $Direction (
|
|
sort keys %{ $LinkListWithDataWorkOrder->{$Object}->{$LinkType} }
|
|
)
|
|
{
|
|
|
|
ID:
|
|
for my $ID (
|
|
sort keys %{
|
|
$LinkListWithDataWorkOrder->{$Object}->{$LinkType}
|
|
->{$Direction}
|
|
}
|
|
)
|
|
{
|
|
|
|
# combine the linked object data from all workorders
|
|
$LinkListWithData->{$Object}->{$LinkType}->{$Direction}->{$ID}
|
|
= $LinkListWithDataWorkOrder->{$Object}->{$LinkType}
|
|
->{$Direction}->{$ID};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# get unique service ids
|
|
my %UniqueServiceIDs;
|
|
my $ServicesRef = $LinkListWithData->{Service} || {};
|
|
for my $LinkType ( sort keys %{$ServicesRef} ) {
|
|
|
|
# extract link type List
|
|
my $LinkTypeList = $ServicesRef->{$LinkType};
|
|
|
|
for my $Direction ( sort keys %{$LinkTypeList} ) {
|
|
|
|
# extract direction list
|
|
my $DirectionList = $ServicesRef->{$LinkType}->{$Direction};
|
|
|
|
# collect unique service ids
|
|
for my $ServiceID ( sort keys %{$DirectionList} ) {
|
|
$UniqueServiceIDs{$ServiceID}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
# get the data for each service
|
|
my @ServicesData;
|
|
SERVICEID:
|
|
for my $ServiceID ( sort keys %UniqueServiceIDs ) {
|
|
|
|
if ( $Self->{Config}->{ShowOnlyChangesWithAllowedServices} ) {
|
|
|
|
# do not show this service if customer is not allowed to use it
|
|
next SERVICEID if !$CustomerUserServices{$ServiceID};
|
|
}
|
|
|
|
# get service data
|
|
my %ServiceData = $ServiceObject->ServiceGet(
|
|
ServiceID => $ServiceID,
|
|
IncidentState => 1,
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# add current incident signal
|
|
$ServiceData{CurInciSignal} = $InciSignals{ $ServiceData{CurInciStateType} || '' } || '';
|
|
|
|
# store service data
|
|
push @ServicesData, \%ServiceData;
|
|
}
|
|
|
|
# sort services data by service name
|
|
@ServicesData = sort { $a->{Name} cmp $b->{Name} } @ServicesData;
|
|
|
|
# do not show the change if it has no services
|
|
next ID if !@ServicesData;
|
|
|
|
# store services data
|
|
$SubElementData{Services} = \@ServicesData;
|
|
|
|
# add block
|
|
$LayoutObject->Block(
|
|
Name => 'Record',
|
|
Data => {},
|
|
);
|
|
|
|
if (@ShowColumns) {
|
|
COLUMN:
|
|
for my $ColumnName (@ShowColumns) {
|
|
$LayoutObject->Block(
|
|
Name => 'Record' . $ColumnName,
|
|
Data => {
|
|
%Data,
|
|
},
|
|
);
|
|
|
|
# check if this column contains sub-elements
|
|
if (
|
|
$SubElementData{$ColumnName}
|
|
&& ref $SubElementData{$ColumnName} eq 'ARRAY'
|
|
)
|
|
{
|
|
|
|
for my $SubElement ( @{ $SubElementData{$ColumnName} } ) {
|
|
|
|
# show sub-elements of column
|
|
$LayoutObject->Block(
|
|
Name => 'Record' . $ColumnName . 'SubElement',
|
|
Data => {
|
|
%Param,
|
|
%Data,
|
|
%{$SubElement},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( !@ServicesData ) {
|
|
$LayoutObject->Block(
|
|
Name => 'Record' . $ColumnName . 'SubElementEmpty',
|
|
Data => {},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# otherwise show no data found message
|
|
else {
|
|
$LayoutObject->Block(
|
|
Name => 'NoDataFoundMsg',
|
|
Data => {},
|
|
);
|
|
}
|
|
|
|
$Output .= $LayoutObject->Output(
|
|
TemplateFile => 'CustomerITSMChangeOverview',
|
|
Data => \%Param,
|
|
);
|
|
|
|
# get page footer
|
|
$Output .= $LayoutObject->CustomerFooter();
|
|
|
|
return $Output;
|
|
}
|
|
|
|
1;
|