Beispiel #1
0
 }
 $pf->setTimeStamp($time_stamp, $enable_rounding);
 if ($i == 0 and isset($pc_data['id']) and $pc_data['id'] != '') {
     Debug::Text('Using existing Punch Control ID: ' . $pc_data['id'], __FILE__, __LINE__, __METHOD__, 10);
     $pf->setPunchControlID($pc_data['id']);
 } else {
     Debug::Text('Finding Punch Control ID: ' . $pc_data['id'], __FILE__, __LINE__, __METHOD__, 10);
     $pf->setPunchControlID($pf->findPunchControlID());
 }
 if ($pf->isNew()) {
     $pf->setActualTimeStamp($time_stamp);
     $pf->setOriginalTimeStamp($pf->getTimeStamp());
 }
 if ($pf->isValid() == TRUE) {
     if ($pf->Save(FALSE) == TRUE) {
         $pcf = new PunchControlFactory();
         $pcf->setId($pf->getPunchControlID());
         $pcf->setPunchObject($pf);
         if ($i == 0 and $pc_data['user_date_id'] != '') {
             //This is important when editing a punch, without it there can be issues calculating exceptions
             //because if a specific punch was modified that caused the day to change, smartReCalculate
             //may only be able to recalculate a single day, instead of both.
             $pcf->setUserDateID($pc_data['user_date_id']);
         }
         if (isset($pc_data['branch_id'])) {
             $pcf->setBranch($pc_data['branch_id']);
         }
         if (isset($pc_data['department_id'])) {
             $pcf->setDepartment($pc_data['department_id']);
         }
         if (isset($pc_data['job_id'])) {
 function setOfflinePunch($data)
 {
     Debug::Text('Setting Offline Punches... Rows: ' . count($data), __FILE__, __LINE__, __METHOD__, 10);
     //
     //WHen in Offline mode, default Type/Status to "AUTO"...
     //That way once I get the punches, I can determine what they should be on my end.
     //
     if (!is_array($data) or count($data) == 0) {
         return FALSE;
     }
     ksort($data);
     //Debug::Arr($data, 'offlinePunchDataArr', __FILE__, __LINE__, __METHOD__,10);
     /*
     		//Original
     		$data[] = array(
     						'user_id' => 1,
     						'time_stamp' => '12:00 PM',
     						'date_stamp' => '03-Dec-05',
     						'branch_id' => 1,
     						'department_id' => NULL,
     						'status_id' => 20,
     						'type_id' => 20,
     						'punch_control_id' => 0,
     						'station_id' => '7D00000023352A81'
     						);
     */
     /*
     		unset($data);
     
     		$data[] = array(
     						'user_id' => 1001,
     						'time_stamp' => '08:00 AM',
     						'date_stamp' => '05-Dec-05',
     						'branch_id' => 5,
     						'department_id' => 3,
     						'status_id' => 0,
     						'type_id' => 0,
     						'punch_control_id' => 0,
     						'station_id' => '7D00000023352A81'
     						);
     
     		$data[] = array(
     						'user_id' => 1001,
     						'time_stamp' => '12:00 PM',
     						'date_stamp' => '05-Dec-05',
     						'branch_id' => 0,
     						'department_id' => 3,
     						'status_id' => 20,
     						'type_id' => 0,
     						'punch_control_id' => 0,
     						'station_id' => '7D00000023352A81'
     						);
     */
     /*
     		$data[] = array(
     						'user_id' => 1001,
     						'time_stamp' => '1:00 PM',
     						'date_stamp' => '05-Dec-05',
     						'branch_id' => 6,
     						'department_id' => 0,
     						'status_id' => 0,
     						'type_id' => 20,
     						'punch_control_id' => 0,
     						'station_id' => '7D00000023352A81'
     						);
     */
     /*
     		$data[] = array(
     						'user_id' => 1001,
     						'time_stamp' => '5:00 PM',
     						'date_stamp' => '05-Dec-05',
     						'branch_id' => 0,
     						'department_id' => 0,
     						'status_id' => 0,
     						'type_id' => 0,
     						'punch_control_id' => 0,
     						'station_id' => '7D00000023352A81'
     						);
     */
     //Debug::Arr($data, 'offlinePunchDataArr', __FILE__, __LINE__, __METHOD__,10);
     //One punch per row
     foreach ($data as $row_key => $punch_row) {
         Debug::Text('--------------------------========================---------------------------', __FILE__, __LINE__, __METHOD__, 10);
         Debug::Text('--------------------------========================---------------------------', __FILE__, __LINE__, __METHOD__, 10);
         Debug::Text('Row Key: ' . $row_key . ' Date: ' . $punch_row['date_stamp'] . ' Time: ' . $punch_row['time_stamp'] . ' Station ID: ' . $punch_row['station_id'], __FILE__, __LINE__, __METHOD__, 10);
         if (isset($punch_row['station_id'])) {
             $slf = new StationListFactory();
             $slf->getByStationId($punch_row['station_id']);
             if ($slf->getRecordCount() > 0) {
                 Debug::Text('Found Station Data...', __FILE__, __LINE__, __METHOD__, 10);
                 $current_station = $slf->getCurrent();
             } else {
                 Debug::Text('DID NOT Find Station Data...', __FILE__, __LINE__, __METHOD__, 10);
                 continue;
             }
             unset($slf);
         }
         if (isset($punch_row['user_id']) and $punch_row['user_id'] != '') {
             $ulf = new UserListFactory();
             $ulf->getById($punch_row['user_id']);
             if ($ulf->getRecordCount() > 0) {
                 $current_user = $ulf->getCurrent();
                 Debug::Text('Valid User ID: ' . $punch_row['user_id'] . ' User Name: ' . $current_user->getFullName(), __FILE__, __LINE__, __METHOD__, 10);
                 //Need to handle timezone somehow. The station should send us the system's timezone
                 //so we can calculate based on that.
                 //Or just use the employees date preference.
                 $current_user->getUserPreferenceObject()->setDateTimePreferences();
             } else {
                 Debug::Text('aInValid User ID: ' . $punch_row['user_id'], __FILE__, __LINE__, __METHOD__, 10);
                 continue;
             }
         } else {
             Debug::Text('bInValid User ID: ' . $punch_row['user_id'], __FILE__, __LINE__, __METHOD__, 10);
             continue;
         }
         //Check to make sure the station is allowed.
         if (is_object($current_station) and is_object($current_user) and $current_station->checkAllowed($current_user->getId(), $current_station->getStation(), $current_station->getType()) == FALSE) {
             Debug::text('Station NOT allowed: Station ID: ' . $current_station->getId() . ' User: '******'date_stamp'] . ' ' . $punch_row['time_stamp']);
         //Make sure time stamp converts properly, otherwise skip this punch.
         if (!is_int($punch_full_time_stamp)) {
             Debug::Text('Failed TimeStamp: ' . $punch_full_time_stamp, __FILE__, __LINE__, __METHOD__, 10);
             continue;
         }
         Debug::Text('Punch Date/Time: ' . $punch_full_time_stamp . ' Offset that was already applied: ' . $punch_row['offset'], __FILE__, __LINE__, __METHOD__, 10);
         $fail_transaction = FALSE;
         $pf = new PunchFactory();
         $pf->StartTransaction();
         $slf = new ScheduleListFactory();
         //Auto Punch
         if (isset($punch_row['status_id']) and $punch_row['status_id'] == 0 or isset($punch_row['type_id']) and $punch_row['type_id'] == 0 or isset($punch_row['branch_id']) and $punch_row['branch_id'] == 0 or isset($punch_row['department_id']) and $punch_row['department_id'] == 0 or isset($punch_row['job_id']) and $punch_row['job_id'] == 0 or isset($punch_row['job_item_id']) and $punch_row['job_item_id'] == 0) {
             $plf = new PunchListFactory();
             $plf->getPreviousPunchByUserIDAndEpoch($punch_row['user_id'], $punch_full_time_stamp);
             if ($plf->getRecordCount() > 0) {
                 Debug::Text(' Found Previous Punch within Continuous Time from now: ', __FILE__, __LINE__, __METHOD__, 10);
                 $prev_punch_obj = $plf->getCurrent();
                 $branch_id = $prev_punch_obj->getPunchControlObject()->getBranch();
                 $department_id = $prev_punch_obj->getPunchControlObject()->getDepartment();
                 $job_id = $prev_punch_obj->getPunchControlObject()->getJob();
                 $job_item_id = $prev_punch_obj->getPunchControlObject()->getJobItem();
                 $quantity = $prev_punch_obj->getPunchControlObject()->getQuantity();
                 $bad_quantity = $prev_punch_obj->getPunchControlObject()->getBadQuantity();
                 if ($branch_id == '' or empty($branch_id) or $department_id == '' or empty($department_id)) {
                     Debug::Text(' Branch or department are null. ', __FILE__, __LINE__, __METHOD__, 10);
                     $s_obj = $slf->getScheduleObjectByUserIdAndEpoch($punch_row['user_id'], $punch_full_time_stamp);
                     if (is_object($s_obj)) {
                         Debug::Text(' Found Schedule!: ', __FILE__, __LINE__, __METHOD__, 10);
                         if ($branch_id == '' or empty($branch_id)) {
                             Debug::Text(' overrriding branch: ' . $s_obj->getBranch(), __FILE__, __LINE__, __METHOD__, 10);
                             $branch_id = $s_obj->getBranch();
                         }
                         if ($department_id == '' or empty($department_id)) {
                             Debug::Text(' overrriding department: ' . $s_obj->getDepartment(), __FILE__, __LINE__, __METHOD__, 10);
                             $department_id = $s_obj->getDepartment();
                         }
                     }
                 }
                 $type_id = $prev_punch_obj->getNextType();
                 $status_id = $prev_punch_obj->getNextStatus();
                 $next_type = $prev_punch_obj->getNextType();
                 //Check for break policy window.
                 if ($next_type != 30 and ($prev_punch_obj->getStatus() != 30 and $prev_punch_obj->getType() != 30)) {
                     $prev_punch_obj->setUser($current_user->getId());
                     $prev_punch_obj->setScheduleID($prev_punch_obj->findScheduleID($punch_full_time_stamp));
                     if ($prev_punch_obj->inBreakPolicyWindow($punch_full_time_stamp, $prev_punch_obj->getTimeStamp()) == TRUE) {
                         Debug::Text(' Setting Type to Break: ', __FILE__, __LINE__, __METHOD__, 10);
                         $next_type = 30;
                     }
                 }
                 //Check for meal policy window.
                 if ($next_type != 20 and ($prev_punch_obj->getStatus() != 20 and $prev_punch_obj->getType() != 20)) {
                     $prev_punch_obj->setUser($current_user->getId());
                     $prev_punch_obj->setScheduleID($prev_punch_obj->findScheduleID($punch_full_time_stamp));
                     if ($prev_punch_obj->inMealPolicyWindow($punch_full_time_stamp, $prev_punch_obj->getTimeStamp()) == TRUE) {
                         Debug::Text(' Setting Type to Lunch: ', __FILE__, __LINE__, __METHOD__, 10);
                         $next_type = 20;
                     }
                 }
             } else {
                 Debug::Text(' DID NOT Find Previous Punch within Continuous Time from now: ', __FILE__, __LINE__, __METHOD__, 10);
                 $branch_id = NULL;
                 $department_id = NULL;
                 $job_id = NULL;
                 $job_item_id = NULL;
                 $s_obj = $slf->getScheduleObjectByUserIdAndEpoch($punch_row['user_id'], $punch_full_time_stamp);
                 if (is_object($s_obj)) {
                     Debug::Text(' Found Schedule!: ', __FILE__, __LINE__, __METHOD__, 10);
                     $branch_id = $s_obj->getBranch();
                     $department_id = $s_obj->getDepartment();
                 } else {
                     $branch_id = $current_user->getDefaultBranch();
                     $department_id = $current_user->getDefaultDepartment();
                     //Check station for default/forced settings.
                     if (is_object($current_station)) {
                         if ($current_station->getDefaultBranch() !== FALSE and $current_station->getDefaultBranch() != 0) {
                             $branch_id = $current_station->getDefaultBranch();
                         }
                         if ($current_station->getDefaultDepartment() !== FALSE and $current_station->getDefaultDepartment() != 0) {
                             $department_id = $current_station->getDefaultDepartment();
                         }
                         if ($current_station->getDefaultJob() !== FALSE and $current_station->getDefaultJob() != 0) {
                             $job_id = $current_station->getDefaultJob();
                         }
                         if ($current_station->getDefaultJobItem() !== FALSE and $current_station->getDefaultJobItem() != 0) {
                             $job_item_id = $current_station->getDefaultJobItem();
                         }
                     }
                 }
                 $status_id = 10;
                 //In
                 $type_id = 10;
                 //Normal
             }
             if (isset($punch_row['status_id']) and $punch_row['status_id'] != 0) {
                 Debug::Text(' Status ID is NOT AUTO: ' . $punch_row['status_id'], __FILE__, __LINE__, __METHOD__, 10);
                 $status_id = $punch_row['status_id'];
             }
             if (isset($punch_row['type_id']) and $punch_row['type_id'] != 0) {
                 Debug::Text(' Type ID is NOT AUTO: ' . $punch_row['type_id'], __FILE__, __LINE__, __METHOD__, 10);
                 $type_id = $punch_row['type_id'];
             }
             if (isset($punch_row['branch_id']) and $punch_row['branch_id'] != 0) {
                 Debug::Text(' Branch ID is NOT AUTO: ' . $punch_row['branch_id'], __FILE__, __LINE__, __METHOD__, 10);
                 $branch_id = $punch_row['branch_id'];
             }
             if (isset($punch_row['department_id']) and $punch_row['department_id'] != 0) {
                 Debug::Text(' Department ID is NOT AUTO: ' . $punch_row['department_id'], __FILE__, __LINE__, __METHOD__, 10);
                 $department_id = $punch_row['department_id'];
             }
             if (isset($punch_row['job_id']) and $punch_row['job_id'] != 0) {
                 Debug::Text(' Job ID is NOT AUTO: ' . $punch_row['job_id'], __FILE__, __LINE__, __METHOD__, 10);
                 $job_id = $punch_row['job_id'];
             }
             if (isset($punch_row['job_item_id']) and $punch_row['job_item_id'] != 0) {
                 Debug::Text(' Job Item ID is NOT AUTO: ' . $punch_row['job_item_id'], __FILE__, __LINE__, __METHOD__, 10);
                 $job_item_id = $punch_row['job_item_id'];
             }
             if (isset($punch_row['quantity'])) {
                 Debug::Text(' Quantity is NOT AUTO: ' . $punch_row['quantity'], __FILE__, __LINE__, __METHOD__, 10);
                 $quantity = $punch_row['quantity'];
             }
             if (isset($punch_row['bad_quantity'])) {
                 Debug::Text(' Bad Quantity is NOT AUTO: ' . $punch_row['bad_quantity'], __FILE__, __LINE__, __METHOD__, 10);
                 $bad_quantity = $punch_row['bad_quantity'];
             }
         } else {
             $status_id = $punch_row['status_id'];
             $type_id = $punch_row['type_id'];
             $branch_id = $punch_row['branch_id'];
             $department_id = $punch_row['department_id'];
             $job_id = $punch_row['job_id'];
             $job_item_id = $punch_row['job_item_id'];
             $quantity = $punch_row['quantity'];
             $bad_quantity = $punch_row['bad_quantity'];
         }
         //Set User before setTimeStamp so rounding can be done properly.
         $pf->setUser($punch_row['user_id']);
         if (isset($punch_row['transfer']) and $punch_row['transfer'] == 1) {
             Debug::Text(' Enabling Transfer!: ', __FILE__, __LINE__, __METHOD__, 10);
             $type_id = 10;
             $status_id = 10;
             $pf->setTransfer(TRUE);
         }
         $pf->setType($type_id);
         $pf->setStatus($status_id);
         $pf->setTimeStamp($punch_full_time_stamp, TRUE);
         //Make sure we round here.
         if (isset($status_id) and $status_id == 20 and isset($punch_row['punch_control_id']) and $punch_row['punch_control_id'] != '' and $punch_row['punch_control_id'] != 0) {
             $pf->setPunchControlID($punch_row['punch_control_id']);
         } else {
             $pf->setPunchControlID($pf->findPunchControlID());
         }
         $pf->setStation($current_station->getId());
         if ($pf->isNew()) {
             $pf->setActualTimeStamp($punch_full_time_stamp);
             $pf->setOriginalTimeStamp($pf->getTimeStamp());
         }
         if ($pf->isValid() == TRUE) {
             if ($pf->Save(FALSE) == TRUE) {
                 $pcf = new PunchControlFactory();
                 $pcf->setId($pf->getPunchControlID());
                 $pcf->setPunchObject($pf);
                 if (isset($branch_id) and $branch_id != '') {
                     $pcf->setBranch($branch_id);
                 }
                 if (isset($department_id) and $department_id != '') {
                     $pcf->setDepartment($department_id);
                 }
                 if (isset($job_id) and $job_id != '') {
                     $pcf->setJob($job_id);
                 }
                 if (isset($job_item_id) and $job_item_id != '') {
                     $pcf->setJobItem($job_item_id);
                 }
                 if (isset($quantity) and $quantity != '') {
                     $pcf->setQuantity($quantity);
                 }
                 if (isset($bad_quantity) and $bad_quantity != '') {
                     $pcf->setBadQuantity($bad_quantity);
                 }
                 if (isset($punch_row['note']) and $punch_row['note'] != '') {
                     $pcf->setNote($punch_row['note']);
                 }
                 if (isset($punch_row['other_id1']) and $punch_row['other_id1'] != '') {
                     $pcf->setOtherID1($punch_row['other_id1']);
                 }
                 if (isset($punch_row['other_id2']) and $punch_row['other_id2'] != '') {
                     $pcf->setOtherID2($punch_row['other_id2']);
                 }
                 if (isset($punch_row['other_id3']) and $punch_row['other_id3'] != '') {
                     $pcf->setOtherID3($punch_row['other_id3']);
                 }
                 if (isset($punch_row['other_id4']) and $punch_row['other_id4'] != '') {
                     $pcf->setOtherID4($punch_row['other_id4']);
                 }
                 if (isset($punch_row['other_id5']) and $punch_row['other_id5'] != '') {
                     $pcf->setOtherID5($punch_row['other_id5']);
                 }
                 $pcf->setEnableStrictJobValidation(TRUE);
                 $pcf->setEnableCalcUserDateID(TRUE);
                 $pcf->setEnableCalcTotalTime(TRUE);
                 $pcf->setEnableCalcSystemTotalTime(TRUE);
                 $pcf->setEnableCalcUserDateTotal(TRUE);
                 $pcf->setEnableCalcException(TRUE);
                 $pcf->setEnablePreMatureException(TRUE);
                 //Enable pre-mature exceptions at this point.
                 if ($pcf->isValid() == TRUE) {
                     Debug::Text(' Punch Control is valid, saving...: ', __FILE__, __LINE__, __METHOD__, 10);
                     if ($pcf->Save(TRUE, TRUE) == TRUE) {
                         //Force isNew() lookup.
                         Debug::text('Saved Punch!', __FILE__, __LINE__, __METHOD__, 10);
                     } else {
                         Debug::text('PCF Save failed... Failing Transaction!', __FILE__, __LINE__, __METHOD__, 10);
                         $fail_transaction = TRUE;
                     }
                 } else {
                     Debug::text('PCF Validate failed... Failing Transaction!', __FILE__, __LINE__, __METHOD__, 10);
                     $fail_transaction = TRUE;
                 }
             } else {
                 Debug::text('PF Save failed... Failing Transaction!', __FILE__, __LINE__, __METHOD__, 10);
                 $fail_transaction = TRUE;
             }
         } else {
             Debug::text('PF Validate failed... Failing Transaction!', __FILE__, __LINE__, __METHOD__, 10);
             $fail_transaction = TRUE;
         }
         if ($fail_transaction == FALSE) {
             $pf->CommitTransaction();
         } else {
             $pf->FailTransaction();
         }
         unset($punch_full_time_stamp, $current_station, $current_user);
         //End Foreach
     }
     return TRUE;
 }
    function getTimesheetDetailReportByCompanyIdAndArrayCriteria($company_id, $filter_data, $limit = NULL, $page = NULL, $where = NULL, $order = NULL)
    {
        //$order = array( 'b.pay_period_id' => 'asc', 'b.user_id' => 'asc' );
        //$order = array( 'b.pay_period_id' => 'asc','uf.last_name' => 'asc', 'b.date_stamp' => 'asc' );
        /*
        if ( $order == NULL ) {
        	$order = array( 'b.pay_period_id' => 'asc', 'b.user_id' => 'asc' );
        	$strict = FALSE;
        } else {
        	$strict = TRUE;
        }
        */
        if (isset($filter_data['punch_branch_ids'])) {
            $filter_data['punch_branch_id'] = $filter_data['punch_branch_ids'];
        }
        if (isset($filter_data['punch_department_ids'])) {
            $filter_data['punch_department_id'] = $filter_data['punch_department_ids'];
        }
        if (isset($filter_data['branch_ids'])) {
            $filter_data['branch_id'] = $filter_data['branch_ids'];
        }
        if (isset($filter_data['department_ids'])) {
            $filter_data['department_id'] = $filter_data['department_ids'];
        }
        $uf = new UserFactory();
        $udf = new UserDateFactory();
        $bf = new BranchFactory();
        $df = new DepartmentFactory();
        $ppf_b = new PayPeriodFactory();
        $uwf = new UserWageFactory();
        $pcf = new PunchControlFactory();
        $pf = new PunchFactory();
        $otpf = new OverTimePolicyFactory();
        $apf = new AbsencePolicyFactory();
        $ppf = new PremiumPolicyFactory();
        $ph = array('company_id' => $company_id);
        //Make it so employees with 0 hours still show up!! Very important!
        //Order dock hours first, so it can be deducted from regular time.
        //Show Min/Max punches based on day/branch/department, so we can split reports out day/branch/department and still show
        //  when the employee punched in/out for each.
        $query = '
					select
							b.user_id as user_id,
							ppf.id as pay_period_id,
							ppf.start_date as pay_period_start_date,
							ppf.end_date as pay_period_end_date,
							ppf.transaction_date as pay_period_transaction_date,
							b.date_stamp as date_stamp,
							bf.name as branch,
							df.name as department,
							a.status_id as status_id,
							a.type_id as type_id,

							a.over_time_policy_id as over_time_policy_id,
							a.absence_policy_id as absence_policy_id,
							a.premium_policy_id as premium_policy_id,

							z.hourly_rate as hourly_rate,
							z.labor_burden_percent as labor_burden_percent,

							min_punch.time_stamp as min_punch_time_stamp,
							max_punch.time_stamp as max_punch_time_stamp,
							sum(total_time) as total_time,
							sum(actual_total_time) as actual_total_time
					from	' . $this->getTable() . ' as a
					LEFT JOIN ' . $udf->getTable() . ' as b ON a.user_date_id = b.id
					LEFT JOIN ' . $uf->getTable() . ' as uf ON b.user_id = uf.id

					LEFT JOIN ' . $bf->getTable() . ' as bf ON a.branch_id = bf.id
					LEFT JOIN ' . $df->getTable() . ' as df ON a.department_id = df.id

					LEFT JOIN ' . $ppf_b->getTable() . ' as ppf ON b.pay_period_id = ppf.id

					LEFT JOIN ' . $otpf->getTable() . ' as m ON (a.over_time_policy_id = m.id AND m.deleted = 0)
					LEFT JOIN ' . $apf->getTable() . ' as o ON (a.absence_policy_id = o.id AND o.deleted = 0)
					LEFT JOIN ' . $ppf->getTable() . ' as q ON (a.premium_policy_id = q.id AND q.deleted = 0)

					LEFT JOIN ' . $uwf->getTable() . ' as z ON z.id = (select z.id
																		from ' . $uwf->getTable() . ' as z
																		where z.user_id = b.user_id
																			and z.effective_date <= b.date_stamp
																			and z.wage_group_id = (CASE WHEN a.over_time_policy_id > 0 THEN m.wage_group_id
																									ELSE
																										CASE WHEN a.absence_policy_id > 0 THEN o.wage_group_id ELSE
																											CASE WHEN a.premium_policy_id > 0 THEN q.wage_group_id ELSE 0 END
																										END
																									END)
																			and z.deleted = 0
																			order by z.effective_date desc limit 1)

					LEFT JOIN ' . $pf->getTable() . ' as min_punch ON min_punch.id = (
																		select pf_a.id
																		from ' . $pf->getTable() . ' as pf_a
																		LEFT JOIN ' . $pcf->getTable() . ' as pcf_a ON pf_a.punch_control_id = pcf_a.id
																		WHERE pcf_a.user_date_id = a.user_date_id
																			AND pcf_a.branch_id = a.branch_id
																			AND pcf_a.department_id = a.department_id
																			AND pf_a.status_id = 10
																		ORDER BY pf_a.time_stamp ASC
																		LIMIT 1
																		)

					LEFT JOIN ' . $pf->getTable() . ' as max_punch ON max_punch.id = (
																		select pf_a.id
																		from ' . $pf->getTable() . ' as pf_a
																		LEFT JOIN ' . $pcf->getTable() . ' as pcf_a ON pf_a.punch_control_id = pcf_a.id
																		WHERE pcf_a.user_date_id = a.user_date_id
																			AND pcf_a.branch_id = a.branch_id
																			AND pcf_a.department_id = a.department_id
																			AND pf_a.status_id = 20
																		ORDER BY pf_a.time_stamp DESC
																		LIMIT 1
																		)

					where 	uf.company_id = ? ';
        $query .= isset($filter_data['permission_children_ids']) ? $this->getWhereClauseSQL('uf.id', $filter_data['permission_children_ids'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['include_user_id']) ? $this->getWhereClauseSQL('uf.id', $filter_data['include_user_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['exclude_user_id']) ? $this->getWhereClauseSQL('uf.id', $filter_data['exclude_user_id'], 'not_numeric_list', $ph) : NULL;
        $query .= isset($filter_data['user_status_id']) ? $this->getWhereClauseSQL('uf.status_id', $filter_data['user_status_id'], 'numeric_list', $ph) : NULL;
        if (isset($filter_data['include_user_subgroups']) and (bool) $filter_data['include_user_subgroups'] == TRUE) {
            $uglf = new UserGroupListFactory();
            $filter_data['user_group_id'] = $uglf->getByCompanyIdAndGroupIdAndSubGroupsArray($company_id, $filter_data['user_group_id'], TRUE);
        }
        $query .= isset($filter_data['user_group_id']) ? $this->getWhereClauseSQL('uf.group_id', $filter_data['user_group_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['default_branch_id']) ? $this->getWhereClauseSQL('uf.default_branch_id', $filter_data['default_branch_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['default_department_id']) ? $this->getWhereClauseSQL('uf.default_department_id', $filter_data['default_department_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['user_title_id']) ? $this->getWhereClauseSQL('uf.title_id', $filter_data['user_title_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['punch_branch_id']) ? $this->getWhereClauseSQL('a.branch_id', $filter_data['punch_branch_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['punch_department_id']) ? $this->getWhereClauseSQL('a.department_id', $filter_data['punch_department_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['pay_period_id']) ? $this->getWhereClauseSQL('b.pay_period_id', $filter_data['pay_period_id'], 'numeric_list', $ph) : NULL;
        $query .= isset($filter_data['tag']) ? $this->getWhereClauseSQL('uf.id', array('company_id' => $company_id, 'object_type_id' => 200, 'tag' => $filter_data['tag']), 'tag', $ph) : NULL;
        if (isset($filter_data['start_date']) and trim($filter_data['start_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['start_date']);
            $query .= ' AND b.date_stamp >= ?';
        }
        if (isset($filter_data['end_date']) and trim($filter_data['end_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['end_date']);
            $query .= ' AND b.date_stamp <= ?';
        }
        //This isn't needed as it lists every status: AND a.status_id in (10,20,30)
        $query .= '
						AND ( a.deleted = 0 AND b.deleted = 0 )
					group by b.user_id, ppf.id, ppf.start_date, ppf.end_date, ppf.transaction_date, bf.name, df.name, b.date_stamp, z.hourly_rate, z.labor_burden_percent, a.status_id, a.type_id, a.over_time_policy_id, a.absence_policy_id, a.premium_policy_id, min_punch.time_stamp, max_punch.time_stamp
					';
        $query .= $this->getSortSQL($order, FALSE);
        $this->ExecuteSQL($query, $ph);
        //Debug::Arr($ph, 'Query: '. $query, __FILE__, __LINE__, __METHOD__,10);
        return $this;
    }
 function createPunchPair($user_id, $in_time_stamp, $out_time_stamp, $data = NULL, $calc_total_time = TRUE)
 {
     $fail_transaction = FALSE;
     Debug::Text('Punch Full In Time Stamp: (' . $in_time_stamp . ') ' . TTDate::getDate('DATE+TIME', $in_time_stamp) . ' Out: (' . $out_time_stamp . ') ' . TTDate::getDate('DATE+TIME', $out_time_stamp), __FILE__, __LINE__, __METHOD__, 10);
     $pf = new PunchFactory();
     $pf->StartTransaction();
     //Out Punch
     //Save out punch first so the $pf object is for the In Punch if there is one.
     if ($out_time_stamp !== NULL) {
         $pf_in = new PunchFactory();
         $pf_in->setTransfer(FALSE);
         $pf_in->setUser($user_id);
         $pf_in->setType($data['out_type_id']);
         $pf_in->setStatus(20);
         $pf_in->setTimeStamp($out_time_stamp);
         if ($pf_in->isNew()) {
             $pf_in->setActualTimeStamp($out_time_stamp);
             $pf_in->setOriginalTimeStamp($pf_in->getTimeStamp());
         }
         $pf_in->setPunchControlID($pf_in->findPunchControlID());
         if ($pf_in->isValid()) {
             if ($pf_in->Save(FALSE) === FALSE) {
                 Debug::Text(' aFail Transaction: ', __FILE__, __LINE__, __METHOD__, 10);
                 $fail_transaction = TRUE;
             }
         }
     }
     if ($in_time_stamp !== NULL) {
         //In Punch
         $pf_out = new PunchFactory();
         $pf_out->setTransfer(FALSE);
         $pf_out->setUser($user_id);
         $pf_out->setType($data['in_type_id']);
         $pf_out->setStatus(10);
         $pf_out->setTimeStamp($in_time_stamp);
         if ($pf_out->isNew()) {
             $pf_out->setActualTimeStamp($in_time_stamp);
             $pf_out->setOriginalTimeStamp($pf_out->getTimeStamp());
         }
         if (isset($pf_in) and $pf_in->getPunchControlID() != FALSE) {
             //Get Punch Control ID from above Out punch.
             $pf_out->setPunchControlID($pf_in->getPunchControlID());
         } else {
             $pf_out->setPunchControlID($pf_out->findPunchControlID());
         }
         if ($pf_out->isValid()) {
             if ($pf_out->Save(FALSE) === FALSE) {
                 Debug::Text(' aFail Transaction: ', __FILE__, __LINE__, __METHOD__, 10);
                 $fail_transaction = TRUE;
             }
         }
     }
     if ($fail_transaction == FALSE) {
         if (isset($pf_in) and is_object($pf_in)) {
             Debug::Text(' Using In Punch Object... TimeStamp: ' . $pf_in->getTimeStamp(), __FILE__, __LINE__, __METHOD__, 10);
             $pf = $pf_in;
         } elseif (isset($pf_out) and is_object($pf_out)) {
             Debug::Text(' Using Out Punch Object... TimeStamp: ' . $pf_out->getTimeStamp(), __FILE__, __LINE__, __METHOD__, 10);
             $pf = $pf_out;
         }
         $pcf = new PunchControlFactory();
         $pcf->setId($pf->getPunchControlID());
         $pcf->setPunchObject($pf);
         $pcf->setBranch($data['branch_id']);
         $pcf->setDepartment($data['department_id']);
         if (isset($data['job_id'])) {
             $pcf->setJob($data['job_id']);
         }
         if (isset($data['job_item_id'])) {
             $pcf->setJobItem($data['job_item_id']);
         }
         if (isset($data['quantity'])) {
             $pcf->setQuantity($data['quantity']);
         }
         if (isset($data['bad_quantity'])) {
             $pcf->setBadQuantity($data['bad_quantity']);
         }
         $pcf->setEnableCalcUserDateID(TRUE);
         $pcf->setEnableCalcTotalTime($calc_total_time);
         $pcf->setEnableCalcSystemTotalTime($calc_total_time);
         $pcf->setEnableCalcWeeklySystemTotalTime($calc_total_time);
         $pcf->setEnableCalcUserDateTotal($calc_total_time);
         $pcf->setEnableCalcException($calc_total_time);
         if ($pcf->isValid() == TRUE) {
             $punch_control_id = $pcf->Save(TRUE, TRUE);
             //Force lookup
             if ($fail_transaction == FALSE) {
                 Debug::Text('Punch Control ID: ' . $punch_control_id, __FILE__, __LINE__, __METHOD__, 10);
                 $pf->CommitTransaction();
                 return TRUE;
             }
         }
     }
     Debug::Text('Failed Creating Punch!', __FILE__, __LINE__, __METHOD__, 10);
     $pf->FailTransaction();
     return FALSE;
 }
    function getDayReportByCompanyIdAndArrayCriteria($company_id, $filter_data, $limit = NULL, $page = NULL, $where = NULL, $order = NULL)
    {
        //$order = array( 'b.pay_period_id' => 'asc', 'b.user_id' => 'asc' );
        $order = array('tmp.pay_period_id' => 'asc', 'z.last_name' => 'asc', 'tmp.date_stamp' => 'asc');
        /*
        if ( $order == NULL ) {
        	$order = array( 'b.pay_period_id' => 'asc', 'b.user_id' => 'asc' );
        	$strict = FALSE;
        } else {
        	$strict = TRUE;
        }
        */
        $ulf = new UserListFactory();
        $udf = new UserDateFactory();
        $uwf = new UserWageFactory();
        $pcf = new PunchControlFactory();
        $pf = new PunchFactory();
        $otpf = new OverTimePolicyFactory();
        $apf = new AbsencePolicyFactory();
        $ppf = new PremiumPolicyFactory();
        $ph = array();
        //Make it so employees with 0 hours still show up!! Very important!
        //Order dock hours first, so it can be deducted from regular time.
        $query = '
				select z.id, tmp.*
				from ' . $ulf->getTable() . ' as z
				LEFT JOIN

					( select
							b.user_id,
							b.pay_period_id as pay_period_id,
							b.date_stamp as date_stamp,
							a.branch_id as branch_id,
							a.department_id as department_id,
							a.status_id as status_id,
							a.type_id as type_id,

							a.over_time_policy_id as over_time_policy_id,
							n.id as over_time_policy_wage_id,
							n.effective_date as over_time_policy_wage_effective_date,

							a.absence_policy_id as absence_policy_id,
							p.id as absence_policy_wage_id,
							p.effective_date as absence_policy_wage_effective_date,

							a.premium_policy_id as premium_policy_id,
							r.id as premium_policy_wage_id,
							r.effective_date as premium_policy_wage_effective_date,

							z.id as user_wage_id,
							z.effective_date as user_wage_effective_date,
							tmp2.min_punch_time_stamp as min_punch_time_stamp,
							tmp2.max_punch_time_stamp as max_punch_time_stamp,
							sum(total_Time) as total_time,
							sum(actual_total_Time) as actual_total_time
					from	' . $this->getTable() . ' as a
					LEFT JOIN ' . $udf->getTable() . ' as b ON a.user_date_id = b.id

					LEFT JOIN ' . $otpf->getTable() . ' as m ON a.over_time_policy_id = m.id
					LEFT JOIN ' . $uwf->getTable() . ' as n ON n.id = (select n.id
																		from ' . $uwf->getTable() . ' as n
																		where n.user_id = b.user_id
																			and n.wage_group_id = m.wage_group_id
																			and n.effective_date <= b.date_stamp
																			and n.deleted = 0
																			order by n.effective_date desc limit 1)

					LEFT JOIN ' . $apf->getTable() . ' as o ON a.absence_policy_id = o.id
					LEFT JOIN ' . $uwf->getTable() . ' as p ON p.id = (select p.id
																		from ' . $uwf->getTable() . ' as p
																		where p.user_id = b.user_id
																			and p.wage_group_id = o.wage_group_id
																			and p.effective_date <= b.date_stamp
																			and p.deleted = 0
																			order by p.effective_date desc limit 1)

					LEFT JOIN ' . $ppf->getTable() . ' as q ON a.premium_policy_id = q.id
					LEFT JOIN ' . $uwf->getTable() . ' as r ON r.id = (select r.id
																		from ' . $uwf->getTable() . ' as r
																		where r.user_id = b.user_id
																			and r.wage_group_id = q.wage_group_id
																			and r.effective_date <= b.date_stamp
																			and r.deleted = 0
																			order by r.effective_date desc limit 1)

					LEFT JOIN ' . $uwf->getTable() . ' as z ON z.id = (select z.id
																		from ' . $uwf->getTable() . ' as z
																		where z.user_id = b.user_id
																			and z.effective_date <= b.date_stamp
																			and z.deleted = 0
																			order by z.effective_date desc limit 1)
					LEFT JOIN (
						select tmp3.id, min(tmp3.min_punch_time_stamp) as min_punch_time_stamp, max(tmp3.max_punch_time_stamp) as max_punch_time_stamp from (
							select tmp2_a.id,
								CASE WHEN tmp2_c.status_id = 10 THEN min(tmp2_c.time_stamp) ELSE NULL END as min_punch_time_stamp,
								CASE WHEN tmp2_c.status_id = 20 THEN max(tmp2_c.time_stamp) ELSE NULL END as max_punch_time_stamp
								from ' . $udf->getTable() . ' as tmp2_a
								LEFT JOIN ' . $pcf->getTable() . ' as tmp2_b ON tmp2_a.id = tmp2_b.user_date_id
								LEFT JOIN ' . $pf->getTable() . ' as tmp2_c ON tmp2_b.id = tmp2_c.punch_control_id
								WHERE 1=1 ';
        if (isset($filter_data['user_id']) and isset($filter_data['user_id'][0]) and !in_array(-1, (array) $filter_data['user_id'])) {
            $query .= ' AND tmp2_a.user_id in (' . $this->getListSQL($filter_data['user_id'], $ph) . ') ';
        }
        if (isset($filter_data['pay_period_ids']) and isset($filter_data['pay_period_ids'][0]) and !in_array(-1, (array) $filter_data['pay_period_ids'])) {
            $query .= ' AND tmp2_a.pay_period_id in (' . $this->getListSQL($filter_data['pay_period_ids'], $ph) . ') ';
        }
        if (isset($filter_data['start_date']) and trim($filter_data['start_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['start_date']);
            $query .= ' AND tmp2_a.date_stamp >= ?';
        }
        if (isset($filter_data['end_date']) and trim($filter_data['end_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['end_date']);
            $query .= ' AND tmp2_a.date_stamp <= ?';
        }
        $query .= '
									AND tmp2_c.time_stamp is not null
									AND ( tmp2_a.deleted = 0 AND tmp2_b.deleted = 0 AND tmp2_c.deleted = 0 )
								group by tmp2_a.id, tmp2_c.status_id
							) as tmp3 group by tmp3.id
					) as tmp2 ON b.id = tmp2.id

					where 	1=1 ';
        if (isset($filter_data['user_id']) and isset($filter_data['user_id'][0]) and !in_array(-1, (array) $filter_data['user_id'])) {
            $query .= ' AND b.user_id in (' . $this->getListSQL($filter_data['user_id'], $ph) . ') ';
        }
        if (isset($filter_data['pay_period_ids']) and isset($filter_data['pay_period_ids'][0]) and !in_array(-1, (array) $filter_data['pay_period_ids'])) {
            $query .= ' AND b.pay_period_id in (' . $this->getListSQL($filter_data['pay_period_ids'], $ph) . ') ';
        }
        if (isset($filter_data['start_date']) and trim($filter_data['start_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['start_date']);
            $query .= ' AND b.date_stamp >= ?';
        }
        if (isset($filter_data['end_date']) and trim($filter_data['end_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['end_date']);
            $query .= ' AND b.date_stamp <= ?';
        }
        $ph[] = $company_id;
        $query .= '
						AND a.status_id in (10,20,30)
						AND ( a.deleted = 0 AND b.deleted = 0 )
					group by b.user_id, b.pay_period_id, a.branch_id, a.department_id, b.date_stamp, user_wage_id, user_wage_effective_date, over_time_policy_wage_id, over_time_policy_wage_effective_date, absence_policy_wage_id, absence_policy_wage_effective_date, premium_policy_wage_id, premium_policy_wage_effective_date, a.status_id, a.type_id, a.over_time_policy_id, a.absence_policy_id, a.premium_policy_id, tmp2.min_punch_time_stamp, tmp2.max_punch_time_stamp
					) as tmp ON z.id = tmp.user_id
				WHERE z.company_id = ? ';
        if (isset($filter_data['user_id']) and isset($filter_data['user_id'][0]) and !in_array(-1, (array) $filter_data['user_id'])) {
            $query .= ' AND z.id in (' . $this->getListSQL($filter_data['user_id'], $ph) . ') ';
        }
        $query .= ' AND z.deleted = 0 ';
        $query .= $this->getSortSQL($order, FALSE);
        $this->rs = $this->db->Execute($query, $ph);
        return $this;
    }
    function getDaysWorkedByTimePeriodAndUserIdAndCompanyIdAndStartDateAndEndDate($time_period, $user_ids, $company_id, $start_date, $end_date, $where = NULL, $order = NULL)
    {
        if ($time_period == '') {
            return FALSE;
        }
        if ($user_ids == '') {
            return FALSE;
        }
        if ($company_id == '') {
            return FALSE;
        }
        if ($start_date == '') {
            return FALSE;
        }
        if ($end_date == '') {
            return FALSE;
        }
        /*
        if ( $order == NULL ) {
        	$order = array( 'date_stamp' => 'asc' );
        	$strict = FALSE;
        } else {
        	$strict = TRUE;
        }
        */
        $uf = new UserFactory();
        $pcf = new PunchControlFactory();
        $ph = array('company_id' => $company_id, 'start_date' => $this->db->BindDate($start_date), 'end_date' => $this->db->BindDate($end_date));
        $query = '
					select 	user_id,
							avg(total) as avg,
							min(total) as min,
							max(total) as max
					from (

						select 	a.user_id,
								(EXTRACT(' . $time_period . ' FROM a.date_stamp) || \'-\' || EXTRACT(year FROM a.date_stamp) ) as date,
								count(*) as total
						from	' . $this->getTable() . ' as a,
								' . $uf->getTable() . ' as b
						where 	a.user_id = b.id
							AND b.company_id = ?
							AND a.date_stamp >= ?
							AND a.date_stamp <= ?
							AND a.user_id in (' . $this->getListSQL($user_ids, $ph) . ')
							AND exists(
										select id
										from ' . $pcf->getTable() . ' as z
										where z.user_date_id = a.id
										AND z.deleted=0
										)
							AND ( a.deleted = 0 AND b.deleted=0 )
							GROUP BY user_id,(EXTRACT(' . $time_period . ' FROM a.date_stamp) || \'-\' || EXTRACT(year FROM a.date_stamp) )
						) tmp
					GROUP BY user_id
					';
        /*
        		$query = '
        					select 	user_id,
        							avg(total) as avg,
        							min(total) as min,
        							max(total) as max
        					from (
        
        						select 	a.user_id,
        								(date_part(\''.$time_period.'\', a.date_stamp) || \'-\' || date_part(\'year\', a.date_stamp) ) as date,
        								count(*) as total
        						from	'. $this->getTable() .' as a,
        								'. $uf->getTable() .' as b
        						where 	a.user_id = b.id
        							AND b.company_id = ?
        							AND a.date_stamp >= ?
        							AND a.date_stamp <= ?
        							AND a.user_id in ('. $this->getListSQL($user_ids, $ph) .')
        							AND exists(
        										select id
        										from '. $pcf->getTable() .' as z
        										where z.user_date_id = a.id
        										AND z.deleted=0
        										)
        							AND ( a.deleted = 0 AND b.deleted=0 )
        							GROUP BY user_id,(date_part(\''. $time_period.'\', a.date_stamp) || \'-\' ||  date_part(\'year\', a.date_stamp) )
        						) tmp
        					GROUP BY user_id
        					';
        */
        //$query .= $this->getWhereSQL( $where );
        //$query .= $this->getSortSQL( $order );
        $this->rs = $this->db->Execute($query, $ph);
        return $this;
    }
    function getLastPunchByCompanyIdAndArrayCriteria($company_id, $filter_data, $limit = NULL, $page = NULL, $where = NULL, $order = NULL)
    {
        if ($company_id == '') {
            return FALSE;
        }
        if (!is_array($order)) {
            //Use Filter Data ordering if its set.
            if (isset($filter_data['sort_column']) and $filter_data['sort_order']) {
                $order = array(Misc::trimSortPrefix($filter_data['sort_column']) => $filter_data['sort_order']);
            }
        }
        //$additional_order_fields = array('b.name', 'c.name', 'd.name', 'e.name');
        $additional_order_fields = array('b.branch_id', 'c.date_stamp', 'd.last_name', 'a.time_stamp', 'a.status_id', 'b.branch_id', 'b.department_id', 'e.type_id');
        if ($order == NULL) {
            $order = array('b.branch_id' => 'asc', 'd.last_name' => 'asc', 'a.time_stamp' => 'desc', 'a.punch_control_id' => 'asc', 'a.status_id' => 'asc');
            $strict = FALSE;
        } else {
            $strict = TRUE;
        }
        //Debug::Arr($order,'Order Data:', __FILE__, __LINE__, __METHOD__,10);
        //Debug::Arr($filter_data,'Filter Data:', __FILE__, __LINE__, __METHOD__,10);
        if (isset($filter_data['exclude_user_ids'])) {
            $filter_data['exclude_id'] = $filter_data['exclude_user_ids'];
        }
        if (isset($filter_data['include_user_ids'])) {
            $filter_data['id'] = $filter_data['include_user_ids'];
        }
        if (isset($filter_data['user_status_ids'])) {
            $filter_data['status_id'] = $filter_data['user_status_ids'];
        }
        if (isset($filter_data['user_title_ids'])) {
            $filter_data['title_id'] = $filter_data['user_title_ids'];
        }
        if (isset($filter_data['group_ids'])) {
            $filter_data['group_id'] = $filter_data['group_ids'];
        }
        if (isset($filter_data['branch_ids'])) {
            $filter_data['default_branch_id'] = $filter_data['branch_ids'];
        }
        if (isset($filter_data['department_ids'])) {
            $filter_data['default_department_id'] = $filter_data['department_ids'];
        }
        if (isset($filter_data['punch_branch_ids'])) {
            $filter_data['punch_branch_id'] = $filter_data['punch_branch_ids'];
        }
        if (isset($filter_data['punch_department_ids'])) {
            $filter_data['punch_department_id'] = $filter_data['punch_department_ids'];
        }
        if (isset($filter_data['exclude_job_ids'])) {
            $filter_data['exclude_id'] = $filter_data['exclude_job_ids'];
        }
        if (isset($filter_data['include_job_ids'])) {
            $filter_data['include_job_id'] = $filter_data['include_job_ids'];
        }
        if (isset($filter_data['job_group_ids'])) {
            $filter_data['job_group_id'] = $filter_data['job_group_ids'];
        }
        if (isset($filter_data['job_item_ids'])) {
            $filter_data['job_item_id'] = $filter_data['job_item_ids'];
        }
        $uf = new UserFactory();
        $udf = new UserDateFactory();
        $pcf = new PunchControlFactory();
        $sf = new StationFactory();
        if (getTTProductEdition() >= TT_PRODUCT_CORPORATE) {
            $jf = new JobFactory();
            $jif = new JobItemFactory();
        }
        $ph = array('company_id' => $company_id);
        $query = '
					select
							a.id as punch_id,
							a.punch_control_id as punch_control_id,
							a.type_id as type_id,
							a.status_id as status_id,
							a.time_stamp as time_stamp,
							a.actual_time_stamp as actual_time_stamp,

							b.user_date_id as user_date_id,
							c.date_stamp as date_stamp,
							b.branch_id as branch_id,
							b.department_id as department_id,
							b.job_id as job_id,
							b.job_item_id as job_item_id,
							b.note as note,

							c.user_id as user_id,

							e.type_id as station_type_id,
							e.station_id as station_station_id,
							e.source as station_source,
							e.description as station_description

					from 	' . $this->getTable() . ' as a
							LEFT JOIN ' . $pcf->getTable() . ' as b ON a.punch_control_id = b.id
							LEFT JOIN ' . $udf->getTable() . ' as c ON b.user_date_id = c.id
							LEFT JOIN ' . $uf->getTable() . ' as d ON c.user_id = d.id
							LEFT JOIN ' . $sf->getTable() . ' as e ON a.station_id = e.id
							LEFT JOIN (
								select tmp2_d.id, max(tmp2_a.time_stamp) as max_punch_time_stamp
									from 	' . $this->getTable() . ' as tmp2_a
									LEFT JOIN ' . $pcf->getTable() . ' as tmp2_b ON tmp2_a.punch_control_id = tmp2_b.id
									LEFT JOIN ' . $udf->getTable() . ' as tmp2_c ON tmp2_b.user_date_id = tmp2_c.id
									LEFT JOIN ' . $uf->getTable() . ' as tmp2_d ON tmp2_c.user_id = tmp2_d.id
									WHERE tmp2_d.company_id = ?';
        if (isset($filter_data['start_date']) and trim($filter_data['start_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['start_date']);
            $query .= ' AND tmp2_c.date_stamp >= ?';
        }
        if (isset($filter_data['end_date']) and trim($filter_data['end_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['end_date']);
            $query .= ' AND tmp2_c.date_stamp <= ?';
        }
        $query .= '
										AND tmp2_a.time_stamp is not null
										AND ( tmp2_a.deleted = 0 AND tmp2_b.deleted = 0 AND tmp2_c.deleted = 0 )
									group by tmp2_d.id
							) as tmp2 ON c.user_id = tmp2.id AND a.time_stamp = tmp2.max_punch_time_stamp

					';
        if (getTTProductEdition() >= TT_PRODUCT_CORPORATE) {
            $query .= '	LEFT JOIN ' . $jf->getTable() . ' as x ON b.job_id = x.id';
            $query .= '	LEFT JOIN ' . $jif->getTable() . ' as y ON b.job_item_id = y.id';
        }
        $ph[] = $company_id;
        $query .= '	WHERE tmp2.id IS NOT NULL AND d.company_id = ?';
        if (isset($filter_data['permission_children_ids']) and isset($filter_data['permission_children_ids'][0]) and !in_array(-1, (array) $filter_data['permission_children_ids'])) {
            $query .= ' AND d.id in (' . $this->getListSQL($filter_data['permission_children_ids'], $ph) . ') ';
        }
        if (isset($filter_data['id']) and isset($filter_data['id'][0]) and !in_array(-1, (array) $filter_data['id'])) {
            $query .= ' AND d.id in (' . $this->getListSQL($filter_data['id'], $ph) . ') ';
        }
        if (isset($filter_data['exclude_id']) and isset($filter_data['exclude_id'][0]) and !in_array(-1, (array) $filter_data['exclude_id'])) {
            $query .= ' AND d.id not in (' . $this->getListSQL($filter_data['exclude_id'], $ph) . ') ';
        }
        if (isset($filter_data['status_id']) and isset($filter_data['status_id'][0]) and !in_array(-1, (array) $filter_data['status_id'])) {
            $query .= ' AND d.status_id in (' . $this->getListSQL($filter_data['status_id'], $ph) . ') ';
        }
        if (isset($filter_data['group_id']) and isset($filter_data['group_id'][0]) and !in_array(-1, (array) $filter_data['group_id'])) {
            if (isset($filter_data['include_subgroups']) and (bool) $filter_data['include_subgroups'] == TRUE) {
                $uglf = new UserGroupListFactory();
                $filter_data['group_id'] = $uglf->getByCompanyIdAndGroupIdAndSubGroupsArray($company_id, $filter_data['group_id'], TRUE);
            }
            $query .= ' AND d.group_id in (' . $this->getListSQL($filter_data['group_id'], $ph) . ') ';
        }
        if (isset($filter_data['default_branch_id']) and isset($filter_data['default_branch_id'][0]) and !in_array(-1, (array) $filter_data['default_branch_id'])) {
            $query .= ' AND d.default_branch_id in (' . $this->getListSQL($filter_data['default_branch_id'], $ph) . ') ';
        }
        if (isset($filter_data['default_department_id']) and isset($filter_data['default_department_id'][0]) and !in_array(-1, (array) $filter_data['default_department_id'])) {
            $query .= ' AND d.default_department_id in (' . $this->getListSQL($filter_data['default_department_id'], $ph) . ') ';
        }
        if (isset($filter_data['title_id']) and isset($filter_data['title_id'][0]) and !in_array(-1, (array) $filter_data['title_id'])) {
            $query .= ' AND d.title_id in (' . $this->getListSQL($filter_data['title_id'], $ph) . ') ';
        }
        if (isset($filter_data['punch_branch_id']) and isset($filter_data['punch_branch_id'][0]) and !in_array(-1, (array) $filter_data['punch_branch_id'])) {
            $query .= ' AND b.branch_id in (' . $this->getListSQL($filter_data['punch_branch_id'], $ph) . ') ';
        }
        if (isset($filter_data['punch_department_id']) and isset($filter_data['punch_department_id'][0]) and !in_array(-1, (array) $filter_data['punch_department_id'])) {
            $query .= ' AND b.department_id in (' . $this->getListSQL($filter_data['punch_department_id'], $ph) . ') ';
        }
        //Use the job_id in the punch_control table so we can filter by '0' or No Job
        if (isset($filter_data['include_job_id']) and isset($filter_data['include_job_id'][0]) and !in_array(-1, (array) $filter_data['include_job_id'])) {
            $query .= ' AND b.job_id in (' . $this->getListSQL($filter_data['include_job_id'], $ph) . ') ';
        }
        if (isset($filter_data['exclude_job_id']) and isset($filter_data['exclude_job_id'][0]) and !in_array(-1, (array) $filter_data['exclude_job_id'])) {
            $query .= ' AND b.job_id not in (' . $this->getListSQL($filter_data['exclude_job_id'], $ph) . ') ';
        }
        if (isset($filter_data['job_group_id']) and isset($filter_data['job_group_id'][0]) and !in_array(-1, (array) $filter_data['job_group_id'])) {
            if (isset($filter_data['include_job_subgroups']) and (bool) $filter_data['include_job_subgroups'] == TRUE) {
                $uglf = new UserGroupListFactory();
                $filter_data['job_group_id'] = $uglf->getByCompanyIdAndGroupIdAndjob_subgroupsArray($company_id, $filter_data['job_group_id'], TRUE);
            }
            $query .= ' AND x.group_id in (' . $this->getListSQL($filter_data['job_group_id'], $ph) . ') ';
        }
        if (isset($filter_data['job_item_id']) and isset($filter_data['job_item_id'][0]) and !in_array(-1, (array) $filter_data['job_item_id'])) {
            $query .= ' AND b.job_item_id in (' . $this->getListSQL($filter_data['job_item_id'], $ph) . ') ';
        }
        if (isset($filter_data['start_date']) and trim($filter_data['start_date']) != '') {
            /*
            $ph[] = $this->db->BindDate($filter_data['start_date']);
            $query  .=	' AND c.date_stamp >= ?';
            */
            $ph[] = $this->db->BindTimeStamp($filter_data['start_date']);
            $query .= ' AND a.time_stamp >= ?';
        }
        if (isset($filter_data['end_date']) and trim($filter_data['end_date']) != '') {
            $ph[] = $this->db->BindTimeStamp($filter_data['end_date']);
            $query .= ' AND a.time_stamp <= ?';
        }
        //The Transfer where clause is an attempt to keep transferred punches from appearing twice.
        $query .= '
						AND ( a.transfer = 0 OR ( a.transfer = 1 AND a.status_id = 10) )
						AND ( a.deleted = 0 AND b.deleted =0 AND c.deleted = 0 AND d.deleted = 0 )
					';
        $query .= $this->getWhereSQL($where);
        $query .= $this->getSortSQL($order, $strict, $additional_order_fields);
        $this->ExecuteSQL($query, $ph, $limit, $page);
        return $this;
    }
 function preSave()
 {
     if ($this->isNew()) {
         //Debug::text(' Setting Original TimeStamp: '. $this->getTimeStamp(), __FILE__, __LINE__, __METHOD__,10);
         $this->setOriginalTimeStamp($this->getTimeStamp());
     }
     if ($this->getDeleted() == FALSE) {
         if ($this->getTransfer() == TRUE and $this->getEnableAutoTransfer() == TRUE) {
             Debug::text(' Transfer is Enabled, automatic punch out of last punch pair: ', __FILE__, __LINE__, __METHOD__, 10);
             //Check to make sure there is an open punch pair.
             $plf = new PunchListFactory();
             $plf->getPreviousPunchByUserIdAndEpoch($this->getUser(), $this->getTimeStamp());
             if ($plf->getRecordCount() > 0) {
                 $p_obj = $plf->getCurrent();
                 Debug::text(' Found Last Punch: ', __FILE__, __LINE__, __METHOD__, 10);
                 if ($p_obj->getStatus() == 10) {
                     Debug::text(' Last Punch was in. Auto Punch Out now: ', __FILE__, __LINE__, __METHOD__, 10);
                     //Make sure the current punch status is IN
                     $this->setStatus(10);
                     //In
                     $this->setType(10);
                     //Normal (can't transfer in/out of lunches?)
                     $pf = new PunchFactory();
                     $pf->setUser($this->getUser());
                     $pf->setEnableAutoTransfer(FALSE);
                     $pf->setPunchControlID($p_obj->getPunchControlID());
                     $pf->setTransfer(TRUE);
                     $pf->setType($p_obj->getNextType());
                     $pf->setStatus(20);
                     //Out
                     $pf->setTimeStamp($this->getTimeStamp(), FALSE);
                     //Disable rounding.
                     $pf->setActualTimeStamp($this->getTimeStamp());
                     $pf->setOriginalTimeStamp($this->getTimeStamp());
                     if ($pf->isValid()) {
                         if ($pf->Save(FALSE) == TRUE) {
                             $p_obj->getPunchControlObject()->setEnableCalcTotalTime(TRUE);
                             $p_obj->getPunchControlObject()->setEnableCalcSystemTotalTime(TRUE);
                             $p_obj->getPunchControlObject()->setEnableCalcUserDateTotal(TRUE);
                             $p_obj->getPunchControlObject()->setEnableCalcException(TRUE);
                             $p_obj->getPunchControlObject()->setEnablePreMatureException(TRUE);
                             if ($p_obj->getPunchControlObject()->isValid()) {
                                 $p_obj->getPunchControlObject()->Save();
                             } else {
                                 Debug::text(' aError saving auto out punch...', __FILE__, __LINE__, __METHOD__, 10);
                             }
                         } else {
                             Debug::text(' bError saving auto out punch...', __FILE__, __LINE__, __METHOD__, 10);
                         }
                     } else {
                         Debug::text(' cError saving auto out punch...', __FILE__, __LINE__, __METHOD__, 10);
                     }
                 } else {
                     Debug::text(' Last Punch was out. No Auto Punch ', __FILE__, __LINE__, __METHOD__, 10);
                 }
             }
             unset($plf, $p_obj, $pf);
         }
         //Split punch at midnight.
         //This has to be an Out punch, and the previous punch has to be an in punch in order for the split to occur.
         //Check to make sure there is an open punch pair.
         //Make sure this punch isn't right at midnight either, as no point in splitting a punch at that time.
         //FIXME: What happens if a supervisor edits a 11:30PM punch and makes it 5:00AM the next day?
         //		We can't split punches when editing, because we have to split punch_control_ids prior to saving etc...
         if ($this->isNew() == TRUE and $this->getStatus() == 20 and $this->getEnableSplitAtMidnight() == TRUE and $this->getTimeStamp() != TTDate::getBeginDayEpoch($this->getTimeStamp()) and (is_object($this->getPunchControlObject()) and is_object($this->getPunchControlObject()->getPayPeriodScheduleObject()) and $this->getPunchControlObject()->getPayPeriodScheduleObject()->getShiftAssignedDay() == 40)) {
             $plf = new PunchListFactory();
             $plf->getPreviousPunchByUserIdAndEpoch($this->getUser(), $this->getTimeStamp());
             if ($plf->getRecordCount() > 0) {
                 $p_obj = $plf->getCurrent();
                 Debug::text(' Found Last Punch: ', __FILE__, __LINE__, __METHOD__, 10);
                 if ($p_obj->getStatus() == 10 and TTDate::doesRangeSpanMidnight($this->getTimeStamp(), $p_obj->getTimeStamp())) {
                     Debug::text(' Last Punch was in and this is an out punch that spans midnight. Split Punch at midnight now: ', __FILE__, __LINE__, __METHOD__, 10);
                     //FIXME: This will fail if a shift spans multiple days!
                     //Make sure the current punch status is OUT
                     //But we can split LUNCH/Break punches, because someone could punch in at 8PM, then out for lunch at 1:00AM, this would need to be split.
                     $this->setStatus(20);
                     //Out
                     //Reduce the out punch by 60 seconds, and increase the current punch by 60seconds so no time is lost.
                     $this->setTimeStamp($this->getTimeStamp() + 60);
                     //FIXME: May need to use ActualTimeStamp here so we aren't double rounding.
                     //Get new punch control ID for the midnight punch and this one.
                     $new_punch_control_id = $this->getPunchControlObject()->getNextInsertId();
                     $this->setPunchControlID($new_punch_control_id);
                     Debug::text(' Split Punch: Punching out just before midnight yesterday...', __FILE__, __LINE__, __METHOD__, 10);
                     //
                     //Punch out just before midnight
                     //
                     $pf = new PunchFactory();
                     $pf->setUser($this->getUser());
                     $pf->setEnableSplitAtMidnight(FALSE);
                     $pf->setTransfer(FALSE);
                     $pf->setEnableAutoTransfer(FALSE);
                     $pf->setType(10);
                     //Normal
                     $pf->setStatus(20);
                     //Out
                     $before_midnight_timestamp = TTDate::getBeginDayEpoch($this->getTimeStamp()) - 60;
                     $pf->setTimeStamp($before_midnight_timestamp, FALSE);
                     //Disable rounding.
                     $pf->setActualTimeStamp($before_midnight_timestamp);
                     $pf->setOriginalTimeStamp($before_midnight_timestamp);
                     $pf->setPunchControlID($p_obj->getPunchControlID());
                     if ($pf->isValid()) {
                         if ($pf->Save(FALSE) == TRUE) {
                             $p_obj->getPunchControlObject()->setEnableCalcTotalTime(TRUE);
                             $p_obj->getPunchControlObject()->setEnableCalcSystemTotalTime(TRUE);
                             $p_obj->getPunchControlObject()->setEnableCalcUserDateTotal(TRUE);
                             $p_obj->getPunchControlObject()->setEnableCalcException(TRUE);
                             $p_obj->getPunchControlObject()->setEnablePreMatureException(TRUE);
                             $p_obj->getPunchControlObject()->Save();
                         }
                     }
                     unset($pf, $p_obj, $before_midnight_timestamp);
                     Debug::text(' Split Punch: Punching int at midnight today...', __FILE__, __LINE__, __METHOD__, 10);
                     //
                     //Punch in again right at midnight.
                     //
                     $pf = new PunchFactory();
                     $pf->setUser($this->getUser());
                     $pf->setEnableSplitAtMidnight(FALSE);
                     $pf->setTransfer(FALSE);
                     $pf->setEnableAutoTransfer(FALSE);
                     $pf->setType(10);
                     //Normal
                     $pf->setStatus(10);
                     //In
                     $at_midnight_timestamp = TTDate::getBeginDayEpoch($this->getTimeStamp());
                     $pf->setTimeStamp($at_midnight_timestamp, FALSE);
                     //Disable rounding.
                     $pf->setActualTimeStamp($at_midnight_timestamp);
                     $pf->setOriginalTimeStamp($at_midnight_timestamp);
                     $pf->setPunchControlID($new_punch_control_id);
                     if ($pf->isValid()) {
                         if ($pf->Save(FALSE) == TRUE) {
                             $pcf = new PunchControlFactory();
                             $pcf->setId($pf->getPunchControlID());
                             $pcf->setPunchObject($pf);
                             $pcf->setBranch($this->getPunchControlObject()->getBranch());
                             $pcf->setDepartment($this->getPunchControlObject()->getDepartment());
                             $pcf->setJob($this->getPunchControlObject()->getJob());
                             $pcf->setJobItem($this->getPunchControlObject()->getJobItem());
                             $pcf->setOtherID1($this->getPunchControlObject()->getOtherID1());
                             $pcf->setOtherID2($this->getPunchControlObject()->getOtherID2());
                             $pcf->setOtherID3($this->getPunchControlObject()->getOtherID3());
                             $pcf->setOtherID4($this->getPunchControlObject()->getOtherID4());
                             $pcf->setOtherID5($this->getPunchControlObject()->getOtherID5());
                             $pcf->setEnableStrictJobValidation(TRUE);
                             $pcf->setEnableCalcUserDateID(TRUE);
                             $pcf->setEnableCalcTotalTime(TRUE);
                             $pcf->setEnableCalcSystemTotalTime(TRUE);
                             $pcf->setEnableCalcWeeklySystemTotalTime(TRUE);
                             $pcf->setEnableCalcUserDateTotal(TRUE);
                             $pcf->setEnableCalcException(TRUE);
                             if ($pcf->isValid() == TRUE) {
                                 $pcf->Save(TRUE, TRUE);
                                 //Force isNEW() lookup.
                             }
                         }
                     }
                     unset($pf, $at_midnight_timestamp, $new_punch_control_id);
                 } else {
                     Debug::text(' Last Punch was out. No Auto Punch ', __FILE__, __LINE__, __METHOD__, 10);
                 }
             }
         }
     }
     return TRUE;
 }
    function setPunchData($data)
    {
        //Debug::Arr($data, 'punchDataArray', __FILE__, __LINE__, __METHOD__,10);
        /*
        <b>TimeTrexSoapServer::setPunchData()</b>:  Data:  <pre> array(10) {
        ["date_stamp"]=>   string(9) "05-Nov-05"
        ["transfer"]=>   int(0)
        ["branch_id"]=>   string(1) "0"
        ["time_stamp"]=>   string(7) "4:42 PM"
        ["user_date_id"]=>   string(5) "14774"
        ["punch_control_id"]=>   string(5) "26614"
        ["type_id"]=>   string(2) "10"
        ["department_id"]=>   string(1) "0"
        ["status_id"]=>   string(2) "20"
        ["user_id"]=>   string(1) "1" } </pre><br>
        */
        //Debug::Arr($data, ' Data: ', __FILE__, __LINE__, __METHOD__,10);
        //User prefs should be set before we parse the date/time.
        $punch_full_time_stamp = TTDate::parseDateTime($data['date_stamp'] . ' ' . $data['time_stamp']);
        Debug::Text(' Punch Full TimeStamp: ' . date('r' . $punch_full_time_stamp) . ' (' . $punch_full_time_stamp . ') TimeZone: ' . $this->getUserObject()->getUserPreferenceObject()->getTimeZone(), __FILE__, __LINE__, __METHOD__, 10);
        $pf = new PunchFactory();
        $pf->StartTransaction();
        //Set User before setTimeStamp so rounding can be done properly.
        $pf->setUser($this->getUserObject()->getId());
        if (isset($data['transfer']) and $data['transfer'] == 1) {
            Debug::Text(' Enabling Transfer!: ', __FILE__, __LINE__, __METHOD__, 10);
            $data['type_id'] = 10;
            $data['status_id'] = 10;
            $pf->setTransfer(TRUE);
        }
        $pf->setType($data['type_id']);
        $pf->setStatus($data['status_id']);
        $pf->setTimeStamp($punch_full_time_stamp);
        if (isset($data['status_id']) and $data['status_id'] == 20 and isset($data['punch_control_id']) and $data['punch_control_id'] != '') {
            $pf->setPunchControlID($data['punch_control_id']);
        } else {
            $pf->setPunchControlID($pf->findPunchControlID());
        }
        $pf->setStation($this->getStationObject()->getId());
        if ($pf->isNew()) {
            $pf->setActualTimeStamp($punch_full_time_stamp);
            $pf->setOriginalTimeStamp($pf->getTimeStamp());
        }
        if ($pf->isValid() == TRUE) {
            $return_date = $pf->getTimeStamp();
            if ($pf->getStatus() == 10) {
                $label = 'In';
            } else {
                $label = 'Out';
            }
            if ($pf->Save(FALSE) == TRUE) {
                $pcf = new PunchControlFactory();
                $pcf->setId($pf->getPunchControlID());
                $pcf->setPunchObject($pf);
                if (isset($data['branch_id']) and $data['branch_id'] != '') {
                    $pcf->setBranch($data['branch_id']);
                }
                if (isset($data['department_id']) and $data['department_id'] != '') {
                    $pcf->setDepartment($data['department_id']);
                }
                if (isset($data['job_id']) and $data['job_id'] != '') {
                    $pcf->setJob($data['job_id']);
                }
                if (isset($data['job_item_id']) and $data['job_item_id'] != '') {
                    $pcf->setJobItem($data['job_item_id']);
                }
                if (isset($data['quantity']) and $data['quantity'] != '') {
                    $pcf->setQuantity($data['quantity']);
                }
                if (isset($data['bad_quantity']) and $data['bad_quantity'] != '') {
                    $pcf->setBadQuantity($data['bad_quantity']);
                }
                //Don't overwrite note if a new one isn't set. This makes it more difficult to delete a note if they want to,
                //But thats better then accidently deleting it.
                if (isset($data['note']) and $data['note'] != '') {
                    $pcf->setNote($data['note']);
                }
                if (isset($data['other_id1']) and $data['other_id1'] != '') {
                    $pcf->setOtherID1($data['other_id1']);
                }
                if (isset($data['other_id2']) and $data['other_id2'] != '') {
                    $pcf->setOtherID2($data['other_id2']);
                }
                if (isset($data['other_id3']) and $data['other_id3'] != '') {
                    $pcf->setOtherID3($data['other_id3']);
                }
                if (isset($data['other_id4']) and $data['other_id4'] != '') {
                    $pcf->setOtherID4($data['other_id4']);
                }
                if (isset($data['other_id5']) and $data['other_id5'] != '') {
                    $pcf->setOtherID5($data['other_id5']);
                }
                //$pcf->setEnableStrictJobValidation( TRUE );
                $pcf->setEnableCalcUserDateID(TRUE);
                $pcf->setEnableCalcTotalTime(TRUE);
                $pcf->setEnableCalcSystemTotalTime(TRUE);
                $pcf->setEnableCalcUserDateTotal(TRUE);
                $pcf->setEnableCalcException(TRUE);
                $pcf->setEnablePreMatureException(TRUE);
                //Enable pre-mature exceptions at this point.
                if ($pcf->isValid() == TRUE) {
                    Debug::Text(' Punch Control is valid, saving...: ', __FILE__, __LINE__, __METHOD__, 10);
                    if ($pcf->Save(TRUE, TRUE) == TRUE) {
                        //Force isNew() lookup.
                        Debug::text('Return Date: ' . $return_date, __FILE__, __LINE__, __METHOD__, 10);
                        $retval = '<div style="font-size:28px; font-weight: bold">
						<table>
						<tr>
							<td>' . $this->getUserObject()->getFullName() . '</td>
						</tr>
						<tr>
							<td>
							Punch ' . $label . ': ' . TTDate::getDate('TIME', $return_date) . '
							</td>
						</tr>
						</table>
						</div>';
                        Debug::text('RetVal: ' . $retval, __FILE__, __LINE__, __METHOD__, 10);
                        //Set stations last punch time stamp so we can filter out duplicates later on.
                        //$pf->FailTransaction();
                        $pf->CommitTransaction();
                        return $retval;
                    } else {
                        Debug::text('Punch Control save failed!', __FILE__, __LINE__, __METHOD__, 10);
                    }
                } else {
                    Debug::text('Punch Control is NOT VALID!', __FILE__, __LINE__, __METHOD__, 10);
                }
            } else {
                Debug::text('Punch save failed!', __FILE__, __LINE__, __METHOD__, 10);
            }
        } else {
            Debug::text('Punch is NOT VALID: ', __FILE__, __LINE__, __METHOD__, 10);
        }
        $pf->FailTransaction();
        Debug::text('Returning FALSE: Action Failed! ', __FILE__, __LINE__, __METHOD__, 10);
        //Get text errors to display to the user.
        $errors = NULL;
        if (isset($pf) and is_object($pf)) {
            $errors .= $pf->Validator->getErrors();
        }
        if (isset($pcf) and is_object($pcf)) {
            $errors .= $pcf->Validator->getErrors();
        }
        $errors = wordwrap($errors, 40, "<br>\n");
        $retval = '<table bgcolor="red">
		<tr>
			<td style="font-size:28px; font-weight: bold">Action Failed!</td>
		</tr>
		<tr>
			<td style="font-size:14px; font-weight: bold">
				' . $errors . '
			</td>
		</tr>
		</table>';
        return $retval;
        //return FALSE;
    }
 //Use the In punch object to find the punch_control_id.
 $pf_out->setActualTimeStamp($pf_out->getTimeStamp());
 $pf_out->setOriginalTimeStamp($pf_out->getTimeStamp());
 if ($pf_out->isValid()) {
     Debug::text('Punch Out: Valid!', __FILE__, __LINE__, __METHOD__, 10);
     $pf_out->setEnableCalcTotalTime(TRUE);
     $pf_out->setEnableCalcSystemTotalTime(TRUE);
     $pf_out->setEnableCalcUserDateTotal(TRUE);
     $pf_out->setEnableCalcException(TRUE);
     $pf_out->Save(FALSE);
 } else {
     Debug::text('Punch Out: InValid!', __FILE__, __LINE__, __METHOD__, 10);
 }
 if ($pf_in->isValid() == TRUE or $pf_out->isValid() == TRUE) {
     Debug::text('Punch In and Out succeeded, saving punch control!', __FILE__, __LINE__, __METHOD__, 10);
     $pcf = new PunchControlFactory();
     $pcf->setId($pf_in->getPunchControlID());
     if ($pf_in->isValid() == TRUE) {
         $pcf->setPunchObject($pf_in);
     } elseif ($pf_out->isValid() == TRUE) {
         $pcf->setPunchObject($pf_out);
     }
     if ($recurring_schedule_shift['branch_id'] == -1) {
         $pcf->setBranch($user_obj->getDefaultBranch());
     } else {
         $pcf->setBranch($recurring_schedule_shift['branch_id']);
     }
     if ($recurring_schedule_shift['department_id'] == -1) {
         $pcf->setDepartment($user_obj->getDefaultDepartment());
     } else {
         $pcf->setDepartment($recurring_schedule_shift['department_id']);
 function dragNdropPunch($src_punch_id, $dst_punch_id, $dst_status_id = NULL, $position = 0, $action = 0, $dst_date = NULL)
 {
     if (!$this->getPermissionObject()->Check('punch', 'enabled') or !($this->getPermissionObject()->Check('punch', 'add') and ($this->getPermissionObject()->Check('punch', 'delete') or $this->getPermissionObject()->Check('punch', 'delete_child') or $this->getPermissionObject()->Check('punch', 'delete_own')))) {
         return $this->getPermissionObject()->PermissionDenied();
     }
     return $this->returnHandler(PunchControlFactory::dragNdropPunch($this->getCurrentCompanyObject()->getId(), $src_punch_id, $dst_punch_id, $dst_status_id, $position, $action, $dst_date));
 }
 function postSave()
 {
     $this->removeCache($this->getId());
     if ($this->getDeleted() == TRUE) {
         Debug::Text('UnAssign Hours from Branch: ' . $this->getId(), __FILE__, __LINE__, __METHOD__, 10);
         //Unassign hours from this branch.
         $pcf = new PunchControlFactory();
         $udtf = new UserDateTotalFactory();
         $uf = new UserFactory();
         $sf = new StationFactory();
         $sf_b = new ScheduleFactory();
         $udf = new UserDefaultFactory();
         $rstf = new RecurringScheduleTemplateFactory();
         $query = 'update ' . $pcf->getTable() . ' set branch_id = 0 where branch_id = ' . $this->getId();
         $this->db->Execute($query);
         $query = 'update ' . $udtf->getTable() . ' set branch_id = 0 where branch_id = ' . $this->getId();
         $this->db->Execute($query);
         $query = 'update ' . $sf_b->getTable() . ' set branch_id = 0 where branch_id = ' . $this->getId();
         $this->db->Execute($query);
         $query = 'update ' . $uf->getTable() . ' set default_branch_id = 0 where company_id = ' . $this->getCompany() . ' AND default_branch_id = ' . $this->getId();
         $this->db->Execute($query);
         $query = 'update ' . $udf->getTable() . ' set default_branch_id = 0 where company_id = ' . $this->getCompany() . ' AND default_branch_id = ' . $this->getId();
         $this->db->Execute($query);
         $query = 'update ' . $sf->getTable() . ' set branch_id = 0 where company_id = ' . $this->getCompany() . ' AND branch_id = ' . $this->getId();
         $this->db->Execute($query);
         $query = 'update ' . $rstf->getTable() . ' set branch_id = 0 where branch_id = ' . $this->getId();
         $this->db->Execute($query);
     }
     return TRUE;
 }
    function getAPISearchByCompanyIdAndArrayCriteria($company_id, $filter_data, $limit = NULL, $page = NULL, $where = NULL, $order = NULL)
    {
        if ($company_id == '') {
            return FALSE;
        }
        if (!is_array($order)) {
            //Use Filter Data ordering if its set.
            if (isset($filter_data['sort_column']) and $filter_data['sort_order']) {
                $order = array(Misc::trimSortPrefix($filter_data['sort_column']) => $filter_data['sort_order']);
            }
        }
        if (isset($filter_data['user_group_id'])) {
            $filter_data['group_id'] = $filter_data['user_group_id'];
        }
        if (isset($filter_data['user_title_id'])) {
            $filter_data['title_id'] = $filter_data['user_title_id'];
        }
        if (isset($filter_data['include_user_id'])) {
            $filter_data['user_id'] = $filter_data['include_user_id'];
        }
        if (isset($filter_data['exception_policy_severity_id'])) {
            $filter_data['severity_id'] = $filter_data['exception_policy_severity_id'];
        }
        $additional_order_fields = array('d.name', 'e.name', 'f.name', 'g.name', 'h.status_id', 'i.severity_id', 'i.type_id', 'c.first_name', 'c.last_name', 'c.country', 'c.province', 'b.date_stamp', 'pgf.name', 'pscf.name', 'ppsf.name');
        $sort_column_aliases = array('status' => 'status_id', 'type' => 'type_id');
        $order = $this->getColumnsFromAliases($order, $sort_column_aliases);
        if ($order == NULL) {
            //$order = array( 'status_id' => 'asc', 'last_name' => 'asc', 'first_name' => 'asc', 'middle_name' => 'asc');
            $order = array('i.severity_id' => 'desc', 'c.last_name' => 'asc', 'b.date_stamp' => 'asc', 'i.type_id' => 'asc');
            $strict = FALSE;
        } else {
            //Do order by column conversions, because if we include these columns in the SQL
            //query, they contaminate the data array.
            if (isset($order['default_branch'])) {
                $order['d.name'] = $order['default_branch'];
                unset($order['default_branch']);
            }
            if (isset($order['default_department'])) {
                $order['e.name'] = $order['default_department'];
                unset($order['default_department']);
            }
            if (isset($order['user_group'])) {
                $order['f.name'] = $order['user_group'];
                unset($order['user_group']);
            }
            if (isset($order['title'])) {
                $order['g.name'] = $order['title'];
                unset($order['title']);
            }
            if (isset($order['exception_policy_type_id'])) {
                $order['i.type_id'] = $order['exception_policy_type_id'];
                unset($order['exception_policy_type_id']);
            }
            if (isset($order['severity_id'])) {
                $order['i.severity_id'] = $order['severity_id'];
                unset($order['severity_id']);
            }
            if (isset($order['severity'])) {
                $order['i.severity_id'] = $order['severity'];
                unset($order['severity']);
            }
            if (isset($order['exception_policy_type'])) {
                $order['i.type_id'] = $order['exception_policy_type'];
                unset($order['exception_policy_type']);
            }
            if (isset($order['exception_policy_type_id'])) {
                $order['i.type_id'] = $order['exception_policy_type_id'];
                unset($order['exception_policy_type_id']);
            }
            if (isset($order['first_name'])) {
                $order['c.first_name'] = $order['first_name'];
                unset($order['first_name']);
            }
            if (isset($order['last_name'])) {
                $order['c.last_name'] = $order['last_name'];
                unset($order['last_name']);
            }
            if (isset($order['country'])) {
                $order['c.country'] = $order['country'];
                unset($order['country']);
            }
            if (isset($order['province'])) {
                $order['c.province'] = $order['province'];
                unset($order['province']);
            }
            if (isset($order['date_stamp'])) {
                $order['b.date_stamp'] = $order['date_stamp'];
                unset($order['date_stamp']);
            }
            if (isset($order['policy_group'])) {
                $order['pgf.name'] = $order['policy_group'];
                unset($order['policy_group']);
            }
            if (isset($order['permission_group'])) {
                $order['pscf.name'] = $order['permission_group'];
                unset($order['permission_group']);
            }
            if (isset($order['pay_period_schedule'])) {
                $order['ppsf.name'] = $order['pay_period_schedule'];
                unset($order['pay_period_schedule']);
            }
            //Always sort by last name,first name after other columns
            if (!isset($order['c.last_name'])) {
                $order['c.last_name'] = 'asc';
            }
            if (!isset($order['c.first_name'])) {
                $order['c.first_name'] = 'asc';
            }
            if (!isset($order['b.date_stamp'])) {
                $order['b.date_stamp'] = 'asc';
            }
            if (!isset($order['i.severity_id'])) {
                $order['i.severity_id'] = 'desc';
            }
            $strict = TRUE;
        }
        //Debug::Arr($order,'Order Data:', __FILE__, __LINE__, __METHOD__,10);
        //Debug::Arr($filter_data,'Filter Data:', __FILE__, __LINE__, __METHOD__,10);
        $udf = new UserDateFactory();
        $uf = new UserFactory();
        $bf = new BranchFactory();
        $df = new DepartmentFactory();
        $ugf = new UserGroupFactory();
        $utf = new UserTitleFactory();
        $ppf = new PayPeriodFactory();
        $ppsf = new PayPeriodScheduleFactory();
        $epf = new ExceptionPolicyFactory();
        $epcf = new ExceptionPolicyControlFactory();
        $pguf = new PolicyGroupUserFactory();
        $pgf = new PolicyGroupFactory();
        $pf = new PunchFactory();
        $pcf = new PunchControlFactory();
        $pscf = new PermissionControlFactory();
        $puf = new PermissionUserFactory();
        $ph = array('company_id' => $company_id);
        $query = '
					select 	a.*,
							b.date_stamp as date_stamp,
							b.pay_period_id as pay_period_id,
							h.pay_period_schedule_id as pay_period_schedule_id,
							i.severity_id as severity_id,
							i.type_id as exception_policy_type_id,
							b.user_id as user_id,
                            h.start_date as pay_period_start_date,
							h.end_date as pay_period_end_date,
							h.transaction_date as pay_period_transaction_date,
							c.first_name as first_name,
							c.last_name as last_name,
                            c.country as country,
                            c.province as province,
							c.status_id as user_status_id,
							c.group_id as group_id,
							f.name as "group",
							c.title_id as title_id,
							g.name as title,
							c.default_branch_id as default_branch_id,
							d.name as default_branch,
							c.default_department_id as default_department_id,
							e.name as default_department,

							pcf.branch_id as branch_id,
							bf.name as branch,
							pcf.department_id as department_id,
							df.name as department,
                            pgf.name as policy_group,
                            pscf.name as permission_group,
                            ppsf.name as pay_period_schedule,

							y.first_name as created_by_first_name,
							y.middle_name as created_by_middle_name,
							y.last_name as created_by_last_name,
							z.first_name as updated_by_first_name,
							z.middle_name as updated_by_middle_name,
							z.last_name as updated_by_last_name
					from 	' . $this->getTable() . ' as a
						LEFT JOIN ' . $pf->getTable() . ' as pf ON ( a.punch_id IS NOT NULL AND a.punch_id = pf.id AND pf.deleted = 0)
						LEFT JOIN ' . $pcf->getTable() . ' as pcf ON ( ( ( pf.id IS NOT NULL AND pf.punch_control_id = pcf.id ) OR ( a.punch_control_id is NOT NULL AND a.punch_control_id = pcf.id ) ) AND pcf.deleted = 0)
						LEFT JOIN ' . $bf->getTable() . ' as bf ON pcf.branch_id = bf.id
						LEFT JOIN ' . $df->getTable() . ' as df ON pcf.department_id = df.id

						LEFT JOIN ' . $udf->getTable() . ' as b ON a.user_date_id = b.id
						LEFT JOIN ' . $uf->getTable() . ' as c ON b.user_id = c.id
						LEFT JOIN ' . $bf->getTable() . ' as d ON c.default_branch_id = d.id
						LEFT JOIN ' . $df->getTable() . ' as e ON c.default_department_id = e.id
						LEFT JOIN ' . $ugf->getTable() . ' as f ON c.group_id = f.id
						LEFT JOIN ' . $utf->getTable() . ' as g ON c.title_id = g.id
						LEFT JOIN ' . $ppf->getTable() . ' as h ON b.pay_period_id = h.id
                        LEFT JOIN ' . $ppsf->getTable() . ' as ppsf ON ppsf.id = h.pay_period_schedule_id
						LEFT JOIN ' . $epf->getTable() . ' as i ON a.exception_policy_id = i.id
                        LEFT JOIN ' . $epcf->getTable() . ' as epcf ON epcf.id = i.exception_policy_control_id
						LEFT JOIN ' . $pguf->getTable() . ' as pguf ON b.user_id = pguf.user_id
                        LEFT JOIN ' . $pgf->getTable() . ' as pgf ON pguf.policy_group_id = pgf.id
                        LEFT JOIN ' . $puf->getTable() . ' as puf ON c.id = puf.user_id
                        LEFT JOIN ' . $pscf->getTable() . ' as pscf ON pscf.id = puf.permission_control_id
						LEFT JOIN ' . $uf->getTable() . ' as y ON ( a.created_by = y.id AND y.deleted = 0 )
						LEFT JOIN ' . $uf->getTable() . ' as z ON ( a.updated_by = z.id AND z.deleted = 0 )
					where	c.company_id = ?
					';
        if (isset($filter_data['permission_children_ids']) and isset($filter_data['permission_children_ids'][0]) and !in_array(-1, (array) $filter_data['permission_children_ids'])) {
            $query .= ' AND c.id in (' . $this->getListSQL($filter_data['permission_children_ids'], $ph) . ') ';
        }
        if (isset($filter_data['id']) and isset($filter_data['id'][0]) and !in_array(-1, (array) $filter_data['id'])) {
            $query .= ' AND a.id in (' . $this->getListSQL($filter_data['id'], $ph) . ') ';
        }
        if (isset($filter_data['user_id']) and isset($filter_data['user_id'][0]) and !in_array(-1, (array) $filter_data['user_id'])) {
            $query .= ' AND c.id in (' . $this->getListSQL($filter_data['user_id'], $ph) . ') ';
        }
        $query .= isset($filter_data['exclude_user_id']) ? $this->getWhereClauseSQL('c.id', $filter_data['exclude_user_id'], 'not_numeric_list', $ph) : NULL;
        if (isset($filter_data['user_status_id']) and isset($filter_data['user_status_id'][0]) and !in_array(-1, (array) $filter_data['user_status_id'])) {
            $query .= ' AND c.status_id in (' . $this->getListSQL($filter_data['user_status_id'], $ph) . ') ';
        }
        if (isset($filter_data['type_id']) and isset($filter_data['type_id'][0]) and !in_array(-1, (array) $filter_data['type_id'])) {
            $query .= ' AND a.type_id in (' . $this->getListSQL($filter_data['type_id'], $ph) . ') ';
        }
        if (isset($filter_data['severity_id']) and isset($filter_data['severity_id'][0]) and !in_array(-1, (array) $filter_data['severity_id'])) {
            $query .= ' AND i.severity_id in (' . $this->getListSQL($filter_data['severity_id'], $ph) . ') ';
        }
        if (isset($filter_data['exception_policy_type_id']) and isset($filter_data['exception_policy_type_id'][0]) and !in_array(-1, (array) $filter_data['exception_policy_type_id'])) {
            $query .= ' AND i.type_id in (' . $this->getListSQL($filter_data['exception_policy_type_id'], $ph) . ') ';
        }
        if (isset($filter_data['pay_period_id']) and isset($filter_data['pay_period_id'][0]) and !in_array(-1, (array) $filter_data['pay_period_id'])) {
            $query .= ' AND b.pay_period_id in (' . $this->getListSQL($filter_data['pay_period_id'], $ph) . ') ';
        }
        if (isset($filter_data['pay_period_status_id']) and isset($filter_data['pay_period_status_id'][0]) and !in_array(-1, (array) $filter_data['pay_period_status_id'])) {
            $query .= ' AND h.status_id in (' . $this->getListSQL($filter_data['pay_period_status_id'], $ph) . ') ';
        }
        if (isset($filter_data['group_id']) and isset($filter_data['group_id'][0]) and !in_array(-1, (array) $filter_data['group_id'])) {
            if (isset($filter_data['include_subgroups']) and (bool) $filter_data['include_subgroups'] == TRUE) {
                $uglf = new UserGroupListFactory();
                $filter_data['group_id'] = $uglf->getByCompanyIdAndGroupIdAndSubGroupsArray($company_id, $filter_data['group_id'], TRUE);
            }
            $query .= ' AND c.group_id in (' . $this->getListSQL($filter_data['group_id'], $ph) . ') ';
        }
        if (isset($filter_data['default_branch_id']) and isset($filter_data['default_branch_id'][0]) and !in_array(-1, (array) $filter_data['default_branch_id'])) {
            $query .= ' AND c.default_branch_id in (' . $this->getListSQL($filter_data['default_branch_id'], $ph) . ') ';
        }
        if (isset($filter_data['default_department_id']) and isset($filter_data['default_department_id'][0]) and !in_array(-1, (array) $filter_data['default_department_id'])) {
            $query .= ' AND c.default_department_id in (' . $this->getListSQL($filter_data['default_department_id'], $ph) . ') ';
        }
        if (isset($filter_data['title_id']) and isset($filter_data['title_id'][0]) and !in_array(-1, (array) $filter_data['title_id'])) {
            $query .= ' AND c.title_id in (' . $this->getListSQL($filter_data['title_id'], $ph) . ') ';
        }
        if (isset($filter_data['branch_id']) and isset($filter_data['branch_id'][0]) and !in_array(-1, (array) $filter_data['branch_id'])) {
            $query .= ' AND pcf.branch_id in (' . $this->getListSQL($filter_data['branch_id'], $ph) . ') ';
        }
        if (isset($filter_data['department_id']) and isset($filter_data['department_id'][0]) and !in_array(-1, (array) $filter_data['department_id'])) {
            $query .= ' AND pcf.department_id in (' . $this->getListSQL($filter_data['department_id'], $ph) . ') ';
        }
        if (isset($filter_data['start_date']) and trim($filter_data['start_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['start_date']);
            $query .= ' AND b.date_stamp >= ?';
        }
        if (isset($filter_data['end_date']) and trim($filter_data['end_date']) != '') {
            $ph[] = $this->db->BindDate($filter_data['end_date']);
            $query .= ' AND b.date_stamp <= ?';
        }
        $query .= isset($filter_data['created_by']) ? $this->getWhereClauseSQL(array('a.created_by', 'y.first_name', 'y.last_name'), $filter_data['created_by'], 'user_id_or_name', $ph) : NULL;
        $query .= isset($filter_data['updated_by']) ? $this->getWhereClauseSQL(array('a.updated_by', 'z.first_name', 'z.last_name'), $filter_data['updated_by'], 'user_id_or_name', $ph) : NULL;
        //Make sure we accept exception rows assign to pay_period_id = 0 (no pay period), as this can happen when punches exist in the future.
        $query .= '
						AND ( a.deleted = 0 AND b.deleted = 0 AND c.deleted = 0 AND pgf.deleted = 0 AND ( h.deleted = 0 OR h.deleted is NULL ) )
					';
        $query .= $this->getWhereSQL($where);
        $query .= $this->getSortSQL($order, $strict, $additional_order_fields);
        //Debug::Arr($ph, 'Query: '. $query, __FILE__, __LINE__, __METHOD__,10);
        $this->ExecuteSQL($query, $ph, $limit, $page);
        return $this;
    }
                 }
             }
         }
     } else {
         echo " \t\t\tFailed!\n";
         $commit_trans = FALSE;
         $e++;
         $errors = $pf->Validator->getErrorsArray();
         if (is_array($errors)) {
             foreach ($errors as $error_arr) {
                 echo "      ERROR: " . $error_arr[0] . "\n";
             }
         }
     }
 }
 $pcf = new PunchControlFactory();
 $pcf->setId($pf->getPunchControlID());
 $pcf->setPunchObject($pf);
 if (isset($mapped_row['branch_id']) and $mapped_row['branch_id'] != '') {
     $pcf->setBranch(Misc::importCallInputParseFunction('branch_id', $mapped_row['branch_id'], $filtered_import_map['branch_id']['default_value'], $filtered_import_map['branch_id']['parse_hint']));
 }
 if (isset($mapped_row['department_id']) and $mapped_row['department_id'] != '') {
     $pcf->setDepartment(Misc::importCallInputParseFunction('department_id', $mapped_row['department_id'], $filtered_import_map['department_id']['default_value'], $filtered_import_map['department_id']['parse_hint']));
 }
 if (getTTProductEdition() == TT_PRODUCT_PROFESSIONAL) {
     if (isset($mapped_row['job_id']) and $mapped_row['job_id'] != '') {
         $pcf->setJob(Misc::importCallInputParseFunction('job_id', $mapped_row['job_id'], $filtered_import_map['job_id']['default_value'], $filtered_import_map['job_id']['parse_hint']));
     }
     if (isset($mapped_row['task_id']) and $mapped_row['task_id'] != '') {
         $pcf->setJobItem(Misc::importCallInputParseFunction('task_id', $mapped_row['task_id'], $filtered_import_map['task_id']['default_value'], $filtered_import_map['task_id']['parse_hint']));
     }
 static function dragNdropPunch($company_id, $src_punch_id, $dst_punch_id, $dst_status_id = NULL, $position = 0, $action = 0, $dst_date = NULL)
 {
     /*
     	FIXME: This needs to handle batches to be able to handle all the differnet corner cases.
     	Operations to handle:
     		- Moving punch from Out to In, or In to Out in same punch pair, this is ALWAYS a move, and not a copy.
     		- Move punch from one pair to another in the same day, this can be a copy or move.
     			- Check moving AND copying Out punch from one punch pair to In in another on the same day. ie: In 8:00AM, Out 1:00PM, Out 5:00PM. Move the 1PM punch to pair with 5PM.
     		- Move punch from one day to another, inserting inbetween other punches if necessary.
     		- Move punch from one day to another without any other punches.
     
     
     		- Inserting BEFORE on a dst_punch_id that is an In punch doesn't do any splitting.
     		- Inserting AFTER on a dst_punch_id that is on a Out punch doesn't do any splitting.
     		- Overwriting should just take the punch time and overwrite the existing punch time.
     		- The first thing this function does it check if there are two punches assigned to the punch control of the destination punch, if there is, it splits the punches
     			across two punch_controls, it then attaches the src_punch_id to the same punch_control_id as the dst_punch_id.
     		- If no dst_punch_id is specified, assume copying to a blank cell, just copy the punch to that date along with the punch_control?
     		- Copying punches that span midnight work, however moving punches does not always
     			since we don't move punches in batches, we do it one at a time, and when the first punch punch
     			gets moved, it can cause other punches to follow it automatically.
     */
     $dst_date = TTDate::getMiddleDayEpoch($dst_date);
     Debug::text('Src Punch ID: ' . $src_punch_id . ' Dst Punch ID: ' . $dst_punch_id . ' Dst Status ID: ' . $dst_status_id . ' Position: ' . $position . ' Action: ' . $action . ' Dst Date: ' . $dst_date, __FILE__, __LINE__, __METHOD__, 10);
     $retval = FALSE;
     //Get source and destination punch objects.
     $plf = TTnew('PunchListFactory');
     $plf->StartTransaction();
     $plf->getByCompanyIDAndId($company_id, $src_punch_id);
     if ($plf->getRecordCount() == 1) {
         $src_punch_obj = $plf->getCurrent();
         $src_punch_date = TTDate::getMiddleDayEpoch($src_punch_obj->getPunchControlObject()->getUserDateObject()->getDateStamp());
         Debug::text('Found SRC punch ID: ' . $src_punch_id . ' Source Punch Date: ' . $src_punch_date, __FILE__, __LINE__, __METHOD__, 10);
         //Get the PunchControlObject as early as possible, before the punch is deleted, as it will be cleared even if Save(FALSE) is called below.
         $src_punch_control_obj = clone $src_punch_obj->getPunchControlObject();
         if (TTDate::getMiddleDayEpoch($src_punch_date) != TTDate::getMiddleDayEpoch($src_punch_obj->getTimeStamp())) {
             Debug::text('Punch spans midnight... Source Punch Date: ' . TTDate::getDATE('DATE+TIME', $src_punch_date) . ' Source Punch TimeStamp: ' . TTDate::getDATE('DATE+TIME', $src_punch_obj->getTimeStamp()), __FILE__, __LINE__, __METHOD__, 10);
             $dst_date_modifier = 86400;
             //Bump day by 24hrs.
         } else {
             $dst_date_modifier = 0;
         }
         //If we are moving the punch, we need to delete the source punch first so it doesn't conflict with the new punch.
         //Especially if we are just moving a punch to fill a gap in the same day.
         //If the punch being moved is in the same day, or within the same punch pair, we don't want to delete the source punch, instead we just modify
         //the necessary bits later on. So we need to short circuit the move functionality when copying/moving punches within the same day.
         if ($action == 1 and $src_punch_id != $dst_punch_id and $src_punch_date != $dst_date or $action == 1 and $src_punch_id != $dst_punch_id and $src_punch_date == $dst_date) {
             //Move
             Debug::text('Deleting original punch...: ' . $src_punch_id, __FILE__, __LINE__, __METHOD__, 10);
             $src_punch_obj->setUser($src_punch_control_obj->getUserDateObject()->getUser());
             $src_punch_obj->setDeleted(TRUE);
             //These aren't doing anything because they aren't acting on the PunchControl object?
             $src_punch_obj->setEnableCalcTotalTime(TRUE);
             $src_punch_obj->setEnableCalcSystemTotalTime(TRUE);
             $src_punch_obj->setEnableCalcWeeklySystemTotalTime(TRUE);
             $src_punch_obj->setEnableCalcUserDateTotal(TRUE);
             $src_punch_obj->setEnableCalcException(TRUE);
             $src_punch_obj->Save(FALSE);
             //Keep object around for later.
         } else {
             Debug::text('NOT Deleting original punch, either in copy mode or condition is not met...', __FILE__, __LINE__, __METHOD__, 10);
         }
         if ($src_punch_id == $dst_punch_id or $dst_punch_id == '') {
             //Assume we are just moving a punch within the same punch pair, unless a new date is specfied.
             //However if we're simply splitting an existing punch pair, like dragging the Out punch from an In/Out pair into its own separate pair.
             if ($src_punch_date != $dst_date or $src_punch_date == $dst_date and $dst_punch_id == '') {
                 Debug::text('aCopying punch to new day...', __FILE__, __LINE__, __METHOD__, 10);
                 //Moving punch to a new date.
                 //Copy source punch to proper location by destination punch.
                 $src_punch_obj->setId(FALSE);
                 $src_punch_obj->setPunchControlId((int) $src_punch_control_obj->getNextInsertId());
                 $src_punch_obj->setDeleted(FALSE);
                 //Just in case it was marked deleted by the MOVE action.
                 $new_time_stamp = TTDate::getTimeLockedDate($src_punch_obj->getTimeStamp(), $dst_date + $dst_date_modifier);
                 Debug::text('SRC TimeStamp: ' . TTDate::getDate('DATE+TIME', $src_punch_obj->getTimeStamp()) . ' DST TimeStamp: ' . TTDate::getDate('DATE+TIME', $new_time_stamp), __FILE__, __LINE__, __METHOD__, 10);
                 $src_punch_obj->setTimeStamp($new_time_stamp, FALSE);
                 $src_punch_obj->setActualTimeStamp($new_time_stamp);
                 $src_punch_obj->setOriginalTimeStamp($new_time_stamp);
                 if ($dst_status_id != '') {
                     $src_punch_obj->setStatus($dst_status_id);
                     //Change the status to fit in the proper place.
                 }
                 $src_punch_obj->setStation(NULL);
                 //When drag&drop copying punches, clear the station.
                 if ($src_punch_obj->isValid() == TRUE) {
                     $insert_id = $src_punch_obj->Save(FALSE);
                     $src_punch_control_obj->shift_data = NULL;
                     //Need to clear the shift data so its obtained from the DB again, otherwise shifts will appear on strange days.
                     $src_punch_control_obj->user_date_obj = NULL;
                     //Need to clear user_date_obj from cache so a new one is obtained.
                     $src_punch_control_obj->setId($src_punch_obj->getPunchControlID());
                     $src_punch_control_obj->setPunchObject($src_punch_obj);
                     if ($src_punch_control_obj->isValid() == TRUE) {
                         Debug::Text(' Punch Control is valid, saving...: ', __FILE__, __LINE__, __METHOD__, 10);
                         //We need to calculate new total time for the day and exceptions because we are never guaranteed that the gaps will be filled immediately after
                         //in the case of a drag & drop or something.
                         $src_punch_control_obj->setEnableStrictJobValidation(TRUE);
                         $src_punch_control_obj->setEnableCalcUserDateID(TRUE);
                         $src_punch_control_obj->setEnableCalcTotalTime(TRUE);
                         $src_punch_control_obj->setEnableCalcSystemTotalTime(TRUE);
                         $src_punch_control_obj->setEnableCalcWeeklySystemTotalTime(TRUE);
                         $src_punch_control_obj->setEnableCalcUserDateTotal(TRUE);
                         $src_punch_control_obj->setEnableCalcException(TRUE);
                         if ($src_punch_control_obj->isValid() == TRUE) {
                             if ($src_punch_control_obj->Save(TRUE, TRUE) == TRUE) {
                                 //Return newly inserted punch_id, so Flex can base other actions on it.
                                 $retval = $insert_id;
                             }
                         }
                     }
                 }
             } else {
                 Debug::text('Copying punch within same pair/day...', __FILE__, __LINE__, __METHOD__, 10);
                 //Moving punch within the same punch pair.
                 $src_punch_obj->setStatus($src_punch_obj->getNextStatus());
                 //Change just the punch status.
                 //$src_punch_obj->setDeleted(FALSE); //Just in case it was marked deleted by the MOVE action.
                 if ($src_punch_obj->isValid() == TRUE) {
                     //Return punch_id, so Flex can base other actions on it.
                     $retval = $src_punch_obj->Save(FALSE);
                 }
             }
         } else {
             Debug::text('bCopying punch to new day...', __FILE__, __LINE__, __METHOD__, 10);
             $plf->getByCompanyIDAndId($company_id, $dst_punch_id);
             if ($plf->getRecordCount() == 1) {
                 Debug::text('Found DST punch ID: ' . $dst_punch_id, __FILE__, __LINE__, __METHOD__, 10);
                 $dst_punch_obj = $plf->getCurrent();
                 $dst_punch_control_obj = $dst_punch_obj->getPunchControlObject();
                 Debug::text('aSRC TimeStamp: ' . TTDate::getDate('DATE+TIME', $src_punch_obj->getTimeStamp()) . ' DST TimeStamp: ' . TTDate::getDate('DATE+TIME', $dst_punch_obj->getTimeStamp()), __FILE__, __LINE__, __METHOD__, 10);
                 $is_punch_control_split = FALSE;
                 if ($position == 0) {
                     //Overwrite
                     Debug::text('Overwriting...', __FILE__, __LINE__, __METHOD__, 10);
                     //All we need to do is update the time of the destination punch.
                     $punch_obj = $dst_punch_obj;
                 } else {
                     //Before or After
                     //Determine if the destination punch needs to split from another punch
                     if ($position == -1 and $dst_punch_obj->getStatus() == 20 or $position == 1 and $dst_punch_obj->getStatus() == 10) {
                         //Before on Out punch, After on In Punch,
                         Debug::text('Need to split destination punch out to its own Punch Control row...', __FILE__, __LINE__, __METHOD__, 10);
                         $is_punch_control_split = PunchControlFactory::splitPunchControl($dst_punch_obj->getPunchControlID());
                         //Once a split occurs, we need to re-get the destination punch as the punch_control_id may have changed.
                         //We could probably optimize this to only occur when the destination punch is an In punch, as the
                         //Out punch is always the one to be moved to a new punch_control_id
                         if ($src_punch_obj->getStatus() != $dst_punch_obj->getStatus()) {
                             $plf->getByCompanyIDAndId($company_id, $dst_punch_id);
                             if ($plf->getRecordCount() == 1) {
                                 $dst_punch_obj = $plf->getCurrent();
                                 Debug::text('Found DST punch ID: ' . $dst_punch_id . ' Punch Control ID: ' . $dst_punch_obj->getPunchControlID(), __FILE__, __LINE__, __METHOD__, 10);
                             }
                         }
                         $punch_control_id = $dst_punch_obj->getPunchControlID();
                     } else {
                         Debug::text('No Need to split destination punch, simply add a new punch/punch_control all on its own.', __FILE__, __LINE__, __METHOD__, 10);
                         //Check to see if the src and dst punches are the same status though.
                         $punch_control_id = (int) $dst_punch_control_obj->getNextInsertId();
                     }
                     //Take the source punch and base our new punch on that.
                     $punch_obj = $src_punch_obj;
                     //Copy source punch to proper location by destination punch.
                     $punch_obj->setId(FALSE);
                     $punch_obj->setDeleted(FALSE);
                     //Just in case it was marked deleted by the MOVE action.
                     $punch_obj->setPunchControlId($punch_control_id);
                 }
                 //$new_time_stamp = TTDate::getTimeLockedDate($src_punch_obj->getTimeStamp(), $dst_punch_obj->getTimeStamp()+$dst_date_modifier );
                 $new_time_stamp = TTDate::getTimeLockedDate($src_punch_obj->getTimeStamp(), $dst_punch_obj->getPunchControlObject()->getUserDateObject()->getDateStamp() + $dst_date_modifier);
                 Debug::text('SRC TimeStamp: ' . TTDate::getDate('DATE+TIME', $src_punch_obj->getTimeStamp()) . ' DST TimeStamp: ' . TTDate::getDate('DATE+TIME', $dst_punch_obj->getTimeStamp()) . ' New TimeStamp: ' . TTDate::getDate('DATE+TIME', $new_time_stamp), __FILE__, __LINE__, __METHOD__, 10);
                 $punch_obj->setTimeStamp($new_time_stamp, FALSE);
                 $punch_obj->setActualTimeStamp($new_time_stamp);
                 $punch_obj->setOriginalTimeStamp($new_time_stamp);
                 $src_punch_obj->setStation(NULL);
                 //When drag&drop copying punches, clear the station.
                 //Need to take into account copying a Out punch and inserting it BEFORE another Out punch in a punch pair.
                 //In this case a split needs to occur, and the status needs to stay the same.
                 //Status also needs to stay the same when overwriting an existing punch.
                 Debug::text('Punch Status: ' . $punch_obj->getStatus() . ' DST Punch Status: ' . $dst_punch_obj->getStatus() . ' Split Punch Control: ' . (int) $is_punch_control_split, __FILE__, __LINE__, __METHOD__, 10);
                 if ($position != 0 and $is_punch_control_split == FALSE and $punch_obj->getStatus() == $dst_punch_obj->getStatus() and $punch_obj->getPunchControlID() == $dst_punch_obj->getPunchControlID()) {
                     Debug::text('Changing punch status to opposite: ' . $dst_punch_obj->getNextStatus(), __FILE__, __LINE__, __METHOD__, 10);
                     $punch_obj->setStatus($dst_punch_obj->getNextStatus());
                     //Change the status to fit in the proper place.
                 }
                 if ($punch_obj->isValid() == TRUE) {
                     $insert_id = $punch_obj->Save(FALSE);
                     $dst_punch_control_obj->shift_data = NULL;
                     //Need to clear the shift data so its obtained from the DB again, otherwise shifts will appear on strange days, or cause strange conflicts.
                     $dst_punch_control_obj->setID($punch_obj->getPunchControlID());
                     $dst_punch_control_obj->setPunchObject($punch_obj);
                     if ($dst_punch_control_obj->isValid() == TRUE) {
                         Debug::Text(' Punch Control is valid, saving...: ', __FILE__, __LINE__, __METHOD__, 10);
                         //We need to calculate new total time for the day and exceptions because we are never guaranteed that the gaps will be filled immediately after
                         //in the case of a drag & drop or something.
                         $dst_punch_control_obj->setEnableStrictJobValidation(TRUE);
                         $dst_punch_control_obj->setEnableCalcUserDateID(TRUE);
                         $dst_punch_control_obj->setEnableCalcTotalTime(TRUE);
                         $dst_punch_control_obj->setEnableCalcSystemTotalTime(TRUE);
                         $dst_punch_control_obj->setEnableCalcWeeklySystemTotalTime(TRUE);
                         $dst_punch_control_obj->setEnableCalcUserDateTotal(TRUE);
                         $dst_punch_control_obj->setEnableCalcException(TRUE);
                         if ($dst_punch_control_obj->isValid() == TRUE) {
                             if ($dst_punch_control_obj->Save(TRUE, TRUE) == TRUE) {
                                 //Force isNew() lookup.
                                 //Return newly inserted punch_id, so Flex can base other actions on it.
                                 $retval = $insert_id;
                                 //$retval = TRUE;
                             }
                         }
                     }
                 }
             }
         }
     }
     if ($retval == FALSE) {
         $plf->FailTransaction();
     }
     //$plf->FailTransaction();
     $plf->CommitTransaction();
     Debug::text('Returning: ' . (int) $retval, __FILE__, __LINE__, __METHOD__, 10);
     return $retval;
 }