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

2543 lines
67 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::System::TimeAccounting;
use strict;
use warnings;
use Kernel::System::DateTime;
use Kernel::System::VariableCheck qw( :all );
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::DB',
'Kernel::System::Log',
'Kernel::System::DateTime',
'Kernel::System::User',
);
=head1 NAME
Kernel::System::TimeAccounting - time accounting lib
=head1 DESCRIPTION
All time accounting functions
=head1 PUBLIC INTERFACE
=head2 new()
create an object
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
$Self->{TimeZone} = $Param{TimeZone}
|| $Param{UserTimeZone}
|| $DateTimeObject->OTRSTimeZoneGet();
return $Self;
}
=head2 UserCurrentPeriodGet()
returns a hash with the current period data of the specified user
my %UserData = $TimeAccountingObject->UserCurrentPeriodGet(
Year => '2005',
Month => '12',
Day => '24',
);
The returned hash contains the following elements:
%UserData = (
1 => {
UserID => 1,
Period => 123,
DateStart => '2005-12-24',
DateEnd => '2005-12-24',
WeeklyHours => 40.4,
LeaveDays => 12,
OverTime => 34,
},
);
=cut
sub UserCurrentPeriodGet {
my ( $Self, %Param ) = @_;
# check needed params
for my $NeededParam (qw(Year Month Day)) {
if ( !$Param{$NeededParam} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "UserCurrentPeriodGet: Need $NeededParam!",
);
return;
}
}
# build date string for given params
my $Date = sprintf "%04d-%02d-%02d 00:00:00", $Param{Year}, $Param{Month}, $Param{Day};
# check cache
if ( $Self->{'Cache::UserCurrentPeriodGet'}{$Date} ) {
return %{ $Self->{'Cache::UserCurrentPeriodGet'}{$Date} };
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
return if !$DBObject->Prepare(
SQL => '
SELECT user_id, preference_period, date_start, date_end, weekly_hours, leave_days,
overtime
FROM time_accounting_user_period
WHERE date_start <= ?
AND date_end >= ?
AND status = ?',
Bind => [ \$Date, \$Date, \1, ],
);
# fetch the data
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
my $UserRef = {
UserID => $Row[0],
Period => $Row[1],
DateStart => substr( $Row[2], 0, 10 ),
DateEnd => substr( $Row[3], 0, 10 ),
WeeklyHours => $Row[4],
LeaveDays => $Row[5],
Overtime => $Row[6],
};
$Data{ $Row[0] } = $UserRef;
}
# check for valid user data
return if !%Data;
# store user data in cache
$Self->{'Cache::UserCurrentPeriodGet'}{$Date} = \%Data;
return %Data;
}
=head2 UserReporting()
returns a hash with information about leave days, overtimes,
working hours etc. of all users
my %Data = $TimeAccountingObject->UserReporting(
Year => '2005',
Month => '12',
Day => '12', # Optional
);
=cut
sub UserReporting {
my ( $Self, %Param ) = @_;
# get log object
my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
# check needed params
for my $NeededParam (qw(Year Month Day)) {
if ( !$Param{$NeededParam} && $NeededParam ne 'Day' ) {
$LogObject->Log(
Priority => 'error',
Message => "UserReporting: Need $NeededParam!"
);
return;
}
}
# check valid date values
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day} || 1,
},
);
return if !$DateTimeObject;
# get days of month if not provided
my $LastDayOfMonth = $DateTimeObject->LastDayOfMonthGet();
$Param{Day} ||= $LastDayOfMonth->{Day};
my %UserCurrentPeriod = $Self->UserCurrentPeriodGet(%Param);
my $YearStart = 1970;
my $MonthStart = 1;
my $DayStart = 1;
my $YearEnd = $Param{Year};
my $MonthEnd = $Param{Month};
my $DayEnd = $Param{Day};
my %Data;
USERID:
for my $UserID ( sort keys %UserCurrentPeriod ) {
if ( $UserCurrentPeriod{$UserID}->{DateStart} =~ m{ \A (\d{4})-(\d{2})-(\d{2}) }xms ) {
$YearStart = $1;
$MonthStart = $2;
$DayStart = $3;
}
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $YearStart,
Month => $MonthStart,
Day => $DayStart,
},
);
if ( !$DateTimeObject ) {
$LogObject->Log(
Priority => 'notice',
Message => 'UserReporting: Invalid start date for user '
. "$UserID: $UserCurrentPeriod{$UserID}{DateStart}",
);
next USERID;
}
my %CurrentUserData = (
LeaveDate => 0,
Sick => 0,
Overtime => 0,
TargetState => 0,
LeaveDayTotal => 0,
SickTotal => 0,
SickTotal => 0,
TargetStateTotal => 0,
);
my $Calendar = { $Self->UserGet( UserID => $UserID ) }->{Calendar};
YEAR:
for my $Year ( $YearStart .. $YearEnd ) {
my $MonthStartPoint = $Year == $YearStart ? $MonthStart : 1;
my $MonthEndPoint = $Year == $YearEnd ? $MonthEnd : 12;
MONTH:
for my $Month ( $MonthStartPoint .. $MonthEndPoint ) {
my $DayStartPoint =
$Year == $YearStart && $Month == $MonthStart
? $DayStart
: 1;
my $DayEndPoint;
if ( $Year == $YearEnd && $Month == $MonthEnd ) {
$DayEndPoint = $DayEnd;
}
else {
$DayEndPoint = $Self->DaysInMonth( $Year, $Month );
}
DAY:
for my $Day ( $DayStartPoint .. $DayEndPoint ) {
my %WorkingUnit = $Self->WorkingUnitsGet(
Year => $Year,
Month => $Month,
Day => $Day,
UserID => $UserID,
);
my $LeaveDay = 0;
my $Sick = 0;
my $Overtime = 0;
my $TargetState = 0;
if ( $WorkingUnit{LeaveDay} ) {
$CurrentUserData{LeaveDayTotal}++;
$LeaveDay = 1;
}
elsif ( $WorkingUnit{Sick} ) {
$CurrentUserData{SickTotal}++;
$Sick = 1;
}
elsif ( $WorkingUnit{Overtime} ) {
$CurrentUserData{OvertimeTotal}++;
$Overtime = 1;
}
$CurrentUserData{WorkingHoursTotal} += $WorkingUnit{Total};
my $VacationCheck = $Self->VacationCheck(
Year => $Year,
Month => $Month,
Day => $Day,
Calendar => $Calendar || '',
);
my $Weekday = $Self->DayOfWeek( $Year, $Month, $Day );
if (
$Weekday != 6
&& $Weekday != 7
&& !$VacationCheck
&& !$Sick
&& !$LeaveDay
)
{
$CurrentUserData{TargetStateTotal}
+= $UserCurrentPeriod{$UserID}{WeeklyHours} / 5;
$TargetState = $UserCurrentPeriod{$UserID}{WeeklyHours} / 5;
}
if ( $Month == $MonthEnd && $Year == $YearEnd ) {
$CurrentUserData{TargetState} += $TargetState;
$CurrentUserData{WorkingHours} += $WorkingUnit{Total};
$CurrentUserData{LeaveDay} += $LeaveDay;
$CurrentUserData{Sick} += $Sick;
}
}
}
}
$CurrentUserData{Overtime} = $CurrentUserData{WorkingHours} - $CurrentUserData{TargetState};
$CurrentUserData{OvertimeTotal} = $UserCurrentPeriod{$UserID}{Overtime}
+ $CurrentUserData{WorkingHoursTotal}
- $CurrentUserData{TargetStateTotal};
$CurrentUserData{OvertimeUntil} = $CurrentUserData{OvertimeTotal} - $CurrentUserData{Overtime};
$CurrentUserData{LeaveDayRemaining} = $UserCurrentPeriod{$UserID}{LeaveDays} - $CurrentUserData{LeaveDayTotal};
$Data{$UserID} = \%CurrentUserData;
}
return %Data;
}
=head2 ProjectSettingsGet()
returns a hash with all the projects' data
my %ProjectData = $TimeAccountingObject->ProjectSettingsGet(
Status => 'valid' || 'invalid', optional default valid && invalid
);
=cut
sub ProjectSettingsGet {
my ( $Self, %Param ) = @_;
my $Where = '';
if ( $Param{Status} ) {
$Where = ' WHERE status = ';
$Where .= $Param{Status} eq 'invalid' ? "'0'" : "'1'";
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
$DBObject->Prepare(
SQL => "
SELECT id, project, description, status
FROM time_accounting_project
$Where",
);
# fetch the data
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
my $ID = $Row[0];
$Data{Project}{$ID} = $Row[1];
$Data{ProjectDescription}{$ID} = $Row[2];
$Data{ProjectStatus}{$ID} = $Row[3];
}
return %Data;
}
=head2 ProjectGet()
returns a hash with the requested project data
my %ProjectData = $TimeAccountingObject->ProjectGet( ID => 2 );
This returns something like:
$TimeAccountingObject = (
Project => 'internal',
ProjectDescription => 'description',
ProjectStatus => 1,
);
or
my %ProjectData = $TimeAccountingObject->ProjectGet( Project => 'internal' );
This returns something like:
$TimeAccountingObject = (
ID => 2,
ProjectDescription => 'description',
ProjectStatus => 1,
);
=cut
sub ProjectGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ID} && !$Param{Project} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ID or project name!'
);
return;
}
my %Project;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# look for the task data with the ID
if ( $Param{ID} ) {
# SQL
return if !$DBObject->Prepare(
SQL => '
SELECT project, description, status
FROM time_accounting_project
WHERE id = ?',
Bind => [ \$Param{ID} ],
);
while ( my @Data = $DBObject->FetchrowArray() ) {
%Project = (
ID => $Param{ID},
Project => $Data[0],
ProjectDescription => $Data[1],
ProjectStatus => $Data[2],
);
}
}
# look for the task data with the task name
else {
# SQL
return if !$DBObject->Prepare(
SQL => '
SELECT id, description, status
FROM time_accounting_project
WHERE project = ?',
Bind => [ \$Param{Project} ],
);
while ( my @Data = $DBObject->FetchrowArray() ) {
%Project = (
Project => $Param{Project},
ID => $Data[0],
ProjectDescription => $Data[1],
ProjectStatus => $Data[2],
);
}
}
return %Project;
}
=head2 ProjectSettingsInsert()
inserts a new project in the db
$TimeAccountingObject->ProjectSettingsInsert(
Project => 'internal', # optional
ProjectDescription => 'description', # optional
ProjectStatus => 1 || 0, # optional
);
returns ID of created project
=cut
sub ProjectSettingsInsert {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
$Param{Project} ||= $ConfigObject->Get('TimeAccounting::DefaultProjectName');
$Param{ProjectDescription} ||= '';
if ( $Param{ProjectStatus} ne '0' && $Param{ProjectStatus} ne '1' ) {
$Param{ProjectStatus} = $ConfigObject->Get('TimeAccounting::DefaultProjectStatus');
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# insert project record
return if !$DBObject->Do(
SQL => '
INSERT INTO time_accounting_project (project, description, status)
VALUES (?, ?, ?)',
Bind => [ \$Param{Project}, \$Param{ProjectDescription}, \$Param{ProjectStatus} ],
);
# get id of newly created project record
return if !$DBObject->Prepare(
SQL => '
SELECT id
FROM time_accounting_project
WHERE project = ?',
Bind => [ \$Param{Project} ],
Limit => 1,
);
# fetch the data
my $ProjectID;
while ( my @Row = $DBObject->FetchrowArray() ) {
$ProjectID = $Row[0];
}
return $ProjectID;
}
=head2 ProjectSettingsUpdate()
updates a project
my $Success = $TimeAccountingObject->ProjectSettingsUpdate(
ID => 123,
Project => 'internal',
ProjectDescription => 'description',
ProjectStatus => 1,
);
=cut
sub ProjectSettingsUpdate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(ID Project)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
# SQL
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => '
UPDATE time_accounting_project
SET project = ?, description = ?, status = ?
WHERE id = ?',
Bind => [
\$Param{Project}, \$Param{ProjectDescription}, \$Param{ProjectStatus}, \$Param{ID},
],
);
return 1;
}
=head2 ActionSettingsGet()
returns a hash with all the actions settings
my %ActionData = $TimeAccountingObject->ActionSettingsGet();
=cut
sub ActionSettingsGet {
my $Self = shift;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
return if !$DBObject->Prepare(
SQL => '
SELECT id, action, status
FROM time_accounting_action',
);
# fetch the data
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Data{ $Row[0] }{Action} = $Row[1];
$Data{ $Row[0] }{ActionStatus} = $Row[2];
}
return %Data;
}
=head2 ActionGet()
returns a hash with the requested action (task) data
my %ActionData = $TimeAccountingObject->ActionGet( ID => 2 );
This returns something like:
$TimeAccountingObject = (
Action => 'My task',
ActionStatus => 1,
);
or
my %ActionData = $TimeAccountingObject->ActionGet( Action => 'My task' );
This returns something like:
$TimeAccountingObject = (
ID => 2,
ActionStatus => 1,
);
=cut
sub ActionGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{ID} && !$Param{Action} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ID or Action!'
);
return;
}
my %Task;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# look for the task data with the ID
if ( $Param{ID} ) {
# SQL
return if !$DBObject->Prepare(
SQL => '
SELECT action, status
FROM time_accounting_action
WHERE id = ?',
Bind => [ \$Param{ID} ],
);
while ( my @Data = $DBObject->FetchrowArray() ) {
%Task = (
ID => $Param{ID},
Action => $Data[0],
ActionStatus => $Data[1],
);
}
}
# look for the task data with the task name
else {
# SQL
return if !$DBObject->Prepare(
SQL => '
SELECT id, status
FROM time_accounting_action
WHERE action = ?',
Bind => [ \$Param{Action} ],
);
while ( my @Data = $DBObject->FetchrowArray() ) {
%Task = (
Action => $Param{Action},
ID => $Data[0],
ActionStatus => $Data[1],
);
}
}
return %Task;
}
=head2 ActionSettingsInsert()
inserts a new action in the db
$TimeAccountingObject->ActionSettingsInsert(
Action => 'meeting', # optional
ActionStatus => 1 || 0, # optional
);
=cut
sub ActionSettingsInsert {
my ( $Self, %Param ) = @_;
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
$Param{Action} ||= $ConfigObject->Get('TimeAccounting::DefaultActionName') || '';
if ( $Param{ActionStatus} ne '0' && $Param{ActionStatus} ne '1' ) {
$Param{ActionStatus} = $ConfigObject->Get('TimeAccounting::DefaultActionStatus');
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db insert
return if !$DBObject->Do(
SQL => '
INSERT INTO time_accounting_action (action, status)
VALUES (?, ?)',
Bind => [ \$Param{Action}, \$Param{ActionStatus}, ],
);
return 1;
}
=head2 ActionSettingsUpdate()
updates an action (task)
my $Success = $TimeAccountingObject->ActionSettingsUpdate(
ActionID => 123,
Action => 'internal',
ActionStatus => 1,
);
=cut
sub ActionSettingsUpdate {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(ActionID Action)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
# SQL
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => '
UPDATE time_accounting_action
SET action = ?, status = ?
WHERE id = ?',
Bind => [
\$Param{Action}, \$Param{ActionStatus}, \$Param{ActionID}
],
);
return 1;
}
=head2 UserList()
returns a hash with the user data of all users
my %UserData = $TimeAccountingObject->UserList();
=cut
sub UserList {
my $Self = shift;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
$DBObject->Prepare(
SQL => '
SELECT user_id, description, show_overtime, create_project, allow_skip, calendar
FROM time_accounting_user',
);
# fetch the data
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Data{ $Row[0] }{UserID} = $Row[0];
$Data{ $Row[0] }{Description} = $Row[1];
$Data{ $Row[0] }{ShowOvertime} = $Row[2];
$Data{ $Row[0] }{CreateProject} = $Row[3];
$Data{ $Row[0] }{AllowSkip} = $Row[4];
$Data{ $Row[0] }{Calendar} = $Row[5];
}
return %Data;
}
=head2 UserGet()
returns a hash with the user data of one user
my %UserData = $TimeAccountingObject->UserGet(
UserID => 15,
);
=cut
sub UserGet {
my ( $Self, %Param ) = @_;
# check needed data
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'UserGet: Need UserID!',
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
$DBObject->Prepare(
SQL => '
SELECT description, show_overtime, create_project, allow_skip, calendar
FROM time_accounting_user
WHERE user_id = ?',
Bind => [ \$Param{UserID} ],
);
# fetch the data
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Data{UserID} = $Param{UserID};
$Data{Description} = $Row[0];
$Data{ShowOvertime} = $Row[1];
$Data{CreateProject} = $Row[2];
$Data{AllowSkip} = $Row[3];
$Data{Calendar} = $Row[4];
}
return %Data;
}
=head2 UserSettingsGet()
returns a hash with the complete user period data for all users
my %UserData = $TimeAccountingObject->UserSettingsGet();
returns:
%UserData = (
3 => {
1 => {
DateEnd => "2015-12-31",
DateStart => "2015-01-01",
LeaveDays => "23.00",
Overtime => "0.00",
Period => 1,
UserID => 3,
UserStatus => 1,
WeeklyHours => "40.00",
},
2 => {
DateEnd => "2015-12-31",
DateStart => "2015-01-01",
LeaveDays => "23.00",
Overtime => "0.00",
Period => 2,
UserID => 3,
UserStatus => 1,
WeeklyHours => "32.00",
},
},
4 => {
1 => {
DateEnd => "2015-12-31",
DateStart => "2015-01-01",
LeaveDays => "23.00",
Overtime => "0.00",
Period => 1,
UserID => 4,
UserStatus => 1,
WeeklyHours => "40.00",
},
},
};
=cut
sub UserSettingsGet {
my $Self = shift;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
$DBObject->Prepare(
SQL => '
SELECT user_id, preference_period, date_start, date_end, weekly_hours, leave_days,
overtime, status
FROM time_accounting_user_period'
);
# fetch the data
my %Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Data{ $Row[0] }{ $Row[1] }{UserID} = $Row[0];
$Data{ $Row[0] }{ $Row[1] }{Period} = $Row[1];
$Data{ $Row[0] }{ $Row[1] }{DateStart} = substr( $Row[2], 0, 10 );
$Data{ $Row[0] }{ $Row[1] }{DateEnd} = substr( $Row[3], 0, 10 );
$Data{ $Row[0] }{ $Row[1] }{WeeklyHours} = $Row[4];
$Data{ $Row[0] }{ $Row[1] }{LeaveDays} = $Row[5];
$Data{ $Row[0] }{ $Row[1] }{Overtime} = $Row[6];
$Data{ $Row[0] }{ $Row[1] }{UserStatus} = $Row[7];
}
return %Data;
}
=head2 SingleUserSettingsGet()
returns a hash with the requested user's period data
my %UserData = $TimeAccountingObject->SingleUserSettingsGet( UserID => 1 );
=cut
sub SingleUserSettingsGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!'
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
$DBObject->Prepare(
SQL => '
SELECT user_id, preference_period, date_start, date_end, weekly_hours, leave_days,
overtime, status
FROM time_accounting_user_period WHERE user_id = ?',
Bind => [ \$Param{UserID} ],
);
# fetch the data
my %UserData;
while ( my @Row = $DBObject->FetchrowArray() ) {
$UserData{ $Row[1] }{UserID} = $Row[0];
$UserData{ $Row[1] }{Period} = $Row[1];
$UserData{ $Row[1] }{DateStart} = substr( $Row[2], 0, 10 );
$UserData{ $Row[1] }{DateEnd} = substr( $Row[3], 0, 10 );
$UserData{ $Row[1] }{WeeklyHours} = $Row[4];
$UserData{ $Row[1] }{LeaveDays} = $Row[5];
$UserData{ $Row[1] }{Overtime} = $Row[6];
$UserData{ $Row[1] }{UserStatus} = $Row[7];
}
return %UserData;
}
=head2 UserLastPeriodNumberGet()
returns the number of the last registered period for the specified user
my $LastPeriodNumber = $TimeAccountingObject->UserLastPeriodNumberGet( UserID => 1 );
=cut
sub UserLastPeriodNumberGet {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need UserID!'
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
$DBObject->Prepare(
SQL => '
SELECT max(preference_period)
FROM time_accounting_user_period
WHERE user_id = ?',
Bind => [ \$Param{UserID} ],
);
# fetch the data
my @Row = $DBObject->FetchrowArray();
my $LastPeriodNumber = $Row[0] || 0;
return $LastPeriodNumber;
}
=head2 UserSettingsInsert()
insert new user data in the db
$TimeAccountingObject->UserSettingsInsert(
UserID => '2',
Period => '2',
);
=cut
sub UserSettingsInsert {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw (UserID Period)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed"
);
return;
}
}
# check if user exists
if ( !$Kernel::OM->Get('Kernel::System::User')->UserLookup( UserID => $Param{UserID} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "UserID $Param{UserID} does not exist!"
);
return;
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
$Param{WeeklyHours} = $ConfigObject->Get('TimeAccounting::DefaultUserWeeklyHours')
|| '40';
$Param{LeaveDays} = $ConfigObject->Get('TimeAccounting::DefaultUserLeaveDays') || '25';
$Param{UserStatus} = $ConfigObject->Get('TimeAccounting::DefaultUserStatus') || '1';
$Param{Overtime} = $ConfigObject->Get('TimeAccounting::DefaultUserOvertime') || '0';
$Param{DateEnd} = $ConfigObject->Get('TimeAccounting::DefaultUserDateEnd')
|| '2019-12-31';
$Param{DateStart} = $ConfigObject->Get('TimeAccounting::DefaultUserDateStart')
|| '2019-01-01';
$Param{Description} = $ConfigObject->Get('TimeAccounting::DefaultUserDescription')
|| 'Put your description here.';
$Param{DateStart} .= ' 00:00:00';
$Param{DateEnd} .= ' 00:00:00';
# delete cache
delete $Self->{'Cache::UserCurrentPeriodGet'};
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db insert
return if !$DBObject->Do(
SQL => '
INSERT INTO time_accounting_user_period (user_id, preference_period, date_start,
date_end, weekly_hours, leave_days, overtime, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
Bind => [
\$Param{UserID}, \$Param{Period}, \$Param{DateStart}, \$Param{DateEnd},
\$Param{WeeklyHours}, \$Param{LeaveDays}, \$Param{Overtime}, \$Param{UserStatus},
],
);
# select UserID
$DBObject->Prepare(
SQL => '
SELECT user_id
FROM time_accounting_user
WHERE user_id = ?',
Bind => [ \$Param{UserID}, ],
Limit => 1,
);
# fetch the data
my $UserID;
while ( my @Row = $DBObject->FetchrowArray() ) {
$UserID = $Row[0];
}
if ( !defined $UserID ) {
# db insert
return if !$DBObject->Do(
SQL => '
INSERT INTO time_accounting_user (user_id, description)
VALUES (?, ?)',
Bind => [ \$Param{UserID}, \$Param{Description}, ],
);
}
return 1;
}
=head2 UserSettingsUpdate()
updates user data in the db
$TimeAccountingObject->UserSettingsUpdate(
UserID => 1,
Description => 'Some Text',
CreateProject => 1 || 0,
ShowOvertime => 1 || 0,
AllowSkip => 1 || 0,
Period => {
1 => {
DateStart => '2015-12-12',
DateEnd => '2015-12-31',
WeeklyHours => '38',
LeaveDays => '25',
Overtime => '38',
UserStatus => 1 || 0,
},
2 => {
DateStart => '2015-12-12',
DateEnd => '2015-12-31',
WeeklyHours => '38',
LeaveDays => '25',
Overtime => '38',
UserStatus => 1 || 0,
},
3 => ......
}
);
=cut
sub UserSettingsUpdate {
my ( $Self, %Param ) = @_;
# delete cache
delete $Self->{'Cache::UserCurrentPeriodGet'};
my $UserID = $Param{UserID};
if ( !defined $Param{Period}->{1}{DateStart} && !defined $Param{Period}->{1}{DateEnd} ) {
return $Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "UserSettingUpdate: No data for user id $UserID!"
);
}
# set default values
$Param{ShowOvertime} ||= 0;
$Param{CreateProject} ||= 0;
$Param{AllowSkip} ||= 0;
$Param{Calendar} ||= 0;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db insert
return if !$DBObject->Do(
SQL => '
UPDATE time_accounting_user
SET description = ?, show_overtime = ?, create_project = ?, allow_skip = ?, calendar = ?
WHERE user_id = ?',
Bind => [
\$Param{Description}, \$Param{ShowOvertime},
\$Param{CreateProject}, \$Param{AllowSkip}, \$Param{Calendar}, \$Param{UserID}
],
);
# update all periods
for my $Period ( sort keys %{ $Param{Period} } ) {
# db insert
return if !$DBObject->Do(
SQL => '
UPDATE time_accounting_user_period
SET leave_days = ?, date_start = ?, date_end = ?, overtime = ?, weekly_hours = ?,
status = ?
WHERE user_id = ?
AND preference_period = ?',
Bind => [
\$Param{Period}->{$Period}{LeaveDays}, \$Param{Period}->{$Period}{DateStart},
\$Param{Period}->{$Period}{DateEnd}, \$Param{Period}->{$Period}{Overtime},
\$Param{Period}->{$Period}{WeeklyHours}, \$Param{Period}->{$Period}{UserStatus},
\$UserID, \$Period,
]
);
}
return 1;
}
=head2 WorkingUnitsCompletnessCheck()
returns a hash with the incomplete working days and
the information if the incomplete working days are in the allowed
range.
my %WorkingUnitsCheck = $TimeAccountingObject->WorkingUnitsCompletnessCheck(
UserID => 123,
);
=cut
sub WorkingUnitsCompletnessCheck {
my ( $Self, %Param ) = @_;
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserID"
);
return;
}
my $UserID = $Param{UserID};
my %UserData = $Self->UserGet( UserID => $UserID );
if ( !%UserData ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Could not get user data',
);
return;
}
# Skip check for users that are not required to log times.
return () if $UserData{AllowSkip};
my %Data = ();
my $WorkingUnitID = 0;
my %CompleteWorkingDays = ();
# get time object
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
my $DateTimeSettings = $DateTimeObject->Get();
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = (
$DateTimeSettings->{Second},
$DateTimeSettings->{Minute},
$DateTimeSettings->{Hour},
$DateTimeSettings->{Day},
$DateTimeSettings->{Month},
$DateTimeSettings->{Year},
);
# TODO: Search only in the CurrentUserPeriod
# TODO: Search only working units where action_id and project_id is true
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
$DBObject->Prepare(
SQL => '
SELECT DISTINCT time_start
FROM time_accounting_table
WHERE user_id = ?',
Bind => [ \$UserID ],
);
# fetch the data
while ( my @Row = $DBObject->FetchrowArray() ) {
if ( $Row[0] =~ /^(\d+)-(\d+)-(\d+)/ ) {
$CompleteWorkingDays{$1}{$2}{$3} = 1;
}
}
my %UserCurrentPeriod = $Self->UserCurrentPeriodGet(
Year => $Year,
Month => $Month,
Day => $Day,
);
my $WorkingDays = 0;
my $YearStart = 1970;
my $MonthStart = 1;
my $DayStart = 1;
my $YearEnd = $Year;
my $MonthEnd = $Month;
my $DayEnd = $Day;
if (
$UserCurrentPeriod{$UserID}->{DateStart}
&& $UserCurrentPeriod{$UserID}->{DateStart} =~ /^(\d+)-(\d+)-(\d+)/
)
{
$YearStart = $1;
$MonthStart = $2;
$DayStart = $3;
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $Calendar = $UserData{Calendar};
for my $Year ( $YearStart .. $YearEnd ) {
my $MonthStartPoint = $Year == $YearStart ? $MonthStart : 1;
my $MonthEndPoint = $Year == $YearEnd ? $MonthEnd : 12;
for my $Month ( $MonthStartPoint .. $MonthEndPoint ) {
my $DayStartPoint = $Year == $YearStart && $Month == $MonthStart ? $DayStart : 1;
my $DayEndPoint;
if ( $Year == $YearEnd && $Month == $MonthEnd ) {
$DayEndPoint = $DayEnd;
}
else {
$DayEndPoint = $Self->DaysInMonth( $Year, $Month );
}
my $MonthString = sprintf "%02d", $Month;
for my $Day ( $DayStartPoint .. $DayEndPoint ) {
my $VacationCheck = $Self->VacationCheck(
Year => $Year,
Month => $Month,
Day => $Day,
Calendar => $Calendar || '',
);
# Get calendar timezone or use the one of time-accounting object.
my $TimeZone = $ConfigObject->Get( "TimeZone::Calendar" . ( $Calendar || '' ) )
|| $Self->{TimeZone};
my $DayStartTime = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Year,
Month => $Month,
Day => $Day,
Hour => 0,
Minute => 0,
Second => 0,
TimeZone => $TimeZone,
},
);
my $DayStopTime = $DayStartTime->Clone();
$DayStopTime->Set(
Hour => 23,
Minute => 59,
Second => 59,
);
# Convert to the timezone of time-accounting object.
$DayStartTime->ToTimeZone(
TimeZone => $Self->{TimeZone},
);
$DayStopTime->ToTimeZone(
TimeZone => $Self->{TimeZone},
);
# Convert to epochs.
$DayStartTime = $DayStartTime->ToEpoch();
$DayStopTime = $DayStopTime->ToEpoch();
# OLD CODE
#my $DateTimeObject = $Kernel::OM->Create(
# 'Kernel::System::DateTime',
# ObjectParams => {
# Year => $Year,
# Month => $Month,
# Day => $Day,
# },
#);
#my $Date = sprintf "%04d-%02d-%02d", $Year, $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 $Zone = $ConfigObject->Get( "TimeZone::Calendar" . ( $Calendar || '' ) );
#if ($Zone) {
# my $ZoneSeconds = $Zone * 60 * 60;
# $DayStartTime = $DayStartTime - $ZoneSeconds;
# $DayStopTime = $DayStopTime - $ZoneSeconds;
#}
# END
my $ThisDayWorkingTime = $Self->WorkingTime(
StartTime => $DayStartTime,
StopTime => $DayStopTime,
Calendar => $Calendar || '',
) || '0';
my $DayString = sprintf "%02d", $Day;
if ( $ThisDayWorkingTime && !$VacationCheck ) {
$WorkingDays += 1;
}
if (
$ThisDayWorkingTime
&& !$VacationCheck
&& !$CompleteWorkingDays{$Year}{$MonthString}{$DayString}
)
{
$Data{Incomplete}{$Year}{$MonthString}{$DayString} = $WorkingDays;
}
}
}
}
my $MaxIntervallOfIncompleteDays = $ConfigObject->Get('TimeAccounting::MaxIntervalOfIncompleteDays') || '5';
my $MaxIntervallOfIncompleteDaysBeforeWarning
= $ConfigObject->Get('TimeAccounting::MaxIntervalOfIncompleteDaysBeforeWarning')
|| '3';
for my $Year ( sort keys %{ $Data{Incomplete} } ) {
for my $Month ( sort keys %{ $Data{Incomplete}{$Year} } ) {
for my $Day ( sort keys %{ $Data{Incomplete}{$Year}{$Month} } ) {
if (
$Data{Incomplete}{$Year}{$Month}{$Day}
< $WorkingDays - $MaxIntervallOfIncompleteDays
)
{
$Data{EnforceInsert} = 1;
}
elsif (
$Data{Incomplete}{$Year}{$Month}{$Day}
< $WorkingDays - $MaxIntervallOfIncompleteDaysBeforeWarning
)
{
$Data{Warning} = 1;
}
}
}
}
return %Data;
}
=head2 WorkingUnitsGet()
returns a hash with the working units data
my %WorkingUnitsData = $TimeAccountingObject->WorkingUnitsGet(
Year => '2005',
Month => '7',
Day => '13',
UserID => '123',
);
=cut
sub WorkingUnitsGet {
my ( $Self, %Param ) = @_;
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserID"
);
return;
}
my $Date = sprintf "%04d-%02d-%02d", $Param{Year}, $Param{Month}, $Param{Day};
my $DateStart = $Date . " 00:00:00";
my $DateStop = $Date . " 23:59:59";
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# ask the database
$DBObject->Prepare(
SQL => '
SELECT user_id, project_id, action_id, remark, time_start, time_end, period
FROM time_accounting_table
WHERE time_start >= ?
AND time_start <= ?
AND user_id = ?
ORDER by id',
Bind => [ \$DateStart, \$DateStop, \$Param{UserID} ],
);
my %Data = (
Total => 0,
Date => $Date,
);
# fetch the result
ROW:
while ( my @Row = $DBObject->FetchrowArray() ) {
next ROW if $Row[4] !~ m{^ (.+?) \s (\d+:\d+) : (\d+) }xms;
# check if it is a special working unit
if ( $Row[1] == -1 ) {
my $ActionID = $Row[2];
$Data{Sick} = $ActionID == -1 ? 1 : 0;
$Data{LeaveDay} = $ActionID == -2 ? 1 : 0;
$Data{Overtime} = $ActionID == -3 ? 1 : 0;
next ROW;
}
my $StartTime = $2;
my $EndTime = '';
if ( $Row[5] =~ m{^(.+?)\s(\d+:\d+):(\d+)}xms ) {
$EndTime = $2;
# replace 23:59:59 with 24:00
if ( $EndTime eq '23:59' && $3 eq '59' ) {
$EndTime = '24:00';
}
}
my %WorkingUnit = (
UserID => $Row[0],
ProjectID => $Row[1],
ActionID => $Row[2],
Remark => $Row[3],
StartTime => $StartTime,
EndTime => $EndTime,
Period => defined( $Row[6] ) ? sprintf( "%.2f", $Row[6] ) : 0,
);
# only count complete working units
if ( $Row[1] && $Row[2] ) {
$Data{Total} += $WorkingUnit{Period};
}
push @{ $Data{WorkingUnits} }, \%WorkingUnit;
}
return %Data;
}
=head2 WorkingUnitsInsert()
insert working units in the db
$TimeAccountingObject->WorkingUnitsInsert(
Year => '2005',
Month => '07',
Day => '02',
LeaveDay => 1, || 0
Sick => 1, || 0
Overtime => 1, || 0
WorkingUnits => [
{
ProjectID => 1,
ActionID => 23,
Remark => 'SomeText',
StartTime => '7:30',
EndTime => '11:00',
Period => '8.5',
},
{ ...... },
],
UserID => 123,
);
=cut
sub WorkingUnitsInsert {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Year Month Day UserID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "WorkingUnitsInsert: Need $Needed!"
);
return;
}
}
my $Date = sprintf "%04d-%02d-%02d", $Param{Year}, $Param{Month}, $Param{Day};
# add special time working units
my %SpecialAction = (
'Sick' => '-1',
'LeaveDay' => '-2',
'Overtime' => '-3',
);
ELEMENT:
for my $Element (qw(LeaveDay Sick Overtime)) {
next ELEMENT if !$Param{$Element};
my %Unit = (
ProjectID => -1,
ActionID => $SpecialAction{$Element},
Remark => '',
StartTime => '',
EndTime => '',
Period => 0,
);
push @{ $Param{WorkingUnits} }, \%Unit;
}
# insert new working units
UNITREF:
for my $UnitRef ( @{ $Param{WorkingUnits} } ) {
my $StartTime = $Date . ' ' . ( $UnitRef->{StartTime} || '00:00' ) . ':00';
my $EndTime = $Date . ' ' . ( $UnitRef->{EndTime} || '00:00' ) . ':00';
# '' does not work in integer field of PostgreSQL
$UnitRef->{ProjectID} ||= 0;
$UnitRef->{ActionID} ||= 0;
$UnitRef->{Period} ||= 0;
# build DQL
my $SQL = '
INSERT INTO time_accounting_table (user_id, project_id, action_id, remark, time_start,
time_end, period, created )
VALUES ( ?, ?, ?, ?, ?, ?, ?, current_timestamp)';
my $Bind = [
\$Param{UserID}, \$UnitRef->{ProjectID}, \$UnitRef->{ActionID},
\$UnitRef->{Remark}, \$StartTime, \$EndTime, \$UnitRef->{Period},
];
# db insert
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => $SQL,
Bind => $Bind
);
}
return 1;
}
=head2 WorkingUnitsDelete()
deletes working units in the db
$TimeAccountingObject->WorkingUnitsDelete(
Year => '2015',
Month => '7',
Day => '13',
UserID => 123,
);
=cut
sub WorkingUnitsDelete {
my ( $Self, %Param ) = @_;
for my $Needed (qw(Year Month Day UserID)) {
if ( !$Param{$Needed} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "WorkingUnitsInsert: Need $Needed!"
);
return;
}
}
my $Date = sprintf "%04d-%02d-%02d", $Param{Year}, $Param{Month}, $Param{Day};
my $StartTime = $Date . ' 00:00:00';
my $EndTime = $Date . ' 23:59:59';
return if !$Kernel::OM->Get('Kernel::System::DB')->Do(
SQL => '
DELETE FROM time_accounting_table
WHERE time_start >= ?
AND time_start <= ?
AND user_id = ?',
Bind => [ \$StartTime, \$EndTime, \$Param{UserID}, ],
);
return 1;
}
=head2 ProjectActionReporting()
returns a hash with the hours dependent project and action data
my %ProjectData = $TimeAccountingObject->ProjectActionReporting(
Year => 2005,
Month => 7,
UserID => 123, # optional; no UserID means 'of all users'
);
=cut
sub ProjectActionReporting {
my ( $Self, %Param ) = @_;
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
for my $Parameter (qw(Year Month)) {
$Param{$Parameter} = $DBObject->Quote( $Param{$Parameter} ) || '';
if ( !$Param{$Parameter} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "ProjectActionReporting: Need $Parameter!"
);
return;
}
}
# hours per month
my $DaysInMonth = $Self->DaysInMonth( $Param{Year}, $Param{Month} );
my $DateString = $Param{Year} . "-" . sprintf( "%02d", $Param{Month} );
my $SQLDate = "$DateString-$DaysInMonth 23:59:59";
my $SQL = '
SELECT project_id, action_id, period
FROM time_accounting_table
WHERE project_id != -1
AND time_start <= ?';
my @Bind = ( \$SQLDate );
if ( $Param{UserID} ) {
$SQL .= ' AND user_id = ?';
push @Bind, \$Param{UserID};
}
# total hours
$DBObject->Prepare(
SQL => $SQL,
Bind => \@Bind,
);
# fetch the data
my %Data;
ROW:
while ( my @Row = $DBObject->FetchrowArray() ) {
next ROW if !$Row[2];
$Data{ $Row[0] }->{Actions}->{ $Row[1] }->{Total} += $Row[2];
}
my $SQLDateStart = "$DateString-01 00:00:00";
$SQL = '
SELECT project_id, action_id, period
FROM time_accounting_table
WHERE project_id != -1
AND time_start >= ?
AND time_start <= ?';
@Bind = ( \$SQLDateStart, \$SQLDate );
if ( $Param{UserID} ) {
$SQL .= ' AND user_id = ?';
push @Bind, \$Param{UserID};
}
$DBObject->Prepare(
SQL => $SQL,
Bind => \@Bind,
);
# fetch the data
ROW:
while ( my @Row = $DBObject->FetchrowArray() ) {
next ROW if !$Row[2];
$Data{ $Row[0] }->{Actions}->{ $Row[1] }->{PerMonth} += $Row[2];
}
# add readable components
my %Project = $Self->ProjectSettingsGet();
my %Action = $Self->ActionSettingsGet();
for my $ProjectID ( sort keys %Data ) {
$Data{$ProjectID}->{Name} = $Project{Project}->{$ProjectID} || '';
$Data{$ProjectID}->{Status} = $Project{ProjectStatus}->{$ProjectID};
$Data{$ProjectID}->{Description} = $Project{ProjectDescription}->{$ProjectID};
my $ActionsRef = $Data{$ProjectID}->{Actions};
for my $ActionID ( sort keys %{$ActionsRef} ) {
$Data{$ProjectID}->{Actions}->{$ActionID}->{Name} = $Action{$ActionID}->{Action} || '';
}
}
return %Data;
}
=head2 ProjectTotalHours()
returns the sum of all hours related to a project
my $ProjectTotalHours = $TimeAccountingObject->ProjectTotalHours(
ProjectID => 15,
);
=cut
sub ProjectTotalHours {
my ( $Self, %Param ) = @_;
# check needed param
if ( !$Param{ProjectID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'ProjectActionReporting: Need ProjectID!'
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# ask the database
return if !$DBObject->Prepare(
SQL => '
SELECT SUM(period)
FROM time_accounting_table
WHERE project_id = ?',
Bind => [ \$Param{ProjectID} ],
Limit => 1,
);
# fetch the result
my $Total = 0;
while ( my @Row = $DBObject->FetchrowArray() ) {
$Total = $Row[0];
}
return $Total;
}
=head2 ProjectHistory()
returns an array with all WorkingUnits related to a project
my @ProjectHistoryArray = $TimeAccountingObject->ProjectHistory(
ProjectID => 15,
);
This would return
@ProjectHistoryArray = (
{
ID => 999,
UserID => 15,
User => 'Tom',
ActionID => 6,
Action => 'misc',
Remark => 'remark',
TimeStart => '7:00',
TimeEnd => '18:00',
Date => '2008-10-31', # the date of the working unit
Period => 11,
Created => '2008-11-01', # the insert time of the working unit
},
{
ID => 999,
UserID => 16,
User => 'Mane',
ActionID => 7,
Action => 'development',
Remark => 'remark',
TimeStart => '7:00',
TimeEnd => '18:00',
Period => 11,
Date => '2008-11-03',
Created => '2008-11-03',
}
);
=cut
sub ProjectHistory {
my ( $Self, %Param ) = @_;
# check needed param
if ( !$Param{ProjectID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'ProjectActionReporting: Need ProjectID!',
);
return;
}
# call action data to get the readable name of the action
my %ActionData = $Self->ActionSettingsGet();
# get user list
my %ShownUsers = $Kernel::OM->Get('Kernel::System::User')->UserList(
Type => 'Long',
Valid => 0,
);
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# ask the database
$DBObject->Prepare(
SQL => '
SELECT id, user_id, action_id, remark, time_start, time_end, period, created
FROM time_accounting_table
WHERE project_id = ?
ORDER BY time_start',
Bind => [ \$Param{ProjectID} ],
);
# fetch the result
my @Data;
while ( my @Row = $DBObject->FetchrowArray() ) {
my $UserRef = {
ID => $Row[0],
UserID => $Row[1],
User => $ShownUsers{ $Row[1] },
ActionID => $Row[2],
Action => $ActionData{ $Row[2] }{Action},
Remark => $Row[3] || '',
TimeStart => $Row[4],
TimeEnd => $Row[5],
Date => $Row[4],
Period => $Row[6],
Created => $Row[7],
};
$UserRef->{Date} =~ s{(\d\d\d\d-\d\d-\d\d) \s .+ }{$1}xms;
push @Data, $UserRef;
}
return @Data;
}
=head2 LastProjectsOfUser()
returns an array with the last projects of the current user
my @LastProjects = $TimeAccountingObject->LastProjectsOfUser(
UserID => 123,
);
=cut
sub LastProjectsOfUser {
my ( $Self, %Param ) = @_;
if ( !$Param{UserID} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need UserID"
);
return;
}
# get database object
my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
# db select
# I don't use distinct because of ORDER BY problems of PostgreSQL
return if !$DBObject->Prepare(
SQL => '
SELECT project_id FROM time_accounting_table
WHERE user_id = ?
AND project_id <> -1
ORDER BY time_start DESC',
Bind => [ \$Param{UserID} ],
Limit => 40,
);
# fetch the result
my %Projects;
my $Counter = 0;
ROW:
while ( my @Row = $DBObject->FetchrowArray() ) {
next ROW if $Counter > 7;
next ROW if $Projects{ $Row[0] };
$Projects{ $Row[0] } = 1;
$Counter++;
}
return keys %Projects;
}
=head2 DayOfWeek()
Substitute for Date::Pcalc::Day_of_Week() which uses Kernel::System::DateTime.
=cut
sub DayOfWeek {
my ( $Self, $Year, $Month, $Day ) = @_;
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Year,
Month => $Month,
Day => $Day,
TimeZone => 'floating',
},
);
if ( !$DateTimeObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => "Error creating DateTime object.",
);
return;
}
my $DateTimeValues = $DateTimeObject->Get();
return $DateTimeValues->{DayOfWeek};
}
=head2 AddDeltaYMD()
Substitute for Date::Pcalc::Add_Delta_YMD() which uses Kernel::System::DateTime.
=cut
sub AddDeltaYMD {
my ( $Self, $Year, $Month, $Day, $YearsToAdd, $MonthsToAdd, $DaysToAdd ) = @_;
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Year,
Month => $Month,
Day => $Day,
TimeZone => 'floating',
},
);
if ( !$DateTimeObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => "Error creating DateTime object.",
);
return ( $Year, $Month, $Day, );
}
$DateTimeObject->Add(
Years => $YearsToAdd || 0,
Months => $MonthsToAdd || 0,
Days => $DaysToAdd || 0,
);
my $DateTimeValues = $DateTimeObject->Get();
return (
$DateTimeValues->{Year},
$DateTimeValues->{Month},
$DateTimeValues->{Day},
);
}
=head2 DaysInMonth()
Substitute for Date::Pcalc::Days_in_Month() which uses Kernel::System::DateTime.
=cut
sub DaysInMonth {
my ( $Self, $Year, $Month ) = @_;
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Year,
Month => $Month,
Day => 1,
TimeZone => 'floating',
},
);
if ( !$DateTimeObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => "error",
Message => "Error creating DateTime object.",
);
return;
}
my $LastDayOfMonth = $DateTimeObject->LastDayOfMonthGet();
return $LastDayOfMonth->{Day};
}
=head2 VacationCheck()
check if the selected day is a vacation (it does not matter if you
insert 01 or 1 for month or day in the function or in the SysConfig)
returns (true) vacation day if exists, returns false if date is no
vacation day
$TimeAccountingObject->VacationCheck(
Year => 2005,
Month => 7 || '07',
Day => 13,
);
$TimeAccountingObject->VacationCheck(
Year => 2005,
Month => 7 || '07',
Day => 13,
Calendar => 3, # '' is default; 0 is handled like ''
);
=cut
sub VacationCheck {
my ( $Self, %Param ) = @_;
# check required params
for (qw(Year Month Day)) {
if ( !$Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "VacationCheck: Need $_!",
);
return;
}
}
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
%Param,
TimeZone => $Self->{TimeZone},
},
);
if ( !$DateTimeObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Error creating DateTime object.",
);
return;
}
return $DateTimeObject->IsVacationDay(
Calendar => $Param{Calendar},
);
}
=head2 WorkingTime()
get the working time in seconds between these local system times.
my $WorkingTime = $TimeAccountingObject->WorkingTime(
StartTime => $Created,
StopTime => $TimeObject->SystemTime(),
);
my $WorkingTime = $TimeAccountingObject->WorkingTime(
StartTime => $Created,
StopTime => $TimeObject->SystemTime(),
Calendar => 3, # '' is default
);
=cut
sub WorkingTime {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(StartTime StopTime)) {
if ( !defined $Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!",
);
return;
}
}
return 0 if $Param{StartTime} >= $Param{StopTime};
my $StartDateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Epoch => $Param{StartTime},
TimeZone => $Self->{TimeZone},
},
);
my $StopDateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Epoch => $Param{StopTime},
TimeZone => $Self->{TimeZone},
},
);
my $Delta = $StartDateTimeObject->Delta(
DateTimeObject => $StopDateTimeObject,
ForWorkingTime => 1,
Calendar => $Param{Calendar},
);
if ( !IsHashRefWithData($Delta) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Error calculating working time.',
);
return;
}
return $Delta->{AbsoluteSeconds};
}
=head2 SystemTime2Date()
converts a system time to a structured date array.
my ($Sec, $Min, $Hour, $Day, $Month, $Year, $WeekDay) = $TimeAccountingObject->SystemTime2Date(
SystemTime => $TimeObject->SystemTime(),
);
$WeekDay is the day of the week, with 0 indicating Sunday and 3 indicating Wednesday.
=cut
sub SystemTime2Date {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !defined $Param{SystemTime} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need SystemTime!',
);
return;
}
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Epoch => $Param{SystemTime},
},
);
$DateTimeObject->ToTimeZone( TimeZone => $Self->{TimeZone} );
my $DateTimeValues = $DateTimeObject->Get();
my $Year = $DateTimeValues->{Year};
my $Month = sprintf "%02d", $DateTimeValues->{Month};
my $Day = sprintf "%02d", $DateTimeValues->{Day};
my $Hour = sprintf "%02d", $DateTimeValues->{Hour};
my $Min = sprintf "%02d", $DateTimeValues->{Minute};
my $Sec = sprintf "%02d", $DateTimeValues->{Second};
my $WDay = $DateTimeValues->{DayOfWeek} == 7 ? 0 : $DateTimeValues->{DayOfWeek};
return ( $Sec, $Min, $Hour, $Day, $Month, $Year, $WDay );
}
=head2 DayOfWeekToName()
Convert a day number into the day name
my $DayName = $TimeAccountingObject->DayOfWeekToName(
Number => 1 # will return 'Monday'
);
=cut
sub DayOfWeekToName {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !defined $Param{Number} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Number!",
);
return;
}
if ( !IsPositiveInteger( $Param{Number} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Number must be a positive integer!",
);
return;
}
if ( $Param{Number} > 7 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Number must not be grater than 7!",
);
return;
}
my @DayNames = (
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday'
);
return $DayNames[ $Param{Number} - 1 ];
}
=head2 Date2SystemTime()
converts a structured date array to system time of OTRS.
my $SystemTime = $TimeAccountingObject->Date2SystemTime(
Year => 2004,
Month => 8,
Day => 14,
Hour => 22,
Minute => 45,
Second => 0,
);
=cut
sub Date2SystemTime {
my ( $Self, %Param ) = @_;
# check needed stuff
for (qw(Year Month Day Hour Minute Second)) {
if ( !defined $Param{$_} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $_!",
);
return;
}
}
my $DateTimeObject = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
%Param,
TimeZone => $Self->{TimeZone},
},
);
if ( !$DateTimeObject ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message =>
"Invalid Date '$Param{Year}-$Param{Month}-$Param{Day} $Param{Hour}:$Param{Minute}:$Param{Second}'!",
);
return;
}
my $SystemTime = $DateTimeObject->ToEpoch();
return $SystemTime;
}
=head2 TimeStamp2SystemTime()
converts a given time stamp to local system time.
my $SystemTime = $TimeAccountingObject->TimeStamp2SystemTime(
String => '2004-08-14 22:45:00',
);
=cut
sub TimeStamp2SystemTime {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !$Param{String} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need String!',
);
return;
}
my $SystemTime = 0;
# match iso date format
if ( $Param{String} =~ /(\d{4})-(\d{1,2})-(\d{1,2})\s(\d{1,2}):(\d{1,2}):(\d{1,2})/ ) {
$SystemTime = $Self->Date2SystemTime(
Year => $1,
Month => $2,
Day => $3,
Hour => $4,
Minute => $5,
Second => $6,
);
}
# match iso date format (wrong format)
elsif ( $Param{String} =~ /(\d{1,2})-(\d{1,2})-(\d{4})\s(\d{1,2}):(\d{1,2}):(\d{1,2})/ ) {
$SystemTime = $Self->Date2SystemTime(
Year => $3,
Month => $2,
Day => $1,
Hour => $4,
Minute => $5,
Second => $6,
);
}
# match euro time format
elsif ( $Param{String} =~ /(\d{1,2})\.(\d{1,2})\.(\d{4})\s(\d{1,2}):(\d{1,2}):(\d{1,2})/ ) {
$SystemTime = $Self->Date2SystemTime(
Year => $3,
Month => $2,
Day => $1,
Hour => $4,
Minute => $5,
Second => $6,
);
}
# match yyyy-mm-ddThh:mm:ss+tt:zz time format
elsif (
$Param{String}
=~ /(\d{4})-(\d{1,2})-(\d{1,2})T(\d{1,2}):(\d{1,2}):(\d{1,2})(\+|\-)((\d{1,2}):(\d{1,2}))/i
)
{
$SystemTime = $Self->Date2SystemTime(
Year => $1,
Month => $2,
Day => $3,
Hour => $4,
Minute => $5,
Second => $6,
);
}
# match mail time format
elsif (
$Param{String}
=~ /((...),\s+|)(\d{1,2})\s(...)\s(\d{4})\s(\d{1,2}):(\d{1,2}):(\d{1,2})\s((\+|\-)(\d{2})(\d{2})|...)/
)
{
my @MonthMap = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
my $Month = 1;
my $MonthString = $4;
for my $MonthCount ( 0 .. $#MonthMap ) {
if ( $MonthString =~ /$MonthMap[$MonthCount]/i ) {
$Month = $MonthCount + 1;
}
}
$SystemTime = $Self->Date2SystemTime(
Year => $5,
Month => $Month,
Day => $3,
Hour => $6,
Minute => $7,
Second => $8,
); # + $Self->{TimeSecDiff};
}
elsif ( # match yyyy-mm-ddThh:mm:ssZ
$Param{String} =~ /(\d{4})-(\d{1,2})-(\d{1,2})T(\d{1,2}):(\d{1,2}):(\d{1,2})Z$/
)
{
$SystemTime = $Self->Date2SystemTime(
Year => $1,
Month => $2,
Day => $3,
Hour => $4,
Minute => $5,
Second => $6,
);
}
# return error
if ( !defined $SystemTime ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Invalid Date '$Param{String}'!",
);
}
# return system time
return $SystemTime;
}
1;
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut