<?php
/*********************************************************************************
 * The contents of this file are subject to the TimeTrex Public License Version
 * 1.1.0 ("License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at http://www.TimeTrex.com/TPL
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * All copies of the Covered Code must include on each user interface screen:
 *    (i) the "Powered by TimeTrex" logo and
 *    (ii) the TimeTrex copyright notice
 * in the same form as they appear in the distribution.  See full license for
 * requirements.
 *
 * The Original Code is: TimeTrex Open Source
 * The Initial Developer of the Original Code is TimeTrex Payroll Services
 * Portions created by TimeTrex are Copyright (C) 2004-2007 TimeTrex Payroll Services;
 * All Rights Reserved.
 *
 ********************************************************************************/
/*
 * $Revision: 2308 $
 * $Id: PunchControlFactory.class.php 2308 2008-12-22 23:06:24Z ipso $
 * $Date: 2008-12-22 15:06:24 -0800 (Mon, 22 Dec 2008) $
 */

/**
 * @package Module_Punch
 */
class PunchControlFactory extends Factory {
	protected $table = 'punch_control';
	protected $pk_sequence_name = 'punch_control_id_seq'; //PK Sequence name

	protected $tmp_data = NULL;

	protected $user_date_obj = NULL;
	protected $job_obj = NULL;
	protected $job_item_obj = NULL;
	protected $meal_policy_obj = NULL;

	function getUserDateObject() {
		if ( is_object($this->user_date_obj) ) {
			return $this->user_date_obj;
		} else {
			$udlf = new UserDateListFactory();
			$udlf->getById( $this->getUserDateID() );
			if ( $udlf->getRecordCount() > 0 ) {
				$this->user_date_obj = $udlf->getCurrent();
				return $this->user_date_obj;
			}

			return FALSE;
		}
	}

	function getJobObject() {
		if ( is_object($this->job_obj) ) {
			return $this->job_obj;
		} else {
			$jlf = new JobListFactory();
			$jlf->getById( $this->getJob() );
			if ( $jlf->getRecordCount() > 0 ) {
				$this->job_obj = $jlf->getCurrent();
				return $this->job_obj;
			}

			return FALSE;
		}
	}

	function getJobItemObject() {
		if ( is_object($this->job_item_obj) ) {
			return $this->job_item_obj;
		} else {
			$jilf = new JobItemListFactory();
			$jilf->getById( $this->getJobItem() );
			if ( $jilf->getRecordCount() > 0 ) {
				$this->job_item_obj = $jilf->getCurrent();
				return $this->job_item_obj;
			}

			return FALSE;
		}
	}

