This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions

View File

@@ -0,0 +1,431 @@
# --
# 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::Stats::Dynamic::ITSMChangeManagement;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::System::DateTime',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::ITSMChange',
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# get the dynamic fields for change object
$Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => ['ITSMCHange'],
);
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMChangeManagement';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get change state list
my $ChangeStates = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleStatesGet(
UserID => 1,
);
my %ChangeStateList = map { $_->{Key} => $_->{Value} } @{$ChangeStates};
# get cip lists
my $Categories = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleCIPGet(
Type => 'Category',
UserID => 1,
);
my %CategoryList = map { $_->{Key} => $_->{Value} } @{$Categories};
my $Impacts = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleCIPGet(
Type => 'Impact',
UserID => 1,
);
my %ImpactList = map { $_->{Key} => $_->{Value} } @{$Impacts};
my $Priorities = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleCIPGet(
Type => 'Priority',
UserID => 1,
);
my %PriorityList = map { $_->{Key} => $_->{Value} } @{$Priorities};
# get current time to fix bug#4870
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'Change State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ChangeStateIDs',
Block => 'MultiSelectField',
Values => \%ChangeStateList,
},
{
Name => 'Category',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CategoryIDs',
Block => 'MultiSelectField',
Values => \%CategoryList,
},
{
Name => 'Priority',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'PriorityIDs',
Block => 'MultiSelectField',
Values => \%PriorityList,
},
{
Name => 'Impact',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ImpactIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%ImpactList,
},
{
Name => 'Timeperiod',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'TimePeriod',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'CreateTimeNewerDate',
TimeStop => 'CreateTimeOlderDate',
},
},
);
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# cycle trough the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
# skip all fields not designed to be supported by statistics
my $IsStatsCondition = $DynamicFieldBackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsStatsCondition',
);
next DYNAMICFIELD if !$IsStatsCondition;
my $PossibleValuesFilter;
# get field html
my $DynamicFieldStatsParameter = $DynamicFieldBackendObject->StatsFieldParameterBuild(
DynamicFieldConfig => $DynamicFieldConfig,
PossibleValuesFilter => $PossibleValuesFilter,
);
if ( IsHashRefWithData($DynamicFieldStatsParameter) ) {
# backward compatibility
if ( !$DynamicFieldStatsParameter->{Block} ) {
$DynamicFieldStatsParameter->{Block} = 'InputField';
if ( IsHashRefWithData( $DynamicFieldStatsParameter->{Values} ) ) {
$DynamicFieldStatsParameter->{Block} = 'MultiSelectField';
}
}
if ( $DynamicFieldStatsParameter->{Block} eq 'Time' ) {
# create object attributes (date/time fields)
my $TimePeriodFormat = $DynamicFieldStatsParameter->{TimePeriodFormat} || 'DateInputFormatLong';
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
TimePeriodFormat => $TimePeriodFormat,
Block => $DynamicFieldStatsParameter->{Block},
TimePeriodFormat => $TimePeriodFormat,
Values => {
TimeStart =>
$DynamicFieldStatsParameter->{Element}
. '_GreaterThanEquals',
TimeStop =>
$DynamicFieldStatsParameter->{Element}
. '_SmallerThanEquals',
},
);
push @ObjectAttributes, \%ObjectAttribute;
}
elsif ( $DynamicFieldStatsParameter->{Block} eq 'MultiSelectField' ) {
# create object attributes (multiple values)
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
Block => $DynamicFieldStatsParameter->{Block},
Values => $DynamicFieldStatsParameter->{Values},
Translation => 0,
IsDynamicField => 1,
ShowAsTree => $DynamicFieldConfig->{Config}->{TreeView} || 0,
);
push @ObjectAttributes, \%ObjectAttribute;
}
else {
# create object attributes (text fields)
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
Block => $DynamicFieldStatsParameter->{Block},
);
push @ObjectAttributes, \%ObjectAttribute;
}
}
}
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
for my $ParameterName ( sort keys %Param ) {
if (
$ParameterName =~ m{ \A DynamicField_ ( [a-zA-Z\d]+ ) (?: _ ( [a-zA-Z\d]+ ) )? \z }xms
)
{
my $FieldName = $1;
my $Operator = $2;
# loop over the dynamic fields configured
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
next DYNAMICFIELD if !$DynamicFieldConfig->{Name};
# skip all fields that do not match with current field name
# without the 'DynamicField_' prefix
next DYNAMICFIELD if $DynamicFieldConfig->{Name} ne $FieldName;
# skip all fields not designed to be supported by statistics
my $IsStatsCondition = $DynamicFieldBackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsStatsCondition',
);
next DYNAMICFIELD if !$IsStatsCondition;
# get new search parameter
my $DynamicFieldStatsSearchParameter = $DynamicFieldBackendObject->StatsSearchFieldParameterBuild(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Param{$ParameterName},
Operator => $Operator,
);
# add new search parameter
if ( !IsHashRefWithData( $Param{"DynamicField_$FieldName"} ) ) {
$Param{"DynamicField_$FieldName"} =
$DynamicFieldStatsSearchParameter;
}
# extend search parameter
elsif ( IsHashRefWithData($DynamicFieldStatsSearchParameter) ) {
$Param{"DynamicField_$FieldName"} = {
%{ $Param{"DynamicField_$FieldName"} },
%{$DynamicFieldStatsSearchParameter},
};
}
}
}
}
# search changes
my $Count = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeSearch(
UserID => 1,
Result => 'COUNT',
Permission => 'ro',
Limit => 100_000_000,
%Param,
);
return $Count;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
# wrap ids to used spelling
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element;
next ELEMENT if !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'ChangeStateIDs' ) {
my $StateList = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleStatesGet( UserID => 1 );
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
STATE:
for my $State ( @{$StateList} ) {
next STATE if $ID->{Content} ne $State->{Key};
$ID->{Content} = $State->{Value};
}
}
}
elsif (
$ElementName eq 'CategoryIDs' || $ElementName eq 'ImpactIDs'
|| $ElementName eq 'PriorityIDs'
)
{
my ($Type) = $ElementName =~ m{ \A (.*?) IDs \z }xms;
my $CIPList = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleCIPGet(
Type => $Type,
UserID => 1,
);
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
ELEMENT:
for my $Element ( @{$CIPList} ) {
next ELEMENT if $ID->{Content} ne $Element->{Key};
$ID->{Content} = $Element->{Value};
}
}
}
}
}
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
# wrap used spelling to ids
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element;
next ELEMENT if !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'ChangeStateIDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my $ChangeStateID = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeStateLookup(
ChangeState => $ID->{Content},
Cache => 1,
);
if ($ChangeStateID) {
$ID->{Content} = $ChangeStateID;
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Import: Can't find state $ID->{Content}!"
);
$ID = undef;
}
}
}
# import wrapper for CIP
for my $Type (qw(Category Impact Priority)) {
if ( $ElementName eq $Type . 'IDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my $CIPID = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeCIPLookup(
CIP => $ID->{Content},
Type => $Type,
);
if ($CIPID) {
$ID->{Content} = $CIPID;
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Import: Can't find $Type $ID->{Content}!"
);
$ID = undef;
}
}
}
}
}
}
return \%Param;
}
1;

View File

@@ -0,0 +1,197 @@
# --
# 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::Stats::Dynamic::ITSMChangeManagementChangesIncidents;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::DateTime',
'Kernel::System::ITSMChange',
'Kernel::System::Ticket',
'Kernel::System::Type',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMChangeManagementChangesIncidents';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get list of ticket types
my %Objects = $Kernel::OM->Get('Kernel::System::Type')->TypeList(
Valid => 1,
);
$Objects{'-1'} = 'Changes';
# get current time to fix bug#4870
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'Objects',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'Object',
Block => 'MultiSelectField',
Values => \%Objects,
SelectedValues => [ keys %Objects ],
},
{
Name => 'Timeperiod',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'TimePeriod',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'CreateTimeNewerDate',
TimeStop => 'CreateTimeOlderDate',
},
},
);
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# delete CreateTimeNewerData as we want to get *ALL* existing objects
delete $Param{CreateTimeNewerDate};
# for tickets the search option is "TicketCreateTimeOlderDate"
$Param{TicketCreateTimeOlderDate} = $Param{CreateTimeOlderDate};
# if this cell should be filled with number of changes
if ( $Param{Object}->[0] == -1 ) {
return $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeSearch(
UserID => 1,
Result => 'COUNT',
Permission => 'ro',
Limit => 100_000_000,
%Param,
);
}
# if this cell should be filled with number of tickets
else {
return $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
UserID => 1,
Result => 'COUNT',
Permission => 'ro',
Limit => 100_000_000,
TypeIDs => [ $Param{Object}->[0] ],
%Param,
);
}
return;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
# get list of ticket types
my %Objects = $Kernel::OM->Get('Kernel::System::Type')->TypeList( Valid => 1 );
$Objects{'-1'} = 'Changes';
# wrap ids to used spelling
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element;
next ELEMENT if !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'Object' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
$ID->{Content} = $Objects{ $ID->{Content} };
}
}
}
}
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
# get list of ticket types
my %Objects = $Kernel::OM->Get('Kernel::System::Type')->TypeList( Valid => 1 );
$Objects{'-1'} = 'Changes';
# wrap used spelling to ids
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element;
next ELEMENT if !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'Object' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
for my $Key ( sort keys %Objects ) {
$ID->{Content} = $Key if $Objects{$Key} eq $ID->{Content};
}
}
}
}
}
return \%Param;
}
1;

View File

