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

1668 lines
56 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Modules::AgentTimeAccountingEdit;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsArrayRefWithData);
use Kernel::Language qw(Translatable);
our $ObjectManagerDisabled = 1;
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {%Param};
bless( $Self, $Type );
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
$Self->{TimeZone} = $Param{TimeZone}
|| $Param{UserTimeZone}
|| $DateTimeObject->OTRSTimeZoneGet();
return $Self;
}
sub PreRun {
my ( $Self, %Param ) = @_;
# permission check
return 1 if !$Self->{AccessRo};
my $DateTimeObjectCurrent = $Kernel::OM->Create('Kernel::System::DateTime');
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = $TimeAccountingObject->SystemTime2Date(
SystemTime => $DateTimeObjectCurrent->ToEpoch(),
);
my %User = $TimeAccountingObject->UserCurrentPeriodGet(
Year => $Year,
Month => $Month,
Day => $Day,
);
return if !$User{ $Self->{UserID} };
my %IncompleteWorkingDays = $TimeAccountingObject->WorkingUnitsCompletnessCheck(
UserID => $Self->{UserID},
);
# redirect if incomplete working day are out of range
if (
$IncompleteWorkingDays{EnforceInsert}
&& $Self->{Action} ne 'AgentTimeAccountingEdit'
&& $Self->{Action} !~ /AgentTimeAccounting/
)
{
return $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Redirect(
OP => 'Action=AgentTimeAccountingEdit',
);
}
return;
}
sub Run {
my ( $Self, %Param ) = @_;
my @MonthArray = (
'', 'January', 'February', 'March', 'April', 'May',
'June', 'July', 'August', 'September', 'October', 'November',
'December',
);
my @WeekdayArray = ( 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', );
# get needed objects
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
my $DateTimeObjectCurrent = $Kernel::OM->Create('Kernel::System::DateTime');
# ---------------------------------------------------------- #
# show confirmation dialog to delete the entry of this day
# ---------------------------------------------------------- #
if ( $ParamObject->GetParam( Param => 'DeleteDialog' ) ) {
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = $TimeAccountingObject->SystemTime2Date(
SystemTime => $DateTimeObjectCurrent->ToEpoch(),
);
# get params
for my $Parameter (qw(Status Year Month Day)) {
$Param{$Parameter} = $ParamObject->GetParam( Param => $Parameter );
}
# Check Date
if ( !$Param{Year} || !$Param{Month} || !$Param{Day} ) {
$Param{Year} = $Year;
$Param{Month} = $Month;
$Param{Day} = $Day;
}
else {
$Param{Year} = sprintf( "%02d", $Param{Year} );
$Param{Month} = sprintf( "%02d", $Param{Month} );
$Param{Day} = sprintf( "%02d", $Param{Day} );
}
my $Output = $LayoutObject->Output(
Data => {%Param},
TemplateFile => 'AgentTimeAccountingDelete',
);
# build the returned data structure
my %Data = (
HTML => $Output,
DialogType => 'Confirmation',
);
# return JSON-String because of AJAX-Mode
my $OutputJSON = $LayoutObject->JSONEncode(
Data => \%Data,
);
return $LayoutObject->Attachment(
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
Content => $OutputJSON,
Type => 'inline',
NoCache => 1,
);
}
# ---------------------------------------------------------- #
# delete object from database
# ---------------------------------------------------------- #
if ( $Self->{Subaction} eq 'Delete' ) {
for my $Parameter (qw(Day Month Year)) {
$Param{$Parameter} = $ParamObject->GetParam( Param => $Parameter );
}
if ( !$Self->{AccessRo} ) {
return $LayoutObject->NoPermission(
WithHeader => 'yes',
);
}
if (
!$TimeAccountingObject->WorkingUnitsDelete(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
UserID => $Self->{UserID},
)
)
{
return $LayoutObject->ErrorScreen();
}
return $LayoutObject->Redirect(
OP =>
"Action=$Self->{Action};Year=$Param{Year};Month=$Param{Month};Day=$Param{Day}",
);
}
#---------------------------------------------------------- #
# mass entry
# ---------------------------------------------------------- #
elsif ( $Self->{Subaction} eq 'MassEntry' ) {
# permission check
if ( !$Self->{AccessRo} ) {
return $LayoutObject->NoPermission(
WithHeader => 'yes',
);
}
# get params
for my $Parameter (qw(Dates LeaveDay Sick Overtime)) {
$Param{$Parameter} = $ParamObject->GetParam( Param => $Parameter ) || '';
}
# split up dates
my @Dates = split /[|]/, $Param{Dates};
my $InsertError = 0;
# save entries in the db
for my $Date (@Dates) {
my ( $Year, $Month, $Day ) = split /[-]/, $Date;
if (
!$TimeAccountingObject->WorkingUnitsInsert(
Year => $Year,
Month => $Month,
Day => $Day,
LeaveDay => $Param{LeaveDay} || 0,
Sick => $Param{Sick} || 0,
Overtime => $Param{Overtime} || 0,
UserID => $Self->{UserID},
)
)
{
$InsertError = 1;
}
}
# redirect to edit screen with log message
return $LayoutObject->Redirect(
OP => 'Action=AgentTimeAccountingEdit;Notification='
. ( $InsertError ? 'Error' : 'Successful' ),
);
}
# ---------------------------------------------------------- #
# edit the time accounting elements
# ---------------------------------------------------------- #
# permission check
if ( !$Self->{AccessRo} ) {
return $LayoutObject->NoPermission(
WithHeader => 'yes',
);
}
# get params
for my $Parameter (qw(Status Year Month Day Notification)) {
$Param{$Parameter} = $ParamObject->GetParam( Param => $Parameter ) || '';
}
$Param{RecordsNumber} = $ParamObject->GetParam( Param => 'RecordsNumber' ) || 8;
$Param{InsertWorkingUnits} = $ParamObject->GetParam( Param => 'InsertWorkingUnits' );
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = $TimeAccountingObject->SystemTime2Date(
SystemTime => $DateTimeObjectCurrent->ToEpoch(),
);
# Check Date
if ( !$Param{Year} || !$Param{Month} || !$Param{Day} ) {
$Param{Year} = $Year;
$Param{Month} = $Month;
$Param{Day} = $Day;
}
else {
$Param{Year} = sprintf( "%02d", $Param{Year} );
$Param{Month} = sprintf( "%02d", $Param{Month} );
$Param{Day} = sprintf( "%02d", $Param{Day} );
}
# check if the given date is a valid date
# if not valid, set the date to today
my $DateTimeValid = $DateTimeObjectCurrent->Validate(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
Hour => 0,
Minute => 0,
Second => 0,
TimeZone => 'UTC',
);
if ( !$DateTimeValid ) {
$Param{Year} = $Year;
$Param{Month} = $Month;
$Param{Day} = $Day;
$Param{'WrongDate'} = 1;
}
my %User = $TimeAccountingObject->UserCurrentPeriodGet(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
);
# for initial use, the first agent with rw-right will be redirected
# to 'Setting', so he can do the initial settings
if ( !$User{ $Self->{UserID} } ) {
return $Self->_FirstUserRedirect();
}
my %IncompleteWorkingDays = $TimeAccountingObject->WorkingUnitsCompletnessCheck(
UserID => $Self->{UserID},
);
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $MaxAllowedInsertDays = $ConfigObject->Get('TimeAccounting::MaxAllowedInsertDays') || '10';
( $Param{YearAllowed}, $Param{MonthAllowed}, $Param{DayAllowed} )
= $TimeAccountingObject->AddDeltaYMD( $Year, $Month, $Day, 0, 0, -$MaxAllowedInsertDays );
my $DateTimeObjectGiven = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
}
);
my $DateTimeObjectAllowed = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Param{YearAllowed},
Month => $Param{MonthAllowed},
Day => $Param{DayAllowed},
}
);
if ( $DateTimeObjectGiven->Compare( DateTimeObject => $DateTimeObjectAllowed ) < 0 ) {
if ( !$IncompleteWorkingDays{Incomplete}{ $Param{Year} }{ $Param{Month} }{ $Param{Day} } ) {
return $LayoutObject->Redirect(
OP =>
"Action=AgentTimeAccountingView;Year=$Param{Year};Month=$Param{Month};Day=$Param{Day}",
);
}
}
# store last screen
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
SessionID => $Self->{SessionID},
Key => 'LastScreen',
Value =>
"Action=$Self->{Action};Year=$Param{Year};Month=$Param{Month};Day=$Param{Day}",
);
$Param{Month_to_Text} = $MonthArray[ $Param{Month} ];
( $Param{YearBack}, $Param{MonthBack}, $Param{DayBack} )
= $TimeAccountingObject->AddDeltaYMD( $Param{Year}, $Param{Month}, $Param{Day}, 0, 0, -1 );
( $Param{YearNext}, $Param{MonthNext}, $Param{DayNext} )
= $TimeAccountingObject->AddDeltaYMD( $Param{Year}, $Param{Month}, $Param{Day}, 0, 0, 1 );
my $ReduceTimeRef = $ConfigObject->Get('TimeAccounting::ReduceTime');
# hashes to store server side errors
my %Errors = ();
my %ServerErrorData = ();
my $ErrorIndex = 1;
my %Data;
my %ActionList = $Self->_ActionList();
# Edit Working Units
if ( $Param{Status} ) {
# arrays to save all start and end times for some checks
my ( @StartTimes, @EndTimes );
# delete previous entries for this day and user
$TimeAccountingObject->WorkingUnitsDelete(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
UserID => $Self->{UserID},
);
my %CheckboxCheck = ();
for my $Element (qw(LeaveDay Sick Overtime)) {
my $Value = $ParamObject->GetParam( Param => $Element );
if ($Value) {
$CheckboxCheck{$Element} = 1;
$Param{$Element} = 'checked="checked"';
}
else {
$Param{$Element} = '';
}
}
# if more than one check box was checked it is a server error
if ( scalar keys %CheckboxCheck > 1 ) {
for my $Checkbox ( sort keys %CheckboxCheck ) {
$Errors{ $Checkbox . 'Invalid' } = 'ServerError';
}
}
else {
# insert values (if any)
if ( scalar keys %CheckboxCheck > 0 ) {
if (
!$TimeAccountingObject->WorkingUnitsInsert(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
LeaveDay => $CheckboxCheck{LeaveDay} || 0,
Sick => $CheckboxCheck{Sick} || 0,
Overtime => $CheckboxCheck{Overtime} || 0,
UserID => $Self->{UserID},
)
)
{
return $LayoutObject->ErrorScreen(
Message => Translatable('Can\'t insert Working Units!'),
);
}
$Param{SuccessfulInsert} = 1;
}
}
# get check item object
my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem');
ID:
for my $ID ( 1 .. $Param{RecordsNumber} ) {
# arrays to save the server errors block to show the error messages
my ( @StartTimeServerErrorBlock, @EndTimeServerErrorBlock, @PeriodServerErrorBlock ) = ();
for my $Parameter (qw(ProjectID ActionID Remark StartTime EndTime Period)) {
$Param{$Parameter} = $ParamObject->GetParam( Param => $Parameter . '[' . $ID . ']' );
if ( $Param{$Parameter} ) {
my $ParamRef = \$Param{$Parameter};
# delete leading and tailing spaces
$ParamRef = $CheckItemObject->StringClean(
StringRef => $ParamRef,
TrimLeft => 1,
TrimRight => 1,
);
}
}
next ID if !$Param{ProjectID} && !$Param{ActionID};
# check for missing values
if ( $Param{ProjectID} && !$Param{ActionID} ) {
$Errors{$ErrorIndex}{ActionIDInvalid} = 'ServerError';
}
if ( !$Param{ProjectID} && $Param{ActionID} ) {
$Errors{$ErrorIndex}{ProjectIDInvalid} = 'ServerError';
}
# create a valid period
my $Period = $Param{Period};
if ( $Period =~ /^(\d+),(\d+)/ ) {
$Period = $1 . "." . $2;
}
#allow format hh:mm
elsif ( $Param{Period} =~ /^(\d+):(\d+)/ ) {
$Period = $1 + $2 / 60;
}
# if start or end times are missing, delete the other entry
if ( !$Param{StartTime} || !$Param{EndTime} ) {
$Param{StartTime} = '';
$Param{EndTime} = '';
}
# add ':00' to the time, if it doesn't have it yet
if ( $Param{StartTime} && $Param{StartTime} !~ /^(\d+):(\d+)$/ ) {
$Param{StartTime} .= ':00';
}
if ( $Param{EndTime} && $Param{EndTime} !~ /^(\d+):(\d+)$/ ) {
$Param{EndTime} .= ':00';
}
if (
( $Param{StartTime} =~ /^(\d+):(\d+)$/ )
&& ( $Param{EndTime} =~ /^(\d+):(\d+)$/ )
)
{
$Param{StartTime} =~ /^(\d+):(\d+)$/;
my $StartTime = $1 * 60 + $2;
$Param{EndTime} =~ /^(\d+):(\d+)$/;
my $EndTime = $1 * 60 + $2;
if ( $ReduceTimeRef->{ $ActionList{ $Param{ActionID} } } ) {
$Period = ( $EndTime - $StartTime ) / 60
* $ReduceTimeRef->{ $ActionList{ $Param{ActionID} } } / 100;
}
else {
$Period = ( $EndTime - $StartTime ) / 60;
}
# end time must be after start time
if ( $EndTime <= $StartTime ) {
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
push @EndTimeServerErrorBlock, 'EndTimeBeforeStartTimeServerError';
}
$StartTimes[$ID] = $StartTime;
$EndTimes[$ID] = $EndTime;
}
else {
if ( $Param{StartTime} && $Param{StartTime} !~ /^(\d+):(\d+)$/ ) {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
push @StartTimeServerErrorBlock, 'StartTimeInvalidFormatServerError';
}
if ( $Param{EndTime} && $Param{EndTime} !~ /^(\d+):(\d+)$/ ) {
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
push @EndTimeServerErrorBlock, 'EndTimeInvalidFormatServerError';
}
}
# negative times are not allowed
if ( $Param{StartTime} =~ /^-(\d+):(\d+)$/ ) {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
push @StartTimeServerErrorBlock, 'StartTimeNegativeServerError';
}
if ( $Param{EndTime} =~ /^-(\d+):(\d+)$/ ) {
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
push @EndTimeServerErrorBlock, 'EndTimeNegativeServerError';
}
# repeated hours are not allowed
POSITION:
for ( my $Position = $ID - 1; $Position >= 1; $Position-- ) {
next POSITION if !defined $StartTimes[$Position];
next POSITION if !defined $StartTimes[$ID];
if (
$StartTimes[$Position] > $StartTimes[$ID]
&& $StartTimes[$Position] < $EndTimes[$ID]
)
{
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
if ( !grep {/^EndTimeRepeatedHourServerError/} @EndTimeServerErrorBlock ) {
push @EndTimeServerErrorBlock, 'EndTimeRepeatedHourServerError';
}
}
if (
$EndTimes[$Position] > $StartTimes[$ID]
&& $EndTimes[$Position] < $EndTimes[$ID]
)
{
if ( $EndTimes[$ID] > $EndTimes[$Position] ) {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
if (
!grep {/^StartTimeRepeatedHourServerError$/}
@StartTimeServerErrorBlock
)
{
push @StartTimeServerErrorBlock, 'StartTimeRepeatedHourServerError';
}
}
else {
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
if ( !grep {/^EndTimeRepeatedHourServerError$/} @EndTimeServerErrorBlock ) {
push @EndTimeServerErrorBlock, 'EndTimeRepeatedHourServerError';
}
}
}
if ( $StartTimes[$Position] == $StartTimes[$ID] ) {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
if ( !grep {/^StartTimeRepeatedHourServerError$/} @StartTimeServerErrorBlock ) {
push @StartTimeServerErrorBlock, 'StartTimeRepeatedHourServerError';
}
}
if ( $EndTimes[$Position] == $EndTimes[$ID] ) {
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
if ( !grep {/^EndTimeRepeatedHourServerError$/} @EndTimeServerErrorBlock ) {
push @EndTimeServerErrorBlock, 'EndTimeRepeatedHourServerError';
}
}
if (
$StartTimes[$ID] > $StartTimes[$Position]
&& $StartTimes[$ID] < $EndTimes[$Position]
)
{
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
if ( !grep {/^StartTimeRepeatedHourServerError$/} @StartTimeServerErrorBlock ) {
push @StartTimeServerErrorBlock, 'StartTimeRepeatedHourServerError';
}
}
if (
$EndTimes[$ID] > $StartTimes[$Position]
&& $EndTimes[$ID] < $EndTimes[$Position]
)
{
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
if ( !grep {/^EndTimeRepeatedHourServerError$/} @EndTimeServerErrorBlock ) {
push @EndTimeServerErrorBlock, 'EndTimeRepeatedHourServerError';
}
}
}
# '24:00' is only permitted as end time
if ( $StartTimes[$ID] && $StartTimes[$ID] == 1440 ) {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
push @StartTimeServerErrorBlock, 'StartTime24Hours';
}
# times superior to 24:00 are not allowed
if ( $StartTimes[$ID] && $StartTimes[$ID] > 1440 ) {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
push @StartTimeServerErrorBlock, 'StartTimeInvalid';
}
if ( $EndTimes[$ID] && $EndTimes[$ID] > 1440 ) {
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
push @EndTimeServerErrorBlock, 'EndTimeInvalid';
}
# add reference to the server error messages to be shown
if ( $Errors{$ErrorIndex} && $Errors{$ErrorIndex}{StartTimeInvalid} ) {
$Errors{$ErrorIndex}{StartTimeServerErrorBlock} = \@StartTimeServerErrorBlock;
}
if ( $Errors{$ErrorIndex} && $Errors{$ErrorIndex}{EndTimeInvalid} ) {
$Errors{$ErrorIndex}{EndTimeServerErrorBlock} = \@EndTimeServerErrorBlock;
}
# overwrite period if there are start and end times
if ( $StartTimes[$ID] && $EndTimes[$ID] ) {
$Period = $EndTimes[$ID] - $StartTimes[$ID];
# convert period from minutes to hours
$Period /= 60;
}
# check for errors in the period
if ( $Period == 0 ) {
push @PeriodServerErrorBlock, 'ZeroHoursPeriodServerError';
if (
$ConfigObject->Get('TimeAccounting::InputHoursWithoutStartEndTime')
)
{
$Errors{$ErrorIndex}{PeriodInvalid} = 'ServerError';
}
else {
$Errors{$ErrorIndex}{StartTimeInvalid} = 'ServerError';
$Errors{$ErrorIndex}{EndTimeInvalid} = 'ServerError';
}
}
else {
if ( $Period < 0 ) {
$Errors{$ErrorIndex}{PeriodInvalid} = 'ServerError';
push @PeriodServerErrorBlock, 'NegativePeriodServerError';
}
}
if ( $Period > 24 ) {
$Errors{$ErrorIndex}{PeriodInvalid} = 'ServerError';
push @PeriodServerErrorBlock, 'InvalidHoursPeriodServerError';
}
if ( $Errors{$ErrorIndex} && $Errors{$ErrorIndex}{PeriodInvalid} ) {
$Errors{$ErrorIndex}{PeriodServerErrorBlock} = \@PeriodServerErrorBlock;
}
# if there was an error on this row, save all data in the server error hash
if ( defined $Errors{$ErrorIndex} ) {
for my $Parameter (qw(ProjectID ActionID Remark StartTime EndTime)) {
$ServerErrorData{$ErrorIndex}{$Parameter} = $Param{$Parameter};
}
$ServerErrorData{$ErrorIndex}{Period} = $Period;
}
# otherwise, save row on the DB
else {
# initialize the array of working units
@{ $Data{WorkingUnits} } = ();
# replace 24:00 for 23:59:59 to be a valid entry in the DB
$Param{EndTime} = $Param{EndTime} eq '24:00' ? '23:59:59' : $Param{EndTime};
my %WorkingUnit = (
ProjectID => $Param{ProjectID},
ActionID => $Param{ActionID},
Remark => $Param{Remark},
StartTime => $Param{StartTime},
EndTime => $Param{EndTime},
Period => $Period,
);
push @{ $Data{WorkingUnits} }, \%WorkingUnit;
$Data{Year} = $Param{Year};
$Data{Month} = $Param{Month};
$Data{Day} = $Param{Day};
$Data{UserID} = $Self->{UserID};
if ( !$TimeAccountingObject->WorkingUnitsInsert(%Data) ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Can\'t insert Working Units!'),
);
}
$Param{SuccessfulInsert} = 1;
}
# increment the error index if there was an error on this row
$ErrorIndex++ if ( defined $Errors{$ErrorIndex} );
}
if (%ServerErrorData) {
$Param{SuccessfulInsert} = undef;
}
}
# Show Working Units
# get existing working units
%Data = $TimeAccountingObject->WorkingUnitsGet(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
UserID => $Self->{UserID},
);
# get number of working units (=records)
# if bigger than RecordsNumber, more than the number of default records were saved for
# this date
if ( $Data{WorkingUnits} ) {
my $WorkingUnitsCount = @{ $Data{WorkingUnits} };
if ( $WorkingUnitsCount > $Param{RecordsNumber} ) {
$Param{RecordsNumber} = $WorkingUnitsCount;
}
}
my %Frontend;
if ( $ConfigObject->Get('TimeAccounting::InputHoursWithoutStartEndTime') ) {
$Param{PeriodBlock} = 'UnitInputPeriod';
$Frontend{PeriodNote} = '*';
}
else {
$Param{PeriodBlock} = 'UnitPeriodWithoutInput';
$Frontend{PeriodNote} = '';
}
if ( $DateTimeObjectCurrent->Compare( DateTimeObject => $DateTimeObjectGiven ) ) {
$LayoutObject->Block(
Name => 'UnitBlock',
Data => { %Param, %Frontend },
);
}
# get sick, leave day and overtime
$Param{Sick} = $Data{Sick} ? 'checked="checked"' : '';
$Param{LeaveDay} = $Data{LeaveDay} ? 'checked="checked"' : '';
$Param{Overtime} = $Data{Overtime} ? 'checked="checked"' : '';
$Param{Total} = $Data{Total};
# set action list and related constraints
# generate a JavaScript Array which will be output to the template
my @ActionIDs = sort { $ActionList{$a} cmp $ActionList{$b} } keys %ActionList;
my @JSActions;
for my $ActionID (@ActionIDs) {
push @JSActions, [
$ActionID,
$ActionList{$ActionID}
];
}
$LayoutObject->AddJSData(
Key => 'ActionList',
Value => \@JSActions
);
my $ActionListConstraints = $ConfigObject->Get('TimeAccounting::ActionListConstraints');
my @JSActionListConstraints;
for my $ProjectNameRegExp ( sort keys %{$ActionListConstraints} ) {
my $ActionNameRegExp = $ActionListConstraints->{$ProjectNameRegExp};
s{(['"\\])}{\\$1}smxg for ( $ProjectNameRegExp, $ActionNameRegExp );
push @JSActionListConstraints, [
$ProjectNameRegExp,
$ActionNameRegExp
];
}
$LayoutObject->AddJSData(
Key => 'ActionListConstraints',
Value => \@JSActionListConstraints
);
# build a working unit array
my @Units = (undef);
if ( $Data{WorkingUnits} ) {
push @Units, @{ $Data{WorkingUnits} };
}
$ErrorIndex = 0;
# build units
for my $ID ( 1 .. $Param{RecordsNumber} ) {
$Param{ID} = $ID;
my $UnitRef = $Units[$ID];
my $ShowError = 0;
if ( !$UnitRef ) {
$ErrorIndex++;
$ShowError = 1;
}
# get data of projects
my $ProjectList = $Self->_ProjectList(
SelectedID => $UnitRef->{ProjectID}
|| $ServerErrorData{$ErrorIndex}{ProjectID}
|| '',
);
$Param{ProjectID} = $UnitRef->{ProjectID}
|| $ServerErrorData{$ErrorIndex}{ProjectID}
|| '';
$Param{ProjectName} = '';
# set common params for project selection
my %ProjectOptionParams = (
Name => "ProjectID[$ID]",
ID => "ProjectID$ID",
Translation => 0,
Class => 'Validate_TimeAccounting_Project ProjectSelection '
. ( $Errors{$ErrorIndex}{ProjectIDInvalid} || '' ),
OnChange => "TimeAccounting.Agent.EditTimeRecords.FillActionList($ID);",
Title => $LayoutObject->{LanguageObject}->Translate("Project"),
);
my $EnableAutoCompletion = $ConfigObject->Get("TimeAccounting::EnableAutoCompletion") || 0;
my $Class = $EnableAutoCompletion ? ' Modernize' : '';
# set params for modern inputs
$ProjectOptionParams{Class} .= $Class;
if ( $EnableAutoCompletion && $ConfigObject->Get("TimeAccounting::UseFilter") ) {
# add filter for the previous projects
$ProjectOptionParams{Data} = $ProjectList->{AllProjects};
$ProjectOptionParams{Filters} = {
LastProjects => {
Name => $LayoutObject->{LanguageObject}->Translate('Last Projects'),
Values => $ProjectList->{LastProjects},
},
};
# if there are the previous projects, expand filter dialog
if ( scalar @{ $ProjectList->{LastProjects} } > 1 ) {
$ProjectOptionParams{ExpandFilters} = 1;
# make the filter active by default if 'ActiveFilter' is enabled
if ( $ConfigObject->Get("TimeAccounting::ActiveFilter") ) {
$ProjectOptionParams{Filters}->{LastProjects}->{Active} = 1;
}
}
}
else {
# set params for traditional selects (and modern input fields if filter is not used)
my @Projects = ( @{ $ProjectList->{LastProjects} }, @{ $ProjectList->{AllProjects} } );
$ProjectOptionParams{Data} = \@Projects;
}
# build projects select
$Frontend{ProjectOption} = $LayoutObject->BuildSelection(
%ProjectOptionParams,
);
# action list initially only contains empty and selected element as well as elements
# configured for selected project
# if no constraints are configured, all actions will be displayed
my $ActionData = $Self->_ActionListConstraints(
ProjectID => $UnitRef->{ProjectID} || $ServerErrorData{$ErrorIndex}->{ProjectID},
ProjectList => $ProjectList,
ActionList => \%ActionList,
ActionListConstraints => $ActionListConstraints,
);
$ActionData->{''} = '-';
if ( $UnitRef && $UnitRef->{ActionID} && $ActionList{ $UnitRef->{ActionID} } ) {
$ActionData->{ $UnitRef->{ActionID} } = $ActionList{ $UnitRef->{ActionID} };
}
elsif (
$ServerErrorData{$ErrorIndex}
&& $ServerErrorData{$ErrorIndex}{ActionID}
&& $ActionList{ $ServerErrorData{$ErrorIndex}{ActionID} }
)
{
$ActionData->{ $ServerErrorData{$ErrorIndex}{ActionID} }
= $ActionList{ $ServerErrorData{$ErrorIndex}{ActionID} };
}
$Frontend{ActionOption} = $LayoutObject->BuildSelection(
Data => $ActionData,
SelectedID => $UnitRef->{ActionID} || $ServerErrorData{$ErrorIndex}{ActionID} || '',
Name => "ActionID[$ID]",
ID => "ActionID$ID",
Translation => 0,
Class => 'Validate_DependingRequiredAND Validate_Depending_ProjectID'
. $ID
. ' ActionSelection '
. ( $Errors{$ErrorIndex}{ActionIDInvalid} || '' )
. $Class,
Title => $LayoutObject->{LanguageObject}->Translate("Task"),
);
$Param{Remark} = $UnitRef->{Remark} || $ServerErrorData{$ErrorIndex}{Remark} || '';
my $Period;
if (
( $UnitRef->{Period} && $UnitRef->{Period} == 0 )
|| (
defined $ServerErrorData{$ErrorIndex}{Period}
&& $ServerErrorData{$ErrorIndex}{Period} == 0
)
)
{
$Period = 0;
}
else {
$Period = $UnitRef->{Period} || $ServerErrorData{$ErrorIndex}{Period} || '';
}
for my $TimePeriod (qw(StartTime EndTime)) {
if ($ShowError) {
$Param{$TimePeriod} = $ServerErrorData{$ErrorIndex}{$TimePeriod} // '';
}
else {
$Param{$TimePeriod} = $UnitRef->{$TimePeriod} // '';
}
}
if (
$Period
&& $Param{StartTime} eq '00:00'
&& $Param{EndTime} eq '00:00'
)
{
$Param{StartTime} = '';
$Param{EndTime} = '';
}
$LayoutObject->Block(
Name => 'Unit',
Data => {
%Param,
%Frontend,
%{ $Errors{$ErrorIndex} },
},
);
# add proper server error message for the start and end times
my $ServerErrorBlockName;
if ( $Errors{$ErrorIndex} && $Errors{$ErrorIndex}{StartTimeInvalid} ) {
if ( scalar @{ $Errors{$ErrorIndex}{StartTimeServerErrorBlock} } > 0 ) {
while ( @{ $Errors{$ErrorIndex}{StartTimeServerErrorBlock} } ) {
$ServerErrorBlockName = shift @{ $Errors{$ErrorIndex}{StartTimeServerErrorBlock} };
$LayoutObject->Block(
Name => $ServerErrorBlockName,
Data => {},
);
}
}
else {
$LayoutObject->Block(
Name => 'StartTimeGenericServerError',
Data => {},
);
}
}
if ( $Errors{$ErrorIndex} && $Errors{$ErrorIndex}{EndTimeInvalid} ) {
if ( scalar @{ $Errors{$ErrorIndex}{EndTimeServerErrorBlock} } > 0 ) {
while ( @{ $Errors{$ErrorIndex}{EndTimeServerErrorBlock} } ) {
$ServerErrorBlockName = shift @{ $Errors{$ErrorIndex}{EndTimeServerErrorBlock} };
$LayoutObject->Block(
Name => $ServerErrorBlockName,
Data => {}
);
}
}
else {
$LayoutObject->Block(
Name => 'EndTimeGenericServerError',
Data => {},
);
}
}
$LayoutObject->Block(
Name => $Param{PeriodBlock},
Data => {
Period => $Period,
ID => $ID,
%{ $Errors{$ErrorIndex} },
},
);
# add proper server error message for the period
if ( $Errors{$ErrorIndex} && $Errors{$ErrorIndex}{PeriodInvalid} ) {
if ( scalar @{ $Errors{$ErrorIndex}{PeriodServerErrorBlock} } > 0 ) {
while ( @{ $Errors{$ErrorIndex}{PeriodServerErrorBlock} } ) {
$ServerErrorBlockName = shift @{ $Errors{$ErrorIndex}{PeriodServerErrorBlock} };
$LayoutObject->Block(
Name => $ServerErrorBlockName,
Data => {},
);
}
}
else {
$LayoutObject->Block(
Name => 'PeriodGenericServerError',
Data => {},
);
}
}
else {
$LayoutObject->Block(
Name => 'PeriodGenericServerError',
Data => {},
);
}
# validity check
if (
$Param{InsertWorkingUnits}
&& $UnitRef->{ProjectID}
&& $UnitRef->{ActionID}
&& $Param{Sick}
)
{
$Param{BlockName} = $LayoutObject->{LanguageObject}
->Translate('Are you sure that you worked while you were on sick leave?');
}
elsif (
$Param{InsertWorkingUnits}
&& $UnitRef->{ProjectID}
&& $UnitRef->{ActionID}
&& $Param{LeaveDay}
)
{
$Param{BlockName} = $LayoutObject->{LanguageObject}
->Translate('Are you sure that you worked while you were on vacation?');
}
elsif (
$Param{InsertWorkingUnits}
&& $UnitRef->{ProjectID}
&& $UnitRef->{ActionID}
&& $Param{Overtime}
)
{
$Param{BlockName} = $LayoutObject->{LanguageObject}
->Translate('Are you sure that you worked while you were on overtime leave?');
}
}
if ( $DateTimeObjectCurrent->Compare( DateTimeObject => $DateTimeObjectGiven ) ) {
$Param{Total} = sprintf( "%.2f", ( $Param{Total} || 0 ) );
$LayoutObject->Block(
Name => 'Total',
Data => { %Param, %Frontend },
);
}
# validity checks start
my $ErrorNote;
if ( $Param{Total} && $Param{Total} > 24 ) {
$ErrorNote = Translatable('Can\'t save settings, because a day has only 24 hours!');
}
elsif ( $Param{InsertWorkingUnits} && $Param{Total} && $Param{Total} > 16 ) {
$Param{BlockName}
= $LayoutObject->{LanguageObject}->Translate('Are you sure that you worked more than 16 hours?');
}
if ($ErrorNote) {
if (
!$TimeAccountingObject->WorkingUnitsDelete(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
UserID => $Self->{UserID},
)
)
{
return $LayoutObject->ErrorScreen(
Message => Translatable('Can\'t delete Working Units!'),
);
}
}
if ( $Param{BlockName} && $Param{SuccessfulInsert} ) {
$LayoutObject->AddJSData(
Key => 'BlockName',
Value => $Param{BlockName},
);
}
$Param{Date} = $LayoutObject->BuildDateSelection(
%Param,
Validate => 1,
Prefix => '',
Format => 'DateInputFormat',
);
if ( $DateTimeObjectGiven->Compare( DateTimeObject => $DateTimeObjectAllowed ) < 0 ) {
if (
$IncompleteWorkingDays{Incomplete}{ $Param{Year} }{ $Param{Month} }{ $Param{Day} }
&& !$Param{SuccessfulInsert}
)
{
$LayoutObject->Block(
Name => 'Readonly',
Data => {
Description =>
Translatable(
'This Date is out of limit, but you haven\'t insert this day yet, so you get one(!) chance to insert'
),
},
);
}
}
# get incomplete working days
my %IncompleteWorkingDaysList;
%IncompleteWorkingDays = $TimeAccountingObject->WorkingUnitsCompletnessCheck(
UserID => $Self->{UserID},
);
for my $YearID ( sort keys %{ $IncompleteWorkingDays{Incomplete} } ) {
for my $MonthID ( sort keys %{ $IncompleteWorkingDays{Incomplete}{$YearID} } ) {
for my $DayID (
sort keys %{ $IncompleteWorkingDays{Incomplete}{$YearID}{$MonthID} }
)
{
$IncompleteWorkingDaysList{"$YearID-$MonthID-$DayID"} = "$YearID-$MonthID-$DayID";
$Param{Incomplete} = 1;
}
}
}
# Show text, if incomplete working days are available
if ( $Param{Incomplete} ) {
# if mass entry option is enabled, show list of working days
if ( $ConfigObject->Get("TimeAccounting::AllowMassEntryForUser") ) {
$LayoutObject->Block(
Name => 'IncompleteWorkingDaysMassEntry',
);
for my $WorkingDays ( sort keys %IncompleteWorkingDaysList ) {
my ( $Year, $Month, $Day ) = split( /-/, $IncompleteWorkingDaysList{$WorkingDays} );
my $DateTimeObjectWorkingDay = $Kernel::OM->Create(
'Kernel::System::DateTime',
ObjectParams => {
Year => $Year,
Month => $Month,
Day => $Day,
}
);
$LayoutObject->Block(
Name => 'IncompleteWorkingDaysMassEntrySingleDay',
Data => {
Date => $IncompleteWorkingDaysList{$WorkingDays},
DateHR => $DateTimeObjectWorkingDay->ToString(),
Weekday => $TimeAccountingObject->DayOfWeekToName(
Number => $TimeAccountingObject->DayOfWeek( $Year, $Month, $Day )
),
Year => $Year,
Month => $Month,
Day => $Day,
},
);
}
}
# otherwise show incomplete working days as a drop-down
else {
# check if current day is incomplete
# if yes, select the current day in the drop-down
# otherwise select the empty element
my $SelectedID;
if ( $IncompleteWorkingDaysList{"$Param{Year}-$Param{Month}-$Param{Day}"} ) {
$SelectedID = "$Param{Year}-$Param{Month}-$Param{Day}";
}
my $IncompleWorkingDaysSelect = $LayoutObject->BuildSelection(
Data => \%IncompleteWorkingDaysList,
SelectedID => $SelectedID,
Name => "IncompleteWorkingDaysList",
PossibleNone => 1,
Title =>
$LayoutObject->{LanguageObject}->Translate("Incomplete Working Days"),
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'IncompleteWorkingDays',
Data => {
IncompleteWorkingDaysSelect => $IncompleWorkingDaysSelect,
},
);
}
}
my %UserData = $TimeAccountingObject->UserGet(
UserID => $Self->{UserID},
);
my $VacationCheck = $TimeAccountingObject->VacationCheck(
Year => $Param{Year},
Month => $Param{Month},
Day => $Param{Day},
Calendar => $UserData{Calendar},
);
$Param{Weekday}
= $Kernel::OM->Get('Kernel::System::TimeAccounting')->DayOfWeek( $Param{Year}, $Param{Month}, $Param{Day} );
# get working days of the user's calendar
my $CalendarName = 'TimeWorkingHours';
$CalendarName .= $UserData{Calendar} ? "::Calendar$UserData{Calendar}" : '';
my $CalendarWorkingHours = $ConfigObject->Get($CalendarName);
# show "other times" block, if necessary
if (
IsArrayRefWithData( $CalendarWorkingHours->{ $WeekdayArray[ $Param{Weekday} - 1 ] } )
&& !$VacationCheck
)
{
$LayoutObject->Block(
Name => 'OtherTimes',
Data => {
%Param,
%Frontend,
%Errors,
},
);
}
$Param{Weekday_to_Text} = $WeekdayArray[ $Param{Weekday} - 1 ];
# integrate the handling for required remarks in relation to projects
$Param{RemarkRegExp} = $Self->_Project2RemarkRegExp();
$LayoutObject->AddJSData(
Key => 'RemarkRegExp',
Value => $Param{RemarkRegExp},
);
# build output
my $Output = $LayoutObject->Header(
Title => 'Edit',
);
$Output .= $LayoutObject->NavigationBar();
$LayoutObject->Block(
Name => 'OverviewProject',
Data => { %Param, %Frontend },
);
if ( !$IncompleteWorkingDays{EnforceInsert} ) {
# show create project link, if allowed
my %UserData = $TimeAccountingObject->UserGet(
UserID => $Self->{UserID},
);
if ( $UserData{CreateProject} ) {
$LayoutObject->Block(
Name => 'CreateProject',
);
}
}
if ($ErrorNote) {
$Output .= $LayoutObject->Notify(
Info => $ErrorNote,
Priority => 'Error',
);
}
elsif ( defined $Param{SuccessfulInsert} )
{
$Output .= $LayoutObject->Notify(
Info => Translatable('Successful insert!'),
);
}
# show mass entry notification
if ( $Param{Notification} eq 'Error' ) {
$Output .= $LayoutObject->Notify(
Info => Translatable('Error while inserting multiple dates!'),
Priority => 'Error',
);
}
elsif ( $Param{Notification} eq 'Successful' ) {
$Output .= $LayoutObject->Notify(
Info => Translatable('Successfully inserted entries for several dates!'),
);
}
# show notification if wrong date was selected
if ( $Param{WrongDate} ) {
$Output .= $LayoutObject->Notify(
Info => Translatable('Entered date was invalid! Date was changed to today.'),
Priority => 'Error',
);
}
$LayoutObject->AddJSData(
Key => 'Year',
Value => $Param{Year},
);
$LayoutObject->AddJSData(
Key => 'Month',
Value => $Param{Month},
);
$LayoutObject->AddJSData(
Key => 'Day',
Value => $Param{Day},
);
$Output .= $LayoutObject->Output(
TemplateFile => 'AgentTimeAccountingEdit',
Data => {
%Param,
%Frontend,
},
);
$Output .= $LayoutObject->Footer();
return $Output;
}
sub _FirstUserRedirect {
my $Self = shift;
# For initial usage, the first agent with 'rw' rights will be redirected to 'Setting'. Then they can configure
# initial settings for the time accounting feature.
# Define action and get its frontend module registration.
my $Action = 'AgentTimeAccountingSetting';
my $Config = $Kernel::OM->Get('Kernel::Config')->Get('Frontend::Module')->{$Action};
# Get group names from config.
my @GroupNames = @{ $Config->{Group} || [] };
my $Permission = 0;
# If access is restricted, allow access only if user has appropriate permissions in configured group(s).
if (@GroupNames) {
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
# Get user groups, where the user has the appropriate permissions.
my %Groups = $GroupObject->GroupMemberList(
UserID => $Self->{UserID},
Type => 'rw',
Result => 'HASH',
);
GROUP:
for my $GroupName (@GroupNames) {
next GROUP if !$GroupName;
# Get the group ID.
my $GroupID = $GroupObject->GroupLookup(
Group => $GroupName,
);
next GROUP if !$GroupID;
# Stop checking if membership in at least one group is found.
if ( $Groups{$GroupID} ) {
$Permission = 1;
last GROUP;
}
}
}
# Otherwise, always allow access.
else {
$Permission = 1;
}
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
return $LayoutObject->Redirect( OP => "Action=$Action" ) if $Permission;
return $LayoutObject->ErrorScreen(
Message =>
Translatable('No time period configured, or the specified date is outside of the defined time periods.'),
Comment => Translatable('Please contact the time accounting administrator to update your time periods!'),
);
}
sub _ActionList {
my $Self = shift;
my %ActionList;
my %Action = $Kernel::OM->Get('Kernel::System::TimeAccounting')->ActionSettingsGet();
# get action settings
ACTIONID:
for my $ActionID ( sort keys %Action ) {
next ACTIONID if !$Action{$ActionID}{ActionStatus};
next ACTIONID if !$Action{$ActionID}{Action};
$ActionList{$ActionID} = $Action{$ActionID}{Action};
}
$ActionList{''} = '';
return %ActionList;
}
sub _ActionListConstraints {
my ( $Self, %Param ) = @_;
my %List;
if ( $Param{ProjectID} && keys %{ $Param{ActionListConstraints} } ) {
my $ProjectName;
PROJECT:
for my $Project ( @{ $Param{ProjectList}->{AllProjects} } ) {
if ( $Project->{Key} eq $Param{ProjectID} ) {
$ProjectName = $Project->{Value};
last PROJECT;
}
}
if ( defined($ProjectName) ) {
# loop over actions to find matches for configured project
# and action reg-exp pairs
for my $ActionID ( sort keys %{ $Param{ActionList} } ) {
my $ActionName = $Param{ActionList}->{$ActionID};
PROJECTNAMEREGEXP:
for my $ProjectNameRegExp ( sort keys %{ $Param{ActionListConstraints} } ) {
my $ActionNameRegExp = $Param{ActionListConstraints}->{$ProjectNameRegExp};
if (
$ProjectName =~ m{$ProjectNameRegExp}smx
&& $ActionName =~ m{$ActionNameRegExp}smx
)
{
$List{$ActionID} = $ActionName;
last PROJECTNAMEREGEXP;
}
}
}
}
}
# all available actions will be added if no action was added above (possible misconfiguration)
if ( !keys %List ) {
for my $ActionID ( sort keys %{ $Param{ActionList} } ) {
my $ActionName = $Param{ActionList}->{$ActionID};
$List{$ActionID} = $ActionName;
}
}
return \%List;
}
sub _ProjectList {
my ( $Self, %Param ) = @_;
# get time accounting object
my $TimeAccountingObject = $Kernel::OM->Get('Kernel::System::TimeAccounting');
# get project settings
my %Project = $TimeAccountingObject->ProjectSettingsGet(
Status => 'valid',
);
if ( !$Self->{LastProjectsRef} ) {
# get the last projects
my @LastProjects = $TimeAccountingObject->LastProjectsOfUser(
UserID => $Self->{UserID},
);
# add the favorites
%{ $Self->{LastProjectsRef} } = map { $_ => 1 } @LastProjects;
}
my @LastProjects = (
{
Key => '',
Value => '-',
},
);
# add the separator
PROJECTID:
for my $ProjectID (
sort { $Project{Project}{$a} cmp $Project{Project}{$b} }
keys %{ $Project{Project} }
)
{
next PROJECTID if !$Self->{LastProjectsRef}->{$ProjectID};
my %Hash = (
Key => $ProjectID,
Value => $Project{Project}{$ProjectID},
);
push @LastProjects, \%Hash;
}
@LastProjects = $Self->_ProjectListConstraints(
List => \@LastProjects,
SelectedID => $Param{SelectedID} || '',
);
my @AllProjects;
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# check if AutoCompletion is disabled
# in this case a separator is needed between two lists of projects (last and all)
if (
!$ConfigObject->Get("TimeAccounting::EnableAutoCompletion")
|| !$ConfigObject->Get("TimeAccounting::UseFilter")
)
{
if ( scalar @LastProjects > 1 ) {
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# add last projects separator to the beginning of the list
my $LastProjectsStr = $LayoutObject->{LanguageObject}->Translate('Last Selected Projects');
unshift @LastProjects, {
Key => '0',
Value => "---$LastProjectsStr---",
Disabled => 1,
};
# add all projects separator right after the last selected projects list
my $AllProjectsStr = $LayoutObject->{LanguageObject}->Translate('All Projects');
push @LastProjects, {
Key => '0',
Value => "---$AllProjectsStr---",
Disabled => 1,
};
}
}
else {
@AllProjects = (
{
Key => '',
Value => '-',
},
);
}
# add all allowed projects to the list
PROJECTID:
for my $ProjectID (
sort { $Project{Project}{$a} cmp $Project{Project}{$b} }
keys %{ $Project{Project} }
)
{
next PROJECTID if !$Project{Project}{$ProjectID};
my %Hash = (
Key => $ProjectID,
Value => $Project{Project}{$ProjectID},
);
if ( $Param{SelectedID} && $Param{SelectedID} eq $ProjectID ) {
$Hash{Selected} = 1;
}
push @AllProjects, \%Hash;
}
@AllProjects = $Self->_ProjectListConstraints(
List => \@AllProjects,
SelectedID => $Param{SelectedID} || '',
);
my %Projects = (
LastProjects => \@LastProjects,
AllProjects => \@AllProjects,
);
return \%Projects;
}
sub _ProjectListConstraints {
my ( $Self, %Param ) = @_;
my @List;
my $ProjectCount = 0;
my $ProjectListConstraints = $Kernel::OM->Get('Kernel::Config')->Get('TimeAccounting::ProjectListConstraints');
if ( keys %{$ProjectListConstraints} ) {
# get groups of current user
my %Groups = $Kernel::OM->Get('Kernel::System::Group')->GroupMemberList(
UserID => $Self->{UserID},
Type => 'ro',
Result => 'HASH',
);
%Groups = map { $Groups{$_} => 1 } keys %Groups;
# get project list constraints
my %ProjectRegex;
for my $ProjectRegex ( sort keys %{$ProjectListConstraints} ) {
for my $ProjectGroup ( split /,\s*/, $ProjectListConstraints->{$ProjectRegex} ) {
if ( $Groups{$ProjectGroup} ) {
$ProjectRegex{$ProjectRegex} = 1;
}
}
}
my @ProjectRegex = keys %ProjectRegex;
# reduce project list according to configuration
if ( ref( $Param{List} ) && @ProjectRegex ) {
my $ElementCount = 0;
for my $Project ( @{ $Param{List} } ) {
my $ProjectName = $Project->{Value};
# empty first element, last projects separator and currently selected project
if ( !$ElementCount || !$Project->{Key} || $Project->{Key} eq $Param{SelectedID} ) {
push @List, $Project;
}
else {
PROJECTREGEXP:
for my $ProjectRegex (@ProjectRegex) {
if ( $ProjectName =~ m{$ProjectRegex}smx ) {
push @List, $Project;
$ProjectCount++;
last PROJECTREGEXP;
}
}
}
$ElementCount++;
}
}
}
# get full project list if constraints resulted in empty project list or if constraints aren't
# configured (possible misconfiguration)
if ( !$ProjectCount ) {
@List = @{ $Param{List} };
}
return @List;
}
# integrate the handling for required remarks in relation to projects
sub _Project2RemarkRegExp {
my $Self = shift;
my @Projects2Remark = ();
my %ProjectData = $Kernel::OM->Get('Kernel::System::TimeAccounting')->ProjectSettingsGet(
Status => 'valid',
);
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
return '' if !$ConfigObject->Get('TimeAccounting::Project2RemarkRegExp');
my $Project2RemarkRegExp = $ConfigObject->Get('TimeAccounting::Project2RemarkRegExp');
for my $ProjectID ( sort keys %{ $ProjectData{Project} } ) {
if ( $ProjectData{Project}{$ProjectID} =~ m{$Project2RemarkRegExp}smx ) {
push @Projects2Remark, $ProjectID;
}
}
return join '|', @Projects2Remark;
}
1;