	function findUserDate($user_id, $epoch, $status_id = 10, $punch_id = 0 ) {
		/*
			Issues to consider:
				1. Employee punches out at 11:00PM, then is called in early at 4AM to start a new shift.
				Don't want to pair these punches.

				2. Employee starts 11:00PM shift late at 1:00AM the next day. Works until 7AM, then comes in again
				at 11:00PM the same day and works until 4AM, then 4:30AM to 7:00AM. The 4AM-7AM punches need to be paired on the same day.

				3. Ambulance EMT works 36hours straight in a single punch.

				*Perhaps we should handle lunch punches and normal punches differently? Lunch punches have
				a different "continuous time setting then normal punches.

				*Change daily continuous time to:
				* Group (Normal) Punches: X hours before midnight to punches X hours after midnight
				* Group (Lunch/Break) Punches: X hours before midnight to punches X hours after midnight
				*	Normal punches X hours after midnight group to punches X hours before midnight.
				*	Lunch/Break punches X hours after midnight group to punches X hours before midnight.

				OR, what if we change continuous time to be just the gap between punches that cause
					a new day to start? Combine this with daily cont. time so we know what the window
					is for punches to begin the gap search. Or we can always just search for a previous
					punch Xhrs before the current punch.
					- Do we look back to a In punch, or look back to an Out punch though? I think an Out Punch.
						What happens if they forgot to punch out though?
					Logic:
						If this is an Out punch:
							Find previous punch back to maximum shift time to find an In punch to pair it with.
						Else, if this is an In punch:
							Find previous punch back to maximum shift time to find an Out punch to combine it with.
							If out punch is found inside of new_shift trigger time, we place this punch on the previous day.
							Else: we place this punch on todays date.


				* Minimum time between punches to cause a new shift to start: Xhrs (default: 4hrs)
					new_day_trigger_time
					Call it: Minimum time-off that triggers new shift:
						Minimum Time-Off Between Shifts:
				* Maximum shift time: Xhrs (for ambulance service) default to 16 or 24hrs?
					This is essentially how far back we look for In punch to pair out punches with.
					maximum_shift_length
					- Add checks to ensure that no punch pair exceeds the maximum_shift_length

		*/
		//Get pay period new_day_trigger/maximum_shift time
		$ppslf = new PayPeriodScheduleListFactory();
		$ppslf->getByUserId( $user_id );
		if ( $ppslf->getRecordCount() == 1 ) {
			$pps_obj = $ppslf->getCurrent();
			Debug::Text(' Pay Period Schedule Maximum Shift Time: '. $pps_obj->getMaximumShiftTime(), __FILE__, __LINE__, __METHOD__,10);

			//If this is an out punch, we need to find the timestamp of the In punch to base
			//maximum shift time off of.
			$plf = new PunchListFactory();
			if ( $status_id == 20 ) {
				//getPreviousPunchByUserIdAndEpoch uses <= $epoch, so we minus one second from it
				//so it doesn't find the punch we may be editing.
				$plf->getPreviousPunchByUserIdAndEpochAndNotPunchIDAndMaximumShiftTime( $user_id, $epoch-1, $punch_id, $pps_obj->getMaximumShiftTime() );
				if ( $plf->getRecordCount() > 0 ) {
					$p_obj = $plf->getCurrent();

					//Make sure the previous punch is an In punch only. This fixes the bug where the employee forgot
					//to punch In the next day after a regular shift, so the first punch of the day was an Out punch
					//causing TimeTrex to place it on the previous day.
					if ( $p_obj->getStatus() == 10 ) {
						$epoch = $p_obj->getTimeStamp();
						Debug::Text('Found In Punch Epoch: '. $epoch .' - '. TTDate::getDate('DATE+TIME', $epoch) , __FILE__, __LINE__, __METHOD__,10);
					} else {
						Debug::Text('Previous punch was an Out punch, not pairing with it.', __FILE__, __LINE__, __METHOD__,10);
					}
					unset($p_obj);
				} else {
					Debug::Text('DID NOT Find In Punch Epoch: '. $epoch, __FILE__, __LINE__, __METHOD__,10);
				}
			}

			//Get previous punch, if its an Out punch, make sure this punch is within the new_day_trigger_time to be grouped
			//on the same day as it.
			$plf->getPreviousPunchByUserIdAndEpochAndNotPunchIDAndMaximumShiftTime( $user_id, $epoch-1, $punch_id, $pps_obj->getNewDayTriggerTime() );
			if ( $plf->getRecordCount() > 0 ) {
				$p_obj = $plf->getCurrent();
				//Debug::Text('Current Punch Epoch: '. $epoch .' First Punch Epoch: '.$p_obj->getTimeStamp(), __FILE__, __LINE__, __METHOD__,10);

				if ( ( $epoch - $p_obj->getTimeStamp() ) <= $pps_obj->getNewDayTriggerTime() ) {
					$user_date_id = $p_obj->getPunchControlObject()->getUserDateID();
					Debug::text(' User Date ID found: '. $user_date_id, __FILE__, __LINE__, __METHOD__,10);

					$this->setUserDateID( $user_date_id );
					return TRUE;
				}
			}
		}

		Debug::Text('Skipping Find...', __FILE__, __LINE__, __METHOD__,10);
		Debug::Text(' Pay Period Schedule Record Count: '. $ppslf->getRecordCount() .' Epoch: '. TTDate::getDate('DATE+TIME', $epoch)  , __FILE__, __LINE__, __METHOD__,10);

		return $this->setUserDate( $user_id, $epoch );
	}

	function setUserDate($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 != '' ) {
			$this->setUserDateID( $user_date_id );
			return TRUE;
		}
		Debug::text(' No User Date ID found', __FILE__, __LINE__, __METHOD__,10);