@@ -0,0 +1,368 @@
# --
# 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::Stats::Dynamic::ITSMChangeManagementChangesPerCIClasses;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::DateTime',
'Kernel::System::DB',
'Kernel::System::GeneralCatalog',
'Kernel::System::ITSMChange',
'Kernel::System::LinkObject',
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMChangeManagementChangesPerCIClasses';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get cip lists
my $Categories = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleCIPGet(
Type => 'Category',
UserID => 1,
);
my %CategoryList = map { $_->{Key} => $_->{Value} } @{$Categories};
# get class list
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
# get deployment state list
my $DeplStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::DeploymentState',
);
# get incident state list
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::Core::IncidentState',
);
# get current time to fix bug#4870
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'ConfigItem Classes',
UseAsXvalue => 0,
UseAsValueSeries => 1,
UseAsRestriction => 0,
Element => 'CIClassIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => $ClassList,
},
{
Name => 'Category',
UseAsXvalue => 1,
UseAsValueSeries => 0,
UseAsRestriction => 0,
Element => 'CategoryIDs',
Block => 'MultiSelectField',
Values => \%CategoryList,
},
{
Name => 'ConfigItem Status',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'CIStateIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => $InciStateList,
},
{
Name => 'Timeperiod',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'TimePeriod',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'CreateTimeNewerDate',
TimeStop => 'CreateTimeOlderDate',
},
},
);
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# get object ids for change and config item
my $ConfigItemObjectID = $Kernel::OM->Get('Kernel::System::LinkObject')->ObjectLookup(
Name => 'ITSMConfigItem',
UserID => 1,
);
return if !$ConfigItemObjectID;
my $ChangeObjectID = $Kernel::OM->Get('Kernel::System::LinkObject')->ObjectLookup(
Name => 'ITSMWorkOrder',
UserID => 1,
);
return if !$ChangeObjectID;
# get change id and config item id
return if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT chi.id AS change_id, ci.id AS ci_id '
. 'FROM change_item chi, change_workorder chw, link_relation lr, configitem ci '
. 'WHERE chi.id = chw.change_id '
. 'AND chi.category_id = ? '
. 'AND ( '
. '( '
. 'chw.id = lr.target_key '
. 'AND lr.target_object_id = ? '
. 'AND lr.source_object_id = ? '
. 'AND lr.source_key = ci.id '
. 'AND ci.class_id = ? '
. ') '
. 'OR '
. '( '
. 'chw.id = lr.source_key '
. 'AND lr.source_object_id = ? '
. 'AND lr.target_object_id = ? '
. 'AND lr.target_key = ci.id '
. 'AND ci.class_id = ? '
. ') '
. ')',
Bind => [
\( $Param{CategoryIDs}->[0] ),
\$ChangeObjectID,
\$ConfigItemObjectID,
\( $Param{CIClassIDs}->[0] ),
\$ChangeObjectID,
\$ConfigItemObjectID,
\( $Param{CIClassIDs}->[0] ),
],
);
# fetch change and config item ids
my @Matches;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
push @Matches, \@Row;
}
# check for each change if the config item is in appropriate status
# if so, count the change
my %ChangesAlreadyCounted;
MATCH:
for my $Match (@Matches) {
next MATCH if $ChangesAlreadyCounted{ $Match->[0] };
# get current state of the config item
next MATCH if !$Kernel::OM->Get('Kernel::System::DB')->Prepare(
SQL => 'SELECT inci_state_id FROM configitem_version '
. 'WHERE configitem_id = ? '
. 'AND create_time >= ? AND create_time <= ?',
Bind => [
\( $Match->[1] ),
\( $Param{CreateTimeNewerDate} ),
\( $Param{CreateTimeOlderDate} ),
],
Limit => 1,
);
# fetch current incident state
my $IncidentStateID;
while ( my @Row = $Kernel::OM->Get('Kernel::System::DB')->FetchrowArray() ) {
$IncidentStateID = $Row[0];
}
next MATCH if !$IncidentStateID;
# check if user has selected this state
my ($Found) = grep { $_ == $IncidentStateID } @{ $Param{CIStateIDs} };
next MATCH if !$Found;
$ChangesAlreadyCounted{ $Match->[0] }++;
}
# return the number of changes
my $Count = keys %ChangesAlreadyCounted;
return $Count;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
# get class list
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
# get incident state list
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::Core::IncidentState',
);
# wrap ids to used spelling
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element || !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'CIStateIDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
$ID->{Content} = $InciStateList->{ $ID->{Content} };
}
}
elsif ( $ElementName eq 'CIClassIDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
$ID->{Content} = $ClassList->{ $ID->{Content} };
}
}
elsif ( $ElementName eq 'CategoryIDs' ) {
my $CIPList = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleCIPGet(
Type => 'Category',
UserID => 1,
);
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
ELEMENT:
for my $Element ( @{$CIPList} ) {
next ELEMENT if $ID->{Content} ne $Element->{Key};
$ID->{Content} = $Element->{Value};
}
}
}
}
}
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
# get class list
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
# get incident state list
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::Core::IncidentState',
);
# wrap used spelling to ids
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element || !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'CIStateIDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
KEY:
for my $Key ( sort keys %{$InciStateList} ) {
if ( $ID->{Content} eq $InciStateList->{$Key} ) {
$ID->{Content} = $Key;
last KEY;
}
}
}
}
elsif ( $ElementName eq 'CIClassIDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
for my $Key ( sort keys %{$ClassList} ) {
if ( $ID->{Content} eq $ClassList->{$Key} ) {
$ID->{Content} = $Key;
}
}
}
}
# import wrapper for CIP
for my $Type (qw(Category)) {
if ( $ElementName eq $Type . 'IDs' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my $CIPID = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeCIPLookup(
CIP => $ID->{Content},
Type => $Type,
);
if ($CIPID) {
$ID->{Content} = $CIPID;
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Import: Can' find $Type $ID->{Content}!"
);
$ID = undef;
}
}
}
}
}
}
return \%Param;
}
1;

View File

@@ -0,0 +1,191 @@
# --
# 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::Stats::Dynamic::ITSMChangeManagementHistory;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::DateTime',
'Kernel::System::ITSMChange',
'Kernel::System::ITSMChange::History',
'Kernel::System::Log',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMChangeManagementHistory';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get change state list
my $ChangeStates = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleStatesGet(
UserID => 1,
);
my %ChangeStateList = map { $_->{Key} => $_->{Value} } @{$ChangeStates};
# get current time to fix bug#4870
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'Change State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'NewValues',
Block => 'MultiSelectField',
Values => \%ChangeStateList,
},
{
Name => 'Timeperiod',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'TimePeriod',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'ChangeTimeNewerDate',
TimeStop => 'ChangeTimeOlderDate',
},
},
);
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# search history
my $IDs = $Kernel::OM->Get('Kernel::System::ITSMChange::History')->HistorySearch(
UserID => 1,
Type => 'Change',
Attribute => 'ChangeStateID',
Limit => 100_000_000,
%Param,
);
my @ChangeNumbers;
if ( $IDs && ref $IDs eq 'ARRAY' ) {
ID:
for my $ID ( @{$IDs} ) {
my $Change = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeGet(
ChangeID => $ID,
UserID => 1,
);
next ID if !$Change;
push @ChangeNumbers, $Change->{ChangeNumber};
}
}
return join "\n", @ChangeNumbers;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
# wrap ids to used spelling
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element || !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'NewValues' ) {
my $StateList = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangePossibleStatesGet( UserID => 1 );
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
STATE:
for my $State ( @{$StateList} ) {
next STATE if $ID->{Content} ne $State->{Key};
$ID->{Content} = $State->{Value};
}
}
}
}
}
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
# wrap used spelling to ids
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element || !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'NewValues' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my $ChangeStateID = $Kernel::OM->Get('Kernel::System::ITSMChange')->ChangeStateLookup(
ChangeState => $ID->{Content},
Cache => 1,
);
if ($ChangeStateID) {
$ID->{Content} = $ChangeStateID;
}
else {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Import: Can' find state $ID->{Content}!"
);
$ID = undef;
}
}
}
}
}
return \%Param;
}
1;

View File

@@ -0,0 +1,199 @@
# --
# 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::Stats::Dynamic::ITSMChangeManagementRfcRequester;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::CustomerUser',
'Kernel::System::DateTime',
'Kernel::System::Ticket',
'Kernel::System::User',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMChangeManagementRfcRequester';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
my $RfCTypes = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::AddChangeLinkTicketTypes');
# get all rfcs
my @TicketIDs = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
UserID => 1,
Permission => 'ro',
Limit => 100_000_000,
Types => $RfCTypes,
Result => 'ARRAY',
);
# get all requester
my %Requester;
TICKETID:
for my $TicketID (@TicketIDs) {
my %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $TicketID,
UserID => 1,
);
next TICKETID if !%Ticket;
if ( $Ticket{CustomerUserID} ) {
my $CustomerUserID = $Ticket{CustomerUserID};
next TICKETID if $Requester{"customer_$CustomerUserID"};
my %CustomerUser = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $CustomerUserID,
);
$Requester{"customer_$CustomerUserID"} = $CustomerUser{UserFullname};
}
else {
my $OwnerID = $Ticket{OwnerID};
next TICKETID if $Requester{"agent_$OwnerID"};
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
UserID => $OwnerID,
);
$Requester{"agent_$OwnerID"} = $User{UserFullname};
}
}
# get current time to fix bug#4870
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'Requester',
UseAsXvalue => 0,
UseAsValueSeries => 1,
UseAsRestriction => 0,
SelectedValues => [ keys %Requester ],
Element => 'Requester',
Block => 'MultiSelectField',
Values => \%Requester,
},
{
Name => 'Timeperiod',
UseAsXvalue => 1,
UseAsValueSeries => 0,
UseAsRestriction => 0,
Element => 'TimePeriod',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'TicketCreateTimeNewerDate',
TimeStop => 'TicketCreateTimeOlderDate',
},
},
);
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# check what type of requester we have
# ticket search criteria differ for agents and customers
my $Type = '';
my $ID;
if ( $Param{Requester} && $Param{Requester}->[0] ) {
( $Type, $ID ) = split /_/, $Param{Requester}->[0];
my $Key = $Type eq 'agent' ? 'OwnerIDs' : 'CustomerUserLogin';
$Param{$Key} = [$ID];
}
# get ticket types that are handled as RfCs
my $RfCTypes = $Kernel::OM->Get('Kernel::Config')->Get('ITSMChange::AddChangeLinkTicketTypes');
# search tickets
my @TicketIDs = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
UserID => 1,
Result => 'ARRAY',
Permission => 'ro',
Limit => 100_000_000,
Types => $RfCTypes,
%Param,
);
# if this wants the number of RfCs for an agent we have to check
# whether a customer id is in the ticket. The owner is *NOT* the
# requester then.
if ( $Type eq 'agent' ) {
my $Count = 0;
TICKETID:
for my $TicketID (@TicketIDs) {
my %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet(
TicketID => $TicketID,
);
next TICKETID if !%Ticket;
next TICKETID if $Ticket{CustomerUserID};
$Count++;
}
return $Count;
}
return scalar @TicketIDs;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
1;

