# -- # 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::AgentTimeAccountingOverview; use strict; use warnings; use Kernel::Language qw(Translatable); use Kernel::System::VariableCheck qw(:all); our $ObjectManagerDisabled = 1; sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {%Param}; bless( $Self, $Type ); my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); $Self->{TimeZone} = $Param{TimeZone} || $Param{UserTimeZone} || $DateTimeObject->OTRSTimeZoneGet(); return $Self; } sub Run { my ( $Self, %Param ) = @_; my @MonthArray = ( '', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ); my @WeekdayArray = ( 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', ); # get time object my $DateTimeObjectCurrent = $Kernel::OM->Create('Kernel::System::DateTime'); my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting'); # ---------------------------------------------------------- # # overview about the users time accounting # ---------------------------------------------------------- # my ( $Sec, $Min, $Hour, $CurrentDay, $Month, $Year ) = $TimeAccountingObject->SystemTime2Date( SystemTime => $DateTimeObjectCurrent->ToEpoch(), ); # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # permission check if ( !$Self->{AccessRo} ) { return $LayoutObject->NoPermission( WithHeader => 'yes', ); } for my $Parameter (qw(Status Day Month Year UserID ProjectStatusShow)) { $Param{$Parameter} = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => $Parameter ); } $Param{Action} = 'AgentTimeAccountingEdit'; if ( !$Param{UserID} ) { $Param{UserID} = $Self->{UserID}; } else { if ( $Param{UserID} != $Self->{UserID} && !$Self->{AccessRw} ) { return $LayoutObject->NoPermission( WithHeader => 'yes', ); } $Param{Action} = 'AgentTimeAccountingView'; } if ( $Param{UserID} != $Self->{UserID} ) { my %ShownUsers = $Kernel::OM->Get('Kernel::System::User')->UserList( Type => 'Long', Valid => 1 ); $Param{User} = $ShownUsers{ $Param{UserID} }; $LayoutObject->Block( Name => 'User', Data => {%Param}, ); } # Check Date if ( !$Param{Year} || !$Param{Month} ) { $Param{Year} = $Year; $Param{Month} = $Month; } else { $Param{Month} = sprintf( "%02d", $Param{Month} ); } # store last screen $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID( SessionID => $Self->{SessionID}, Key => 'LastScreen', Value => "Action=$Self->{Action};Year=$Param{Year};Month=$Param{Month}", ); $Param{Month_to_Text} = $MonthArray[ $Param{Month} ]; # create one base object my $DateTimeObjectGiven = $Kernel::OM->Create( 'Kernel::System::DateTime', ObjectParams => { Year => $Param{Year}, Month => $Param{Month}, Day => 1, }, ); my $DateTimeObjectNext = $DateTimeObjectGiven->Clone(); my $DateTimeObjectPrev = $DateTimeObjectGiven->Clone(); my $DateParamsCurrent = $DateTimeObjectGiven->Get(); # calculate the next month $DateTimeObjectNext->Add( Months => 1, ); my $DateParamsNext = $DateTimeObjectNext->Get(); $Param{YearNext} = $DateParamsNext->{Year}; $Param{MonthNext} = $DateParamsNext->{Month}; $Param{DayNext} = $DateParamsNext->{Day}; # calculate the next month $DateTimeObjectPrev->Subtract( Months => 1, ); my $DateParamsBack = $DateTimeObjectPrev->Get(); $Param{YearBack} = $DateParamsBack->{Year}; $Param{MonthBack} = $DateParamsBack->{Month}; $Param{DayBack} = $DateParamsBack->{Day}; # Overview per day my $LastDayOfMonth = $DateTimeObjectGiven->LastDayOfMonthGet(); my $DaysOfMonth = $LastDayOfMonth->{Day}; my %UserData = $TimeAccountingObject->UserGet( UserID => $Param{UserID}, ); for my $Day ( 1 .. $DaysOfMonth ) { $Param{Day} = sprintf( "%02d", $Day ); $Param{Weekday} = $TimeAccountingObject->DayOfWeek( $Param{Year}, $Param{Month}, $Param{Day} ); my $VacationCheck = $TimeAccountingObject->VacationCheck( Year => $Param{Year}, Month => $Param{Month}, Day => $Day, Calendar => $UserData{Calendar}, ); my $Date = sprintf( "%04d-%02d-%02d", $Param{Year}, $Param{Month}, $Day ); my $DateTimeObjectStart = $Kernel::OM->Create( 'Kernel::System::DateTime', ObjectParams => { String => $Date . ' 00:00:00', } ); my $DayStartTime = $DateTimeObjectStart->ToEpoch(); my $DateTimeObjectStop = $Kernel::OM->Create( 'Kernel::System::DateTime', ObjectParams => { String => $Date . ' 23:59:59', } ); my $DayStopTime = $DateTimeObjectStop->ToEpoch(); # add time zone to calculation my $UserCalendar = $UserData{Calendar} || ''; my $Zone = $Kernel::OM->Get('Kernel::Config')->Get( "TimeZone::Calendar" . $UserCalendar ); if ($Zone) { my $ZoneSeconds = $Zone * 60 * 60; $DayStartTime = $DayStartTime - $ZoneSeconds; $DayStopTime = $DayStopTime - $ZoneSeconds; } my $ThisDayWorkingTime = $TimeAccountingObject->WorkingTime( StartTime => $DayStartTime, StopTime => $DayStopTime, Calendar => $UserCalendar, ) || '0'; if ( $Param{Year} eq $Year && $Param{Month} eq $Month && $CurrentDay eq $Day ) { $Param{Class} = 'Active'; } elsif ($VacationCheck) { $Param{Class} = 'Vacation'; $Param{Comment} = $VacationCheck; } elsif ($ThisDayWorkingTime) { $Param{Class} = 'WorkingDay'; } else { $Param{Class} = 'NonWorkingDay'; } my %Data = $TimeAccountingObject->WorkingUnitsGet( Year => $Param{Year}, Month => $Param{Month}, Day => $Param{Day}, UserID => $Param{UserID}, ); $Param{Comment} = $Data{Sick} ? Translatable('Sick leave') : $Data{LeaveDay} ? Translatable('On vacation') : $Data{Overtime} ? Translatable('On overtime leave') : ''; $Param{WorkingHours} = $Data{Total} ? sprintf( "%.2f", $Data{Total} ) : ''; $Param{Weekday_to_Text} = $WeekdayArray[ $Param{Weekday} - 1 ]; $LayoutObject->Block( Name => 'Row', Data => {%Param}, ); $Param{Comment} = ''; } my %UserReport = $TimeAccountingObject->UserReporting( Year => $Param{Year}, Month => $Param{Month}, ); for my $ReportElement ( qw(TargetState TargetStateTotal WorkingHoursTotal WorkingHours Overtime OvertimeTotal OvertimeUntil LeaveDay LeaveDayTotal LeaveDayRemaining Sick SickTotal SickRemaining) ) { $UserReport{ $Param{UserID} }{$ReportElement} ||= 0; $Param{$ReportElement} = sprintf( "%.2f", $UserReport{ $Param{UserID} }{$ReportElement} ); } if ( $UserData{ShowOvertime} ) { $LayoutObject->Block( Name => 'Overtime', Data => \%Param, ); } # Overview per project and action my %ProjectData = $TimeAccountingObject->ProjectActionReporting( Year => $Param{Year}, Month => $Param{Month}, UserID => $Param{UserID}, ); if ( IsHashRefWithData( \%ProjectData ) ) { # show the report sort by projects if ( !$Param{ProjectStatusShow} || $Param{ProjectStatusShow} eq 'valid' ) { $Param{ProjectStatusShow} = 'all'; } elsif ( $Param{ProjectStatusShow} eq 'all' ) { $Param{ProjectStatusShow} = 'valid'; } $Param{ShowProjects} = 'Show ' . $Param{ProjectStatusShow} . ' projects'; $LayoutObject->Block( Name => 'ProjectTable', Data => {%Param}, ); PROJECTID: for my $ProjectID ( sort { $ProjectData{$a}{Name} cmp $ProjectData{$b}{Name} } keys %ProjectData ) { my $ProjectRef = $ProjectData{$ProjectID}; my $ActionsRef = $ProjectRef->{Actions}; $Param{Project} = ''; $Param{Status} = $ProjectRef->{Status} ? '' : 'passiv'; my $Total = 0; my $TotalTotal = 0; next PROJECTID if $Param{ProjectStatusShow} eq 'all' && $Param{Status}; if ($ActionsRef) { for my $ActionID ( sort { $ActionsRef->{$a}{Name} cmp $ActionsRef->{$b}{Name} } keys %{$ActionsRef} ) { my $ActionRef = $ActionsRef->{$ActionID}; $Param{Action} = $ActionRef->{Name}; $Param{Hours} = sprintf( "%.2f", $ActionRef->{PerMonth} || 0 ); $Param{HoursTotal} = sprintf( "%.2f", $ActionRef->{Total} || 0 ); $Total += $Param{Hours}; $TotalTotal += $Param{HoursTotal}; $LayoutObject->Block( Name => 'Action', Data => {%Param}, ); if ( !$Param{Project} ) { $Param{Project} = $ProjectRef->{Name}; my $ProjectDescription = $LayoutObject->Ascii2Html( Text => $ProjectRef->{Description}, HTMLResultMode => 1, NewLine => 50, ); $LayoutObject->Block( Name => 'Project', Data => { RowSpan => ( 1 + scalar keys %{$ActionsRef} ), Status => $Param{Status}, }, ); if ($ProjectDescription) { $LayoutObject->Block( Name => 'ProjectDescription', Data => { ProjectDescription => $ProjectDescription, }, ); } if ( $UserData{CreateProject} ) { # persons who are allowed to see the create object link are # allowed to see the project reporting $LayoutObject->Block( Name => 'ProjectLink', Data => { Project => $ProjectRef->{Name}, ProjectID => $ProjectID, }, ); } else { $LayoutObject->Block( Name => 'ProjectNoLink', Data => { Project => $ProjectRef->{Name} }, ); } } } # Now show row with total result of all actions of this project $Param{Hours} = sprintf( "%.2f", $Total ); $Param{HoursTotal} = sprintf( "%.2f", $TotalTotal ); $Param{TotalHours} += $Total; $Param{TotalHoursTotal} += $TotalTotal; $LayoutObject->Block( Name => 'ActionTotal', Data => {%Param}, ); } } if ( defined( $Param{TotalHours} ) ) { $Param{TotalHours} = sprintf( "%.2f", $Param{TotalHours} ); } if ( defined( $Param{TotalHoursTotal} ) ) { $Param{TotalHoursTotal} = sprintf( "%.2f", $Param{TotalHoursTotal} ); } $LayoutObject->Block( Name => 'GrandTotal', Data => {%Param}, ); } # build output my $Output = $LayoutObject->Header( Title => Translatable('Overview'), ); $Output .= $LayoutObject->NavigationBar(); $Output .= $LayoutObject->Output( Data => \%Param, TemplateFile => 'AgentTimeAccountingOverview' ); $Output .= $LayoutObject->Footer(); return $Output; } sub _CheckValidityUserPeriods { my ( $Self, %Param ) = @_; my %Errors = (); my %GetParam; # get time object my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting'); for ( my $Period = 1; $Period <= $Param{Period}; $Period++ ) { # check for needed data for my $Parameter (qw(DateStart DateEnd LeaveDays)) { $GetParam{$Parameter} = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => $Parameter . "[$Period]" ); if ( !$GetParam{$Parameter} ) { $Errors{ $Parameter . '-' . $Period . 'Invalid' } = 'ServerError'; $Errors{ $Parameter . '-' . $Period . 'ErrorType' } = 'MissingValue'; } } my ( $Year, $Month, $Day ) = split( '-', $GetParam{DateStart} ); my $StartDate = $TimeAccountingObject->Date2SystemTime( Year => $Year, Month => $Month, Day => $Day, Hour => 0, Minute => 0, Second => 0, ); ( $Year, $Month, $Day ) = split( '-', $GetParam{DateEnd} ); my $EndDate = $TimeAccountingObject->Date2SystemTime( Year => $Year, Month => $Month, Day => $Day, Hour => 0, Minute => 0, Second => 0, ); if ( !$StartDate ) { $Errors{ 'DateStart-' . $Period . 'Invalid' } = 'ServerError'; $Errors{ 'DateStart-' . $Period . 'ErrorType' } = 'Invalid'; } if ( !$EndDate ) { $Errors{ 'DateEnd-' . $Period . 'Invalid' } = 'ServerError'; $Errors{ 'DateEnd-' . $Period . 'ErrorType' } = 'Invalid'; } if ( $StartDate && $EndDate && $StartDate >= $EndDate ) { $Errors{ 'DateEnd-' . $Period . 'Invalid' } = 'ServerError'; $Errors{ 'DateEnd-' . $Period . 'ErrorType' } = 'BeforeDateStart'; } } return %Errors; } 1;