		return FALSE;
	}

	function getOldUserDateID() {
		if ( isset($this->tmp_data['old_user_date_id']) ) {
			return $this->tmp_data['old_user_date_id'];
		}

		return FALSE;
	}
	function setOldUserDateID($id) {
		Debug::Text(' Setting Old User Date ID: '. $id, __FILE__, __LINE__, __METHOD__,10);
		$this->tmp_data['old_user_date_id'] = $id;

		return TRUE;
	}


	function getUserDateID() {
		if ( isset($this->data['user_date_id']) ) {
			return $this->data['user_date_id'];
		}

		return FALSE;
	}

	function setUserDateID($id = NULL) {
		$id = trim($id);

		$udlf = new UserDateListFactory();

		if (  $this->Validator->isResultSetWithRows(	'user_date',
														$udlf->getByID($id),
														TTi18n::gettext('Invalid User Date ID')
														) ) {
			$this->data['user_date_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getBranch() {
		if ( isset($this->data['branch_id']) ) {
			return $this->data['branch_id'];
		}

		return FALSE;
	}
	function setBranch($id) {
		$id = trim($id);

		if ( $id == FALSE OR $id == 0 OR $id == '' ) {
			$id = 0;
		}

		$blf = new BranchListFactory();

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'branch',
														$blf->getByID($id),
														TTi18n::gettext('Branch does not exist')
														) ) {
			$this->data['branch_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getDepartment() {
		if ( isset($this->data['department_id']) ) {
			return $this->data['department_id'];
		}

		return FALSE;
	}
	function setDepartment($id) {
		$id = trim($id);

		if ( $id == FALSE OR $id == 0 OR $id == '' ) {
			$id = 0;
		}

		$dlf = new DepartmentListFactory();

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'department',
														$dlf->getByID($id),
														TTi18n::gettext('Department does not exist')
														) ) {
			$this->data['department_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getJob() {
		if ( isset($this->data['job_id']) ) {
			return $this->data['job_id'];
		}

		return FALSE;
	}
	function setJob($id) {
		$id = trim($id);

		if ( $id == FALSE OR $id == 0 OR $id == '' ) {
			$id = 0;
		}

		if ( getTTProductEdition() == TT_PRODUCT_PROFESSIONAL ) {
			$jlf = new JobListFactory();
		}

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'job',
														$jlf->getByID($id),
														TTi18n::gettext('Job does not exist')
														) ) {
			$this->data['job_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getJobItem() {
		if ( isset($this->data['job_item_id']) ) {
			return $this->data['job_item_id'];
		}

		return FALSE;
	}
	function setJobItem($id) {
		$id = trim($id);

		if ( $id == FALSE OR $id == 0 OR $id == '' ) {
			$id = 0;
		}

		if ( getTTProductEdition() == TT_PRODUCT_PROFESSIONAL ) {
			$jilf = new JobItemListFactory();
		}

		if (  $id == 0
				OR
				$this->Validator->isResultSetWithRows(	'job_item',
														$jilf->getByID($id),
														TTi18n::gettext('Job Item does not exist')
														) ) {
			$this->data['job_item_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getQuantity() {
		if ( isset($this->data['quantity']) ) {
			return (float)$this->data['quantity'];
		}

		return FALSE;
	}
	function setQuantity($val) {
		$val = (float)$val;

		if ( $val == FALSE OR $val == 0 OR $val == '' ) {
			$val = 0;
		}

		if 	(	$val == 0
				OR
				$this->Validator->isFloat(			'quantity',
													$val,
													TTi18n::gettext('Incorrect quantity')) ) {
			$this->data['quantity'] = $val;

			return TRUE;
		}

		return FALSE;
	}

	function getBadQuantity() {
		if ( isset($this->data['bad_quantity']) ) {
			return (float)$this->data['bad_quantity'];
		}

		return FALSE;
	}
	function setBadQuantity($val) {
		$val = (float)$val;

		if ( $val == FALSE OR $val == 0 OR $val == '' ) {
			$val = 0;
		}

		if 	(	$val == 0
				OR
				$this->Validator->isFloat(			'bad_quantity',
													$val,
													TTi18n::gettext('Incorrect bad quantity')) ) {
			$this->data['bad_quantity'] = $val;

			return TRUE;
		}

		return FALSE;
	}

	function getTotalTime() {
		if ( isset($this->data['total_time']) ) {
			return (int)$this->data['total_time'];
		}
		return FALSE;
	}
	function setTotalTime($int) {
		$int = (int)$int;

		if 	(	$this->Validator->isNumeric(		'total_time',
													$int,
													TTi18n::gettext('Incorrect total time')) ) {
			$this->data['total_time'] = $int;

			return TRUE;
		}

		return FALSE;
	}

	function getActualTotalTime() {
		if ( isset($this->data['actual_total_time']) ) {
			return (int)$this->data['actual_total_time'];
		}
		return FALSE;
	}
	function setActualTotalTime($int) {
		$int = (int)$int;

		if ( $int < 0 ) {
			$int = 0;
		}

		if 	(	$this->Validator->isNumeric(		'actual_total_time',
													$int,
													TTi18n::gettext('Incorrect actual total time')) ) {
			$this->data['actual_total_time'] = $int;

			return TRUE;
		}

		return FALSE;
	}

	function getMealPolicyID() {
		if ( isset($this->data['meal_policy_id']) ) {
			return $this->data['meal_policy_id'];
		}

		return FALSE;
	}
	function setMealPolicyID($id) {
		$id = trim($id);

		if ( $id == '' OR empty($id) ) {
			$id = NULL;
		}

		$mplf = new MealPolicyListFactory();

		if ( $id == NULL
				OR
				$this->Validator->isResultSetWithRows(	'meal_policy',
														$mplf->getByID($id),
														TTi18n::gettext('Meal Policy is invalid')
													) ) {

			$this->data['meal_policy_id'] = $id;

			return TRUE;
		}

		return FALSE;
	}

	function getNote() {
		if ( isset($this->data['note']) ) {
			return $this->data['note'];
		}
	}
	function setNote($val) {
		$val = trim($val);

		if 	(	$val == ''
				OR
				$this->Validator->isLength(		'note',
												$val,
												TTi18n::gettext('Note is too short or too long'),
												0,
												1024) ) {

			$this->data['note'] = $val;

			return TRUE;
		}

		return FALSE;
	}

	function getOtherID1() {
		if ( isset($this->data['other_id1']) ) {
			return $this->data['other_id1'];
		}

		return FALSE;
	}
	function setOtherID1($value) {
		$value = trim($value);

		if (	$value == ''
				OR
				$this->Validator->isLength(	'other_id1',
											$value,
											TTi18n::gettext('Other ID 1 is invalid'),
											1,255) ) {

			$this->data['other_id1'] = $value;

			return TRUE;
		}

		return FALSE;
	}

	function getOtherID2() {
		if ( isset($this->data['other_id2']) ) {
			return $this->data['other_id2'];
		}

		return FALSE;
	}
	function setOtherID2($value) {
		$value = trim($value);

		if (	$value == ''
				OR
				$this->Validator->isLength(	'other_id2',
											$value,
											TTi18n::gettext('Other ID 2 is invalid'),
											1,255) ) {

			$this->data['other_id2'] = $value;

			return TRUE;
		}

		return FALSE;
	}

	function getOtherID3() {
		if ( isset($this->data['other_id3']) ) {
			return $this->data['other_id3'];
		}

		return FALSE;
	}
	function setOtherID3($value) {
		$value = trim($value);

		if (	$value == ''
				OR
				$this->Validator->isLength(	'other_id3',
											$value,
											TTi18n::gettext('Other ID 3 is invalid'),
											1,255) ) {

			$this->data['other_id3'] = $value;

			return TRUE;
		}

		return FALSE;
	}

	function getOtherID4() {
		if ( isset($this->data['other_id4']) ) {
			return $this->data['other_id4'];
		}

		return FALSE;
	}
	function setOtherID4($value) {
		$value = trim($value);

		if (	$value == ''
				OR
				$this->Validator->isLength(	'other_id4',
											$value,
											TTi18n::gettext('Other ID 4 is invalid'),
											1,255) ) {

			$this->data['other_id4'] = $value;

			return TRUE;
		}

		return FALSE;
	}

	function getOtherID5() {
		if ( isset($this->data['other_id5']) ) {
			return $this->data['other_id5'];
		}

		return FALSE;
	}
	function setOtherID5($value) {
		$value = trim($value);

		if (	$value == ''
				OR
				$this->Validator->isLength(	'other_id5',
											$value,
											TTi18n::gettext('Other ID 5 is invalid'),
											1,255) ) {

			$this->data['other_id5'] = $value;

			return TRUE;
		}

		return FALSE;
	}
/*
	function calcAutoDeductMealPolicyTime( $total_time, $schedule_obj) {

		****THIS IS HANDLED IN UserDateTotal FACTORY NOW****
		If users are transferring branches/departs, autodeducts are no longer working.
		But if they are transferring, why just punch out for lunch?
		Auto-Deduct has many issues, especially when it comes to job costing.
		 Several Solutions:
		 	1. Just deduct the meal policy amount from the total per day. This will make it so each punch pair
		 	total doesn't match the day total time, however this will cause all sorts of weirdness, and is probably
		 	extremely bad idea.

		 	2. Deduct time from each punch pair based on percentage of total time of the day? The problem with this
		 	is we don't know when the end of the day is, so we have to wait 24hrs at least to even calculate it.

			3. Add new "hour type" that either adds or subjects time from the total per day. This way
			it can be separated out in the TimeSheet is well, ie:
			Auto-Deduct: 30min
			Auto-Include: 30min


		$amount = 0;
		$meal_policy_id = NULL;
		Debug::text('Total Time: '. $total_time, __FILE__, __LINE__, __METHOD__, 10);

		if ( is_object($schedule_obj) AND is_object($schedule_obj->getSchedulePolicyObject() ) ) {
			Debug::text('Passed Schedule Obj: ', __FILE__, __LINE__, __METHOD__, 10);
			if ( is_object($schedule_obj->getSchedulePolicyObject()->getMealPolicyObject() )
					AND $schedule_obj->getSchedulePolicyObject()->getMealPolicyObject()->getType() == 10 ) {
				Debug::text('Found Scheduled Auto-Deduct Meal Policy: '. $schedule_obj->getSchedulePolicyObject()->getMealPolicyObject()->getId(), __FILE__, __LINE__, __METHOD__, 10);

				if ( $total_time > $schedule_obj->getSchedulePolicyObject()->getMealPolicyObject()->getTriggerTime() ) {
					$meal_policy_id = $schedule_obj->getSchedulePolicyObject()->getMealPolicyObject()->getId();
					$amount = $schedule_obj->getSchedulePolicyObject()->getMealPolicyObject()->getAmount();
				}
			}
		} else {
			Debug::text('NOT Passed Schedule Obj: ', __FILE__, __LINE__, __METHOD__, 10);
			$mplf = new MealPolicyListFactory();
			$mplf->getByPolicyGroupUserId( $this->getUserDateObject()->getUser() );
			if ( $mplf->getRecordCount() == 1 ) {
				$mp_obj = $mplf->getCurrent();
				Debug::text('Found Meal Policy: '. $mp_obj->getId(), __FILE__, __LINE__, __METHOD__, 10);

				if ( $mp_obj->getType() == 10 ) {
					Debug::text('Found Auto-Deduct Meal Policy: '. $mp_obj->getId(), __FILE__, __LINE__, __METHOD__, 10);

					if ( $total_time > $mp_obj->getTriggerTime() ) {
						$meal_policy_id = $mp_obj->getId();
						$amount = $mp_obj->getAmount();
					}
				}
			}
		}

		Debug::text('AutoDeduct Meal Policy Amount: '. $amount, __FILE__, __LINE__, __METHOD__, 10);
		return array('amount' => $amount, 'meal_policy_id' => $meal_policy_id);
	}
*/
	function calcTotalTime() {
		$plf = new PunchListFactory();
		$plf->getByPunchControlId( $this->getId() );

		//Make sure punches are in In/Out pairs before we bother calculating.
		if ( ( $plf->getRecordCount() % 2 ) == 0 ) {
			Debug::text(' Found Punches to calculate.', __FILE__, __LINE__, __METHOD__,10);
			//$prev_status = NULL;
			$in_pair = FALSE;
			$schedule_obj = NULL;
			foreach( $plf as $punch_obj ) {
				//Check for proper in/out pairs
				//First row should be an Out status (reverse ordering)
				Debug::text(' Punch: Status: '. $punch_obj->getStatus() .' TimeStamp: '. $punch_obj->getTimeStamp(), __FILE__, __LINE__, __METHOD__,10);
				if ( $punch_obj->getStatus() == 20 ) {
					Debug::text(' Found Out Status, starting pair: ', __FILE__, __LINE__, __METHOD__,10);
					$out_stamp = $punch_obj->getTimeStamp();
					$out_actual_stamp = $punch_obj->getActualTimeStamp();
					$in_pair = TRUE;
				} elseif ( $in_pair == TRUE ) {
					$punch_obj->setScheduleId(); //Find Schedule Object for this Punch
					$schedule_obj = $punch_obj->getScheduleObject();
					$in_stamp = $punch_obj->getTimeStamp();
					$in_actual_stamp = $punch_obj->getActualTimeStamp();
					//Got a pair... Totaling.
					Debug::text(' Found a pair... Totaling: ', __FILE__, __LINE__, __METHOD__,10);
					if ( $out_stamp != '' AND $in_stamp != '' ) {
						$total_time = $out_stamp - $in_stamp;
					}
					if ( $out_actual_stamp != '' AND $in_actual_stamp != '' ) {
						$actual_total_time = $out_actual_stamp - $in_actual_stamp;
					}
				}
			}

			if ( isset($total_time) ) {
				Debug::text(' Setting TotalTime...', __FILE__, __LINE__, __METHOD__,10);
				/*
				//This is handled in UserDateTotal factory now.
				//Check for AutoDeduct Meal Policy
				$meal_auto_deduct_arr = $this->calcAutoDeductMealPolicyTime( $total_time, $schedule_obj );

				$this->setTotalTime( $total_time - $meal_auto_deduct_arr['amount'] );
				$this->setActualTotalTime( $actual_total_time - $meal_auto_deduct_arr['amount'] );
				$this->setMealPolicyID( $meal_auto_deduct_arr['meal_policy_id'] );
				*/

				$this->setTotalTime( $total_time);
				$this->setActualTotalTime( $actual_total_time);

				return TRUE;
			}
		} else {
			Debug::text(' No Punches to calculate, or punches arent in pairs. Set total to 0', __FILE__, __LINE__, __METHOD__,10);
			$this->setTotalTime( 0 );
			$this->setActualTotalTime( 0 );

			return TRUE;
		}

		return FALSE;
	}

/*
	function calcUserDateTotalTime() {

		//Add a row to the user date total table, as "worked" hours.
		//Edit if it already exists and is not set as override.
		$udtlf = new UserDateTotalListFactory();
		$udtlf->getByUserDateIdAndPunchControlId( $this->getUserDateID(), $this->getId() );
		if ( $udtlf->getRecordCount() > 0 ) {
			Debug::text(' Found Conflicting User Date Total Records, removing them before re-calc', __FILE__, __LINE__, __METHOD__,10);
			foreach($udtlf as $udt_obj) {
				$udt_obj->setDeleted(TRUE);
				$udt_obj->Save();
			}
		}

		$udtf = new UserDateTotalFactory();
		$udtf->setUserDateID( $this->getUserDateID() );
		$udtf->setPunchControlID( $this->getId() );
		$udtf->setStatus( 20 ); //Worked
		$udtf->setType( 10 ); //Total
		$udtf->setBranch( $this->getBranch() );
		$udtf->setDepartment( $this->getDepartment() );
		$udtf->setTotalTime( $this->getTotalTime() );
		$udtf->setEnableCalcSystemTotalTime( TRUE );
		return $udtf->Save();

		return TRUE;
	}
*/

	function getEnableCalcSystemTotalTime() {
		if ( isset($this->calc_system_total_time) ) {
			return $this->calc_system_total_time;
		}

		return FALSE;
	}
	function setEnableCalcSystemTotalTime($bool) {
		$this->calc_system_total_time = $bool;

		return TRUE;
	}

	function getEnableCalcWeeklySystemTotalTime() {
		if ( isset($this->calc_weekly_system_total_time) ) {
			return $this->calc_weekly_system_total_time;
		}

		return FALSE;
	}
	function setEnableCalcWeeklySystemTotalTime($bool) {
		$this->calc_weekly_system_total_time = $bool;

		return TRUE;
	}

	function getEnableCalcException() {
		if ( isset($this->calc_exception) ) {
			return $this->calc_exception;
		}

		return FALSE;
	}
	function setEnableCalcException($bool) {
		$this->calc_exception = $bool;

		return TRUE;
	}

	function getEnablePreMatureException() {
		if ( isset($this->premature_exception) ) {
			return $this->premature_exception;
		}

		return FALSE;
	}
	function setEnablePreMatureException($bool) {
		$this->premature_exception = $bool;

		return TRUE;
	}

	function getEnableCalcUserDateTotal() {
		if ( isset($this->calc_user_date_total) ) {
			return $this->calc_user_date_total;
		}

		return FALSE;
	}
	function setEnableCalcUserDateTotal($bool) {
		$this->calc_user_date_total = $bool;

		return TRUE;
	}

	function getEnableCalcTotalTime() {
		if ( isset($this->calc_total_time) ) {
			return $this->calc_total_time;
		}

		return FALSE;
	}
	function setEnableCalcTotalTime($bool) {
		$this->calc_total_time = $bool;

		return TRUE;
	}

	function getEnableStrictJobValidation() {
		if ( isset($this->strict_job_validiation) ) {
			return $this->strict_job_validiation;
		}

		return FALSE;
	}
	function setEnableStrictJobValidation($bool) {
		$this->strict_job_validiation = $bool;

		return TRUE;
	}

	function Validate() {
		Debug::text('Validating...', __FILE__, __LINE__, __METHOD__,10);
		Debug::text('User Date Id: '. $this->getUserDateID(), __FILE__, __LINE__, __METHOD__,10);
		if ( $this->getUserDateObject() == FALSE OR $this->getUserDateObject()->getPayPeriodObject() == FALSE ) {
			$this->Validator->isTRUE(	'pay_period',
										FALSE,
										TTi18n::gettext('Date/Time is incorrect, or pay period does not exist for this date. Please create a pay period schedule if you have not done so already') );
		} elseif ( $this->getUserDateObject() == FALSE OR $this->getUserDateObject()->getPayPeriodObject()->getIsLocked() == TRUE ) {
			$this->Validator->isTRUE(	'pay_period',
										FALSE,
										TTi18n::gettext('Pay Period is Currently Locked') );
		}

		if ( getTTProductEdition() == TT_PRODUCT_PROFESSIONAL AND $this->getEnableStrictJobValidation() == TRUE ) {
			if ( $this->getJob() > 0 ) {
				$jlf = new JobListFactory();
				$jlf->getById( $this->getJob() );
				if ( $jlf->getRecordCount() > 0 ) {
					$j_obj = $jlf->getCurrent();

					if ( $j_obj->getUserAllowAll() == FALSE AND is_array( $j_obj->getUser() ) AND !in_array( $this->getUserDateObject()->getUser(), $j_obj->getUser() ) ) {
						$this->Validator->isTRUE(	'job',
													FALSE,
													TTi18n::gettext('Employee is not assigned to this job') );
					}

					if ( $this->getJobItem() > 0 AND $j_obj->getItemAllowAll() == FALSE AND is_array( $j_obj->getItem() ) AND !in_array( $this->getJobItem(), $j_obj->getItem() ) ) {
						$this->Validator->isTRUE(	'job_item',
													FALSE,
													TTi18n::gettext('Task is not assigned to this job') );
					}
				}
			}
		}

		return TRUE;
	}

	function preSave() {
		if ( $this->getBranch() === FALSE ) {
			$this->setBranch(0);
		}

		if ( $this->getDepartment() === FALSE ) {
			$this->setDepartment(0);
		}

		if ( $this->getJob() === FALSE ) {
			$this->setJob(0);
		}

		if ( $this->getJobItem() === FALSE ) {
			$this->setJobItem(0);
		}

		if ( $this->getQuantity() === FALSE ) {
			$this->setQuantity(0);
		}

		if ( $this->getBadQuantity() === FALSE ) {
			$this->setBadQuantity(0);
		}

		//Set Job default Job Item if required.
		if ( $this->getJob() != FALSE AND $this->getJobItem() == '' ) {
			Debug::text(' Job is set ('.$this->getJob().'), but no task is... Using default job item...', __FILE__, __LINE__, __METHOD__,10);

			if ( is_object( $this->getJobObject() ) ){
				Debug::text(' Default Job Item: '. $this->getJobObject()->getDefaultItem(), __FILE__, __LINE__, __METHOD__,10);
				$this->setJobItem( $this->getJobObject()->getDefaultItem() );
			}
		}

		if ( $this->getEnableCalcTotalTime() == TRUE ) {
			$this->calcTotalTime();
		}

		return TRUE;
	}

	function postSave() {
		$this->removeCache( $this->getId() );
		
		//Do this regardless of if EnableCalcUserDateTotal is set.
		Debug::text(' Old User Date ID: '. $this->getOldUserDateId() .' RAW: '. $this->tmp_data['old_user_date_id'], __FILE__, __LINE__, __METHOD__,10);
		if ( $this->getOldUserDateId() !== FALSE ) {
			Debug::text(' Recalculating Old User Date ID: '. $this->getOldUserDateId(), __FILE__, __LINE__, __METHOD__,10);
			UserDateTotalFactory::reCalculateDay( $this->getOldUserDateId(), TRUE );
		}

		if ( $this->getEnableCalcUserDateTotal() == TRUE ) {
			//Add a row to the user date total table, as "worked" hours.
			//Edit if it already exists and is not set as override.
			$udtlf = new UserDateTotalListFactory();
			$udtlf->getByUserDateIdAndPunchControlId( $this->getUserDateID(), $this->getId() );
			if ( $udtlf->getRecordCount() > 0 ) {
				Debug::text(' Found Conflicting User Date Total Records, removing them before re-calc', __FILE__, __LINE__, __METHOD__,10);
				foreach($udtlf as $udt_obj) {
					if ( $udt_obj->getOverride() == FALSE ) {
						Debug::text(' bFound Conflicting User Date Total Records, removing them before re-calc', __FILE__, __LINE__, __METHOD__,10);
						$udt_obj->Delete();
					}
				}
			}

			Debug::text(' cFound Conflicting User Date Total Records, removing them before re-calc: PreMature: '. (int)$this->getEnablePreMatureException(), __FILE__, __LINE__, __METHOD__,10);
			if ( $this->getDeleted() == FALSE ) {
				Debug::text(' Calculating Total Time for day.', __FILE__, __LINE__, __METHOD__,10);
				$udtf = new UserDateTotalFactory();
				$udtf->setUserDateID( $this->getUserDateID() );
				$udtf->setPunchControlID( $this->getId() );
				$udtf->setStatus( 20 ); //Worked
				$udtf->setType( 10 ); //Total

				$udtf->setBranch( $this->getBranch() );
				$udtf->setDepartment( $this->getDepartment() );

				$udtf->setJob( $this->getJob() );
				$udtf->setJobItem( $this->getJobItem() );
				$udtf->setQuantity( $this->getQuantity() );
				$udtf->setBadQuantity( $this->getBadQuantity() );

				$udtf->setTotalTime( $this->getTotalTime() );
				$udtf->setActualTotalTime( $this->getActualTotalTime() );

				$udtf->setEnableCalcSystemTotalTime( $this->getEnableCalcSystemTotalTime() );
				$udtf->setEnableCalcWeeklySystemTotalTime( $this->getEnableCalcWeeklySystemTotalTime() );
				$udtf->setEnableCalcException( $this->getEnableCalcException() );
				$udtf->setEnablePreMatureException( $this->getEnablePreMatureException() );
				if ( $udtf->isValid() ) {
					$udtf->Save();
				}
			} else {
				//Deleted is true. Just re-calc exceptions
				ExceptionPolicyFactory::calcExceptions( $this->getUserDateID() );
			}
		}

		/*
		if ( $this->getDeleted() == TRUE ) {
			$plf = new PunchListFactory();
			$plf->getByPunchControlID( $this->getId() );
			if ( $plf->getRecordCount() > 0 ) {
				foreach( $plf as $p_obj ) {
					$p_obj->setDeleted(TRUE);
					$p_obj->Save();
				}
			}
		}
		*/

		return TRUE;
	}
}
?>