View File

@@ -0,0 +1,320 @@
# --
# 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::Stats::Dynamic::ITSMConfigItem;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::System::GeneralCatalog',
'Kernel::System::ITSMConfigItem',
'Kernel::System::DateTime',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectName {
my $Self = shift;
return 'ITSMConfigItem';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get class list
my $ClassList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::Class',
);
# get deployment state list
my $DeplStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::ConfigItem::DeploymentState',
);
# get incident state list
my $InciStateList = $Kernel::OM->Get('Kernel::System::GeneralCatalog')->ItemList(
Class => 'ITSM::Core::IncidentState',
);
# get current time to fix bug#3830
my $TimeStamp = $Kernel::OM->Create('Kernel::System::DateTime')->ToString();
# create object attribute array
my @ObjectAttributes = (
{
Name => 'Class',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ClassIDs',
Block => 'MultiSelectField',
LanguageTranslation => 0,
Values => $ClassList,
},
{
Name => 'Deployment State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'DeplStateIDs',
Block => 'MultiSelectField',
Values => $DeplStateList,
},
{
Name => 'Incident State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'InciStateIDs',
Block => 'MultiSelectField',
Values => $InciStateList,
},
{
Name => 'Number',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Number',
Block => 'InputField',
},
{
Name => 'Name',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Name',
Block => 'InputField',
},
{
Name => 'Create Time',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreateTime',
TimePeriodFormat => 'DateInputFormat',
Block => 'Time',
Values => {
TimeStart => 'ConfigItemCreateTimeNewerDate',
TimeStop => 'ConfigItemCreateTimeOlderDate',
},
},
{
Name => 'Change Time',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ChangeTime',
TimePeriodFormat => 'DateInputFormat',
Block => 'Time',
TimeStop => $TimeStamp,
Values => {
TimeStart => 'ConfigItemChangeTimeNewerDate',
TimeStop => 'ConfigItemChangeTimeOlderDate',
},
},
);
# add the xml data
CLASSID:
for my $ClassID ( sort keys %{$ClassList} ) {
# get xml definition hash
my $XMLDefinition = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->DefinitionGet(
ClassID => $ClassID,
);
next CLASSID if !$XMLDefinition->{DefinitionID};
$Self->_XMLAttributeAdd(
ObjectAttributes => \@ObjectAttributes,
XMLDefinition => $XMLDefinition->{DefinitionRef},
Prefix => 'XML::' . $ClassID,
PrefixName => $ClassList->{$ClassID},
);
}
return @ObjectAttributes;
}
sub _XMLAttributeAdd {
my ( $Self, %Param ) = @_;
return if !$Param{ObjectAttributes};
return if !$Param{XMLDefinition};
return if ref $Param{XMLDefinition} ne 'ARRAY';
if ( $Param{Prefix} ) {
$Param{Prefix} .= '::';
}
if ( $Param{PrefixName} ) {
$Param{PrefixName} .= '::';
}
$Param{Level} ||= 0;
$Param{Prefix} ||= '';
$Param{PrefixName} ||= '';
ITEM:
for my $Item ( @{ $Param{XMLDefinition} } ) {
next ITEM if !$Item->{Searchable} && !$Item->{Sub};
# create key and name
my $Key = $Param{Prefix} . $Item->{Key};
my $Name = $Param{PrefixName} . $Item->{Name};
# add attribute
my $Attribute = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->XMLStatsAttributeCreate(
Key => $Key,
Item => $Item,
Name => $Name,
);
next ITEM if !$Attribute;
next ITEM if ref $Attribute ne 'ARRAY';
next ITEM if !scalar @{$Attribute};
# add attributes to object array
push @{ $Param{ObjectAttributes} }, @{$Attribute};
next ITEM if !$Item->{Sub};
# start recursion, if "Sub" was found
$Self->_XMLAttributeAdd(
ObjectAttributes => $Param{ObjectAttributes},
XMLDefinition => $Item->{Sub},
Level => $Param{Level} + 1,
Prefix => $Key,
PrefixName => $Name,
);
}
return 1;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return ( int rand 500 ) + 1;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# set limit
$Param{Limit} = 1_000_000;
# extract all xml param keys from the param hash
my @XMLParams = grep { $_ =~ m{\A XML::}xms } sort keys %Param;
if (@XMLParams) {
return 'You must define a class in one axis.' if !$Param{ClassIDs};
return 'You must define a class in one axis.' if ref $Param{ClassIDs} ne 'ARRAY';
}
# to collect date fields separately
my %XMLParamsDateFields;
my %XMLClassIDs;
PARAMKEY:
for my $ParamKey (@XMLParams) {
# extract search values
my $SearchValues = $Param{$ParamKey};
# prepare param value
if ( !ref $SearchValues ) {
$SearchValues = [$SearchValues];
}
next PARAMKEY if !@{$SearchValues};
# split param key
my ( $ClassID, $SearchKey ) = $ParamKey =~ m{ \A XML:: ( \d+ ) :: (.+) \z }xms;
# prepare search key
$SearchKey =~ s[ :: ]['}[%]{']xmsg;
# Add class id to xml class id hash
$XMLClassIDs{$ClassID} = 1;
# collect date fields separately (e.g WarrantyExpirationDateNewerDate)
if ( $SearchKey =~ m{ (.+) ( NewerDate | OlderDate ) \z }xms ) {
$XMLParamsDateFields{$1}->{$2} = $SearchValues->[0];
next PARAMKEY;
}
# create search hash
my $SearchHash = {
'[1]{\'Version\'}[1]{\'' . $SearchKey . '\'}[%]{\'Content\'}' => $SearchValues,
};
push @{ $Param{What} }, $SearchHash;
}
# build search hash for date fields
for my $DateFieldName ( sort keys %XMLParamsDateFields ) {
my $SearchHash = {
'[1]{\'Version\'}[1]{\'' . $DateFieldName . '\'}[%]{\'Content\'}' => {
'-between' => [
$XMLParamsDateFields{$DateFieldName}->{NewerDate},
$XMLParamsDateFields{$DateFieldName}->{OlderDate},
],
},
};
push @{ $Param{What} }, $SearchHash;
}
if (%XMLClassIDs) {
my @Exists = grep { $XMLClassIDs{$_} } @{ $Param{ClassIDs} };
return 0 if !@Exists;
}
# start config item extended search
my $ConfigItemIDs = $Kernel::OM->Get('Kernel::System::ITSMConfigItem')->ConfigItemSearchExtended(%Param);
return scalar @{$ConfigItemIDs};
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
1;

View File

@@ -0,0 +1,503 @@
# --
# 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::Stats::Dynamic::ITSMTicketFirstLevelSolutionRate;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DateTime',
'Kernel::System::DB',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Priority',
'Kernel::System::Queue',
'Kernel::System::SLA',
'Kernel::System::Service',
'Kernel::System::State',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
'Kernel::System::Type',
'Kernel::System::User',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{DBSlaveObject} = $Param{DBSlaveObject} || $Kernel::OM->Get('Kernel::System::DB');
# get the dynamic fields for ticket object
$Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
);
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMTicketFirstLevelSolutionRate';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get user list
my %UserList = $Kernel::OM->Get('Kernel::System::User')->UserList(
Type => 'Long',
Valid => 0,
);
# get state list
my %StateList = $Kernel::OM->Get('Kernel::System::State')->StateGetStatesByType(
StateType => ['closed'],
Result => 'HASH',
UserID => 1,
);
# get queue list
my %QueueList = $Kernel::OM->Get('Kernel::System::Queue')->GetAllQueues();
# get priority list
my %PriorityList = $Kernel::OM->Get('Kernel::System::Priority')->PriorityList(
UserID => 1,
);
# get current time to fix bug#3830
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'Queue',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'QueueIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%QueueList,
},
{
Name => 'State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'StateIDs',
Block => 'MultiSelectField',
Values => \%StateList,
},
{
Name => 'Priority',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'PriorityIDs',
Block => 'MultiSelectField',
Values => \%PriorityList,
},
{
Name => 'Created in Queue',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedQueueIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%QueueList,
},
{
Name => 'Created Priority',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedPriorityIDs',
Block => 'MultiSelectField',
Values => \%PriorityList,
},
{
Name => 'Created State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedStateIDs',
Block => 'MultiSelectField',
Values => \%StateList,
},
{
Name => 'Title',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Title',
Block => 'InputField',
},
{
Name => 'CustomerUserLogin',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'CustomerUserLogin',
Block => 'InputField',
},
{
Name => 'From',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'From',
Block => 'InputField',
},
{
Name => 'To',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'To',
Block => 'InputField',
},
{
Name => 'Cc',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Cc',
Block => 'InputField',
},
{
Name => 'Subject',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Subject',
Block => 'InputField',
},
{
Name => 'Text',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Body',
Block => 'InputField',
},
{
Name => 'Create Time',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreateTime',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'TicketCreateTimeNewerDate',
TimeStop => 'TicketCreateTimeOlderDate',
},
},
);
if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service') ) {
# get service list
my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList(
UserID => 1,
);
# get sla list
my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList(
UserID => 1,
);
my @ObjectAttributeAdd = (
{
Name => 'Service',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ServiceIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%Service,
},
{
Name => 'SLA',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'SLAIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%SLA,
},
);
unshift @ObjectAttributes, @ObjectAttributeAdd;
}
if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Type') ) {
# get ticket type list
my %Type = $Kernel::OM->Get('Kernel::System::Type')->TypeList(
UserID => 1,
);
my %ObjectAttribute1 = (
Name => 'Type',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'TypeIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%Type,
);
unshift @ObjectAttributes, \%ObjectAttribute1;
}
if ( $Kernel::OM->Get('Kernel::Config')->Get('Stats::UseAgentElementInStats') ) {
my @ObjectAttributeAdd = (
{
Name => 'Agent/Owner',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'OwnerIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
{
Name => 'Created by Agent/Owner',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedUserIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
{
Name => 'Responsible',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ResponsibleIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
);
push @ObjectAttributes, @ObjectAttributeAdd;
}
if ( $Kernel::OM->Get('Kernel::Config')->Get('Stats::CustomerIDAsMultiSelect') ) {
# Get CustomerID
# (This way also can be the solution for the CustomerUserID)
$Self->{DBSlaveObject}->Prepare(
SQL => 'SELECT DISTINCT customer_id FROM ticket',
);
# fetch the result
my %CustomerID;
while ( my @Row = $Self->{DBSlaveObject}->FetchrowArray() ) {
if ( $Row[0] ) {
$CustomerID{ $Row[0] } = $Row[0];
}
}
my %ObjectAttribute = (
Name => 'CustomerID',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CustomerID',
Block => 'MultiSelectField',
Values => \%CustomerID,
);
push @ObjectAttributes, \%ObjectAttribute;
}
else {
my %ObjectAttribute = (
Name => 'CustomerID',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'CustomerID',
Block => 'InputField',
);
push @ObjectAttributes, \%ObjectAttribute;
}
# cycle trough the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $PossibleValuesFilter;
# set possible values filter from ACLs
my $ACL = $Kernel::OM->Get('Kernel::System::Ticket')->TicketAcl(
Action => 'AgentStats',
Type => 'DynamicField_' . $DynamicFieldConfig->{Name},
ReturnType => 'Ticket',
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
Data => $DynamicFieldConfig->{Config}->{PossibleValues} || {},
UserID => 1,
);
if ($ACL) {
my %Filter = $Kernel::OM->Get('Kernel::System::Ticket')->TicketAclData();
$PossibleValuesFilter = \%Filter;
}
# get field html
my $DynamicFieldStatsParameter
= $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->StatsFieldParameterBuild(
DynamicFieldConfig => $DynamicFieldConfig,
PossibleValuesFilter => $PossibleValuesFilter,
);
if ( IsHashRefWithData($DynamicFieldStatsParameter) ) {
if ( IsHashRefWithData( $DynamicFieldStatsParameter->{Values} ) ) {
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
Block => 'MultiSelectField',
Values => $DynamicFieldStatsParameter->{Values},
Translation => 0,
);
push @ObjectAttributes, \%ObjectAttribute;
}
else {
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
Block => 'InputField',
);
push @ObjectAttributes, \%ObjectAttribute;
}
}
}
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# use all closed stats if no states are given
if ( !$Param{StateIDs} ) {
$Param{StateType} = ['closed'];
}
# start ticket search
my @TicketSearchIDs = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
%Param,
Result => 'ARRAY',
Limit => 100_000_000,
UserID => 1,
Permission => 'ro',
);
return 0 if !@TicketSearchIDs;
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $FirstLevelSolutionTickets = 0;
TICKETID:
for my $TicketID (@TicketSearchIDs) {
my @Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
IsVisibleForCustomer => 1,
);
next TICKETID if !@Articles;
next TICKETID if scalar @Articles > 2;
# get sender type of first article
my $SenderTypeFirstArticle = $ArticleObject->ArticleSenderTypeLookup(
SenderTypeID => $Articles[0]->{SenderTypeID},
);
next TICKETID if $SenderTypeFirstArticle eq 'system';
# if the ticket could be solved within the first contact
if ( !$Articles[1] ) {
$FirstLevelSolutionTickets++;
next TICKETID;
}
# noe we handle the case where the first article is from the customer
next TICKETID if $SenderTypeFirstArticle ne 'customer';
# get sender type of second article
my $SenderTypeSecondArticle = $ArticleObject->ArticleSenderTypeLookup(
SenderTypeID => $Articles[1]->{SenderTypeID},
);
# the scond article is from the agent
next TICKETID if $SenderTypeSecondArticle ne 'agent';
$FirstLevelSolutionTickets++;
}
return $FirstLevelSolutionTickets;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
1;

