TimeTrex/classes/modules/core/UserDateFactory.class.php

431 lines
14 KiB
PHP

<?php
/*********************************************************************************
*
* TimeTrex is a Workforce Management program developed by
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
*
* You should have received a copy of the GNU Affero General Public License along
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
*
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
*
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by TimeTrex".
*
********************************************************************************/
/**
* @package Core
*/
class UserDateFactory extends Factory {
protected $table = 'user_date';
protected $pk_sequence_name = 'user_date_id_seq'; //PK Sequence name
var $user_obj = null;
var $pay_period_obj = null;
/**
* @return bool
*/
function getUserObject() {
return $this->getGenericObject( 'UserListFactory', $this->getUser(), 'user_obj' );
}
/**
* @return bool
*/
function getPayPeriodObject() {
return $this->getGenericObject( 'PayPeriodListFactory', $this->getPayPeriod(), 'pay_period_obj' );
}
/**
* @return mixed
*/
function getUser() {
return $this->getGenericDataValue( 'user_id' );
}
/**
* @param string $value UUID
* @return bool
*/
function setUser( $value ) {
$value = TTUUID::castUUID( $value );
return $this->setGenericDataValue( 'user_id', $value );
}
/**
* @return bool
*/
function findPayPeriod() {
if ( $this->getDateStamp() > 0
&& TTUUID::isUUID( $this->getUser() ) && $this->getUser() != TTUUID::getZeroID() && $this->getUser() != TTUUID::getNotExistID() ) {
$pplf = TTnew( 'PayPeriodListFactory' ); /** @var PayPeriodListFactory $pplf */
$pplf->getByUserIdAndEndDate( $this->getUser(), $this->getDateStamp() );
if ( $pplf->getRecordCount() == 1 ) {
$pay_period_id = $pplf->getCurrent()->getID();
Debug::Text( 'Pay Period Id: ' . $pay_period_id, __FILE__, __LINE__, __METHOD__, 10 );
return $pay_period_id;
}
}
Debug::Text( 'Unable to find pay period for User ID: ' . $this->getUser() . ' Date Stamp: ' . $this->getDateStamp(), __FILE__, __LINE__, __METHOD__, 10 );
return false;
}
/**
* @return bool|mixed
*/
function getPayPeriod() {
return $this->getGenericDataValue( 'pay_period_id' );
}
/**
* @param string $value UUID
* @return bool
*/
function setPayPeriod( $value = null ) {
$value = trim( $value );
if ( $value == null ) {
$value = $this->findPayPeriod();
}
$value = TTUUID::castUUID( $value );
return $this->setGenericDataValue( 'pay_period_id', $value );
}
/**
* @param bool $raw
* @return bool|int
*/
function getDateStamp( $raw = false ) {
$value = $this->getGenericDataValue( 'date_stamp' );
if ( $value !== false ) {
if ( $raw === true ) {
return $value;
} else {
//return $this->db->UnixTimeStamp( $this->data['start_date'] );
//strtotime is MUCH faster than UnixTimeStamp
//Must use ADODB for times pre-1970 though.
return TTDate::strtotime( $value );
}
}
return false;
}
/**
* @param int $value EPOCH
* @return bool
*/
function setDateStamp( $value ) {
$value = ( !is_int( $value ) && $value !== null ) ? trim( $value ) : $value;//Dont trim integer values, as it changes them to strings.
return $this->setGenericDataValue( 'date_stamp', $value );
}
/**
* @param string $user_id UUID
* @param int $date EPOCH
* @param null $timezone
* @return bool
*/
static function findOrInsertUserDate( $user_id, $date, $timezone = null ) {
//Allow user_id=0 for saving open schedule shifts.
$user_id = TTUUID::castUUID( $user_id );
if ( $user_id != '' && $date > 0 ) {
$date = TTDate::getMiddleDayEpoch( $date ); //Use mid day epoch so the timezone conversion across DST doesn't affect the date.
if ( $timezone == null ) {
//Find the employees preferred timezone, base the user date off that instead of the pay period timezone,
//as it can be really confusing to the user if they punch in at 10AM on Sept 27th, but it records as Sept 26th because
//the PP Schedule timezone is 12hrs different or something.
$uplf = TTnew( 'UserPreferenceListFactory' ); /** @var UserPreferenceListFactory $uplf */
$uplf->getByUserID( $user_id );
if ( $uplf->getRecordCount() > 0 ) {
$timezone = $uplf->getCurrent()->getTimeZone();
}
}
$date = TTDate::convertTimeZone( $date, $timezone );
//Debug::text(' Using TimeZone: '. $timezone .' Date: '. TTDate::getDate('DATE+TIME', $date) .' ('.$date.')', __FILE__, __LINE__, __METHOD__, 10);
$udlf = TTnew( 'UserDateListFactory' ); /** @var UserDateListFactory $udlf */
$udlf->getByUserIdAndDate( $user_id, $date );
if ( $udlf->getRecordCount() == 1 ) {
$id = $udlf->getCurrent()->getId();
//Debug::text(' Found Already Existing User Date ID: '. $id, __FILE__, __LINE__, __METHOD__, 10);
return $id;
} else if ( $udlf->getRecordCount() == 0 ) {
Debug::text( ' Inserting new UserDate row. User ID: ' . $user_id . ' Date: ' . $date, __FILE__, __LINE__, __METHOD__, 10 );
//Insert new row
$udf = TTnew( 'UserDateFactory' ); /** @var UserDateFactory $udf */
$udf->setUser( $user_id );
$udf->setDateStamp( $date );
$udf->setPayPeriod();
if ( $udf->isValid() ) {
return $udf->Save();
} else {
Debug::text( ' INVALID user date row. Pay Period Locked?', __FILE__, __LINE__, __METHOD__, 10 );
}
} else if ( $udlf->getRecordCount() > 1 ) {
Debug::text( ' More then 1 user date row was detected!!: ' . $udlf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
}
} else {
Debug::text( ' Invalid arguments... User ID: ' . $user_id . ' Date: ' . $date, __FILE__, __LINE__, __METHOD__, 10 );
}
Debug::text( ' Cant find or insert User Date ID. User ID: ' . $user_id . ' Date: ' . $date, __FILE__, __LINE__, __METHOD__, 10 );
return false;
}
/**
* @param string $user_id UUID
* @param int $date EPOCH
* @return bool
*/
static function getUserDateID( $user_id, $date ) {
$user_date_id = UserDateFactory::findOrInsertUserDate( $user_id, $date );
Debug::text( ' User Date ID: ' . $user_date_id, __FILE__, __LINE__, __METHOD__, 10 );
if ( $user_date_id != '' ) {
return $user_date_id;
}
Debug::text( ' No User Date ID found', __FILE__, __LINE__, __METHOD__, 10 );
return false;
}
/**
* This function deletes all rows from other tables that require a user_date row.
* We need to keep this in its own function so we can call it BEFORE
* actually deleting the user_date row. As we need to have a unique
* index on user_id, date_stamp so we never get duplicate rows, essentially making the deleted
* column useless.
* @param string $user_date_id UUID
* @return bool
*/
static function deleteChildren( $user_date_id ) {
return false;
}
/**
* @return bool
*/
function isUnique() {
//Allow user_id=0 for OPEN scheduled shifts.
if ( $this->getUser() === false ) {
return false;
}
if ( $this->getDateStamp() == false ) {
return false;
}
$ph = [
'user_id' => $this->getUser(),
'date_stamp' => $this->db->BindDate( $this->getDateStamp() ),
];
$query = 'select id from ' . $this->getTable() . ' where user_id = ? AND date_stamp = ? AND deleted=0';
$user_date_id = $this->db->GetOne( $query, $ph );
Debug::Arr( $user_date_id, 'Unique User Date.', __FILE__, __LINE__, __METHOD__, 10 );
if ( $user_date_id === false ) {
return true;
} else {
if ( $user_date_id == $this->getId() ) {
return true;
}
}
return false;
}
/**
* @param bool $ignore_warning
* @return bool
*/
function Validate( $ignore_warning = true ) {
//
// BELOW: Validation code moved from set*() functions.
// User
if ( $this->getUser() != TTUUID::getZeroID() ) {
$ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */
$this->Validator->isResultSetWithRows( 'user',
$ulf->getByID( $this->getUser() ),
TTi18n::gettext( 'Invalid Employee' )
);
}
// Pay Period
if ( $this->getPayPeriod() != false ) {
$pplf = TTnew( 'PayPeriodListFactory' ); /** @var PayPeriodListFactory $pplf */
$this->Validator->isResultSetWithRows( 'pay_period',
$pplf->getByID( $this->getPayPeriod() ),
TTi18n::gettext( 'Invalid Pay Period' )
);
}
// Date
$this->Validator->isDate( 'date_stamp',
$this->getDateStamp(),
TTi18n::gettext( 'Incorrect date' )
);
if ( $this->Validator->isError( 'date_stamp' ) == false ) {
if ( $this->getDateStamp() <= 0 ) {
$this->Validator->isTRUE( 'date_stamp',
false,
TTi18n::gettext( 'Incorrect date' )
);
}
}
//
// ABOVE: Validation code moved from set*() functions.
//
//Make sure pay period isn't locked!
if ( TTUUID::isUUID( $this->getPayPeriod() ) && $this->getPayPeriod() != TTUUID::getZeroID() && $this->getPayPeriod() != TTUUID::getNotExistID() ) {
if ( is_object( $this->getPayPeriodObject() ) && $this->getPayPeriodObject()->getIsLocked() == true ) {
$this->Validator->isTRUE( 'pay_period',
false,
TTi18n::gettext( 'Pay Period is Currently Locked' ) );
}
}
//Make sure this is a UNIQUE user_date row.
$this->Validator->isTRUE( 'date_stamp',
$this->isUnique(),
TTi18n::gettext( 'Employee can not have duplicate entries on the same day' ) );
//Make sure the date isn't BEFORE the first pay period.
$pplf = TTnew( 'PayPeriodListFactory' ); /** @var PayPeriodListFactory $pplf */
$pplf->getByUserID( $this->getUser(), 1, null, null, [ 'a.start_date' => 'asc' ] );
if ( $pplf->getRecordCount() > 0 ) {
$first_pp_obj = $pplf->getCurrent();
if ( $this->getDateStamp() < $first_pp_obj->getStartDate() ) {
$this->Validator->isTRUE( 'pay_period',
false,
TTi18n::gettext( 'Date specified is before the first pay period started' ) );
}
}
//else {
//This causes a validation error when saving a record without a pay period (ie: in the future a few weeks)
//Therefore its breaking critical functionality and should be disabled.
//This also affects saving OPEN shifts when as no user is assigned to them and therefore no pay period.
/*
$this->Validator->isTRUE( 'pay_period',
FALSE,
TTi18n::gettext('Pay period missing or employee is not assigned to a pay period schedule') );
*/
//}
return true;
}
/**
* @return bool
*/
function preSave() {
if ( $this->getDeleted() == true ) {
//Delete (for real) any already deleted rows in hopes to prevent a
//unique index conflict across user_id, date_stamp, deleted
$udlf = TTnew( 'UserDateListFactory' ); /** @var UserDateListFactory $udlf */
$udlf->deleteByUserIdAndDateAndDeleted( $this->getUser(), $this->getDateStamp(), true );
}
return true;
}
/**
* @return bool
*/
function postSave() {
$this->removeCache( $this->getId() );
//Debug::Text('Post Save... Deleted: '. (int)$this->getDeleted(), __FILE__, __LINE__, __METHOD__, 10);
//Delete punch control/schedules assigned to this.
if ( $this->getDeleted() == true ) {
//Delete schedules assigned to this user date.
//Turn off any re-calc's
$slf = TTnew( 'ScheduleListFactory' ); /** @var ScheduleListFactory $slf */
$slf->getByUserDateID( $this->getId() );
if ( $slf->getRecordCount() > 0 ) {
foreach ( $slf as $schedule_obj ) {
$schedule_obj->setDeleted( true );
$schedule_obj->Save();
}
}
$pclf = TTnew( 'PunchControlListFactory' ); /** @var PunchControlListFactory $pclf */
$pclf->getByUserDateID( $this->getId() );
if ( $pclf->getRecordCount() > 0 ) {
foreach ( $pclf as $pc_obj ) {
$pc_obj->setDeleted( true );
$pc_obj->Save();
}
}
//Delete exceptions
$elf = TTnew( 'ExceptionListFactory' ); /** @var ExceptionListFactory $elf */
$elf->getByUserDateID( $this->getId() );
if ( $elf->getRecordCount() > 0 ) {
foreach ( $elf as $e_obj ) {
$e_obj->setDeleted( true );
$e_obj->Save();
}
}
//Delete user_date_total rows too
$udtlf = TTnew( 'UserDateTotalListFactory' ); /** @var UserDateTotalListFactory $udtlf */
$udtlf->getByUserDateID( $this->getId() );
if ( $udtlf->getRecordCount() > 0 ) {
foreach ( $udtlf as $udt_obj ) {
$udt_obj->setDeleted( true );
$udt_obj->Save();
}
}
}
return true;
}
}
?>