function postSave() { //Handle dirty work here. Debug::Text('ID we just saved: ' . $this->getId(), __FILE__, __LINE__, __METHOD__, 10); if ($this->getEnableReCalculate() == TRUE) { //Set User Termination date to Last Day. $ulf = TTnew('UserListFactory'); $ulf->getById($this->getUser()); if ($ulf->getRecordCount() > 0) { Debug::Text('Setting User Termination Date', __FILE__, __LINE__, __METHOD__, 10); $user_obj = $ulf->getCurrent(); $user_obj->setStatus(20); //Set status to terminated, now that pay stubs will always generate anyways. $user_obj->setTerminationDate($this->getLastDate()); if ($user_obj->isValid()) { $user_obj->Save(); UserGenericStatusFactory::queueGenericStatus($this->getUserObject()->getFullName(TRUE) . ' - ' . TTi18n::gettext('Employee Record'), 30, TTi18n::gettext('Setting employee termination date to:') . ' ' . TTDate::getDate('DATE', $this->getLastDate()), NULL); } } $this->ReCalculate(); } return TRUE; }
function generatePayStubs($pay_period_ids, $user_ids = NULL, $enable_correction = FALSE) { global $profiler; Debug::Text('Generate Pay Stubs!', __FILE__, __LINE__, __METHOD__, 10); if (!$this->getPermissionObject()->Check('pay_period_schedule', 'enabled') or !($this->getPermissionObject()->Check('pay_period_schedule', 'edit') or $this->getPermissionObject()->Check('pay_period_schedule', 'edit_own'))) { return $this->getPermissionObject()->PermissionDenied(); } if (!is_array($pay_period_ids)) { $pay_period_ids = array($pay_period_ids); } if ($user_ids !== NULL and !is_array($user_ids) and $user_ids > 0) { $user_ids = array($user_ids); } elseif (is_array($user_ids) and isset($user_ids[0]) and $user_ids[0] == 0) { $user_ids = NULL; } foreach ($pay_period_ids as $pay_period_id) { Debug::text('Pay Period ID: ' . $pay_period_id, __FILE__, __LINE__, __METHOD__, 10); $pplf = TTnew('PayPeriodListFactory'); $pplf->getByIdAndCompanyId($pay_period_id, $this->getCurrentCompanyObject()->getId()); $epoch = TTDate::getTime(); foreach ($pplf as $pay_period_obj) { Debug::text('Pay Period Schedule ID: ' . $pay_period_obj->getPayPeriodSchedule(), __FILE__, __LINE__, __METHOD__, 10); if ($pay_period_obj->isPreviousPayPeriodClosed() == TRUE) { //Grab all users for pay period $ppsulf = TTnew('PayPeriodScheduleUserListFactory'); if (is_array($user_ids) and count($user_ids) > 0) { Debug::text('Generating pay stubs for specific users...', __FILE__, __LINE__, __METHOD__, 10); TTLog::addEntry($this->getCurrentCompanyObject()->getId(), 500, TTi18n::gettext('Calculating Company Pay Stubs for Pay Period') . ': ' . $pay_period_id, $this->getCurrentUserObject()->getId(), 'pay_stub'); //Notice $ppsulf->getByCompanyIDAndPayPeriodScheduleIdAndUserID($this->getCurrentCompanyObject()->getId(), $pay_period_obj->getPayPeriodSchedule(), $user_ids); } else { TTLog::addEntry($this->getCurrentCompanyObject()->getId(), 500, TTi18n::gettext('Calculating Employee Pay Stub for Pay Period') . ': ' . $pay_period_id, $this->getCurrentUserObject()->getId(), 'pay_stub'); $ppsulf->getByCompanyIDAndPayPeriodScheduleId($this->getCurrentCompanyObject()->getId(), $pay_period_obj->getPayPeriodSchedule()); } $total_pay_stubs = $ppsulf->getRecordCount(); $this->getProgressBarObject()->start($this->getAMFMessageID(), $total_pay_stubs, NULL, TTi18n::getText('Generating Paystubs...')); //Delete existing pay stub. Make sure we only //delete pay stubs that are the same as what we're creating. $pslf = TTnew('PayStubListFactory'); $pslf->getByPayPeriodId($pay_period_obj->getId()); foreach ($pslf as $pay_stub_obj) { if (is_array($user_ids) and count($user_ids) > 0 and in_array($pay_stub_obj->getUser(), $user_ids) == FALSE) { continue; //Only generating pay stubs for individual employees, skip ones not in the list. } Debug::text('Existing Pay Stub: ' . $pay_stub_obj->getId(), __FILE__, __LINE__, __METHOD__, 10); //Check PS End Date to match with PP End Date //So if an ROE was generated, it won't get deleted when they generate all other Pay Stubs //later on. if ($pay_stub_obj->getStatus() <= 25 and $pay_stub_obj->getTainted() === FALSE and TTDate::getMiddleDayEpoch($pay_stub_obj->getEndDate()) == TTDate::getMiddleDayEpoch($pay_period_obj->getEndDate())) { Debug::text('Deleting pay stub: ' . $pay_stub_obj->getId(), __FILE__, __LINE__, __METHOD__, 10); $pay_stub_obj->setDeleted(TRUE); $pay_stub_obj->Save(); } else { Debug::text('Pay stub does not need regenerating, or it is LOCKED! ID: ' . $pay_stub_obj->getID() . ' Status: ' . $pay_stub_obj->getStatus() . ' Tainted: ' . (int) $pay_stub_obj->getTainted() . ' Pay Stub End Date: ' . $pay_stub_obj->getEndDate() . ' Pay Period End Date: ' . $pay_period_obj->getEndDate(), __FILE__, __LINE__, __METHOD__, 10); } } $i = 1; foreach ($ppsulf as $pay_period_schdule_user_obj) { Debug::text('Pay Period User ID: ' . $pay_period_schdule_user_obj->getUser(), __FILE__, __LINE__, __METHOD__, 10); Debug::text('Total Pay Stubs: ' . $total_pay_stubs . ' - ' . ceil(1 / (100 / $total_pay_stubs)), __FILE__, __LINE__, __METHOD__, 10); $profiler->startTimer('Calculating Pay Stub'); //Calc paystubs. $cps = new CalculatePayStub(); $cps->setEnableCorrection((bool) $enable_correction); $cps->setUser($pay_period_schdule_user_obj->getUser()); $cps->setPayPeriod($pay_period_obj->getId()); $cps->calculate(); unset($cps); $profiler->stopTimer('Calculating Pay Stub'); $this->getProgressBarObject()->set($this->getAMFMessageID(), $i); //sleep(1); /////////////////////////////// FOR TESTING ONLY ////////////////// $i++; } unset($ppsulf); $this->getProgressBarObject()->stop($this->getAMFMessageID()); } else { UserGenericStatusFactory::queueGenericStatus(TTi18n::gettext('ERROR'), 10, TTi18n::gettext('Pay period prior to %1 is not closed, please close all previous pay periods and try again...', array(TTDate::getDate('DATE', $pay_period_obj->getStartDate()) . ' -> ' . TTDate::getDate('DATE', $pay_period_obj->getEndDate()))), NULL); } } } if (UserGenericStatusFactory::isStaticQueue() == TRUE) { $ugsf = TTnew('UserGenericStatusFactory'); $ugsf->setUser($this->getCurrentUserObject()->getId()); $ugsf->setBatchID($ugsf->getNextBatchId()); $ugsf->setQueue(UserGenericStatusFactory::getStaticQueue()); $ugsf->saveQueue(); $user_generic_status_batch_id = $ugsf->getBatchID(); } else { $user_generic_status_batch_id = FALSE; } unset($ugsf); return $this->returnHandler(TRUE, TRUE, FALSE, FALSE, FALSE, $user_generic_status_batch_id); }
$sf->setStartTime($start_time); $sf->setEndTime($end_time); if ($sf->isValid()) { $sf->setEnableReCalculateDay(TRUE); if ($sf->Save() != TRUE) { UserGenericStatusFactory::queueGenericStatus($user_generic_status_label, 10, $sf->Validator->getTextErrors(), NULL); $fail_transaction = TRUE; } else { if ($conflicting_shifts == TRUE) { UserGenericStatusFactory::queueGenericStatus($user_generic_status_label, 20, NULL, NULL); } else { UserGenericStatusFactory::queueGenericStatus($user_generic_status_label, 30, NULL, NULL); } } } else { UserGenericStatusFactory::queueGenericStatus($user_generic_status_label, 10, $sf->Validator->getTextErrors(), NULL); $fail_transaction = TRUE; } //Debug::Text('Setting Percent: '. Misc::calculatePercent( $x, $total_shifts ), __FILE__, __LINE__, __METHOD__,10); $progress_bar->setValue(Misc::calculatePercent($x, $total_shifts)); $progress_bar->display(); $x++; } } else { Debug::Text(' Skipping Day Of Week: (' . TTDate::getDayOfWeek($time_stamp) . ') ' . TTDate::getDate('DATE+TIME', $time_stamp), __FILE__, __LINE__, __METHOD__, 10); $x++; } $time_stamp = $time_stamp + 86400; } //$sf->FailTransaction(); $sf->CommitTransaction();
function calculate($epoch = NULL) { if ($this->getUserObject() == FALSE) { return FALSE; } if ($this->getPayPeriodObject() == FALSE) { return FALSE; } if ($epoch == NULL or $epoch == '') { $epoch = TTDate::getTime(); } //Use User Termination Date instead of ROE. if ($this->getUserObject()->getTerminationDate() != '' and $this->getUserObject()->getTerminationDate() >= $this->getPayPeriodObject()->getStartDate() and $this->getUserObject()->getTerminationDate() <= $this->getPayPeriodObject()->getEndDate()) { Debug::text('User has been terminated in this pay period!', __FILE__, __LINE__, __METHOD__, 10); $is_terminated = TRUE; } else { $is_terminated = FALSE; } //Allow generating pay stubs for employees who have any status, but if its not ID=10 //Then the termination date must fall within the start/end date of the pay period, or after the end date (if its the current pay period) //The idea here is to allow employees to be marked terminated (or on leave) and still get their previous or final pay stub generated. //Also allow pay stubs to be generated in pay periods *before* their termination date. if ($this->getUserObject()->getStatus() != 10 and ($is_terminated == FALSE and ($this->getUserObject()->getTerminationDate() == '' or $this->getUserObject()->getTerminationDate() < $this->getPayPeriodObject()->getStartDate()))) { Debug::text('Pay Period is after users termination date (' . $this->getUserObject()->getTerminationDate() . '), or no termination date is set...', __FILE__, __LINE__, __METHOD__, 10); return FALSE; } Debug::text('User Id: ' . $this->getUser() . ' Pay Period End Date: ' . TTDate::getDate('DATE+TIME', $this->getPayPeriodObject()->getEndDate()), __FILE__, __LINE__, __METHOD__, 10); $generic_queue_status_label = $this->getUserObject()->getFullName(TRUE) . ' - ' . TTi18n::gettext('Pay Stub'); $pay_stub = TTnew('PayStubFactory'); $pay_stub->StartTransaction(); $old_pay_stub_id = NULL; if ($this->getEnableCorrection() == TRUE) { Debug::text('Correction Enabled!', __FILE__, __LINE__, __METHOD__, 10); $pay_stub->setTemp(TRUE); //Check for current pay stub ID so we can compare against it. $pslf = TTnew('PayStubListFactory'); $pslf->getByUserIdAndPayPeriodId($this->getUser(), $this->getPayPeriod()); if ($pslf->getRecordCount() > 0) { $old_pay_stub_id = $pslf->getCurrent()->getId(); Debug::text('Comparing Against Pay Stub ID: ' . $old_pay_stub_id, __FILE__, __LINE__, __METHOD__, 10); } } $pay_stub->setUser($this->getUser()); $pay_stub->setPayPeriod($this->getPayPeriod()); $pay_stub->setCurrency($this->getUserObject()->getCurrency()); $pay_stub->setStatus(10); //New if ($is_terminated == TRUE) { Debug::text('User is Terminated, assuming final pay, setting End Date to terminated date: ' . TTDate::getDate('DATE+TIME', $this->getUserObject()->getTerminationDate()), __FILE__, __LINE__, __METHOD__, 10); $pay_stub->setStartDate($pay_stub->getPayPeriodObject()->getStartDate()); $pay_stub->setEndDate($this->getUserObject()->getTerminationDate()); //Use the PS generation date instead of terminated date... //Unlikely they would pay someone before the pay stub is generated. //Perhaps still use the pay period transaction date for this too? //Anything we set won't be correct for everyone. Maybe a later date is better though? //Perhaps add to the user factory under Termination Date a: "Final Transaction Date" for this purpose? //Use the end of the current date for the transaction date, as if the employee is terminated //on the same day they are generating the pay stub, the transaction date could be before the end date //as the end date is at 11:59PM //For now make sure that the transaction date for a terminated employee is never before their termination date. if (TTDate::getEndDayEpoch(TTDate::getTime()) < $this->getUserObject()->getTerminationDate()) { $pay_stub->setTransactionDate($this->getUserObject()->getTerminationDate()); } else { $pay_stub->setTransactionDate(TTDate::getEndDayEpoch(TTDate::getTime())); } } else { Debug::text('User Termination Date is NOT set, assuming normal pay.', __FILE__, __LINE__, __METHOD__, 10); $pay_stub->setDefaultDates(); } //This must go after setting advance if ($this->getEnableCorrection() == FALSE and $pay_stub->IsUniquePayStub() == FALSE) { Debug::text('Pay Stub already exists', __FILE__, __LINE__, __METHOD__, 10); $this->CommitTransaction(); UserGenericStatusFactory::queueGenericStatus($generic_queue_status_label, 20, TTi18n::gettext('Pay Stub for this employee already exists, skipping...'), NULL); return FALSE; } if ($pay_stub->isValid() == TRUE) { $pay_stub->Save(FALSE); $pay_stub->setStatus(25); //Open } else { Debug::text('Pay Stub isValid failed!', __FILE__, __LINE__, __METHOD__, 10); UserGenericStatusFactory::queueGenericStatus($generic_queue_status_label, 10, $pay_stub->Validator->getTextErrors(), NULL); $this->FailTransaction(); $this->CommitTransaction(); return FALSE; } $pay_stub->loadPreviousPayStub(); $user_date_total_arr = $this->getWageObject()->getUserDateTotalArray(); if (isset($user_date_total_arr['entries']) and is_array($user_date_total_arr['entries'])) { foreach ($user_date_total_arr['entries'] as $udt_arr) { //Allow negative amounts so flat rate premium policies can reduce an employees wage if need be. if ($udt_arr['amount'] != 0) { Debug::text('Adding Pay Stub Entry: ' . $udt_arr['pay_stub_entry'] . ' Amount: ' . $udt_arr['amount'], __FILE__, __LINE__, __METHOD__, 10); $pay_stub->addEntry($udt_arr['pay_stub_entry'], $udt_arr['amount'], TTDate::getHours($udt_arr['total_time']), $udt_arr['rate']); } else { Debug::text('NOT Adding ($0 amount) Pay Stub Entry: ' . $udt_arr['pay_stub_entry'] . ' Amount: ' . $udt_arr['amount'], __FILE__, __LINE__, __METHOD__, 10); } } } else { //No Earnings, CHECK FOR PS AMENDMENTS next for earnings. Debug::text('NO TimeSheet EARNINGS ON PAY STUB... Checking for PS amendments', __FILE__, __LINE__, __METHOD__, 10); } //Get all PS amendments and Tax / Deductions so we can determine the proper order to calculate them in. $psalf = TTnew('PayStubAmendmentListFactory'); $psalf->getByUserIdAndAuthorizedAndStartDateAndEndDate($this->getUser(), TRUE, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate()); $udlf = TTnew('UserDeductionListFactory'); $udlf->getByCompanyIdAndUserId($this->getUserObject()->getCompany(), $this->getUserObject()->getId()); if (getTTProductEdition() >= TT_PRODUCT_ENTERPRISE and $this->getUserObject()->getCompanyObject()->getProductEdition() >= TT_PRODUCT_ENTERPRISE) { $uelf = TTnew('UserExpenseListFactory'); $uelf->getByUserIdAndAuthorizedAndStartDateAndEndDate($this->getUser(), TRUE, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate()); Debug::text('Total User Expenses: ' . $uelf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10); } else { $uelf = FALSE; } $deduction_order_arr = $this->getOrderedDeductionAndPSAmendment($udlf, $psalf, $uelf); if (is_array($deduction_order_arr) and count($deduction_order_arr) > 0) { foreach ($deduction_order_arr as $calculation_order => $data_arr) { Debug::text('Found PS Amendment/Deduction: Type: ' . $data_arr['type'] . ' Name: ' . $data_arr['name'] . ' Order: ' . $calculation_order, __FILE__, __LINE__, __METHOD__, 10); if (isset($data_arr['obj']) and is_object($data_arr['obj'])) { if ($data_arr['type'] == 'UserDeductionListFactory') { $ud_obj = $data_arr['obj']; //Determine if this deduction is valid based on start/end dates. //Determine if this deduction is valid based on min/max length of service. //Determine if this deduction is valid based on min/max user age. if ($ud_obj->getCompanyDeductionObject()->isActiveDate($pay_stub->getPayPeriodObject()->getEndDate()) == TRUE and $ud_obj->getCompanyDeductionObject()->isActiveLengthOfService($this->getUserObject(), $pay_stub->getPayPeriodObject()->getEndDate()) == TRUE and $ud_obj->getCompanyDeductionObject()->isActiveUserAge($this->getUserObject(), $pay_stub->getPayPeriodObject()->getEndDate()) == TRUE and $ud_obj->getCompanyDeductionObject()->inApplyFrequencyWindow($pay_stub->getPayPeriodObject()->getStartDate(), $pay_stub->getPayPeriodObject()->getEndDate(), $this->getUserObject()->getHireDate(), $this->getUserObject()->getTerminationDate(), $this->getUserObject()->getBirthDate()) == TRUE) { $amount = $ud_obj->getDeductionAmount($this->getUserObject()->getId(), $pay_stub, $this->getPayPeriodObject()); Debug::text('User Deduction: ' . $ud_obj->getCompanyDeductionObject()->getName() . ' Amount: ' . $amount . ' Calculation Order: ' . $ud_obj->getCompanyDeductionObject()->getCalculationOrder(), __FILE__, __LINE__, __METHOD__, 10); //Allow negative amounts, so they can reduce previously calculated deductions or something. if (isset($amount) and $amount != 0) { $pay_stub->addEntry($ud_obj->getCompanyDeductionObject()->getPayStubEntryAccount(), $amount, NULL, NULL, $ud_obj->getCompanyDeductionObject()->getPayStubEntryDescription()); } else { Debug::text('Amount is 0, skipping...', __FILE__, __LINE__, __METHOD__, 10); } } unset($amount, $ud_obj); } elseif ($data_arr['type'] == 'PayStubAmendmentListFactory') { $psa_obj = $data_arr['obj']; Debug::text('Found Pay Stub Amendment: ID: ' . $psa_obj->getID() . ' Entry Name ID: ' . $psa_obj->getPayStubEntryNameId() . ' Type: ' . $psa_obj->getType(), __FILE__, __LINE__, __METHOD__, 10); $amount = $psa_obj->getCalculatedAmount($pay_stub); if (isset($amount) and $amount != 0) { Debug::text('Pay Stub Amendment Amount: ' . $amount, __FILE__, __LINE__, __METHOD__, 10); //Keep in mind this causes pay stubs to be re-generated every time, as this modifies the updated time //to slightly more then the pay stub creation time. $psa_obj->setStatus(52); //InUse if ($psa_obj->isValid()) { $pay_stub->addEntry($psa_obj->getPayStubEntryNameId(), $amount, $psa_obj->getUnits(), $psa_obj->getRate(), $psa_obj->getDescription(), $psa_obj->getID(), NULL, NULL, $psa_obj->getYTDAdjustment()); $psa_obj->Save(); } } else { Debug::text('bPay Stub Amendment Amount is not set...', __FILE__, __LINE__, __METHOD__, 10); } unset($amount, $psa_obj); } elseif ($data_arr['type'] == 'UserExpenseListFactory') { $ue_obj = $data_arr['obj']; Debug::text('Found User Expense: ID: ' . $ue_obj->getID() . ' Expense Policy ID: ' . $ue_obj->getExpensePolicy(), __FILE__, __LINE__, __METHOD__, 10); $amount = $ue_obj->getReimburseAmount(); if (isset($amount) and $amount != 0) { Debug::text('User Expense reimbursable Amount: ' . $amount, __FILE__, __LINE__, __METHOD__, 10); $pay_stub->addEntry($ue_obj->getExpensePolicyObject()->getPayStubEntryAccount(), $amount, NULL, NULL, NULL, NULL, NULL, NULL, FALSE, $ue_obj->getID()); //Keep in mind this causes pay stubs to be re-generated every time, as this modifies the updated time //to slightly more then the pay stub creation time. $ue_obj->setStatus(35); //InUse $ue_obj->Save(); } else { Debug::text('bUser Expense Amount is not set...', __FILE__, __LINE__, __METHOD__, 10); } unset($amount, $ue_obj); } } } } unset($deduction_order_arr, $calculation_order, $data_arr); $pay_stub_id = $pay_stub->getId(); $pay_stub->setEnableProcessEntries(TRUE); $pay_stub->processEntries(); if ($pay_stub->isValid() == TRUE) { Debug::text('Pay Stub is valid, final save.', __FILE__, __LINE__, __METHOD__, 10); $pay_stub->setEnableCalcYTD(TRUE); //When recalculating old pay stubs in the middle of the year, we need to make sure YTD values are updated. $pay_stub->Save(); if ($this->getEnableCorrection() == TRUE) { if (isset($old_pay_stub_id)) { Debug::text('bCorrection Enabled - Doing Comparison here', __FILE__, __LINE__, __METHOD__, 10); PayStubFactory::CalcDifferences($pay_stub_id, $old_pay_stub_id); } //Delete newly created temp paystub. //This used to be in the above IF block that depended on $old_pay_stub_id //being set, however in cases where the old pay stub didn't exist //TimeTrex wouldn't delete these temporary pay stubs. //Moving this code outside that IF statement so it only depends on EnableCorrection() //to be TRUE should fix that issue. $pslf = TTnew('PayStubListFactory'); $pslf->getById($pay_stub_id); if ($pslf->getRecordCount() > 0) { $tmp_ps_obj = $pslf->getCurrent(); $tmp_ps_obj->setDeleted(TRUE); $tmp_ps_obj->Save(); unset($tmp_ps_obj); } } $pay_stub->CommitTransaction(); UserGenericStatusFactory::queueGenericStatus($generic_queue_status_label, 30, NULL, NULL); return TRUE; } Debug::text('Pay Stub is NOT valid returning FALSE', __FILE__, __LINE__, __METHOD__, 10); UserGenericStatusFactory::queueGenericStatus($generic_queue_status_label, 10, $pay_stub->Validator->getTextErrors(), NULL); $pay_stub->FailTransaction(); //Reduce transaction count by one. $pay_stub->CommitTransaction(); return FALSE; }