View File

@@ -0,0 +1,684 @@
# --
# 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::Stats::Dynamic::ITSMTicketSolutionTimeAverage;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DateTime',
'Kernel::System::DB',
'Kernel::System::DynamicField',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Priority',
'Kernel::System::Queue',
'Kernel::System::SLA',
'Kernel::System::Service',
'Kernel::System::State',
'Kernel::System::Ticket',
'Kernel::System::Type',
'Kernel::System::User',
'Kernel::System::Stats',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{DBSlaveObject} = $Param{DBSlaveObject} || $Kernel::OM->Get('Kernel::System::DB');
# get the dynamic fields for ticket object
$Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
);
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'ITSMTicketSolutionTimeAverage';
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
# get user list
my %UserList = $Kernel::OM->Get('Kernel::System::User')->UserList(
Type => 'Long',
Valid => 0,
);
# get state list
my %StateList = $Kernel::OM->Get('Kernel::System::State')->StateGetStatesByType(
StateType => ['closed'],
Result => 'HASH',
UserID => 1,
);
# get queue list
my %QueueList = $Kernel::OM->Get('Kernel::System::Queue')->GetAllQueues();
# get priority list
my %PriorityList = $Kernel::OM->Get('Kernel::System::Priority')->PriorityList(
UserID => 1,
);
# get current time to fix bug#3830
my $Today = $Kernel::OM->Create('Kernel::System::DateTime')->Format( Format => '%Y-%m-%d 23:59:59' );
my @ObjectAttributes = (
{
Name => 'Queue',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'QueueIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%QueueList,
},
{
Name => 'State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'StateIDs',
Block => 'MultiSelectField',
Values => \%StateList,
},
{
Name => 'Priority',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'PriorityIDs',
Block => 'MultiSelectField',
Values => \%PriorityList,
},
{
Name => 'Created in Queue',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedQueueIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%QueueList,
},
{
Name => 'Created Priority',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedPriorityIDs',
Block => 'MultiSelectField',
Values => \%PriorityList,
},
{
Name => 'Created State',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedStateIDs',
Block => 'MultiSelectField',
Values => \%StateList,
},
{
Name => 'Title',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Title',
Block => 'InputField',
},
{
Name => 'CustomerUserLogin',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'CustomerUserLogin',
Block => 'InputField',
},
{
Name => 'From',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'From',
Block => 'InputField',
},
{
Name => 'To',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'To',
Block => 'InputField',
},
{
Name => 'Cc',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Cc',
Block => 'InputField',
},
{
Name => 'Subject',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Subject',
Block => 'InputField',
},
{
Name => 'Text',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Body',
Block => 'InputField',
},
{
Name => 'Create Time',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreateTime',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
TimeStop => $Today,
Values => {
TimeStart => 'TicketCreateTimeNewerDate',
TimeStop => 'TicketCreateTimeOlderDate',
},
},
);
if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service') ) {
# get service list
my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList(
UserID => 1,
);
# get sla list
my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList(
UserID => 1,
);
my @ObjectAttributeAdd = (
{
Name => 'Service',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ServiceIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%Service,
},
{
Name => 'SLA',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'SLAIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%SLA,
},
);
unshift @ObjectAttributes, @ObjectAttributeAdd;
}
if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Type') ) {
# get ticket type list
my %Type = $Kernel::OM->Get('Kernel::System::Type')->TypeList(
UserID => 1,
);
my %ObjectAttribute1 = (
Name => 'Type',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'TypeIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%Type,
);
unshift @ObjectAttributes, \%ObjectAttribute1;
}
if ( $Kernel::OM->Get('Kernel::Config')->Get('Stats::UseAgentElementInStats') ) {
my @ObjectAttributeAdd = (
{
Name => 'Agent/Owner',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'OwnerIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
{
Name => 'Created by Agent/Owner',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CreatedUserIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
{
Name => 'Responsible',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'ResponsibleIDs',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
);
push @ObjectAttributes, @ObjectAttributeAdd;
}
if ( $Kernel::OM->Get('Kernel::Config')->Get('Stats::CustomerIDAsMultiSelect') ) {
# Get CustomerID
# (This way also can be the solution for the CustomerUserID)
$Self->{DBSlaveObject}->Prepare(
SQL => 'SELECT DISTINCT customer_id FROM ticket',
);
# fetch the result
my %CustomerID;
while ( my @Row = $Self->{DBSlaveObject}->FetchrowArray() ) {
if ( $Row[0] ) {
$CustomerID{ $Row[0] } = $Row[0];
}
}
my %ObjectAttribute = (
Name => 'CustomerID',
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => 'CustomerID',
Block => 'MultiSelectField',
Values => \%CustomerID,
);
push @ObjectAttributes, \%ObjectAttribute;
}
else {
my %ObjectAttribute = (
Name => 'CustomerID',
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'CustomerID',
Block => 'InputField',
);
push @ObjectAttributes, \%ObjectAttribute;
}
# cycle trough the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $PossibleValuesFilter;
# set possible values filter from ACLs
my $ACL = $Kernel::OM->Get('Kernel::System::Ticket')->TicketAcl(
Action => 'AgentStats',
Type => 'DynamicField_' . $DynamicFieldConfig->{Name},
ReturnType => 'Ticket',
ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name},
Data => $DynamicFieldConfig->{Config}->{PossibleValues} || {},
UserID => 1,
);
if ($ACL) {
my %Filter = $Kernel::OM->Get('Kernel::System::Ticket')->TicketAclData();
$PossibleValuesFilter = \%Filter;
}
# get field html
my $DynamicFieldStatsParameter
= $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->StatsFieldParameterBuild(
DynamicFieldConfig => $DynamicFieldConfig,
PossibleValuesFilter => $PossibleValuesFilter,
);
if ( IsHashRefWithData($DynamicFieldStatsParameter) ) {
if ( IsHashRefWithData( $DynamicFieldStatsParameter->{Values} ) ) {
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 1,
UseAsValueSeries => 1,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
Block => 'MultiSelectField',
Values => $DynamicFieldStatsParameter->{Values},
Translation => 0,
);
push @ObjectAttributes, \%ObjectAttribute;
}
else {
my %ObjectAttribute = (
Name => $DynamicFieldStatsParameter->{Name},
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => $DynamicFieldStatsParameter->{Element},
Block => 'InputField',
);
push @ObjectAttributes, \%ObjectAttribute;
}
}
}
return @ObjectAttributes;
}
sub GetStatElementPreview {
my ( $Self, %Param ) = @_;
return int rand 50;
}
sub GetStatElement {
my ( $Self, %Param ) = @_;
# use all closed stats if no states are given
if ( !$Param{StateIDs} ) {
$Param{StateType} = ['closed'];
}
# start ticket search
my @TicketSearchIDs = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
%Param,
Result => 'ARRAY',
Limit => 100_000_000,
UserID => 1,
Permission => 'ro',
);
return '-' if !@TicketSearchIDs;
my $Time = 0;
TICKETID:
for my $TicketID (@TicketSearchIDs) {
# get ticket data
my $TicketData = $Self->_TicketDataGet(
TicketID => $TicketID,
);
return 'ERROR' if !%{$TicketData};
# get relevant ticket history
my $HistoryData = $Self->_TicketHistoryDataGet(
TicketID => $TicketID,
);
return 'ERROR' if !$HistoryData;
# if ticket is closed in the ticket create mask
if ( @{$HistoryData} == 1 ) {
$Time += ( 3 * 60 );
next TICKETID;
}
my %Timespans;
my $Counter = 0;
ENTRY:
for my $Entry ( @{$HistoryData} ) {
if ( $Timespans{$Counter} ) {
next ENTRY if $Entry->{Viewable};
# set stop time
$Timespans{$Counter}->{StopTime} = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Entry->{CreateTime},
},
)->ToEpoch();
$Counter++;
}
else {
next ENTRY if !$Entry->{Viewable};
# set start time
$Timespans{$Counter}->{StartTime} = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
String => $Entry->{CreateTime},
},
)->ToEpoch();
}
}
# get calendar
my $Calendar = $Self->_CalendarGet(
TicketData => $TicketData,
);
for my $Count ( sort keys %Timespans ) {
# extract timestamp
my $Timespan = $Timespans{$Count};
$Timespan->{StopTime} ||= $Timespan->{StartTime} + ( 3 * 60 );
my $WorkingTimeStartDateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Epoch => $Timespan->{StartTime},
},
);
my $WorkingTimeStopDateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Epoch => $Timespan->{StopTime},
},
);
my $Delta = $WorkingTimeStartDateTimeObject->Delta(
DateTimeObject => $WorkingTimeStopDateTimeObject,
ForWorkingTime => 1,
Calendar => $Calendar,
);
$Time += $Delta->{AbsoluteSeconds};
}
}
my $TicketCount = @TicketSearchIDs;
my $AverageTime = $Time / $TicketCount;
# translate seconds in a readable format
my $Value = $Kernel::OM->Get('Kernel::System::Stats')->_HumanReadableAgeGet(
Age => int $AverageTime,
);
return $Value;
}
sub _TicketDataGet {
my ( $Self, %Param ) = @_;
return {} if !$Param{TicketID};
# ask database
$Self->{DBSlaveObject}->Prepare(
SQL => 'SELECT queue_id, sla_id, create_time
FROM ticket
WHERE id = ?',
Bind => [ \$Param{TicketID} ],
Limit => 1,
);
# fetch the result
my %TicketData;
while ( my @Row = $Self->{DBSlaveObject}->FetchrowArray() ) {
$TicketData{QueueID} = $Row[0];
$TicketData{SLAID} = $Row[1];
$TicketData{CreateTime} = $Row[2];
}
return \%TicketData;
}
sub _CalendarGet {
my ( $Self, %Param ) = @_;
# get config option
$Self->{TicketServiceFeature} ||= $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service');
my %EscalationData;
if ( $Self->{TicketServiceFeature} && $Param{TicketData}->{SLAID} ) {
%EscalationData = $Kernel::OM->Get('Kernel::System::SLA')->SLAGet(
SLAID => $Param{TicketData}->{SLAID},
UserID => 1,
Cache => 1,
);
}
else {
%EscalationData = $Kernel::OM->Get('Kernel::System::Queue')->QueueGet(
ID => $Param{TicketData}->{QueueID},
UserID => 1,
Cache => 1,
);
}
return $EscalationData{Calendar} || undef;
}
sub _TicketHistoryDataGet {
my ( $Self, %Param ) = @_;
return if !$Param{TicketID};
# get id of histoy type StateUpdate
if ( !$Self->{StateUpdateID} ) {
$Self->{StateUpdateID} = $Kernel::OM->Get('Kernel::System::Ticket')->HistoryTypeLookup(
Type => 'StateUpdate',
);
}
# get id of histoy type NewTicket
if ( !$Self->{NewTicketID} ) {
$Self->{NewTicketID} = $Kernel::OM->Get('Kernel::System::Ticket')->HistoryTypeLookup(
Type => 'NewTicket',
);
}
# get viewable state ids
if ( !$Self->{ViewableStateIDs} ) {
my @ViewableStateIDs = $Kernel::OM->Get('Kernel::System::State')->StateGetStatesByType(
Type => 'Viewable',
Result => 'ID',
);
my %ViewableStateIDList;
for my $StateID (@ViewableStateIDs) {
$ViewableStateIDList{$StateID} = 1;
}
$Self->{ViewableStateIDs} = \%ViewableStateIDList;
}
# ask database
$Self->{DBSlaveObject}->Prepare(
SQL => 'SELECT state_id, create_time
FROM ticket_history
WHERE ticket_id = ?
AND history_type_id IN ( ?, ? )
ORDER BY create_time',
Bind => [
\$Param{TicketID},
\$Self->{StateUpdateID},
\$Self->{NewTicketID},
],
);
# fetch the result
my @TicketHistoryList;
while ( my @Row = $Self->{DBSlaveObject}->FetchrowArray() ) {
my %HistoryData;
$HistoryData{StateID} = $Row[0];
$HistoryData{CreateTime} = $Row[1];
push @TicketHistoryList, \%HistoryData;
}
ENTRY:
for my $Entry (@TicketHistoryList) {
$Entry->{Viewable} = 0;
next ENTRY if !$Self->{ViewableStateIDs}->{ $Entry->{StateID} };
$Entry->{Viewable} = 1;
}
return \@TicketHistoryList;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
return \%Param;
}
1;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,904 @@
# --
# 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::Stats::Dynamic::TimeAccounting;
use strict;
use warnings;
use Kernel::Language qw(Translatable);
use Kernel::System::VariableCheck qw(IsArrayRefWithData);
our @ObjectDependencies = (
'Kernel::System::Log',
'Kernel::System::DateTime',
'Kernel::System::TimeAccounting',
'Kernel::System::User',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectName {
my ( $Self, %Param ) = @_;
return 'TimeAccounting';
}
sub GetObjectAttributes {
my ( $Self, %Param ) = @_;
my $DateTimeObjectCurrent = $Kernel::OM->Create('Kernel::System::DateTime');
# set predefined start time
my $TimeStamp = $DateTimeObjectCurrent->ToEpoch();
my ($Date) = split /\s+/, $TimeStamp;
my $Today = sprintf "%s 23:59:59", $Date;
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# get project list
my %ProjectList = $TimeAccountingObject->ProjectSettingsGet(
Status => 'valid',
);
# get action list
my %ActionListSource = $TimeAccountingObject->ActionSettingsGet();
my %ActionList;
for my $Action ( sort keys %ActionListSource ) {
$ActionList{$Action} = $ActionListSource{$Action}->{Action};
}
# get user list
my %UserList = $Kernel::OM->Get('Kernel::System::User')->UserList(
Type => 'Long',
Valid => 0,
);
my @Attributes = (
{
Name => Translatable('Project'),
UseAsXvalue => 1,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Project',
Block => 'MultiSelectField',
Translation => 0,
Values => $ProjectList{Project},
},
{
Name => Translatable('User'),
UseAsXvalue => 1,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'User',
Block => 'MultiSelectField',
Translation => 0,
Values => \%UserList,
},
{
Name => Translatable('Sort sequence'),
UseAsXvalue => 0,
UseAsValueSeries => 1,
UseAsRestriction => 0,
Element => 'SortSequence',
Block => 'SelectField',
Translation => 1,
Values => {
Up => 'ascending',
Down => 'descending',
},
},
{
Name => Translatable('Task'),
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'ProjectAction',
Block => 'MultiSelectField',
Translation => 0,
Values => \%ActionList,
},
{
Name => Translatable('Period'),
UseAsXvalue => 0,
UseAsValueSeries => 0,
UseAsRestriction => 1,
Element => 'Period',
TimePeriodFormat => 'DateInputFormat', # 'DateInputFormatLong',
Block => 'Time',
Values => {
TimeStart => 'TimeAccountingPeriodStart',
TimeStop => 'TimeAccountingPeriodStop',
},
},
);
return @Attributes;
}
sub GetHeaderLine {
my ( $Self, %Param ) = @_;
my @HeaderLine = ("");
# Users as X-value
if ( $Param{XValue}->{Element} && $Param{XValue}->{Element} eq 'User' ) {
# user have been selected as x-value
my @UserIDs = @{ $Param{XValue}->{SelectedValues} };
# get user object
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
# iterate over selected users
USERID:
for my $UserID (@UserIDs) {
my $UserName = $UserObject->UserName(
UserID => $UserID,
);
push @HeaderLine, $UserName;
}
}
# Projects as X-value
else {
# projects have been selected as x-value
my @ProjectIDs = @{ $Param{XValue}->{SelectedValues} };
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# iterate over selected projects
PROJECTID:
for my $ProjectID (@ProjectIDs) {
my %ProjectData = $TimeAccountingObject->ProjectGet(
ID => $ProjectID,
);
push @HeaderLine, $ProjectData{Project};
}
}
return \@HeaderLine;
}
sub GetStatTable {
my ( $Self, %Param ) = @_;
my @StatArray;
my @UserIDs;
# Users as X-value
if ( $Param{XValue}->{Element} && $Param{XValue}->{Element} eq 'User' ) {
# user have been selected as x-value
@UserIDs = @{ $Param{XValue}->{SelectedValues} };
# get stat data
my $StatData = $Self->_GetStatData(
Param => \%Param,
UserIDs => \@UserIDs,
);
# check stat data
return if !$StatData;
return if ref $StatData ne 'ARRAY';
my @RawStatArray = @{$StatData};
return if !@RawStatArray;
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# get list of needed data
my %ProjectData = $TimeAccountingObject->ProjectSettingsGet();
my %ProjectList = %{ $ProjectData{Project} || {} };
my %ActionData = $TimeAccountingObject->ActionSettingsGet();
my %ActionList = map { ( $_ => $ActionData{$_}->{Action} ) } keys %ActionData;
my @SortedProjectIDs = sort { $ProjectList{$a} cmp $ProjectList{$b} } keys %ProjectList;
my @SortedActionIDs = sort { $ActionList{$a} cmp $ActionList{$b} } keys %ActionList;
# re-sort projects depending on selected sequence
if (
IsArrayRefWithData( $Param{ValueSeries} )
&& $Param{ValueSeries}->[0]->{SelectedValues}->[0] eq 'Down'
)
{
@SortedProjectIDs = reverse @SortedProjectIDs;
}
# iterate over sorted project list
SORTEDPROJECTID:
for my $SortedProjectID (@SortedProjectIDs) {
# check for unselected projects
if (
$Param{Restrictions}->{Project}
&& !grep { $_ == $SortedProjectID } @{ $Param{Restrictions}->{Project} || [] }
)
{
next SORTEDPROJECTID;
}
# get the current project data of current project
my @ProjectStatData = grep { $_->{ProjectID} == $SortedProjectID } @RawStatArray;
# iterate over sorted action list
SORTEDACTIONID:
for my $SortedActionID (@SortedActionIDs) {
# check for unselected actions
if (
$Param{Restrictions}->{ProjectAction}
&& !grep { $_ == $SortedActionID } @{ $Param{Restrictions}->{ProjectAction} || [] }
)
{
next SORTEDACTIONID;
}
# get the current action out of the current project
my @ActionStatData = grep { $_->{ActionID} == $SortedActionID } @ProjectStatData;
my @RowData;
# add descriptive first column
my $RowLabel = "$ProjectList{$SortedProjectID}::$ActionList{$SortedActionID}";
push @RowData, $RowLabel;
# iterate over selected users
USERID:
for my $UserID (@UserIDs) {
# at least get '0' for user data
my $UserPeriodSum = 0;
# iterate over period data of user
for my $PeriodData ( grep { $_->{UserID} == $UserID } @ActionStatData ) {
$UserPeriodSum += $PeriodData->{Period};
}
# safe user data to row data
push @RowData, $UserPeriodSum;
}
# store current row to global stat array
push @StatArray, \@RowData;
}
}
}
# Projects as X-value
else {
# projects have been selected as x-value
my @ProjectIDs = @{ $Param{XValue}->{SelectedValues} };
# get user object
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
# we need to get all users
my %UserIDs = $UserObject->UserList(
Type => 'Short',
Valid => 1,
);
@UserIDs = keys %UserIDs;
# get calculated stats data
my $StatData = $Self->_GetStatData(
Param => \%Param,
UserIDs => \@UserIDs,
);
# check stat data
return if !$StatData;
return if ref $StatData ne 'ARRAY';
my @RawStatArray = @{$StatData};
return if !@RawStatArray;
# get list of needed data
my %UserList = $UserObject->UserList(
Type => 'Long',
Valid => 1,
);
my @SortedUserIDs = sort { $UserList{$a} cmp $UserList{$b} } keys %UserList;
# re-sort users depending on selected sequence
if (
IsArrayRefWithData( $Param{ValueSeries} )
&& $Param{ValueSeries}->[0]->{SelectedValues}->[0] eq 'Down'
)
{
@SortedUserIDs = reverse @SortedUserIDs;
}
# iterate over sorted user list
SORTEDUSERID:
for my $SortedUserID (@SortedUserIDs) {
# check for unselected users
if (
$Param{Restrictions}->{User}
&& !grep { $_ == $SortedUserID } @{ $Param{Restrictions}->{User} || [] }
)
{
next SORTEDUSERID;
}
# get the current user data of current user
my @UserStatData = grep { $_->{UserID} == $SortedUserID } @RawStatArray;
my @RowData;
# add descriptive first column
my $RowLabel = $UserList{$SortedUserID};
push @RowData, $RowLabel;
# iterate over selected projects
PROJECTID:
for my $ProjectID (@ProjectIDs) {
# at least get '0' for user data
my $ProjectPeriodSum = 0;
# iterate over period data of user
for my $PeriodData ( grep { $_->{ProjectID} == $ProjectID } @UserStatData ) {
$ProjectPeriodSum += $PeriodData->{Period};
}
# safe user data to row data
push @RowData, $ProjectPeriodSum;
}
# store current row to global stat array
push @StatArray, \@RowData;
}
}
return @StatArray;
}
sub GetStatTablePreview {
my ( $Self, %Param ) = @_;
my @StatArray;
my @UserIDs;
# Users as X-value
if ( $Param{XValue}->{Element} && $Param{XValue}->{Element} eq 'User' ) {
# user have been selected as x-value
@UserIDs = @{ $Param{XValue}->{SelectedValues} };
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# get list of needed data
my %ProjectData = $TimeAccountingObject->ProjectSettingsGet();
my %ProjectList = %{ $ProjectData{Project} || {} };
my %ActionData = $TimeAccountingObject->ActionSettingsGet();
my %ActionList = map { ( $_ => $ActionData{$_}->{Action} ) } keys %ActionData;
my @SortedProjectIDs = sort { $ProjectList{$a} cmp $ProjectList{$b} } keys %ProjectList;
my @SortedActionIDs = sort { $ActionList{$a} cmp $ActionList{$b} } keys %ActionList;
# re-sort projects depending on selected sequence
if (
IsArrayRefWithData( $Param{ValueSeries} )
&& $Param{ValueSeries}->[0]->{SelectedValues}->[0] eq 'Down'
)
{
@SortedProjectIDs = reverse @SortedProjectIDs;
}
# iterate over sorted project list
SORTEDPROJECTID:
for my $SortedProjectID (@SortedProjectIDs) {
# check for unselected projects
if (
$Param{Restrictions}->{Project}
&& !grep { $_ == $SortedProjectID } @{ $Param{Restrictions}->{Project} || [] }
)
{
next SORTEDPROJECTID;
}
# iterate over sorted action list
SORTEDACTIONID:
for my $SortedActionID (@SortedActionIDs) {
# check for unselected actions
if (
$Param{Restrictions}->{ProjectAction}
&& !grep { $_ == $SortedActionID } @{ $Param{Restrictions}->{ProjectAction} || [] }
)
{
next SORTEDACTIONID;
}
my @RowData;
# add descriptive first column
my $RowLabel = "$ProjectList{$SortedProjectID}::$ActionList{$SortedActionID}";
push @RowData, $RowLabel;
# iterate over selected users
USERID:
for my $UserID (@UserIDs) {
# safe user data to row data
push @RowData, int rand 50;
}
# store current row to global stat array
push @StatArray, \@RowData;
}
}
}
# Projects as X-value
else {
# projects have been selected as x-value
my @ProjectIDs = @{ $Param{XValue}{SelectedValues} };
# get user object
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
# we need to get all users
my %UserIDs = $UserObject->UserList(
Type => 'Short',
Valid => 1,
);
@UserIDs = keys %UserIDs;
# get list of needed data
my %UserList = $UserObject->UserList(
Type => 'Long',
Valid => 1,
);
my @SortedUserIDs = sort { $UserList{$a} cmp $UserList{$b} } keys %UserList;
# re-sort users depending on selected sequence
if (
IsArrayRefWithData( $Param{ValueSeries} )
&& $Param{ValueSeries}->[0]->{SelectedValues}->[0] eq 'Down'
)
{
@SortedUserIDs = reverse @SortedUserIDs;
}
# iterate over sorted user list
SORTEDUSERID:
for my $SortedUserID (@SortedUserIDs) {
# check for unselected users
if (
$Param{Restrictions}->{User}
&& !grep { $_ == $SortedUserID } @{ $Param{Restrictions}->{User} || [] }
)
{
next SORTEDUSERID;
}
my @RowData;
# add descriptive first column
my $RowLabel = $UserList{$SortedUserID};
push @RowData, $RowLabel;
# iterate over selected projects
PROJECTID:
for my $ProjectID (@ProjectIDs) {
# safe user data to row data
push @RowData, int rand 50;
}
# store current row to global stat array
push @StatArray, \@RowData;
}
}
return @StatArray;
}
sub ExportWrapper {
my ( $Self, %Param ) = @_;
# get needed objects
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# wrap ids to used spelling
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element;
next ELEMENT if !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'User' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
$ID->{Content} = $UserObject->UserLookup(
UserID => $ID->{Content}
);
}
}
elsif ( $ElementName eq 'Project' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my %TmpProjectData = $TimeAccountingObject->ProjectGet(
ID => $ID->{Content}
);
$ID->{Content} = $TmpProjectData{Project};
}
}
elsif ( $ElementName eq 'ProjectAction' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my %TmpActionData = $TimeAccountingObject->ActionGet(
ID => $ID->{Content}
);
$ID->{Content} = $TmpActionData{Action};
}
}
}
}
return \%Param;
}
sub ImportWrapper {
my ( $Self, %Param ) = @_;
# get needed objects
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# wrap used spelling to ids
for my $Use (qw(UseAsValueSeries UseAsRestriction UseAsXvalue)) {
ELEMENT:
for my $Element ( @{ $Param{$Use} } ) {
next ELEMENT if !$Element;
next ELEMENT if !$Element->{SelectedValues};
my $ElementName = $Element->{Element};
my $Values = $Element->{SelectedValues};
if ( $ElementName eq 'User' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my $UserID = $UserObject->UserLookup(
UserLogin => $ID->{Content},
);
if ($UserID) {
$ID->{Content} = $UserID;
}
else {
$LogObject->Log(
Priority => 'error',
Message => "Import: Can' find the user $ID->{Content}!"
);
$ID = undef;
}
}
}
elsif ( $ElementName eq 'Project' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my %Project = $TimeAccountingObject->ProjectGet(
Project => $ID->{Content},
);
if ( $Project{ID} ) {
$ID->{Content} = $Project{ID};
}
else {
$LogObject->Log(
Priority => 'error',
Message => "Import: Can' find project $ID->{Content}!"
);
$ID = undef;
}
}
}
elsif ( $ElementName eq 'ProjectAction' ) {
ID:
for my $ID ( @{$Values} ) {
next ID if !$ID;
my %Action = $TimeAccountingObject->ActionGet(
Action => $ID->{Content},
);
if ( $Action{ID} ) {
$ID->{Content} = $Action{ID};
}
else {
$LogObject->Log(
Priority => 'error',
Message => "Import: Can' find action $ID->{Content}!"
);
$ID = undef;
}
}
}
}
}
return \%Param;
}
sub GetExtendedTitle {
my ( $Self, %Param ) = @_;
return if $Param{Restrictions}->{TimeAccountingPeriodStart} || $Param{Restrictions}->{TimeAccountingPeriodStop};
my %DateIndexToName = (
'Second' => 0,
'Minute' => 1,
'Hour' => 2,
'Day' => 3,
'Month' => 4,
'Year' => 5,
);
my %PreviousMonthDates = $Self->_GetPreviousMonthDates(
DateIndexToName => \%DateIndexToName,
);
my $StartDate = sprintf "%04d-%02d-%02d 00:00:00", $PreviousMonthDates{NewStartDate}->[0],
$PreviousMonthDates{NewStartDate}->[1], $PreviousMonthDates{NewStartDate}->[2];
my $StopDate = sprintf "%04d-%02d-%02d 00:00:00", $PreviousMonthDates{NewStopDate}->[0],
$PreviousMonthDates{NewStopDate}->[1], $PreviousMonthDates{NewStopDate}->[2];
return "$StartDate-$StopDate";
}
sub _GetStatData {
my ( $Self, %Param ) = @_;
my @Return;
my @UserIDs = @{ $Param{UserIDs} || [] };
my %DateIndexToName = (
'Second' => 0,
'Minute' => 1,
'Hour' => 2,
'Day' => 3,
'Month' => 4,
'Year' => 5,
);
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# looping over all or selected users
for my $UserID (@UserIDs) {
my $StartDate;
my $StopDate;
# check if time period has been selected
if ( $Param{Param}->{Restrictions}->{TimeAccountingPeriodStart} ) {
# get UNIX time-stamp of start and end values
$StartDate = $TimeAccountingObject->TimeStamp2SystemTime(
String => $Param{Param}->{Restrictions}->{TimeAccountingPeriodStart},
);
$StopDate = $TimeAccountingObject->TimeStamp2SystemTime(
String => $Param{Param}->{Restrictions}->{TimeAccountingPeriodStop},
);
}
else {
# IMPORTANT:
# If no time period had been selected previous month will be used as period!
my %PreviousMonthDates = $Self->_GetPreviousMonthDates(
DateIndexToName => \%DateIndexToName,
);
# Calculate UNIX timestamps for start and stop date.
$StartDate = $TimeAccountingObject->Date2SystemTime(
Year => $PreviousMonthDates{NewStartDate}->[0],
Month => $PreviousMonthDates{NewStartDate}->[1],
Day => $PreviousMonthDates{NewStartDate}->[2],
Hour => 0,
Minute => 0,
Second => 0,
);
$StopDate = $TimeAccountingObject->Date2SystemTime(
Year => $PreviousMonthDates{NewStopDate}->[0],
Month => $PreviousMonthDates{NewStopDate}->[1],
Day => $PreviousMonthDates{NewStopDate}->[2],
Hour => 23,
Minute => 59,
Second => 59,
);
}
# calculate number of days within the given range
my $Days = int( ( $StopDate - $StartDate ) / 86400 ) + 1;
DAY:
for my $Day ( 0 .. $Days ) {
# get day relative to start date
my $DateOfPeriod = $StartDate + $Day * 86400;
# get needed date values out of time-stamp
my @DateValues = $TimeAccountingObject->SystemTime2Date(
SystemTime => $DateOfPeriod,
);
# get working unit for user and day
my %WorkingUnit = $TimeAccountingObject->WorkingUnitsGet(
Year => $DateValues[ $DateIndexToName{'Year'} ],
Month => $DateValues[ $DateIndexToName{'Month'} ],
Day => $DateValues[ $DateIndexToName{'Day'} ],
UserID => $UserID,
);
# extract detailed information
my @DayWorkingUnits = @{ $WorkingUnit{WorkingUnits} || [] };
# check for project restrictions
if (
$Param{Param}->{Restrictions}->{Project}
&& ref $Param{Param}->{Restrictions}->{Project} eq 'ARRAY'
)
{
# build matching hash for selected projects
my %SelectedProjectIDs = map { ( $_ => 1 ) } @{ $Param{Param}->{Restrictions}->{Project} };
# filter only selected projects
my @FilteredProjectWUs = grep {
$SelectedProjectIDs{ $_->{ProjectID} }
} @DayWorkingUnits;
@DayWorkingUnits = @FilteredProjectWUs;
}
# check for task restrictions
if (
$Param{Param}->{Restrictions}->{ProjectAction}
&& ref $Param{Param}->{Restrictions}->{ProjectAction} eq 'ARRAY'
)
{
# build matching hash for selected actions
my %SelectedActionIDs = map { ( $_ => 1 ) } @{ $Param{Param}->{Restrictions}->{ProjectAction} };
# filter only selected actions
my @FilteredActionWUs = grep { $SelectedActionIDs{ $_->{ActionID} } } @DayWorkingUnits;
@DayWorkingUnits = @FilteredActionWUs;
}
# check for user restrictions
if (
$Param{Param}->{Restrictions}->{User}
&& ref $Param{Param}->{Restrictions}->{User} eq 'ARRAY'
)
{
# build matching hash for selected actions
my %SelectedUserIDs = map { ( $_ => 1 ) } @{ $Param{Param}->{Restrictions}->{User} };
# filter only selected actions
my @FilteredUserWUs = grep { $SelectedUserIDs{ $_->{UserID} } } @DayWorkingUnits;
@DayWorkingUnits = @FilteredUserWUs;
}
# do not store data if no data is available
next DAY if !@DayWorkingUnits;
# add data to global result set
push @Return, @DayWorkingUnits;
}
}
return \@Return;
}
sub _GetPreviousMonthDates {
my ( $Self, %Param ) = @_;
if ( !$Param{DateIndexToName} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need DateIndexToName!",
);
return;
}
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
my $DateTimeObjectCurrent = $Kernel::OM->Create('Kernel::System::DateTime');
# Get current date values.
my @CurrentDate = $TimeAccountingObject->SystemTime2Date(
SystemTime => $DateTimeObjectCurrent->ToEpoch(),
);
# Get first day of previous month.
my @NewStartDate = $TimeAccountingObject->AddDeltaYMD(
$CurrentDate[ $Param{DateIndexToName}->{'Year'} ],
$CurrentDate[ $Param{DateIndexToName}->{'Month'} ],
1,
0,
-1,
0,
);
# Get first day of next month relative to previous month.
my @NewStopDate = $TimeAccountingObject->AddDeltaYMD(
$NewStartDate[0],
$NewStartDate[1],
$NewStartDate[2],
0,
+1,
0,
);
# Get last of day previous month.
@NewStopDate = $TimeAccountingObject->AddDeltaYMD(
$NewStopDate[0],
$NewStopDate[1],
$NewStopDate[2],
0,
0,
-1,
);
return (
NewStartDate => \@NewStartDate,
NewStopDate => \@NewStopDate,
);
}
1;

