Пример #1
0
 function test_DST()
 {
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('03-Nov-12 10:00PM'), strtotime('04-Nov-12 1:59AM')), FALSE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('03-Nov-12 10:00PM'), strtotime('04-Nov-12 2:00AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('03-Nov-12 10:00PM'), strtotime('04-Nov-12 2:01AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('04-Nov-12 12:30AM'), strtotime('04-Nov-12 1:59AM')), FALSE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('04-Nov-12 12:30AM'), strtotime('04-Nov-12 2:00AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('04-Nov-12 1:00AM'), strtotime('04-Nov-12 6:30AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('09-Mar-13 10:00PM'), strtotime('10-Mar-13 1:59AM')), FALSE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('09-Mar-13 10:00PM'), strtotime('10-Mar-13 2:00AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('09-Mar-13 10:00PM'), strtotime('10-Mar-13 2:01AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('10-Mar-13 12:30AM'), strtotime('10-Mar-13 1:59AM')), FALSE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('10-Mar-13 12:30AM'), strtotime('10-Mar-13 2:00AM')), TRUE);
     $this->assertEquals(TTDate::doesRangeSpanDST(strtotime('10-Mar-13 1:30AM'), strtotime('10-Mar-13 6:30AM')), TRUE);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('03-Nov-12 10:00PM'), strtotime('04-Nov-12 1:59AM')), 0);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('03-Nov-12 10:00PM'), strtotime('04-Nov-12 2:00AM')), -3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('03-Nov-12 10:00PM'), strtotime('04-Nov-12 2:01AM')), -3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('04-Nov-12 12:30AM'), strtotime('04-Nov-12 1:59AM')), 0);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('04-Nov-12 12:30AM'), strtotime('04-Nov-12 2:00AM')), -3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('04-Nov-12 1:00AM'), strtotime('04-Nov-12 6:30AM')), -3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('09-Mar-13 10:00PM'), strtotime('10-Mar-13 1:59AM')), 0);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('09-Mar-13 10:00PM'), strtotime('10-Mar-13 2:00AM')), 3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('09-Mar-13 10:00PM'), strtotime('10-Mar-13 2:01AM')), 3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('10-Mar-13 12:30AM'), strtotime('10-Mar-13 1:59AM')), 0);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('10-Mar-13 12:30AM'), strtotime('10-Mar-13 2:00AM')), 3600);
     $this->assertEquals(TTDate::getDSTOffset(strtotime('10-Mar-13 1:30AM'), strtotime('10-Mar-13 6:30AM')), 3600);
 }
 function Validate()
 {
     Debug::text('Validating...', __FILE__, __LINE__, __METHOD__, 10);
     //Call this here so getShiftData can get the correct total time, before we call findUserDate.
     if ($this->getEnableCalcTotalTime() == TRUE) {
         $this->calcTotalTime();
     }
     if (is_object($this->getPunchObject())) {
         $this->findUserDate();
     }
     Debug::text('User Date Id: ' . $this->getUserDateID(), __FILE__, __LINE__, __METHOD__, 10);
     //Don't check for a valid pay period here, do that in PunchFactory->Validate(), as we need to allow users to delete punches that were created outside pay periods in legacy versions.
     if ($this->getDeleted() == FALSE and $this->getUserDateObject() == FALSE) {
         $this->Validator->isTRUE('date_stamp', FALSE, TTi18n::gettext('Date/Time is incorrect, or pay period does not exist for this date. Please create a pay period schedule and assign this employee to it if you have not done so already'));
     } elseif (is_object($this->getUserDateObject()) and is_object($this->getUserDateObject()->getPayPeriodObject()) and $this->getUserDateObject()->getPayPeriodObject()->getIsLocked() == TRUE) {
         $this->Validator->isTRUE('date_stamp', FALSE, TTi18n::gettext('Pay Period is Currently Locked'));
     }
     //Make sure the user isn't entering punches before the employees hire or after termination date, as its likely they wouldn't have a wage
     //set for that anyways and wouldn't get paid for it.
     if ($this->getDeleted() == FALSE and (is_object($this->getPunchObject()) and $this->getPunchObject()->getDeleted() == FALSE) and is_object($this->getUserDateObject()) and is_object($this->getUserDateObject()->getUserObject())) {
         if ($this->getUserDateObject()->getUserObject()->getHireDate() != '' and TTDate::getBeginDayEpoch($this->getUserDateObject()->getDateStamp()) < TTDate::getBeginDayEpoch($this->getUserDateObject()->getUserObject()->getHireDate())) {
             $this->Validator->isTRUE('date_stamp', FALSE, TTi18n::gettext('Punch is before employees hire date'));
         }
         if ($this->getUserDateObject()->getUserObject()->getTerminationDate() != '' and TTDate::getEndDayEpoch($this->getUserDateObject()->getDateStamp()) > TTDate::getEndDayEpoch($this->getUserDateObject()->getUserObject()->getTerminationDate())) {
             $this->Validator->isTRUE('date_stamp', FALSE, TTi18n::gettext('Punch is after employees termination date'));
         }
     }
     //Skip these checks if they are deleting a punch.
     if (is_object($this->getPunchObject()) and $this->getPunchObject()->getDeleted() == FALSE) {
         $plf = $this->getPLFByPunchControlID();
         if ($plf !== NULL and ($this->isNew() and $plf->getRecordCount() == 2 or $plf->getRecordCount() > 2)) {
             //TTi18n::gettext('Punch Control can not have more than two punches. Please use the Add Punch button instead')
             //They might be trying to insert a punch inbetween two others?
             $this->Validator->isTRUE('punch_control', FALSE, TTi18n::gettext('Time conflicts with another punch on this day (c)'));
         }
         //Sometimes shift data won't return all the punches to proper check for conflicting punches.
         //So we need to make sure other punches assigned to this punch_control record are proper.
         //This fixes the bug of having shifts: 2:00AM Lunch Out, 2:30AM Lunch In, 6:00AM Out, 10:00PM In (in that order), then trying to move the 10PM punch to the open IN slot before the 2AM punch.
         if ($plf->getRecordCount() > 0) {
             foreach ($plf as $p_obj) {
                 if ($p_obj->getID() != $this->getPunchObject()->getID()) {
                     if ($this->getPunchObject()->getStatus() == 10 and $p_obj->getStatus() == 10 and $this->getPunchObject()->getTimeStamp() > $p_obj->getTimeStamp()) {
                         //Make sure we match on status==10 for both sides, otherwise this fails to catch the problem case.
                         $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('In punches cannot occur after an out punch, in the same punch pair (a)'));
                     } elseif ($this->getPunchObject()->getStatus() == 20 and $p_obj->getStatus() == 10 and $this->getPunchObject()->getTimeStamp() < $p_obj->getTimeStamp()) {
                         $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Out punches cannot occur before an in punch, in the same punch pair (a)'));
                     }
                 }
             }
         }
         unset($p_obj);
         if ($this->Validator->isValid() == TRUE) {
             //Don't bother checking these resource intensive issues if there are already validation errors.
             $shift_data = $this->getShiftData();
             if (is_array($shift_data) and $this->Validator->hasError('time_stamp') == FALSE) {
                 foreach ($shift_data['punches'] as $punch_data) {
                     //Make sure there aren't two In punches, or two Out punches in the same pair.
                     //This fixes the bug where if you have an In punch, then click the blank cell below it
                     //to add a new punch, but change the status from Out to In instead.
                     if (isset($punches[$punch_data['punch_control_id']][$punch_data['status_id']])) {
                         if ($punch_data['status_id'] == 10) {
                             $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('In punches cannot occur twice in the same punch pair, you may want to make this an out punch instead'));
                         } else {
                             $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Out punches cannot occur twice in the same punch pair, you may want to make this an in punch instead'));
                         }
                     }
                     //Debug::text(' Current Punch Object: ID: '. $this->getPunchObject()->getId() .' TimeStamp: '. $this->getPunchObject()->getTimeStamp() .' Status: '. $this->getPunchObject()->getStatus(), __FILE__, __LINE__, __METHOD__,10);
                     //Debug::text(' Looping Punch Object: ID: '. $punch_data['id'] .' TimeStamp: '. $punch_data['time_stamp'] .' Status: '.$punch_data['status_id'], __FILE__, __LINE__, __METHOD__,10);
                     //Check for another punch that matches the timestamp and status.
                     if ($this->getPunchObject()->getID() != $punch_data['id']) {
                         if ($this->getPunchObject()->getTimeStamp() == $punch_data['time_stamp'] and $this->getPunchObject()->getStatus() == $punch_data['status_id']) {
                             $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Time and status match that of another punch, this could be due to rounding') . ' (' . TTDate::getDate('DATE+TIME', $punch_data['time_stamp']) . ')');
                             break;
                             //Break the loop on validation error, so we don't get multiple errors that may be confusing.
                         }
                     }
                     //Check for another punch that matches the timestamp and NOT status in the SAME punch pair.
                     if ($this->getPunchObject()->getID() != $punch_data['id'] and $this->getID() == $punch_data['punch_control_id']) {
                         if ($this->getPunchObject()->getTimeStamp() == $punch_data['time_stamp'] and $this->getPunchObject()->getStatus() != $punch_data['status_id']) {
                             $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Time matches another punch in the same punch pair, this could be due to rounding') . ' (' . TTDate::getDate('DATE+TIME', $punch_data['time_stamp']) . ')');
                             break;
                             //Break the loop on validation error, so we don't get multiple errors that may be confusing.
                         }
                     }
                     $punches[$punch_data['punch_control_id']][$punch_data['status_id']] = $punch_data;
                 }
                 unset($punch_data);
                 if (isset($punches[$this->getID()])) {
                     Debug::text('Current Punch ID: ' . $this->getPunchObject()->getId() . ' Punch Control ID: ' . $this->getID() . ' Status: ' . $this->getPunchObject()->getStatus(), __FILE__, __LINE__, __METHOD__, 10);
                     //Debug::Arr($punches, 'Punches Arr: ', __FILE__, __LINE__, __METHOD__,10);
                     if ($this->getPunchObject()->getStatus() == 10 and isset($punches[$this->getID()][20]) and $this->getPunchObject()->getTimeStamp() > $punches[$this->getID()][20]['time_stamp']) {
                         $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('In punches cannot occur after an out punch, in the same punch pair'));
                     } elseif ($this->getPunchObject()->getStatus() == 20 and isset($punches[$this->getID()][10]) and $this->getPunchObject()->getTimeStamp() < $punches[$this->getID()][10]['time_stamp']) {
                         $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Out punches cannot occur before an in punch, in the same punch pair'));
                     } else {
                         Debug::text('bPunch does not match any other punch pair.', __FILE__, __LINE__, __METHOD__, 10);
                         $punch_neighbors = Misc::getArrayNeighbors($punches, $this->getID(), 'both');
                         //Debug::Arr($punch_neighbors, ' Punch Neighbors: ', __FILE__, __LINE__, __METHOD__,10);
                         if (isset($punch_neighbors['next']) and isset($punches[$punch_neighbors['next']])) {
                             Debug::text('Found Next Punch...', __FILE__, __LINE__, __METHOD__, 10);
                             if (isset($punches[$punch_neighbors['next']][10]) and $this->getPunchObject()->getTimeStamp() > $punches[$punch_neighbors['next']][10]['time_stamp'] or isset($punches[$punch_neighbors['next']][20]) and $this->getPunchObject()->getTimeStamp() > $punches[$punch_neighbors['next']][20]['time_stamp']) {
                                 $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Time conflicts with another punch on this day') . ' (a)');
                             }
                         }
                         if (isset($punch_neighbors['prev']) and isset($punches[$punch_neighbors['prev']])) {
                             Debug::text('Found prev Punch...', __FILE__, __LINE__, __METHOD__, 10);
                             //This needs to take into account DST. Specifically if punches are like this:
                             //03-Nov-12: IN: 10:00PM
                             //04-Nov-12: OUT: 1:00AM L
                             //04-Nov-12: IN: 1:30AM L
                             //04-Nov-12: OUT: 6:30AM L
                             //Since the 1AM to 2AM occur twice due to the "fall back" DST change, we need to allow those punches to be entered.
                             if (isset($punches[$punch_neighbors['prev']][10]) and ($this->getPunchObject()->getTimeStamp() < $punches[$punch_neighbors['prev']][10]['time_stamp'] and TTDate::doesRangeSpanDST($this->getPunchObject()->getTimeStamp(), $punches[$punch_neighbors['prev']][10]['time_stamp']) == FALSE) or isset($punches[$punch_neighbors['prev']][20]) and ($this->getPunchObject()->getTimeStamp() < $punches[$punch_neighbors['prev']][20]['time_stamp'] and TTDate::doesRangeSpanDST($this->getPunchObject()->getTimeStamp(), $punches[$punch_neighbors['prev']][20]['time_stamp']) == FALSE)) {
                                 $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Time conflicts with another punch on this day') . ' (b)');
                             }
                         }
                     }
                     //Check to make sure punches don't exceed maximum shift time.
                     $maximum_shift_time = $plf->getPayPeriodMaximumShiftTime($this->getPunchObject()->getUser());
                     Debug::text('Maximum shift time: ' . $maximum_shift_time, __FILE__, __LINE__, __METHOD__, 10);
                     if ($shift_data['total_time'] > $maximum_shift_time) {
                         $this->Validator->isTRUE('time_stamp', FALSE, TTi18n::gettext('Punch exceeds maximum shift time of') . ' ' . TTDate::getTimeUnit($maximum_shift_time) . ' ' . TTi18n::getText('hrs set for this pay period schedule'));
                     }
                 }
                 unset($punches);
             }
         }
     }
     if (getTTProductEdition() >= TT_PRODUCT_CORPORATE and $this->getEnableStrictJobValidation() == TRUE) {
         if ($this->getJob() > 0) {
             $jlf = TTnew('JobListFactory');
             $jlf->getById($this->getJob());
             if ($jlf->getRecordCount() > 0) {
                 $j_obj = $jlf->getCurrent();
                 if (is_object($this->getUserDateObject()) and $j_obj->isAllowedUser($this->getUserDateObject()->getUser()) == FALSE) {
                     $this->Validator->isTRUE('job', FALSE, TTi18n::gettext('Employee is not assigned to this job'));
                 }
                 if ($j_obj->isAllowedItem($this->getJobItem()) == FALSE) {
                     $this->Validator->isTRUE('job_item', FALSE, TTi18n::gettext('Task is not assigned to this job'));
                 }
             }
         }
     }
     return TRUE;
 }