# --
# 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).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L.
=cut