View File

@@ -0,0 +1,252 @@
# --
# 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::Stats::Static::FAQAccess;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::FAQ',
'Kernel::System::Log',
'Kernel::System::DateTime',
);
sub new {
my ( $Type, %Param ) = @_;
# Allocate new hash for object.
my $Self = {%Param};
bless( $Self, $Type );
return $Self;
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 0,
);
return %Behaviours;
}
sub Param {
my $Self = shift;
my @Params = ();
# Get current time.
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
);
my $DateTimeSettings = $DateTimeObject->Get();
my $D = sprintf( "%02d", $DateTimeSettings->{Day} );
my $M = sprintf( "%02d", $DateTimeSettings->{Month} );
my $Y = sprintf( "%02d", $DateTimeSettings->{Year} );
# Create possible time selections.
my %Year = map { $_ => $_ } ( $Y - 10 .. $Y + 1 );
my %Month = map { sprintf( "%02d", $_ ) => sprintf( "%02d", $_ ) } ( 1 .. 12 );
my %Day = map { sprintf( "%02d", $_ ) => sprintf( "%02d", $_ ) } ( 1 .. 31 );
push @Params, {
Frontend => 'Start day',
Name => 'StartDay',
Multiple => 0,
Size => 0,
SelectedID => '01',
Data => {
%Day,
},
};
push @Params, {
Frontend => 'Start month',
Name => 'StartMonth',
Multiple => 0,
Size => 0,
SelectedID => $M,
Data => {
%Month,
},
};
push @Params, {
Frontend => 'Start year',
Name => 'StartYear',
Multiple => 0,
Size => 0,
SelectedID => $Y,
Data => {
%Year,
},
};
push @Params, {
Frontend => 'End day',
Name => 'EndDay',
Multiple => 0,
Size => 0,
SelectedID => $D,
Data => {
%Day,
},
};
push @Params, {
Frontend => 'End month',
Name => 'EndMonth',
Multiple => 0,
Size => 0,
SelectedID => $M,
Data => {
%Month,
},
};
push @Params, {
Frontend => 'End year',
Name => 'EndYear',
Multiple => 0,
Size => 0,
SelectedID => $Y,
Data => {
%Year,
},
};
return @Params;
}
sub Run {
my ( $Self, %Param ) = @_;
# Check needed stuff.
for my $ParamName (qw(StartYear StartMonth StartDay EndYear EndMonth EndDay)) {
if ( !$Param{$ParamName} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $ParamName!",
);
return;
}
}
my $DateTimeObjectStart = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Param{StartYear},
Month => $Param{StartMonth},
Day => 1,
TimeZone => 'floating',
},
);
my $LastDayOfMonthStart;
if ( defined $DateTimeObjectStart ) {
$LastDayOfMonthStart = $DateTimeObjectStart->LastDayOfMonthGet();
}
# Correct start day of month if entered wrong by user.
my $StartDay = sprintf( "%02d", $LastDayOfMonthStart );
if ( $Param{StartDay} < $StartDay ) {
$StartDay = $Param{StartDay};
}
my $DateTimeObjectEnd = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Param{EndYear},
Month => $Param{EndMonth},
Day => 1,
TimeZone => 'floating',
},
);
my $LastDayOfMonthEnd;
if ( defined $DateTimeObjectEnd ) {
$LastDayOfMonthEnd = $DateTimeObjectEnd->LastDayOfMonthGet();
}
# Correct end day of month if entered wrong by user.
my $EndDay = sprintf( "%02d", $LastDayOfMonthEnd );
if ( $Param{EndDay} < $EndDay ) {
$EndDay = $Param{EndDay};
}
# Set start and end date.
my $StartDate = "$Param{StartYear}-$Param{StartMonth}-$StartDay 00:00:00";
my $EndDate = "$Param{EndYear}-$Param{EndMonth}-$EndDay 23:59:59";
my $FAQObject = $Kernel::OM->Get('Kernel::System::FAQ');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $FAQTop10LimitConfig = $ConfigObject->Get('FAQ::Explorer::Top10::Limit');
# Get a count of all FAQ articles.
my $Top10ItemIDsRef = $FAQObject->FAQTop10Get(
Interface => 'internal',
StartDate => $StartDate,
EndDate => $EndDate,
UserID => 1,
Limit => $FAQTop10LimitConfig,
) || [];
# Build result table.
my @Data;
for my $ItemIDRef ( @{$Top10ItemIDsRef} ) {
my %FAQData = $FAQObject->FAQGet(
ItemID => $ItemIDRef->{ItemID},
ItemFields => 0,
UserID => 1,
);
my $VoteData = $FAQObject->ItemVoteDataGet(
ItemID => $ItemIDRef->{ItemID},
UserID => 1,
);
my $VoteResult = sprintf(
"%0."
. $ConfigObject->Get(
"FAQ::Explorer::ItemList::VotingResultDecimalPlaces"
)
. "f",
$VoteData->{Result}
|| 0
);
my $Votes = $VoteData->{Votes} || 0;
# Build table row.
push @Data, [
$FAQData{Number},
$FAQData{Title},
$ItemIDRef->{Count},
$VoteResult,
$Votes,
];
}
# Set report title.
my $Title = "$Param{StartYear}-$Param{StartMonth}-$StartDay - $Param{EndYear}-$Param{EndMonth}-$EndDay";
# Table headlines.
my @HeadData = (
'FAQ #',
'Title',
'Count',
'Vote Result',
'Votes',
);
my @Result = ( [$Title], [@HeadData], @Data );
return @Result;
}
1;

View File

@@ -0,0 +1,186 @@
# --
# 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::Stats::Static::StateAction;
## nofilter(TidyAll::Plugin::OTRS::Perl::Time)
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Language',
'Kernel::System::DB',
'Kernel::System::DateTime',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
return $Self;
}
sub GetObjectBehaviours {
my ( $Self, %Param ) = @_;
my %Behaviours = (
ProvidesDashboardWidget => 1,
);
return %Behaviours;
}
sub Param {
my $Self = shift;
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
# get one month before
$DateTimeObject->Subtract( Months => 1 );
my $DateTimeSettings = $DateTimeObject->Get();
# create possible time selections
my %Year = map { $_ => $_; } ( $DateTimeSettings->{Year} - 10 .. $DateTimeSettings->{Year} );
my %Month = map { $_ => sprintf( "%02d", $_ ); } ( 1 .. 12 );
my @Params = (
{
Frontend => 'Year',
Name => 'Year',
Multiple => 0,
Size => 0,
SelectedID => $DateTimeSettings->{Year},
Data => \%Year,
},
{
Frontend => 'Month',
Name => 'Month',
Multiple => 0,
Size => 0,
SelectedID => $DateTimeSettings->{Month},
Data => \%Month,
},
);
return @Params;
}
sub Run {
my ( $Self, %Param ) = @_;
return if !$Param{Year} || !$Param{Month};
# get language object
my $LanguageObject = $Kernel::OM->Get('Kernel::Language');
my $Year = $Param{Year};
my $Month = $Param{Month};
my %States = $Self->_GetHistoryTypes();
my @PossibleStates;
for my $StateID ( sort { $States{$a} cmp $States{$b} } keys %States ) {
$States{$StateID} = $LanguageObject->Translate( $States{$StateID} );
push @PossibleStates, $States{$StateID};
}
# build x axis
# first take epoch for 12:00 on the 1st of given month
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Param{Year},
Month => $Param{Month},
Day => 1,
Hour => 12,
Minute => 0,
Second => 0,
},
);
my $DateTimeValues = $DateTimeObject->Get();
my @Data;
my @Days = ();
my %StateDate = ();
# execute for all days of this month
while ( $DateTimeValues->{Month} == int $Param{Month} ) {
# x-label is of format 'Mon 1, Tue 2,' etc
my $Text = $LanguageObject->Translate( $DateTimeValues->{DayAbbr} ) . ' ' . $DateTimeValues->{Day};
push @Days, $Text;
my @Row = ();
for my $StateID ( sort { $States{$a} cmp $States{$b} } keys %States ) {
my $Count = $Self->_GetDBDataPerDay(
Year => $Year,
Month => $Month,
Day => $DateTimeValues->{Day},
StateID => $StateID,
);
push @Row, $Count;
$StateDate{$Text}->{$StateID} = ( $StateDate{$Text}->{$StateID} || 0 ) + $Count;
}
# move to next day
$DateTimeObject->Add( Days => 1 );
$DateTimeValues = $DateTimeObject->Get();
}
for my $StateID ( sort { $States{$a} cmp $States{$b} } keys %States ) {
my @Row = ( $States{$StateID} );
for my $Day (@Days) {
my %Hash = %{ $StateDate{$Day} };
push @Row, $Hash{$StateID};
}
push @Data, \@Row;
}
my $Title = "$Year-$Month";
return ( [$Title], [ $LanguageObject->Translate('Days'), @Days ], @Data );
}
sub _GetHistoryTypes {
my $Self = shift;
my $SQL = 'SELECT id, name FROM ticket_history_type WHERE valid_id = 1';
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
$DBObject->Prepare( SQL => $SQL );
my %Stats;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Stats{ $Row[0] } = $Row[1];
}
return %Stats;
}
sub _GetDBDataPerDay {
my ( $Self, %Param ) = @_;
my $Start = "$Param{Year}-$Param{Month}-$Param{Day} 00:00:01";
my $End = "$Param{Year}-$Param{Month}-$Param{Day} 23:59:59";
my $SQL = 'SELECT count(*) FROM ticket_history '
. 'WHERE history_type_id = ? AND create_time >= ? AND create_time <= ?';
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
$DBObject->Prepare(
SQL => $SQL,
Bind => [ \$Param{StateID}, \$Start, \$End ]
);
my $DayData = 0;
while ( my @Row = $DBObject->FetchrowArray() ) {
$DayData = $Row[0];
}
return $DayData;
}
1;