TimeTrex Community Edition v16.2.0
This commit is contained in:
2
maint/.htaccess
Normal file
2
maint/.htaccess
Normal file
@@ -0,0 +1,2 @@
|
||||
Order allow,deny
|
||||
Deny from all
|
81
maint/AddAccrualPolicyTime.php
Normal file
81
maint/AddAccrualPolicyTime.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Adds time to employee accruals based on calendar milestones
|
||||
* This file should run once a day.
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//Debug::setVerbosity(11);
|
||||
|
||||
$current_epoch = TTDate::getTime();
|
||||
//$current_epoch = strtotime('01-Aug-17 1:00 AM');
|
||||
|
||||
// If this maintenance job is run at 1A EST, if a customer is in PST, it will adjust to that timezone resulting in 10P on the previous day.
|
||||
// This would problems with per pay period frequencies, since on one day it might be outside the apply frequency, but the next day it moves to the next pay period and is still outside the apply frequency.
|
||||
// Therefore no matter when the maintenance job is run, assume its running at noon on the current day, so timezone changes won't result in it changing to another day.
|
||||
$current_epoch = TTDate::getMiddleDayEpoch( $current_epoch );
|
||||
|
||||
$offset = ( 86400 - ( 3600 * 2 ) ); //22hrs of variance. Must be less than 24hrs which is how often this script runs.
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] );
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( in_array( $c_obj->getStatus(), [ 10, 20, 23 ] ) ) { //10=Active, 20=Hold, 23=Expired
|
||||
$aplf = new AccrualPolicyListFactory();
|
||||
$aplf->getByCompanyIdAndTypeId( $c_obj->getId(), [ 20, 30 ] ); //Include hour based accruals so rollover adjustments can be calculated.
|
||||
if ( $aplf->getRecordCount() > 0 ) {
|
||||
foreach ( $aplf as $ap_obj ) {
|
||||
//Accrue for the previous day rather than the current day. So if an employee is hired on August 1st and entered on August 1st,
|
||||
// the next morning they will see accruals if it happens to be a frequency date.
|
||||
//This will make it seem like accruals are delayed by one day though in all other cases, but see #2334
|
||||
$ap_obj->addAccrualPolicyTime( ( $current_epoch - 86400 ), $offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
97
maint/AddPayPeriod.php
Normal file
97
maint/AddPayPeriod.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Adds pay periods X hrs in advance, so schedules/shifts have something to attach to.
|
||||
* This file should/can be run as often as it needs to (once an hour)
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
$current_epoch = TTDate::getTime();
|
||||
|
||||
//If offset is only 24hrs then adding user_date rows can happen before the pay period was added.
|
||||
$offset = ( 86400 * 14 ); //14 days - Every pay period schedule below can overwrite this.
|
||||
|
||||
$ppslf = new PayPeriodScheduleListFactory();
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] ); //10=Active, 20=Hold, 23=Expired
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
$system_job_queue_batch_id = TTUUID::generateUUID();
|
||||
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( in_array( $c_obj->getStatus(), [ 10, 20, 23 ] ) ) { //10=Active, 20=Hold, 23=Expired
|
||||
if ( !isset( $config_vars['other']['enable_job_queue'] ) || $config_vars['other']['enable_job_queue'] == true ) {
|
||||
SystemJobQueue::Add( TTi18n::getText( 'Add Pay Periods' ), $system_job_queue_batch_id, 'PayPeriodScheduleFactory', 'createPayPeriodsForJobQueue', [ $c_obj->getID() ], 110 );
|
||||
} else {
|
||||
//Get all pay period schedules.
|
||||
$ppslf->getByCompanyId( $c_obj->getId() );
|
||||
foreach ( $ppslf as $pay_period_schedule_obj ) {
|
||||
/** @var PayPeriodScheduleFactory $pay_period_schedule_obj */
|
||||
$end_date = null;
|
||||
|
||||
//Create pay periods X days in the future as set in Pay Period Schedule by the user.
|
||||
$offset = ( $pay_period_schedule_obj->getCreateDaysInAdvance() * 86400 );
|
||||
|
||||
$i = 0;
|
||||
$max = 53; //Never create more than this number of pay periods in a row.
|
||||
$repeat_pay_period_creation = true;
|
||||
while ( $i <= $max && $repeat_pay_period_creation === true ) {
|
||||
//Repeat function until returns false. (No more pay periods to create)
|
||||
$repeat_pay_period_creation = $pay_period_schedule_obj->createNextPayPeriod( $end_date, $offset );
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ( PRODUCTION == true && DEMO_MODE == false ) {
|
||||
$pay_period_schedule_obj->forceClosePreviousPayPeriods( $current_epoch );
|
||||
}
|
||||
|
||||
unset( $pay_period_schedule_obj );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
107
maint/AddRecurringHoliday.php
Normal file
107
maint/AddRecurringHoliday.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Adds recurring holidays X days in advance,
|
||||
* This file should run once a day.
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
$offset = 86400 * 371; //371 days - Every Holiday policy below can overwrite this.
|
||||
|
||||
$hplf = new HolidayPolicyListFactory();
|
||||
|
||||
//Get all holiday policies
|
||||
$hplf->getAll( null, null, null );
|
||||
|
||||
$epoch = time();
|
||||
|
||||
foreach ( $hplf as $hp_obj ) { /** @var $hp_obj HolidayPolicyFactory */
|
||||
//Get all recurring holidays
|
||||
$recurring_holiday_ids = $hp_obj->getRecurringHoliday();
|
||||
|
||||
//Must order recurring holidays to come in order based on static days, so Xmas Eve, Xmas, Boxing day are always in order so they can all be moved to nearest weekdays as a group.
|
||||
$rhlf = new RecurringHolidayListFactory();
|
||||
$rhlf->getByIdAndCompanyId( $recurring_holiday_ids, $hp_obj->getCompany(), null, [ 'type_id' => 'asc', 'month_int' => 'asc', 'day_of_month' => 'asc' ] );
|
||||
if ( $rhlf->getRecordCount() > 0 ) {
|
||||
Debug::Text( 'Found Recurring Holidays: '. $rhlf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
foreach( $rhlf as $rh_obj ) {
|
||||
Debug::Text( 'Found Recurring Holiday: ' . $rh_obj->getName() .' ID: '. $rh_obj->getID(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
// How many days in the future holidays are populated. Set per holiday policy by users.
|
||||
$offset = ( $hp_obj->getHolidayDisplayDays() * 86400 );
|
||||
|
||||
//Get all existing holidays that are already populated, so if two holidays occur one right after another (ie: Xmas Eve, Xmas, Boxing Day), we can adjust them accordingly.
|
||||
$exclude_dates = [];
|
||||
$hlf = TTnew( 'HolidayListFactory' ); /** @var HolidayListFactory $hlf */
|
||||
$hlf->getByHolidayPolicyIdAndStartDateAndEndDate( $hp_obj->getId(), ( $epoch - 86400 ), ( $epoch + $offset ) );
|
||||
if ( $hlf->getRecordCount() > 0 ) {
|
||||
foreach( $hlf as $h_obj ) {
|
||||
if ( strtolower( trim( $rh_obj->getName() ) ) != strtolower( trim( $h_obj->getName() ) ) ) { //Skip the holiday we are trying to populate so it doesn't get duplicated.
|
||||
$exclude_dates[] = TTDate::getBeginDayEpoch( $h_obj->getDateStamp() );
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $hlf, $h_obj );
|
||||
|
||||
$next_holiday_date = $rh_obj->getNextDate( $epoch, $exclude_dates );
|
||||
Debug::Text( 'Next Holiday Date: ' . TTDate::getDate( 'DATE+TIME', $next_holiday_date ) .' Excluded Dates: '. count( $exclude_dates ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
if ( $next_holiday_date <= ( $epoch + $offset ) ) {
|
||||
Debug::Text( 'Next Holiday Date is within Time Period (offset) adding...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
$hf = new HolidayFactory();
|
||||
$hf->setHolidayPolicyId( $hp_obj->getId() );
|
||||
$hf->setDateStamp( $next_holiday_date );
|
||||
$hf->setName( $rh_obj->getName() );
|
||||
if ( $hf->isValid() ) {
|
||||
$hf->Save();
|
||||
}
|
||||
} else {
|
||||
Debug::Text( 'Next Holiday Date is NOT within Time Period (offset)!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
186
maint/AddRecurringScheduleShift.php
Normal file
186
maint/AddRecurringScheduleShift.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//
|
||||
//
|
||||
// See tools/fix_recurring_schedule.php when making changes.
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
$minimum_add_shift_offset = ( 3600 * 2 ); //Add shifts at least 2hrs before they start.
|
||||
$lookup_shift_offset = ( 3600 * 10 ); //Lookup shifts that started X hrs before now.
|
||||
|
||||
$current_epoch = TTDate::getTime();
|
||||
//$current_epoch = strtotime('13-Sep-2015 10:00 PM');
|
||||
Debug::text( 'Current Epoch: ' . TTDate::getDate( 'DATE+TIME', $current_epoch ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Initial Start/End dates need to cover all timezones, we narrow it done further once we change to each users timezone later on.
|
||||
$initial_start_date = TTDate::getBeginDayEpoch( $current_epoch - $lookup_shift_offset );
|
||||
$initial_end_date = ( $current_epoch + 86400 );
|
||||
Debug::text( 'Initial Start Date: ' . TTDate::getDate( 'DATE+TIME', $initial_start_date ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $initial_end_date ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Bulk delete any orphaned recurring schedules for all companies first.
|
||||
// This only happened due to a bug creating orphaned records to begin with around Mar-Aug 2021.
|
||||
// In theory this should only be needed to run once and never again. Therefore we could probably get rid of it in a year or so.
|
||||
$rslf = new RecurringScheduleListFactory();
|
||||
$rslf->bulkDelete( $rslf->getIDSByListFactory( $rslf->getOrphan() ) );
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] );
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
$system_job_queue_batch_id = TTUUID::generateUUID();
|
||||
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( $c_obj->getStatus() != 30 ) {
|
||||
Debug::text( 'Company: ' . $c_obj->getName() . ' ID: ' . $c_obj->getID(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
if ( !isset( $config_vars['other']['enable_job_queue'] ) || $config_vars['other']['enable_job_queue'] == true ) {
|
||||
SystemJobQueue::Add( TTi18n::getText( 'Add Recurring Schedule Shifts' ), $system_job_queue_batch_id, 'RecurringScheduleFactory', 'addRecurringScheduleShiftForJobQueue', [ $c_obj->getID(), $current_epoch, $initial_start_date, $initial_end_date ], 125 );
|
||||
} else {
|
||||
//
|
||||
// Purge recurring schedules control rows 90 days after end date has passed.
|
||||
//
|
||||
$rsclf = new RecurringScheduleControlListFactory();
|
||||
$rsclf->getByCompanyIdAndEndDate( $c_obj->getId(), ( $current_epoch - ( 86400 * 91 ) ) );
|
||||
Debug::text( ' Recurring Schedule Control records 90days past its end date: ' . $rsclf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( $rsclf->getRecordCount() ) {
|
||||
foreach ( $rsclf as $rsc_obj ) {
|
||||
Debug::text( ' Deleting RSC... ID: ' . $rsc_obj->getID() . ' End Date: ' . TTDate::getDate( 'DATE', $rsc_obj->getEndDate() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$rsc_obj->setDeleted( true );
|
||||
if ( $rsc_obj->isValid() ) {
|
||||
$rsc_obj->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $rsc_obj );
|
||||
|
||||
// FIXME: Purge recurring schedule control rows if all users assigned to them are inactive or past their termination date by 90days?
|
||||
|
||||
|
||||
//
|
||||
// Add new recurring schedules.
|
||||
//
|
||||
$rsclf->getByCompanyIdAndStartDateAndEndDate( $c_obj->getId(), $initial_start_date, $initial_end_date );
|
||||
if ( $rsclf->getRecordCount() > 0 ) {
|
||||
Debug::text( 'CRON: Recurring Schedule Control List Record Count: ' . $rsclf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
foreach ( $rsclf as $rsc_obj ) {
|
||||
$rsclf->StartTransaction(); // Wrap each individual schedule in its own transaction instead.
|
||||
|
||||
//Since cron jobs run in system timezone (ie: 'America/Vancouver') and date_stamp and start_date/end_date (timestamptz) columns being different data types
|
||||
//we need to try to switch into a timezone at least within the same day as the final timezone before we get the recurring schedules.
|
||||
//Once we do something with the date_stamp column or store timezones, we can remove this, as its not a 100% fix.
|
||||
$rstc_obj = $rsc_obj->getRecurringScheduleTemplateControlObject();
|
||||
if ( is_object( $rstc_obj ) ) {
|
||||
Debug::text( 'Recurring Schedule Template Control last updated by: ' . $rstc_obj->getUpdatedBy(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( TTUUID::isUUID( $rstc_obj->getUpdatedBy() ) && $rstc_obj->getUpdatedBy() != TTUUID::getZeroID() ) {
|
||||
$ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */
|
||||
$ulf->getById( $rstc_obj->getUpdatedBy() );
|
||||
if ( $ulf->getRecordCount() == 1 ) {
|
||||
$ulf->getCurrent()->getUserPreferenceObject()->setTimeZonePreferences();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Default to system timezone if no other timezone is specified.
|
||||
if ( !isset( $ulf ) || ( isset( $ulf ) && $ulf->getRecordCount() != 1 ) ) {
|
||||
TTDate::setTimeZone(); //Use system timezone.
|
||||
}
|
||||
unset( $ulf );
|
||||
|
||||
//Make sure its always at least the display weeks based on the end of the current week.
|
||||
$maximum_end_date = $rsc_obj->getMaximumEndDate( $current_epoch );
|
||||
|
||||
Debug::text( 'Recurring Schedule ID: ' . $rsc_obj->getID() . ' Maximum End Date: ' . TTDate::getDate( 'DATE+TIME', $maximum_end_date ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
$rsf = TTnew( 'RecurringScheduleFactory' ); /** @var RecurringScheduleFactory $rsf */
|
||||
$rslf = TTNew( 'RecurringScheduleListFactory' ); /** @var RecurringScheduleListFactory $rslf */
|
||||
|
||||
//Clear out recurring schedules for anything older than 1 week to keep the recurring schedule table small as historical recurring schedules really don't matter once they have been committed anyways.
|
||||
$rsf->clearRecurringSchedulesFromRecurringScheduleControl( $rsc_obj->getID(), ( $current_epoch - ( 86400 * 720 ) ), TTDate::getEndWeekEpoch( ( TTDate::getBeginWeekEpoch( $current_epoch ) - ( 86400 * 8 ) ) ) );
|
||||
|
||||
//Grab the earliest last day of the recurring schedule, so we can start from there and add the next week.
|
||||
//We actually want to get the last day of each recurring schedule, and just add to that. Rather then rebuilding the entire schedule.
|
||||
//$minimum_start_date = TTDate::getBeginDayEpoch( ( TTDate::getMiddleDayEpoch( $rslf->getMinimumStartTimeByRecurringScheduleControlID( $rsc_obj->getID() ) + 86400 ) ) );
|
||||
$minimum_start_date = $rslf->getMinimumStartTimeByRecurringScheduleControlID( $rsc_obj->getID() );
|
||||
if ( $minimum_start_date != '' ) {
|
||||
//$new_week_start_date = TTDate::getBeginWeekEpoch( ( TTDate::getEndWeekEpoch( TTDate::getMiddleDayEpoch( $minimum_start_date ) ) + 86400 ) );
|
||||
//Use the exact date that we left off with the last recurring schedule.
|
||||
//This should fix a bug where if the recurring schedule last shift was on Mon Jun 12th, it would skip a week due to taking that time and starting in the next week.
|
||||
//I think we always need to overlap by at least a week in cases where the last schedule ends on a Wed or Mon or something.
|
||||
$new_week_start_date = TTDate::getBeginWeekEpoch( $minimum_start_date );
|
||||
Debug::text( ' Starting from where we last left off: ' . TTDate::getDate( 'DATE+TIME', $new_week_start_date ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
} else {
|
||||
//$new_week_start_date = TTDate::getBeginWeekEpoch( ( TTDate::getMiddleDayEpoch( $initial_start_date ) - (86400 * 8) ) );
|
||||
$new_week_start_date = TTDate::getBeginWeekEpoch( $initial_start_date );
|
||||
Debug::text( ' Setting new week start date to: ' . TTDate::getDate( 'DATE+TIME', $new_week_start_date ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
$new_week_end_date = TTDate::getEndWeekEpoch( $new_week_start_date );
|
||||
Debug::text( ' Start Date: ' . TTDate::getDate( 'DATE+TIME', $new_week_start_date ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $new_week_end_date ) . ' Maximum End Date: ' . TTDate::getDate( 'DATE+TIME', $maximum_end_date ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
if ( $new_week_end_date <= $maximum_end_date ) {
|
||||
//Add new schedules for the upcoming week.
|
||||
//$rsf->clearRecurringSchedulesFromRecurringScheduleControl( $rsc_obj->getID(), $new_week_start_date, $new_week_end_date );
|
||||
//$rsf->addRecurringSchedulesFromRecurringScheduleControl( $rsc_obj->getCompany(), $rsc_obj->getID(), $new_week_start_date, $new_week_end_date );
|
||||
|
||||
//Rather than just add new schedules for one week, bring them up to the maximum end date, which in most cases should be just 1 week unless some kind of issue has occurred.
|
||||
//This will help in cases where maintenance jobs haven't run for a while, or in other strange cases too maybe.
|
||||
$rsf->clearRecurringSchedulesFromRecurringScheduleControl( $rsc_obj->getID(), $new_week_start_date, $maximum_end_date );
|
||||
$rsf->addRecurringSchedulesFromRecurringScheduleControl( $rsc_obj->getCompany(), $rsc_obj->getID(), $new_week_start_date, $maximum_end_date ); //Always create schedules out to the maximum end date each time.
|
||||
} else {
|
||||
Debug::text( ' Already past maximum end date of: ' . TTDate::getDate( 'DATE+TIME', $maximum_end_date ) . ' Display Weeks: ' . $rsc_obj->getDisplayWeeks() . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $rsc_obj->getEndDate() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
//$rsclf->FailTransaction();
|
||||
$rsclf->CommitTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug::text( 'Company is not ACTIVE: ' . $c_obj->getId(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
81
maint/AddScheduleShift.php
Normal file
81
maint/AddScheduleShift.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
$minimum_add_shift_offset = ( 3600 * 8.5 ); //Add shifts at least 8.5hrs before they start.
|
||||
$lookup_shift_offset = ( 3600 * 4.5 ); //Lookup shifts that started X hrs before now. Since this should run every 4hrs, check for shifts that may have been missed on the last run.
|
||||
|
||||
$current_epoch = TTDate::getTime();
|
||||
//$current_epoch = strtotime('05-Sep-2013 10:00 PM');
|
||||
Debug::text( 'Current Epoch: ' . TTDate::getDate( 'DATE+TIME', $current_epoch ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Initial Start/End dates need to cover all timezones, we narrow it done further once we change to each users timezone later on.
|
||||
$initial_start_date = ( $current_epoch - $lookup_shift_offset );
|
||||
$initial_end_date = ( $current_epoch + $minimum_add_shift_offset );
|
||||
Debug::text( 'Initial Start Date: ' . TTDate::getDate( 'DATE+TIME', $initial_start_date ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $initial_end_date ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] );
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
$system_job_queue_batch_id = TTUUID::generateUUID();
|
||||
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( $c_obj->getStatus() != 30 ) {
|
||||
Debug::text( 'Company: ' . $c_obj->getName() . ' ID: ' . $c_obj->getID(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//
|
||||
//Get recurring schedules that are ready to be committed.
|
||||
//
|
||||
if ( !isset( $config_vars['other']['enable_job_queue'] ) || $config_vars['other']['enable_job_queue'] == true ) {
|
||||
SystemJobQueue::Add( TTi18n::getText( 'Commit Recurring Schedules' ), $system_job_queue_batch_id, 'RecurringScheduleFactory', 'addScheduleFromRecurringScheduleForJobQueue', [ $c_obj->getID(), $initial_start_date, $initial_end_date ], 105 );
|
||||
} else {
|
||||
$rsf = TTNew( 'RecurringScheduleFactory' ); /** @var RecurringScheduleFactory $rsf */
|
||||
$rsf->addScheduleFromRecurringSchedule( $c_obj, $initial_start_date, $initial_end_date );
|
||||
}
|
||||
} else {
|
||||
Debug::text( 'Company is not ACTIVE: ' . $c_obj->getId(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
68
maint/AutoUpgrade.php
Normal file
68
maint/AutoUpgrade.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php /** @noinspection PhpUndefinedVariableInspection */
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Automatically performans an upgrade if a new version is available...
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
if ( DEPLOYMENT_ON_DEMAND == false
|
||||
&& ( !isset( $config_vars['other']['disable_auto_upgrade'] ) || isset( $config_vars['other']['disable_auto_upgrade'] ) && $config_vars['other']['disable_auto_upgrade'] != true ) ) {
|
||||
Debug::Text( 'Auto Upgrade is enabled, checking for new version...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
sleep( rand( 0, 60 ) ); //Further randomize when calls are made.
|
||||
|
||||
$php_cli = $config_vars['path']['php_cli'];
|
||||
if ( is_executable( $php_cli ) ) {
|
||||
ini_set( 'max_execution_time', 0 );
|
||||
|
||||
$command = $php_cli . ' ' . Environment::getBasePath() . DIRECTORY_SEPARATOR . 'tools' . DIRECTORY_SEPARATOR . 'unattended_upgrade.php';
|
||||
system( $command, $output );
|
||||
Debug::Arr( $output, 'Auto Upgrade Command: ' . $command . ' Output: ', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
} else {
|
||||
Debug::Text( 'ERROR PHP CLI not executable!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
} else {
|
||||
Debug::Text( 'Auto Upgrade is disabled!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
?>
|
123
maint/CheckForUpdate.php
Normal file
123
maint/CheckForUpdate.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Checks for any version updates...
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//Pass "-f" on the command line to force update check.
|
||||
if ( in_array( '-f', $argv ) ) {
|
||||
$force = true;
|
||||
} else {
|
||||
$force = false;
|
||||
}
|
||||
|
||||
$ttsc = new TimeTrexSoapClient();
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getAll();
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
sleep( rand( 0, 60 ) ); //Further randomize when calls are made.
|
||||
|
||||
$i = 0;
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( $i == 0 && $ttsc->getLocalRegistrationKey() == false || $ttsc->getLocalRegistrationKey() == '' ) {
|
||||
$ttsc->saveRegistrationKey();
|
||||
}
|
||||
|
||||
//We must ensure that the data is up to date
|
||||
//Otherwise version check will fail.
|
||||
$ttsc->sendCompanyData( $c_obj->getId() );
|
||||
$ttsc->sendCompanyUserLocationData( $c_obj->getId() );
|
||||
$ttsc->sendCompanyUserCountData( $c_obj->getId() );
|
||||
$ttsc->sendCompanyVersionData( $c_obj->getId() );
|
||||
|
||||
//Check for new license once it starts expiring.
|
||||
//Help -> About, checking for new versions also gets the updated license file.
|
||||
if ( $i == 0 && getTTProductEdition() >= TT_PRODUCT_PROFESSIONAL ) {
|
||||
if ( !isset( $system_settings['license'] ) ) {
|
||||
$system_settings['license'] = null;
|
||||
}
|
||||
|
||||
$obj_class = "\124\124\114\x69\x63\x65\x6e\x73\x65"; $license = @new $obj_class;
|
||||
$license->checkLicenseFile( $system_settings['license'] );
|
||||
}
|
||||
|
||||
//Only if forced or update notifications are enabled do we need to create the notification.
|
||||
// The above code still needs to be run though to perform the necessary check.
|
||||
if ( $force == true || $ttsc->isUpdateNotifyEnabled() == true ) {
|
||||
//Only need to call this on the last company
|
||||
if ( $i == ( $clf->getRecordCount() - 1 ) ) {
|
||||
$latest_version = $ttsc->isLatestVersion( $c_obj->getId() );
|
||||
if ( $latest_version == false ) {
|
||||
SystemSettingFactory::setSystemSetting( 'new_version', 1 );
|
||||
|
||||
if ( DEMO_MODE == false && PRODUCTION == true && isset( $config_vars['other']['primary_company_id'] ) ) {
|
||||
$link = ( $c_obj->getProductEdition() == TT_PRODUCT_COMMUNITY ) ? 'https://www.timetrex.com/r.php?id=19' : 'https://www.timetrex.com/r.php?id=9';
|
||||
$notification_data = [
|
||||
'object_id' => TTUUID::getNotExistID( 1010 ),
|
||||
'object_type_id' => 0,
|
||||
'type_id' => 'system',
|
||||
'title_short' => TTi18n::getText( 'NOTICE: New version of %1 is available.', [ APPLICATION_NAME ] ),
|
||||
'body_long_html' => TTi18n::getText( 'NOTICE: New version of %1 is available, it is highly recommended that you upgrade as soon as possible. <a href="%2">Click here</a> to download the latest version.', [ APPLICATION_NAME, $link ] ),
|
||||
'body_short' => TTi18n::getText( 'NOTICE: New version of %1 is available, it is highly recommended that you upgrade as soon as possible. Click here to download the latest version.', [ APPLICATION_NAME ] ),
|
||||
'payload' => [ 'link_target' => '_blank', 'link' => $link ],
|
||||
'effective_date' => Notification::getNextDecentEffectiveDate(), //Since this maintenance job runs at night or early morning, date the notification to a decent hour like 8AM.
|
||||
];
|
||||
|
||||
Notification::sendNotificationToAllUsers( 90, true, true, $notification_data, ( 14 * 86400 ), $config_vars['other']['primary_company_id'] ); //Only send to primary company
|
||||
}
|
||||
|
||||
} else {
|
||||
SystemSettingFactory::setSystemSetting( 'new_version', 0 );
|
||||
}
|
||||
}
|
||||
} else if ( $i == 0 ) { //Just display this once.
|
||||
Debug::Text( 'Auto Update Notifications are disabled!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
?>
|
317
maint/MiscDaily.php
Normal file
317
maint/MiscDaily.php
Normal file
@@ -0,0 +1,317 @@
|
||||
<?php /** @noinspection PhpUndefinedVariableInspection */
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//
|
||||
// Update PRIMARY_COMPANY_ID if its invalid or does not exist anymore.
|
||||
//
|
||||
$clf = TTnew( 'CompanyListFactory' ); /** @var CompanyListFactory $clf */
|
||||
$clf->getByID( PRIMARY_COMPANY_ID );
|
||||
Debug::text( 'Primary Company ID: ' . PRIMARY_COMPANY_ID, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( $clf->getRecordCount() != 1 ) {
|
||||
//Get all companies and try to determine which one should be the primary, based on created date and status.
|
||||
$clf->getAll( 1, null, [ 'status_id' => '= 10' ], [ 'created_date' => 'asc' ] );
|
||||
Debug::text( ' Total Companies: ' . $clf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( $c_obj->getDeleted() == false && $c_obj->getStatus() == 10 ) { //10=Active
|
||||
Debug::text( ' Setting PRIMARY_COMPANY_ID to: ' . $c_obj->getId() . ' Name: ' . $c_obj->getName(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$install_obj = new Install();
|
||||
$tmp_config_vars['other']['primary_company_id'] = (string)$c_obj->getId();
|
||||
$write_config_result = $install_obj->writeConfigFile( $tmp_config_vars );
|
||||
unset( $install_obj, $tmp_config_vars, $write_config_result );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug::text( ' Valid PRIMARY_COMPANY_ID, not modifying...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
unset( $clf, $c_obj );
|
||||
|
||||
|
||||
//
|
||||
// Set system_timezone .ini setting to the most commonly used value if it hasn't been changed from the default of GMT.
|
||||
//
|
||||
if ( TTDate::getTimeZone() == 'GMT' ) {
|
||||
$uplf = TTNew( 'UserPreferenceListFactory' ); /** @var UserPreferenceListFactory $uplf */
|
||||
$most_common_time_zone = $uplf->getMostCommonTimeZone();
|
||||
Debug::text( 'Most Common TimeZone: ' . $most_common_time_zone, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
if ( $most_common_time_zone != '' && $most_common_time_zone != 'GMT' ) {
|
||||
$install_obj = new Install();
|
||||
$tmp_config_vars['other']['system_timezone'] = (string)$most_common_time_zone;
|
||||
$write_config_result = $install_obj->writeConfigFile( $tmp_config_vars );
|
||||
Debug::text( ' Setting System TimeZone to: ' . $most_common_time_zone, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
unset( $uplf, $most_common_time_zone, $install_obj, $tmp_config_vars, $write_config_result );
|
||||
}
|
||||
|
||||
//Purge idempotent requests older than 24hrs.
|
||||
IdempotentRequestFactory::Purge();
|
||||
|
||||
//Purge system job queue
|
||||
SystemJobQueue::Purge();
|
||||
|
||||
if ( DEMO_MODE == false && PRODUCTION == true ) {
|
||||
// Check pay periods past transaction date have not been closed.
|
||||
PayPeriodScheduleFactory::checkPayPeriodClosed();
|
||||
|
||||
// Checking primary company for license issues.
|
||||
CompanyFactory::checkCompanyLicense();
|
||||
|
||||
// Check if application out if date.
|
||||
Install::checkApplicationOutOfDate();
|
||||
|
||||
// Check application version matches database.
|
||||
Install::checkApplicationVersionMatchesDatabase();
|
||||
|
||||
// Product Edition sync issues.
|
||||
Install::syncProductEdition();
|
||||
|
||||
// Database schema sync issues.
|
||||
Install::checkDatabaseInSync();
|
||||
|
||||
// Give an early warning to installs using older stack components before the next version is released that forces the upgrade.
|
||||
Install::checkStackComponentsOutOfDate();
|
||||
|
||||
// User has no email and may miss important information.
|
||||
UserFactory::checkUserHasEmail();
|
||||
|
||||
// Sync notifications from master server.
|
||||
Notification::syncNotifications( SystemSettingFactory::getSystemSettingValueByKey( 'last_notification_sync_time' ) );
|
||||
}
|
||||
|
||||
//
|
||||
// Backup database if script exists.
|
||||
// Always backup the database first before doing anything else like purging tables.
|
||||
//
|
||||
if ( !isset( $config_vars['other']['disable_backup'] )
|
||||
|| isset( $config_vars['other']['disable_backup'] ) && $config_vars['other']['disable_backup'] != true ) {
|
||||
if ( PHP_OS == 'WINNT' ) {
|
||||
$backup_script = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'backup_database.bat';
|
||||
} else {
|
||||
$backup_script = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'backup_database';
|
||||
}
|
||||
Debug::Text( 'Backup Database Command: ' . $backup_script, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( file_exists( $backup_script ) ) {
|
||||
Debug::Text( 'Running Backup: ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
exec( '"' . $backup_script . '"', $output, $retcode );
|
||||
Debug::Text( 'Backup Completed: ' . TTDate::getDate( 'DATE+TIME', time() ) . ' RetCode: ' . $retcode, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
$backup_history_files = [];
|
||||
|
||||
$backup_dir = dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..';
|
||||
if ( is_dir( $backup_dir ) && is_readable( $backup_dir ) ) {
|
||||
$fh = opendir( $backup_dir );
|
||||
while ( ( $file = readdir( $fh ) ) !== false ) {
|
||||
# loop through the files, skipping . and .., and recursing if necessary
|
||||
if ( strcmp( $file, '.' ) == 0 || strcmp( $file, '..' ) == 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filepath = $backup_dir . DIRECTORY_SEPARATOR . $file;
|
||||
if ( !is_dir( $filepath ) ) {
|
||||
//Be more strict with regex to avoid: PHP ERROR - WARNING(2): filemtime(): stat failed for C:\TimeTrex\timetrex\maint\..\..\timetrex_database_???.sql File: C:\TimeTrex\timetrex\maint\MiscDaily.php Line: 74
|
||||
if ( preg_match( '/timetrex_database_[A-Za-z0-9\-]+\.sql/i', $file ) == 1 ) {
|
||||
$file_mtime = @filemtime( $filepath );
|
||||
if ( $file_mtime !== false ) {
|
||||
$backup_history_files[$file_mtime] = $filepath;
|
||||
} else {
|
||||
Debug::Text( 'ERROR: Unable to get filemtime on: ' . $filepath, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort( $backup_history_files );
|
||||
|
||||
if ( is_array( $backup_history_files ) && count( $backup_history_files ) > 7 ) {
|
||||
reset( $backup_history_files );
|
||||
$delete_backup_file = current( $backup_history_files );
|
||||
Debug::Text( 'Deleting oldest backup: ' . $delete_backup_file . ' Of Total: ' . count( $backup_history_files ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( @unlink( $delete_backup_file ) == false ) { //PHP ERROR - WARNING(2): unlink(C:\TimeTrex\timetrex\maint\..\..\timetrex_database_20160322.sql): Permission denied File: C:\TimeTrex\timetrex\maint\MiscDaily.php Line: 85
|
||||
Debug::Text( 'ERROR: Unable to delete backup file, possible permission denied error?', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
unset( $delete_backup_file );
|
||||
}
|
||||
}
|
||||
unset( $backup_script, $output, $retcode, $backup_dir, $fh, $file, $filepath, $backup_history_files );
|
||||
}
|
||||
|
||||
//
|
||||
// Rotate log files
|
||||
//
|
||||
if ( !isset( $config_vars['other']['disable_log_rotate'] )
|
||||
|| isset( $config_vars['other']['disable_log_rotate'] ) && $config_vars['other']['disable_log_rotate'] != true ) {
|
||||
$log_rotate_config[] = [
|
||||
'directory' => $config_vars['path']['log'],
|
||||
'recurse' => false,
|
||||
'file' => 'timetrex.log',
|
||||
'frequency' => 'DAILY',
|
||||
'history' => 10,
|
||||
]; //Keep more than a weeks worth, so we can better diagnose maintenance jobs that just run once per week.
|
||||
|
||||
$log_rotate_config[] = [
|
||||
'directory' => $config_vars['path']['log'] . DIRECTORY_SEPARATOR . 'client',
|
||||
'recurse' => true,
|
||||
'file' => '*',
|
||||
'frequency' => 'DAILY',
|
||||
'history' => 10,
|
||||
];
|
||||
|
||||
$log_rotate_config[] = [
|
||||
'directory' => $config_vars['path']['log'] . DIRECTORY_SEPARATOR . 'time_clock',
|
||||
'recurse' => true,
|
||||
'file' => '*',
|
||||
'frequency' => 'DAILY',
|
||||
'history' => 10,
|
||||
];
|
||||
|
||||
$lr = new LogRotate( $log_rotate_config );
|
||||
$lr->Rotate();
|
||||
}
|
||||
|
||||
if ( ( isset( $config_vars['other']['installer_enabled'] ) && $config_vars['other']['installer_enabled'] == false ) && ( !isset( $config_vars['other']['down_for_maintenance'] ) || isset( $config_vars['other']['down_for_maintenance'] ) && $config_vars['other']['down_for_maintenance'] == '' ) ) {
|
||||
//
|
||||
// Check cache file directories and permissions.
|
||||
//
|
||||
if ( !isset( $config_vars['other']['disable_cache_permission_check'] )
|
||||
|| isset( $config_vars['other']['disable_cache_permission_check'] ) && $config_vars['other']['disable_cache_permission_check'] != true ) {
|
||||
if ( isset( $config_vars['cache']['enable'] ) && $config_vars['cache']['enable'] == true && isset( $config_vars['cache']['dir'] ) && $config_vars['cache']['dir'] != '' ) {
|
||||
Debug::Text( 'Validating Cache Files/Directory: ' . $config_vars['cache']['dir'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Just as a precaution, confirm that cache directory exists, if not try to create it.
|
||||
//If the cache directory doesnt exist, then LockFile class can't create lock files, and therefore no cron jobs will run.
|
||||
//So also have LockFile class try to create the directory so we can at least get to this point.
|
||||
if ( file_exists( $config_vars['cache']['dir'] ) == false ) {
|
||||
//Try to create cache directory
|
||||
Debug::Text( 'Cache directory does not exist, attempting to create it: ' . $config_vars['cache']['dir'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$mkdir_result = @mkdir( $config_vars['cache']['dir'], 0777, true );
|
||||
if ( $mkdir_result == false ) {
|
||||
Debug::Text( 'ERROR: Unable to create cache directory: ' . $config_vars['cache']['dir'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
Misc::disableCaching();
|
||||
} else {
|
||||
Debug::Text( 'Cache directory created successfully: ' . $config_vars['cache']['dir'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
unset( $mkdir_result );
|
||||
}
|
||||
|
||||
//Check all cache files and make sure they are owned by the same users.
|
||||
try {
|
||||
$prev_file_owner = null;
|
||||
$files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $config_vars['cache']['dir'], FilesystemIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST );
|
||||
foreach ( $files as $file_obj ) {
|
||||
$cache_file = $file_obj->getRealPath(); //Check both files and directories.
|
||||
|
||||
$file_owner = @fileowner( $cache_file );
|
||||
if ( $prev_file_owner !== null && $file_owner != $prev_file_owner ) {
|
||||
Debug::Text( 'ERROR: Cache directory contains files from several different owners. Its likely that their permission conflict.', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
Debug::Text( 'Cache File Owner UIDs: ' . $prev_file_owner . ', ' . $file_owner, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
Misc::disableCaching();
|
||||
|
||||
break; //Stop loop as soon as more than one owner is detected.
|
||||
}
|
||||
|
||||
$prev_file_owner = $file_owner;
|
||||
}
|
||||
unset( $prev_file_owner, $files, $cache_file, $file_owner );
|
||||
} catch ( Exception $e ) {
|
||||
Debug::Text( 'Failed opening/reading file or directory: ' . $e->getMessage(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
Misc::disableCaching();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Update Company contacts so they are always valid.
|
||||
//
|
||||
$clf = TTNew( 'CompanyListFactory' ); /** @var CompanyListFactory $clf */
|
||||
$clf->getAllByInValidContacts();
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
foreach ( $clf as $c_obj ) {
|
||||
Debug::Text( 'Attempting to update Company Contacts for Company: ' . $c_obj->getName() . '(' . $c_obj->getID() . ')', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$default_company_contact_user_id = $c_obj->getDefaultContact();
|
||||
if ( TTUUID::isUUID( $default_company_contact_user_id ) && $default_company_contact_user_id != TTUUID::getZeroID() ) {
|
||||
Debug::text( 'Found alternative contact: ' . $default_company_contact_user_id, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
$user_obj = $c_obj->getUserObject( $c_obj->getAdminContact() );
|
||||
if ( !is_object( $user_obj ) || ( is_object( $user_obj ) && $user_obj->getStatus() == 10 && $user_obj->getId() != $default_company_contact_user_id ) ) {
|
||||
$c_obj->setAdminContact( $default_company_contact_user_id );
|
||||
Debug::text( 'Replacing Admin Contact with: ' . $default_company_contact_user_id, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
$user_obj = $c_obj->getUserObject( $c_obj->getBillingContact() );
|
||||
if ( !is_object( $user_obj ) || ( is_object( $user_obj ) && $user_obj->getStatus() == 10 && $user_obj->getId() != $default_company_contact_user_id ) ) {
|
||||
$c_obj->setBillingContact( $default_company_contact_user_id );
|
||||
Debug::text( 'Replacing Billing Contact with: ' . $default_company_contact_user_id, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
$user_obj = $c_obj->getUserObject( $c_obj->getSupportContact() );
|
||||
if ( !is_object( $user_obj ) || ( is_object( $user_obj ) && $user_obj->getStatus() == 10 && $user_obj->getId() != $default_company_contact_user_id ) ) {
|
||||
$c_obj->setSupportContact( $default_company_contact_user_id );
|
||||
Debug::text( 'Replacing Support Contact with: ' . $default_company_contact_user_id, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
if ( $c_obj->isValid() ) {
|
||||
Debug::Text( 'Saving company record...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$c_obj->Save();
|
||||
}
|
||||
} else {
|
||||
Debug::Text( 'Unable to find default contact!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $clf, $c_obj, $default_company_contact_user_id, $user_obj );
|
||||
|
||||
//
|
||||
// Expire Logins
|
||||
//
|
||||
$ulf = TTNew( 'UserListFactory' ); /** @var UserListFactory $ulf */
|
||||
$ulf->disableExpiredLogins( time() );
|
||||
?>
|
121
maint/MiscWeekly.php
Normal file
121
maint/MiscWeekly.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php /** @noinspection PhpUndefinedVariableInspection */
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Checks for any version updates...
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//
|
||||
//Check system requirements.
|
||||
//
|
||||
if ( PRODUCTION == true && DEPLOYMENT_ON_DEMAND == false ) {
|
||||
Debug::Text( 'Checking system requirements... ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$install_obj = new Install();
|
||||
|
||||
Debug::Text( 'Cleaning orphaned files... ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$install_obj->cleanOrphanFiles(); //Clean orphaned files once per week to ensure the installation is clean.
|
||||
|
||||
$failed_requirment_requirements = $install_obj->getFailedRequirements( false, [ 'base_url', 'clean_cache', 'file_checksums' ] );
|
||||
|
||||
if ( is_array( $failed_requirment_requirements ) && count( $failed_requirment_requirements ) > 1 ) {
|
||||
SystemSettingFactory::setSystemSetting( 'valid_install_requirements', 0 );
|
||||
|
||||
//sends notification for invalid install requirments
|
||||
SystemSettingFactory::checkValidInstallRequirments( $failed_requirment_requirements );
|
||||
|
||||
Debug::Text( 'Failed system requirements: ' . implode( '', $failed_requirment_requirements ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
TTLog::addEntry( 0, 510, 'Failed system requirements: ' . implode( '', $failed_requirment_requirements ), 0, 'company' );
|
||||
} else {
|
||||
SystemSettingFactory::setSystemSetting( 'valid_install_requirements', 1 );
|
||||
}
|
||||
|
||||
unset( $install_obj, $check_all_requirements );
|
||||
Debug::Text( 'Checking system requirements complete... ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
//
|
||||
// Purge database tables
|
||||
//
|
||||
if ( !isset( $config_vars['other']['disable_database_purging'] )
|
||||
|| isset( $config_vars['other']['disable_database_purging'] ) && $config_vars['other']['disable_database_purging'] != true ) {
|
||||
PurgeDatabase::Execute();
|
||||
}
|
||||
|
||||
//Ensure we aren't running this maintenance when installer is running or we are down for maintenance.
|
||||
// Even though cron jobs don't run when this is the case, its possible that this mode is entered during a long running job.
|
||||
if ( ( isset( $config_vars['other']['installer_enabled'] ) && $config_vars['other']['installer_enabled'] == false ) && ( !isset( $config_vars['other']['down_for_maintenance'] ) || isset( $config_vars['other']['down_for_maintenance'] ) && $config_vars['other']['down_for_maintenance'] == '' ) ) {
|
||||
//
|
||||
// Clean cache directories
|
||||
// - Make sure cache directory is set, and log/storage directories are not contained within it.
|
||||
//
|
||||
if ( !isset( $config_vars['other']['disable_cache_purging'] )
|
||||
|| isset( $config_vars['other']['disable_cache_purging'] ) && $config_vars['other']['disable_cache_purging'] != true ) {
|
||||
|
||||
if ( isset( $config_vars['cache']['dir'] )
|
||||
&& $config_vars['cache']['dir'] != ''
|
||||
&& strpos( $config_vars['path']['log'], $config_vars['cache']['dir'] ) === false
|
||||
&& strpos( $config_vars['path']['storage'], $config_vars['cache']['dir'] ) === false ) {
|
||||
|
||||
Debug::Text( 'Purging Cache directory: ' . $config_vars['cache']['dir'] . ' - ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$install_obj = new Install();
|
||||
$install_obj->cleanCacheDirectory( '\.lock|.state' ); //Don't exclude .ZIP/Upgrade Staging files, so if there is a corrupt one it will be redownloaded within a week.
|
||||
Debug::Text( 'Purging Cache directory complete: ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
} else {
|
||||
Debug::Text( 'Cache directory is invalid: ' . TTDate::getDate( 'DATE+TIME', time() ), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//Check for severely out of date versions and take out of production mode if necessary.
|
||||
//
|
||||
if ( PRODUCTION == true && getTTProductEdition() == TT_PRODUCT_COMMUNITY && DEPLOYMENT_ON_DEMAND == false && ( ( time() - (int)APPLICATION_VERSION_DATE ) > ( 86400 * 455 ) ) ) {
|
||||
Debug::Text( 'ERROR: Application version is severely out of date, changing production mode... ', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$install_obj = new Install();
|
||||
$tmp_config_vars['debug']['production'] = 'FALSE';
|
||||
$write_config_result = $install_obj->writeConfigFile( $tmp_config_vars );
|
||||
unset( $install_obj, $tmp_config_vars, $write_config_result );
|
||||
}
|
||||
?>
|
88
maint/Notifications.php
Normal file
88
maint/Notifications.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Handle sending post dated notifications and notifications that failed to send
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
$ndtlf = TTnew( 'NotificationListFactory' ); /** @var NotificationListFactory $ndtlf */
|
||||
$ndtlf->getPending();
|
||||
if ( $ndtlf->getRecordCount() > 0 ) {
|
||||
Debug::Text( ' Pending notifications, or ones that need to be resent: '. $ndtlf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
foreach ( $ndtlf as $n_obj ) { /** @var NotificationFactory $n_obj */
|
||||
$payload = $n_obj->getPayloadData();
|
||||
|
||||
$attempt_send = false;
|
||||
if ( isset( $payload['retries'] ) ) {
|
||||
// This is at least the first retry attempt - a post dated notification will not have retry data yet.
|
||||
$time_difference = ( TTDate::getTime() - $payload['retries']['last_attempt_date'] );
|
||||
if ( $payload['retries']['attempts'] == 1 && $time_difference >= 120 ) { //2 minutes
|
||||
$attempt_send = true;
|
||||
} else if ( $payload['retries']['attempts'] == 2 && $time_difference >= 900 ) { // 15 minutes
|
||||
$attempt_send = true;
|
||||
} else if ( $payload['retries']['attempts'] == 3 && $time_difference >= 3600 ) { // 1 hour
|
||||
$attempt_send = true;
|
||||
} else if ( $payload['retries']['attempts'] == 4 && $time_difference >= 14400 ) { // 4 hours
|
||||
$attempt_send = true;
|
||||
} else if ( $payload['retries']['attempts'] == 5 && $time_difference >= 28800 ) { // 8 hours
|
||||
$attempt_send = true;
|
||||
} else if ( $payload['retries']['attempts'] == 6 && $time_difference >= 86400 ) { // 24 hours
|
||||
$attempt_send = true;
|
||||
} else if ( $payload['retries']['attempts'] >= 7 && $time_difference >= 172800 ) { // 48 hours
|
||||
$attempt_send = true;
|
||||
}
|
||||
} else {
|
||||
// Post dated messages have no retry data yet and this is the first send attempt
|
||||
$attempt_send = true;
|
||||
}
|
||||
|
||||
if ( $attempt_send == true && $n_obj->isValid() ) {
|
||||
$n_obj->Save();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug::Text( ' No pending notifications, or ones that need to be resent...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
?>
|
396
maint/QueueWorker.php
Normal file
396
maint/QueueWorker.php
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
ini_set( 'max_execution_time', 0 );
|
||||
|
||||
$my_worker_pid = getmypid();
|
||||
if ( function_exists( 'proc_nice' ) ) {
|
||||
proc_nice( 19 ); //Low priority.
|
||||
}
|
||||
|
||||
$config_vars['database']['persistent_connections'] = true; //Force persistent connections so LISTEN/NOTIFY works properly.
|
||||
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
function getRequirementData() {
|
||||
global $__initial_requirement_data;
|
||||
|
||||
clearstatcache( true, CONFIG_FILE );
|
||||
clearstatcache( true, __FILE__ );
|
||||
clearstatcache( true, dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
|
||||
$retarr = [
|
||||
'self' => filemtime( __FILE__ ),
|
||||
'global' => filemtime( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' ),
|
||||
'config' => filemtime( CONFIG_FILE ),
|
||||
|
||||
//Can't use these as we need to reload the global.inc.php for them to be updated.
|
||||
//'application_version' => APPLICATION_VERSION,
|
||||
//'application_version_date' => APPLICATION_VERSION_DATE,
|
||||
];
|
||||
|
||||
if ( !isset( $__initial_requirement_data ) ) {
|
||||
$__initial_requirement_data = $retarr;
|
||||
}
|
||||
|
||||
return $retarr;
|
||||
}
|
||||
|
||||
function isValidIdleRequirements() {
|
||||
Debug::Text( 'Checking valid requirements...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
global $config_vars;
|
||||
if ( isset( $config_vars['other']['enable_job_queue'] ) && $config_vars['other']['enable_job_queue'] != true ) { //If job queue is disabled, fail out early.
|
||||
Debug::Text( ' Job Queue is disabled in .ini file...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check that we aren't down for maintenance (.ini file changing could be the same?)
|
||||
|
||||
//Check that memory usage hasn't exceeded maximum limit.
|
||||
if ( memory_get_usage( true ) > 100000000 ) {
|
||||
Debug::Text( ' Memory usage exceeds maximum!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ( time() - $_SERVER['REQUEST_TIME'] ) > 14400 ) { //Keep this low (4hrs) initially as we are seeing fairly high PGSQL memory usage for these idle connections for some reason. Could be a memory leak in PGSQL v12 or older?
|
||||
Debug::Text( ' Process started more than 4hrs ago!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check that files haven't changed out from underneath us.
|
||||
global $__initial_requirement_data;
|
||||
$requirement_data = getRequirementData();
|
||||
$requirement_data_diff = array_diff_assoc( $__initial_requirement_data, $requirement_data );
|
||||
if ( !empty( $requirement_data_diff ) ) {
|
||||
//Debug::Arr( $__initial_requirement_data, 'Initial Requirement Data: ', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
//Debug::Arr( $requirement_data, 'Current Requirement Data: ', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
Debug::Text( ' Files have changed or application was upgraded!', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
return false;
|
||||
} else {
|
||||
Debug::Text( 'Current Requirement Data all matches.', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
//Debug::Arr( $__initial_requirement_data, 'Initial Requirement Data: ', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
//Debug::Arr( $requirement_data, 'Current Requirement Data: ', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection, $force = false, $restart_worker = false ) {
|
||||
if ( $force == true ) {
|
||||
$exit = true;
|
||||
} else {
|
||||
$exit = false;
|
||||
|
||||
sleep( rand( 0, 5 ) ); //Random sleep so we aren't checking for worker processes at the exact same time.
|
||||
|
||||
$total_running_jobs = $sjqlf->getRunning(); //Total running jobs in the entire queue, across all servers.
|
||||
$current_running_processes = $sjqlf->getRunningWorkerProcesses(); //This is running processes on the current server, not total running jobs in the entire queue.
|
||||
if ( $current_running_processes > 1 && $current_running_processes >= ( $total_running_jobs + 1 ) ) { //Make sure there is always at least 1 extra queue worker than running jobs, in case they are only long running jobs.
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Shutdown: Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'] . ' Running Processes: ' . $current_running_processes .' Total Running Jobs: '. $total_running_jobs, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$exit = true;
|
||||
} else {
|
||||
if ( isValidIdleRequirements() == true ) {
|
||||
//Check if our lock file is still valid.
|
||||
if ( $bg_lock_file->getCurrentPID() == $bg_lock_file->readPIDFile( $bg_lock_file->getFileName() ) ) {
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Shutdown: Unable due to last process, or running processes... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'] . ' Running Processes: ' . $current_running_processes . ' Total Running Jobs: ' . $total_running_jobs, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
} else {
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Shutdown: ERROR! Own lock file is stale/invalid... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'] . ' Running Processes: ' . $current_running_processes . ' Total Running Jobs: ' . $total_running_jobs, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$exit = true;
|
||||
}
|
||||
} else {
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Shutdown even though last process due to requirements mismatch... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'] . ' Running Processes: ' . $current_running_processes .' Total Running Jobs: '. $total_running_jobs .' Restart: '. (int)$restart_worker, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$exit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug::writeToLog();
|
||||
if ( $exit == true ) {
|
||||
//Delete background pool lock file.
|
||||
if ( isset( $bg_lock_file ) ) {
|
||||
$bg_lock_file->delete();
|
||||
}
|
||||
|
||||
//Try to close the DB connection, even though its persistent and might not actually close it.
|
||||
@pg_close( $worker_db_connection );
|
||||
|
||||
if ( $restart_worker == true ) {
|
||||
$sjqlf = new SystemJobQueueListFactory();
|
||||
$sjqlf->startWorkerProcess( __FILE__ );
|
||||
}
|
||||
|
||||
//Disconnect from the database. Especially important to prevent persistent connections from stacking up.
|
||||
// Because pg_close() is called above, this can try to close the connection twice, which could cause PHP fatal errors. Doesn't seem to be an easy way to check for that case either.
|
||||
//global $db;
|
||||
//if ( is_object( $db ) ) {
|
||||
// $db->Close();
|
||||
//}
|
||||
|
||||
Debug::Display();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ( Debug::getPHPErrors() == 0 ) {
|
||||
Debug::clearBuffer();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//First CLI option is the background process lock file name.
|
||||
if ( isset( $argv[1] ) && $argv[1] != '' ) {
|
||||
$background_process_lock_file_name = $argv[1];
|
||||
Debug::text( 'Spawned queue worker using Background Lock File Name: ' . $background_process_lock_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Create lock file for background pooling.
|
||||
$bg_lock_file = new LockFile( $background_process_lock_file_name );
|
||||
if ( $bg_lock_file->create() == false ) {
|
||||
Debug::text( 'ERROR: Unable to create lock file, exiting...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//This must go after lock file created, so we can clear the lock file
|
||||
if ( ( isset( $config_vars['other']['installer_enabled'] ) && $config_vars['other']['installer_enabled'] == 1 ) || ( isset( $config_vars['other']['down_for_maintenance'] ) && $config_vars['other']['down_for_maintenance'] == 1 ) ) {
|
||||
Debug::text( 'Installer Enable or Down for Maintenance, not processing job queue...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( isset( $bg_lock_file ) ) {
|
||||
$bg_lock_file->delete();
|
||||
}
|
||||
//Debug::writeToLog(); //Handled in TTShutdown now.
|
||||
//Debug::Display(); //Handled in TTShutdown now.
|
||||
exit(0);
|
||||
}
|
||||
|
||||
getRequirementData(); //Save initial requirement data so we can check against to later to see if anything changed.
|
||||
|
||||
$idle_sleep = 20000; //microseconds, 100000 = 0.1 second.
|
||||
$idle_timeout_force_check = 30; //Seconds to force pending job check without notify being received. **THIS IS CRITICAL FOR REAL-TIME EXCEPTIONS OR FUTURE DATED JOBS**
|
||||
$idle_timeout_shutdown = 60; //Seconds to shutdown a queue worker process.
|
||||
$max_executed_jobs = 1000;
|
||||
|
||||
//Randomize some of the timeouts so processes can't get "synced" together, and all start/stop at the same time, or always check for new jobs at the same time.
|
||||
$idle_sleep += rand( ( $idle_sleep * -0.20 ), ( $idle_sleep * 0.20 ) );
|
||||
$idle_timeout_force_check += rand( ( $idle_timeout_force_check * -0.20 ), ( $idle_timeout_force_check * 0.40 ) );
|
||||
$idle_timeout_shutdown += rand( ( $idle_timeout_shutdown * -0.20 ), ( $idle_timeout_shutdown * 0.20 ) );
|
||||
$max_executed_jobs += rand( ( $max_executed_jobs * -0.20 ), ( $max_executed_jobs * 0.20 ) );
|
||||
$max_executed_jobs = ( $max_executed_jobs < 1 ) ? 1 : $max_executed_jobs; //Make sure we never go below 1 as the max jobs to execute.
|
||||
$system_load_throttle_threshold_multiplier = 0.75; //This is a multiplier on the max system load. (ie: 90% of max load)
|
||||
$system_load_throttle_sleep_max = 750000; //Max amount of time to sleep when system load reaches the max threshold, in micro seconds. (ie: 500000 = 0.5 seconds)
|
||||
|
||||
//Check if we are using load balancing or not.
|
||||
if ( strpos( $config_vars['database']['host'], ',' ) !== false ) {
|
||||
$worker_db_connection = $db->getConnection( 'write' )->_connectionID;
|
||||
} else {
|
||||
$worker_db_connection = $db->_connectionID;
|
||||
}
|
||||
|
||||
$jobs_executed = 0;
|
||||
$last_received_notification = null;
|
||||
$force_job_check = true; //On startup always force a check for a new job.
|
||||
$idle_timeout_force_epoch = null;
|
||||
$execution_time = [ 'active' => 0, 'idle' => 0 ];
|
||||
|
||||
Debug::text( ' Listening for Pending Jobs...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$db->Execute( 'SET SESSION idle_in_transaction_session_timeout = 300000'); //5min in milliseconds -- If connections happen to start leaking, try to prevent them from stacking up.
|
||||
$db->Execute( 'LISTEN "system_job_queue"');
|
||||
|
||||
Debug::writeToLog();
|
||||
if ( Debug::getPHPErrors() == 0 ) {
|
||||
Debug::clearBuffer();
|
||||
}
|
||||
|
||||
$sjqlf = new SystemJobQueueListFactory();
|
||||
$max_allowed_processes = $sjqlf->getMaxProcesses();
|
||||
|
||||
$i = 0;
|
||||
while( 1 ) {
|
||||
$loop_start_microtime = microtime( true );
|
||||
$_SERVER['REQUEST_TIME_FLOAT'] = microtime( true ); //This restarts the DEBUG time on each loop as if its a separate request. This helps prevent long request WARNING from being triggered like in reports.
|
||||
|
||||
if ( $force_job_check !== true ) {
|
||||
$notify = pg_get_notify( $worker_db_connection );
|
||||
if ( isset( $notify['payload'] ) && $notify['payload'] != '' ) {
|
||||
Debug::Text( ' PG Notify Payload: '. $notify['payload'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
if ( strtolower( $notify['payload'] ) == 'exit' ) {
|
||||
Debug::text( ' WARNING: Notify command to exit received, shutting down...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection, true ); //Force immediate shutdown
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$notify = false;
|
||||
}
|
||||
|
||||
if ( ( $force_job_check == true || $notify !== false ) ) {
|
||||
//If only a max of 1 process is allowed, it must disable max executed jobs,
|
||||
// since if we exit the last process we can't restart it right away because the lock file isn't deleted until we exit, and the have to start a new process before we exit. (chicken and egg problem)
|
||||
if ( $jobs_executed <= $max_executed_jobs || $max_allowed_processes == 1 ) {
|
||||
|
||||
$max_system_load = Misc::getMaxSystemLoad();
|
||||
$current_system_load = Misc::getSystemLoad();
|
||||
$system_load_throttle_threshold = ( $max_system_load * $system_load_throttle_threshold_multiplier );
|
||||
if ( ( $current_system_load <= $max_system_load ) ) {
|
||||
$force_job_check = false;
|
||||
|
||||
$last_received_notification = $loop_start_microtime;
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Forced Job Check... Jobs Executed: ' . $jobs_executed . ' Force: ' . (int)$force_job_check . ' Idle Time: ' . $execution_time['idle'] .' Lock File: '. $background_process_lock_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Check to make sure the database connection is still operational before getting next pending job.
|
||||
$workder_db_connection_status = pg_connection_status( $worker_db_connection );
|
||||
if ( $workder_db_connection_status !== PGSQL_CONNECTION_OK ) {
|
||||
Debug::text( ' WARNING: Database connection was lost, shutting down...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection, true ); //Force immediate shutdown
|
||||
}
|
||||
|
||||
$sjqlf->getPendingAndLock( ( time() + 1 ) ); //Single SQL query is about 2-3x faster than separate queries for selecting and locking.
|
||||
if ( $sjqlf->getRecordCount() > 0 ) {
|
||||
foreach ( $sjqlf as $sjq_obj ) { /** @var SystemJobQueueFactory $sjq_obj */
|
||||
Debug::text( ' Found Pending Jobs: ' . $sjqlf->getRecordCount() .' Notify: '. ( ( $notify !== false ) ? 'Y' : 'N' ) .' Force: '. (int)$force_job_check .' Load: '. $current_system_load, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Whenever we are busy working on one job, make sure there is always another process ready to handle other ones, up to the max process limit of course.
|
||||
// This must happen after we lock the row, otherwise the child worker that spawns will just take the job instead.
|
||||
// Only spawn new worker processes when the load is less than half the max though, as that will just increase the load and likely slow everything down with multiple processes just waiting on the load coming down.
|
||||
// Since every time we try to start a new worker process, we have to check/count lock files, only start new processes when we are notified of new jobs,
|
||||
// or when we run the first job after spawning.
|
||||
if ( ( $notify !== false || $force_job_check == true || $jobs_executed == 0 ) && $current_system_load <= $system_load_throttle_threshold ) {
|
||||
$sjqlf->startWorkerProcess( __FILE__ );
|
||||
}
|
||||
|
||||
$sjq_obj->Run();
|
||||
|
||||
$jobs_executed++;
|
||||
}
|
||||
|
||||
$execution_time['active'] += microtime( true ) - $loop_start_microtime;
|
||||
$force_job_check = true; //Make sure we check for more jobs on the immediate next loop.
|
||||
} else {
|
||||
Debug::text( ' Pending Jobs: ' . $sjqlf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
//Received notification, but all jobs have all been processed. Clear out pg_get_notify() queue in case there is a backlog.
|
||||
$clear_queue = null;
|
||||
$cleared_backlogged_queue = -1;
|
||||
while ( $clear_queue !== false ) {
|
||||
$clear_queue = pg_get_notify( $worker_db_connection );
|
||||
$cleared_backlogged_queue++;
|
||||
}
|
||||
|
||||
if ( $cleared_backlogged_queue > 0 ) {
|
||||
Debug::Text( ' Cleared ' . $cleared_backlogged_queue . ' backlogged notifications...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$force_job_check = true; //Force one last check after notify backlog is cleared.
|
||||
}
|
||||
|
||||
$execution_time['idle'] += microtime( true ) - $loop_start_microtime;
|
||||
}
|
||||
|
||||
//Check if we are approaching max system load and throttle.
|
||||
if ( $current_system_load >= $system_load_throttle_threshold ) {
|
||||
$throttle_sleep_timeout = (int)Misc::reScaleRange( $current_system_load, $system_load_throttle_threshold, $max_system_load, 0, $system_load_throttle_sleep_max );
|
||||
$throttle_sleep_timeout += (int)rand( $throttle_sleep_timeout * -0.10, $throttle_sleep_timeout * 0.10 );
|
||||
Debug::Text( ' Load Throttle Sleep Timeout (microseconds): ' . $throttle_sleep_timeout .' Current Load: '. $current_system_load .' Threshold: '. $system_load_throttle_threshold, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
usleep( $throttle_sleep_timeout );
|
||||
}
|
||||
} else { //Max Load
|
||||
shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection );
|
||||
sleep( rand( 30, 90 ) ); //Make sure we are distributing the job checks.
|
||||
$execution_time['idle'] += microtime( true ) - $loop_start_microtime;
|
||||
}
|
||||
} else { //Max Executed Jobs
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Maximum jobs executed, shutdown... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'] .' Lock File: '. $background_process_lock_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection, false, true );
|
||||
$force_job_check = true; //Force job check next as that will also cause another job to spawn.
|
||||
|
||||
//Reduce by 10, so if for some reason we didn't exit (ie: not enough running workers), we will execute at least 10 more jobs before getting back to here.
|
||||
// Otherwise we could get stuck in a loop where every iteration we are at the max executed jobs and we constantly call shutdownQueueWorker() which has a random sleep, and might not exit on the next iteration anyways.
|
||||
$jobs_executed -= 10;
|
||||
}
|
||||
|
||||
Debug::writeToLog();
|
||||
if ( Debug::getPHPErrors() == 0 ) {
|
||||
Debug::clearBuffer();
|
||||
}
|
||||
} else {
|
||||
usleep($idle_sleep); //Can't see any CPU used when monitoring 'top' at these settings.
|
||||
$execution_time['idle'] += microtime( true ) - $loop_start_microtime;
|
||||
}
|
||||
|
||||
//If a worker is started and on its first loop jobs_executed=0, assume there is nothing to do and attempt to shutdown immediately if there are more than 1 processes running.
|
||||
// Its likely we were started from CRON every minute in that case, so no point in keeping processes running that aren't needed.
|
||||
if ( $execution_time['idle'] > $idle_timeout_shutdown || ( $i == 0 && $jobs_executed == 0 ) ) {
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Idle... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'] .' I: '. $i .' Lock File: '. $background_process_lock_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection );
|
||||
$execution_time['idle'] = 0;
|
||||
} else {
|
||||
//Since this loop is triggered multiple time per second, we need to jump through hoops to prevent the below from being triggered 10x in a row in the same second that idle first becomes active.
|
||||
if ( ( $idle_timeout_force_epoch == null || floor( microtime( true ) - $idle_timeout_force_epoch ) > 1 ) && (int)$execution_time['idle'] > 1 && ( (int)$execution_time['idle'] % $idle_timeout_force_check ) == 0 ) {
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Idle wait... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . (int)$execution_time['idle'] .' Mod: '. ( (int)$execution_time['idle'] % $idle_timeout_force_check ) .' I: '. $i .' Lock File: '. $background_process_lock_file_name, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
if ( isValidIdleRequirements() == false ) {
|
||||
Debug::text( 'PID: ' . $my_worker_pid . ' Idle wait invalid requirements... Jobs Executed: ' . $jobs_executed . ' Idle Time: ' . $execution_time['idle'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
shutdownQueueWorker( $sjqlf, $bg_lock_file, $jobs_executed, $execution_time, $my_worker_pid, $worker_db_connection );
|
||||
}
|
||||
|
||||
$force_job_check = true;
|
||||
$idle_timeout_force_epoch = $loop_start_microtime;
|
||||
}
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
} else {
|
||||
$current_system_load = Misc::getSystemLoad();
|
||||
if ( $current_system_load < Misc::getMaxSystemLoad() ) {
|
||||
$sjqlf = new SystemJobQueueListFactory();
|
||||
//When being called from cron, only launch new processes if we are not at the maximum number of workers already.
|
||||
// This is needed so if there is only one worker and its running a long task (ie: backup/DB purge) and all other workers stopped due to idle,
|
||||
// we need to launch another worker to process any new pending jobs that are created without having to wait for the long task to finish.
|
||||
// Also check PIDs to ensure processes are running too incase any crashed.
|
||||
$sjqlf->startWorkerProcess( __FILE__ );
|
||||
|
||||
//if ( $sjqlf->getRunningWorkerProcesses( true ) < 1 ) {
|
||||
// Debug::text( 'PID: ' . $my_worker_pid . ' Started from cron, spawning first worker process...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
// $sjqlf->startWorkerProcess( __FILE__ );
|
||||
//}
|
||||
} else {
|
||||
Debug::text( ' WARNING: System load too high, not spawning new queue worker process... Current Load: '. $current_system_load, __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
?>
|
96
maint/RemittanceAgencyEvent.php
Normal file
96
maint/RemittanceAgencyEvent.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Handle remittance agency events, and send out email reminders.
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] );
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
Debug::Text( 'PROCESSING Payroll Remittance Agency Event Reminders.', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( $c_obj->getStatus() != 30 ) {
|
||||
$praelf = new PayrollRemittanceAgencyEventListFactory();
|
||||
//Handle pay period/on hire/on termination event frequencies that have no dates (update them)
|
||||
$praelf->getByCompanyIdAndFrequencyIdAndDueDateIsNull( $c_obj->getId(), [ 1000, 90100, 90200, 90310 ] );
|
||||
if ( $praelf->getRecordCount() > 0 ) {
|
||||
Debug::Text( ' Payroll Remittance Agency Event Reminders for ' . $c_obj->getName() . ': ' . $praelf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
foreach ( $praelf as $prae_obj ) {
|
||||
if ( ( $prae_obj->getStatus() == 10 || $prae_obj->getStatus() == 15 ) ) {
|
||||
Debug::Text( 'Updating pay-period/on hire/on termination frequency dates for Event: ' . $prae_obj->getId(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
$prae_obj->setEnableRecalculateDates( true );
|
||||
if ( $prae_obj->isValid() ) {
|
||||
$prae_obj->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $praelf, $prae_obj );
|
||||
}
|
||||
|
||||
$praelf = new PayrollRemittanceAgencyEventListFactory();
|
||||
$praelf->getPendingReminder( $c_obj->getId() );
|
||||
if ( $praelf->getRecordCount() > 0 ) {
|
||||
Debug::Text( ' Found ' . $praelf->getRecordCount() . ' Payroll Remittance Agency Event Reminders for ' . $c_obj->getName(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
foreach ( $praelf as $prae_obj ) {
|
||||
if ( ( $prae_obj->getStatus() == 10 || $prae_obj->getStatus() == 15 ) && $prae_obj->getNextReminderDate() != '' ) {
|
||||
$prae_obj->sendNotificationReminder();
|
||||
//Need to track last reminder date so we don't spam every time this runs.
|
||||
$prae_obj->setLastReminderDate( time() );
|
||||
if ( $prae_obj->isValid() ) {
|
||||
$prae_obj->save();
|
||||
}
|
||||
} else {
|
||||
Debug::Text( ' Next reminder date is blank, or event is disabled...', __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
unset( $praelf, $prae_obj );
|
||||
} else {
|
||||
Debug::Text( 'No pending reminders found for ' . $c_obj->getName(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
62
maint/UpdateCurrencyRates.php
Normal file
62
maint/UpdateCurrencyRates.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Updates Currency Exchange Rates.
|
||||
* This file should run once a day.
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//Debug::setVerbosity(11);
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] );
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( in_array( $c_obj->getStatus(), [ 10, 20, 23 ] ) ) { //10=Active, 20=Hold, 23=Expired
|
||||
CurrencyFactory::updateCurrencyRates( $c_obj->getId() );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
104
maint/UserCount.php
Normal file
104
maint/UserCount.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Counts the total active/inactive/deleted users for each company once a day.
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
$cuclf = new CompanyUserCountListFactory();
|
||||
$cuclf->getActiveUsers();
|
||||
if ( $cuclf->getRecordCount() > 0 ) {
|
||||
foreach ( $cuclf as $cuc_obj ) {
|
||||
$user_counts[$cuc_obj->getColumn( 'company_id' )]['active'] = $cuc_obj->getColumn( 'total' );
|
||||
}
|
||||
}
|
||||
|
||||
$cuclf->getInActiveUsers();
|
||||
if ( $cuclf->getRecordCount() > 0 ) {
|
||||
foreach ( $cuclf as $cuc_obj ) {
|
||||
$user_counts[$cuc_obj->getColumn( 'company_id' )]['inactive'] = $cuc_obj->getColumn( 'total' );
|
||||
}
|
||||
}
|
||||
|
||||
$cuclf->getDeletedUsers();
|
||||
if ( $cuclf->getRecordCount() > 0 ) {
|
||||
foreach ( $cuclf as $cuc_obj ) {
|
||||
$user_counts[$cuc_obj->getColumn( 'company_id' )]['deleted'] = $cuc_obj->getColumn( 'total' );
|
||||
}
|
||||
}
|
||||
|
||||
$cuclf->StartTransaction();
|
||||
if ( isset( $user_counts ) && count( $user_counts ) > 0 ) {
|
||||
foreach ( $user_counts as $company_id => $user_count_arr ) {
|
||||
|
||||
$cucf = new CompanyUserCountFactory();
|
||||
$cucf->setCompany( $company_id );
|
||||
$cucf->setDateStamp( time() );
|
||||
if ( !isset( $user_count_arr['active'] ) ) {
|
||||
$user_count_arr['active'] = 0;
|
||||
}
|
||||
$cucf->setActiveUsers( $user_count_arr['active'] );
|
||||
|
||||
if ( !isset( $user_count_arr['inactive'] ) ) {
|
||||
$user_count_arr['inactive'] = 0;
|
||||
}
|
||||
$cucf->setInActiveUsers( $user_count_arr['inactive'] );
|
||||
|
||||
if ( !isset( $user_count_arr['deleted'] ) ) {
|
||||
$user_count_arr['deleted'] = 0;
|
||||
}
|
||||
$cucf->setDeletedUsers( $user_count_arr['deleted'] );
|
||||
|
||||
Debug::text( 'Company ID: ' . $company_id . ' Active: ' . $user_count_arr['active'] . ' InActive: ' . $user_count_arr['inactive'] . ' Deleted: ' . $user_count_arr['deleted'], __FILE__, __LINE__, __METHOD__, 10 );
|
||||
|
||||
if ( $cucf->isValid() ) {
|
||||
$cucf->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
$cuclf->CommitTransaction();
|
||||
|
||||
Debug::Display();
|
||||
?>
|
200
maint/calcExceptions.php
Normal file
200
maint/calcExceptions.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Calculate Exceptions for the previous day. This helps especially for
|
||||
* the "Unscheuled Absence" exception.
|
||||
*
|
||||
* Run this once a day. AFTER AddUserDate
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
//Debug::setVerbosity(5);
|
||||
|
||||
$execution_time = time();
|
||||
|
||||
$flags = [
|
||||
//Since this needs to calculate 'undertime_absence', it pretty much needs to calculate all other policies too.
|
||||
//Its less error prone if we calculate them all as well.
|
||||
'meal' => false,
|
||||
'undertime_absence' => true, //Required to properly handle undertime absences when no shifts were worked. See comments in CalculatePolicy->calculateUnderTimeAbsencePolicy()
|
||||
'break' => false,
|
||||
'holiday' => false,
|
||||
'schedule_absence' => false,
|
||||
'absence' => false,
|
||||
'regular' => false,
|
||||
'overtime' => false,
|
||||
'premium' => false,
|
||||
'accrual' => false,
|
||||
|
||||
'exception' => true,
|
||||
//Exception options
|
||||
'exception_premature' => true, //Calculates premature exceptions, this will automatically disable itself if based on the current time.
|
||||
'exception_future' => false, //Calculates exceptions in the future.
|
||||
|
||||
//Calculate policies for future dates.
|
||||
'future_dates' => false, //Calculates dates in the future.
|
||||
];
|
||||
|
||||
$clf = new CompanyListFactory();
|
||||
$clf->getByStatusID( [ 10, 20, 23 ], null, [ 'a.id' => 'asc' ] );
|
||||
$x = 0;
|
||||
if ( $clf->getRecordCount() > 0 ) {
|
||||
foreach ( $clf as $c_obj ) {
|
||||
if ( $c_obj->getStatus() != 30 ) {
|
||||
$company_start_time = microtime( true );
|
||||
Debug::text( 'Company: ' . $c_obj->getName() . ' (' . $c_obj->getId() . ')', __FILE__, __LINE__, __METHOD__, 5 );
|
||||
|
||||
TTDate::setTimeZone(); //Reset timezone to system defaults for each company.
|
||||
|
||||
//Recalculate at least the last two days.
|
||||
$start_date = TTDate::getMiddleDayEpoch( ( $execution_time - ( 86400 * 2 ) ) );
|
||||
$end_date = TTDate::getMiddleDayEpoch( ( $execution_time - 86400 ) );
|
||||
Debug::text( 'X: ' . $x . ' Start Date: ' . TTDate::getDate( 'DATE+TIME', $start_date ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $end_date ), __FILE__, __LINE__, __METHOD__, 5 );
|
||||
|
||||
//Get the last time cron ran this script.
|
||||
$cjlf = new CronJobListFactory();
|
||||
$cjlf->getByName( 'calcExceptions' );
|
||||
if ( $cjlf->getRecordCount() > 0 ) {
|
||||
foreach ( $cjlf as $cj_obj ) {
|
||||
$tmp_start_date = TTDate::getMiddleDayEpoch( $cj_obj->getLastRunDate() );
|
||||
if ( $tmp_start_date != '' && $tmp_start_date < $start_date ) {
|
||||
$start_date = $tmp_start_date;
|
||||
Debug::text( ' CRON Job hasnt run in more then 48hrs, reducing Start Date to: ' . TTDate::getDate( 'DATE+TIME', $start_date ), __FILE__, __LINE__, __METHOD__, 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $cjlf, $cj_obj, $tmp_start_date );
|
||||
|
||||
//Get maximum shift time for each pay period schedule, so we know how far back
|
||||
//we have to recalculate days at the minimum.
|
||||
$ppslf = new PayPeriodScheduleListFactory();
|
||||
$ppslf->getByCompanyId( $c_obj->getId() );
|
||||
if ( $ppslf->getRecordCount() > 0 ) {
|
||||
foreach ( $ppslf as $pps_obj ) {
|
||||
$tmp_start_date = TTDate::getMiddleDayEpoch( $execution_time ) - $pps_obj->getMaximumShiftTime();
|
||||
if ( $tmp_start_date != '' && $tmp_start_date < $start_date ) {
|
||||
$start_date = $tmp_start_date;
|
||||
Debug::text( ' Maximum Shift Time is greater then 48hrs, reducing Start Date to: ' . TTDate::getDate( 'DATE+TIME', $start_date ), __FILE__, __LINE__, __METHOD__, 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $ppslf, $pps_obj, $tmp_start_date );
|
||||
|
||||
//Get earliest pre_mature exception in a NON-closed pay period.
|
||||
//Cap the limit at going back 90 days. This prevents the case where they open pay periods in the previous year and forget to close them.
|
||||
// If that happens we don't want to start trying to recalculate pay periods from a year ago.
|
||||
$elf = new ExceptionListFactory();
|
||||
$elf->getByCompanyIDAndTypeAndPayPeriodStatusAndMinimumDateStamp( $c_obj->getId(), 5, [ 10, 12, 15, 30 ], ( $end_date - ( 86400 * 90 ) ), 1, null, null, [ 'a.date_stamp' => 'asc' ] ); //Limit 1
|
||||
if ( $elf->getRecordCount() > 0 ) {
|
||||
foreach ( $elf as $e_obj ) {
|
||||
$tmp_start_date = TTDate::getMiddleDayEpoch( $e_obj->getDateStamp() );
|
||||
if ( $tmp_start_date != '' && $tmp_start_date < $start_date ) {
|
||||
$start_date = $tmp_start_date;
|
||||
Debug::text( ' Pre-Mature exceptions occur before start date, reducing to: ' . TTDate::getDate( 'DATE+TIME', $start_date ) . '(' . $e_obj->getId() . ')', __FILE__, __LINE__, __METHOD__, 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
unset( $elf, $e_obj, $tmp_start_date );
|
||||
|
||||
$date_arr = TTDate::getDateArray( $start_date, $end_date );
|
||||
if ( is_array( $date_arr ) ) {
|
||||
//Loop over all employees
|
||||
$ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */
|
||||
$ulf->getByCompanyIdAndStatus( $c_obj->getId(), 10 ); //Only active employees
|
||||
if ( $ulf->getRecordCount() > 0 ) {
|
||||
$system_job_queue_batch_id = TTUUID::generateUUID();
|
||||
$i = 0;
|
||||
foreach ( $ulf as $u_obj ) {
|
||||
//Timezone is set in calculate() function below.
|
||||
|
||||
//Recalculate system time, and exceptions for the day.
|
||||
//Because if its a Monday, it will also recalculate the rest of the days in the week.
|
||||
//Shouldn't be a big deal though.
|
||||
//This isn't needed, since we now do it in AddRecurringScheduleShift, so dock time is
|
||||
//applied at the beginning of the day.
|
||||
//The problem is that AddRecurringScheduleShift does it, then for the entire day someone with
|
||||
//a dock policy shows up as dock time. Some users have complained about this a few times.
|
||||
|
||||
//Reason for doing two days ago is that if someone starts a shift at 11pm, but doesn't end it in
|
||||
//time, it still needs to be re-calculated a day later.
|
||||
//Could maybe get around this by getting all punches of yesterday, and getting their date_ids
|
||||
//and just recalculating those.
|
||||
|
||||
//Enable pre-mature exceptions if we're recalculating just one day ago.
|
||||
//Problem is a late shift on say Monday: 2:00PM to 11:00PM won't trigger the exception at 1AM the next day,
|
||||
//but by 1AM the following day (2days later) its too late and emails are disabled if enable_premature_exceptions are disabled.
|
||||
//**With new CalculatePolicy code we can calculate multiple days in a single pass, so always enable pre-mature exceptions, and they will be disabled automatically in CalculatePolicy if necessary.
|
||||
Debug::text( $x . ': (' . $i . '). User: ' . $u_obj->getID() . ' Start Date: ' . TTDate::getDate( 'DATE+TIME', $start_date ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $end_date ), __FILE__, __LINE__, __METHOD__, 5 );
|
||||
|
||||
if ( !isset( $config_vars['other']['enable_job_queue'] ) || $config_vars['other']['enable_job_queue'] == true ) {
|
||||
SystemJobQueue::Add( TTi18n::getText( 'ReCalculating Exceptions' ), $system_job_queue_batch_id, 'CalculatePolicy', 'reCalculateForJobQueue', [ $u_obj->getID(), 'calcExceptions', $date_arr ], 120 );
|
||||
} else {
|
||||
$transaction_function = function () use ( $u_obj, $flags, $date_arr ) {
|
||||
$cp = TTNew( 'CalculatePolicy' ); /** @var CalculatePolicy $cp */
|
||||
$cp->setFlag( $flags );
|
||||
$cp->setUserObject( $u_obj );
|
||||
$cp->getUserObject()->setTransactionMode( 'REPEATABLE READ' );
|
||||
$cp->addPendingCalculationDate( $date_arr );
|
||||
$cp->calculate(); //This sets timezone itself.
|
||||
$cp->Save();
|
||||
$cp->getUserObject()->setTransactionMode(); //Back to default isolation level.
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$u_obj->RetryTransaction( $transaction_function, 2, 3 ); //Set retry_sleep this fairly high so real-time punches have a chance to get saved between retries.
|
||||
}
|
||||
|
||||
$i++; //User Counter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$x++; //Company counter
|
||||
|
||||
Debug::text( 'Company: ' . $c_obj->getName() . '(' . $c_obj->getId() . ') Finished In: ' . ( microtime( true ) - $company_start_time ) . 's', __FILE__, __LINE__, __METHOD__, 5 );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
127
maint/calcQuickExceptions.php
Normal file
127
maint/calcQuickExceptions.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Calculate Exceptions for the previous day. This helps especially for
|
||||
* the "Unscheuled Absence" exception.
|
||||
*
|
||||
* Run this once a day. AFTER AddUserDate
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
if ( !isset( $config_vars['other']['enable_job_queue'] ) || $config_vars['other']['enable_job_queue'] == true ) {
|
||||
exit( 0 ); //Switched to real-time recalculation of timesheets triggered from ScheduleFactory::handleFutureTimeSheetRecalculationForExceptions(), PunchFactory::handleFutureTimeSheetRecalculationForExceptions()
|
||||
}
|
||||
|
||||
//Debug::setVerbosity(5);
|
||||
$execution_time = time();
|
||||
|
||||
|
||||
//Calculate exceptions just for today and yesterday, because some shifts may start late in the day and need to be handled first thing in the morning.
|
||||
//Make sure we also go one day in the future too, since the servers can be PST and if its 11:00PM, it will stop at midnight for that day, so
|
||||
//shifts that would have already started in a different timezone (say EST) will not receive exceptions until we have moved into the next day for PST (3hrs late)
|
||||
$start_date = TTDate::getBeginDayEpoch( ( TTDate::getMiddleDayEpoch( $execution_time ) - 86400 ) );
|
||||
$end_date = TTDate::getEndDayEpoch( ( TTDate::getMiddleDayEpoch( $execution_time ) + 86400 ) );
|
||||
|
||||
$flags = [
|
||||
'meal' => false,
|
||||
'undertime_absence' => false,
|
||||
'break' => false,
|
||||
'holiday' => false,
|
||||
'schedule_absence' => false,
|
||||
'absence' => false,
|
||||
'regular' => false,
|
||||
'overtime' => false,
|
||||
'premium' => false,
|
||||
'accrual' => false,
|
||||
|
||||
'exception' => true,
|
||||
//Exception options
|
||||
'exception_premature' => true, //Calculates premature exceptions
|
||||
'exception_future' => false, //Calculates exceptions in the future.
|
||||
|
||||
//Calculate policies for future dates.
|
||||
'future_dates' => false, //Calculates dates in the future.
|
||||
];
|
||||
|
||||
$udtlf = new UserDateTotalListFactory();
|
||||
//Use optimized query to speed this process up significantly.
|
||||
$udtlf->getMidDayExceptionsByStartDateAndEndDateAndPayPeriodStatus( $start_date, $end_date, [ 10, 12, 15, 30 ] );
|
||||
Debug::text( ' calcQuickExceptions: Start Date: ' . TTDate::getDate( 'DATE+TIME', $start_date ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', $end_date ) . ' Rows: ' . $udtlf->getRecordCount(), __FILE__, __LINE__, __METHOD__, 5 );
|
||||
if ( $udtlf->getRecordCount() > 0 ) {
|
||||
$system_job_queue_batch_id = TTUUID::generateUUID();
|
||||
|
||||
$i = 0;
|
||||
foreach ( $udtlf as $udt_obj ) {
|
||||
Debug::text( '(' . $i . '). User: ' . $udt_obj->getUser() . ' Start Date: ' . TTDate::getDate( 'DATE+TIME', strtotime( $udt_obj->getColumn( 'start_date' ) ) ) . ' End Date: ' . TTDate::getDate( 'DATE+TIME', strtotime( $udt_obj->getColumn( 'end_date' ) ) ), __FILE__, __LINE__, __METHOD__, 5 );
|
||||
|
||||
// See also: ScheduleFactory::handleFutureTimeSheetRecalculationForExceptions(), PunchFactory::handleFutureTimeSheetRecalculationForExceptions()
|
||||
//SystemJobQueue::Add( TTi18n::getText( 'ReCalculating Quick Exceptions' ), $system_job_queue_batch_id, 'CalculatePolicy', 'reCalculateForJobQueue', [ $udt_obj->getUser(), 'calcQuickExceptions', strtotime( $udt_obj->getColumn( 'start_date' ) ), strtotime( $udt_obj->getColumn( 'end_date' ) ) ], 110 );
|
||||
|
||||
if ( is_object( $udt_obj->getUserObject() ) ) {
|
||||
//Calculate pre-mature exceptions, so pre-mature Missing Out Punch exceptions aren't made active until they are ready.
|
||||
//Don't calculate future exceptions though.
|
||||
$transaction_function = function () use ( $udt_obj, $flags ) {
|
||||
$cp = TTNew( 'CalculatePolicy' ); /** @var CalculatePolicy $cp */
|
||||
$cp->setFlag( $flags );
|
||||
$cp->setUserObject( $udt_obj->getUserObject() );
|
||||
$cp->getUserObject()->setTransactionMode( 'REPEATABLE READ' );
|
||||
$cp->addPendingCalculationDate( strtotime( $udt_obj->getColumn( 'start_date' ) ), strtotime( $udt_obj->getColumn( 'end_date' ) ) );
|
||||
$cp->calculate( strtotime( $udt_obj->getColumn( 'start_date' ) ) ); //This sets timezone itself.
|
||||
$cp->Save();
|
||||
$cp->getUserObject()->setTransactionMode(); //Back to default isolation level.
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$udt_obj->RetryTransaction( $transaction_function, 2, 3 ); //Set retry_sleep this fairly high so real-time punches have a chance to get saved between retries.
|
||||
|
||||
} else {
|
||||
Debug::Arr( $udt_obj->getUserObject(), 'ERROR: Invalid UserObject: User ID: ' . $udt_obj->getUser(), __FILE__, __LINE__, __METHOD__, 10 );
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
Debug::text( ' calcQuickExceptions: Done', __FILE__, __LINE__, __METHOD__, 5 );
|
||||
?>
|
109
maint/cron.php
Normal file
109
maint/cron.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php /** @noinspection PhpUndefinedVariableInspection */
|
||||
/*********************************************************************************
|
||||
*
|
||||
* TimeTrex is a Workforce Management program developed by
|
||||
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License version 3 as published by
|
||||
* the Free Software Foundation with the addition of the following permission
|
||||
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
|
||||
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
|
||||
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*
|
||||
*
|
||||
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
|
||||
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
|
||||
*
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
|
||||
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||
* the words "Powered by TimeTrex".
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/*
|
||||
* Cron replica
|
||||
* Run this script every minute from the real cron.
|
||||
*
|
||||
*/
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'global.inc.php' );
|
||||
require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'CLI.inc.php' );
|
||||
|
||||
if ( isset( $config_vars['other']['installer_enabled'] ) && $config_vars['other']['installer_enabled'] == true ) {
|
||||
Debug::text( 'CRON: Installer is enabled, skipping cron jobs for now...', __FILE__, __LINE__, __METHOD__, 0 );
|
||||
} else if ( isset( $config_vars['other']['down_for_maintenance'] ) && $config_vars['other']['down_for_maintenance'] == true ) {
|
||||
Debug::text( 'CRON: System is down for maintenance, skipping cron jobs for now...', __FILE__, __LINE__, __METHOD__, 0 );
|
||||
} else {
|
||||
if ( isset( $config_vars['path']['php_cli'] ) && $config_vars['path']['php_cli'] != '' ) {
|
||||
//$current_epoch = strtotime('28-Mar-08 1:30 PM');
|
||||
$current_epoch = TTDate::getTime();
|
||||
|
||||
$executed_jobs = 0;
|
||||
|
||||
if ( strpos( $config_vars['database']['host'], ',' ) !== false ) {
|
||||
$db = $db->getConnection( 'write' ); //Force connection to master/write database, so status_id can't get out-of-sync and cause duplicate jobs to run at the same time.
|
||||
}
|
||||
|
||||
$cjlf = new CronJobListFactory();
|
||||
$job_arr = $cjlf->getArrayByListFactory( $cjlf->getAll() );
|
||||
if ( is_array( $job_arr ) ) {
|
||||
$total_jobs = count( $job_arr );
|
||||
|
||||
foreach ( $job_arr as $job_id => $job_name ) {
|
||||
//Get each cronjob row again individually incase the status has changed, since we wait for execution to occur in some cases and these can be relatively long running.
|
||||
$cjlf = new CronJobListFactory();
|
||||
$cjlf->getById( $job_id ); //Let Execute determine if job is running or not so it can find orphans. -- This should also *not* be cached in case the same job is run multiple times due to a invalid cached status_id.
|
||||
if ( $cjlf->getRecordCount() > 0 ) {
|
||||
foreach ( $cjlf as $cjf_obj ) {
|
||||
//Debug::text('Checking if Job ID: '. $job_id .' is scheduled to run...', __FILE__, __LINE__, __METHOD__, 0);
|
||||
if ( $cjf_obj->isScheduledToRun( $current_epoch ) == true ) {
|
||||
$executed_jobs++;
|
||||
$cjf_obj->Execute( $config_vars['path']['php_cli'], dirname( __FILE__ ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$total_jobs = 0;
|
||||
}
|
||||
|
||||
//Make sure a QueueWorker is launched each time this is executed.
|
||||
// **EVEN IF JOB QUEUE IS DISABLED, AS THERE COULD BE LATENT JOBS STILL IN THE QUEUE THAT NEED TO BE PROCESSED IF THE JOB QUEUE WAS DISABLED**
|
||||
// Regardless if there are any jobs executed, since the QueueWorker might be used for real-time jobs trigger by the UI.
|
||||
$bp = new BackgroundProcess();
|
||||
$command = '"' . $config_vars['path']['php_cli'] . '" "'. dirname( __FILE__ ) . DIRECTORY_SEPARATOR .'QueueWorker.php"'; //Make sure we use full path, otherwise the file might not be found on Windows especially.
|
||||
$bp->BackgroundExec( Misc::getEnvironmentVariableConfigFile() . $command ); //Skips all max process checks and just launch the queue worker immediately in the background.
|
||||
unset( $bp, $command );
|
||||
|
||||
echo "NOTE: Jobs are scheduled to run at specific times each day, therefore it is normal for only some jobs to be queued each time this file is run.\n";
|
||||
echo "Jobs Queued For Running: $executed_jobs of $total_jobs\n";
|
||||
Debug::text( 'CRON: Jobs Queued For Running: ' . $executed_jobs . ' of ' . $total_jobs, __FILE__, __LINE__, __METHOD__, 0 );
|
||||
} else {
|
||||
echo "ERROR: timetrex.ini.php does not define 'php_cli' option in the [path] section. Unable to run maintenance jobs!\n";
|
||||
Debug::text( 'PHP_CLI not defined in timetrex.ini.php file.', __FILE__, __LINE__, __METHOD__, 0 );
|
||||
}
|
||||
|
||||
//Save file to log directory with the last executed date, so we know if the CRON daemon is actually calling us.
|
||||
$file_name = $config_vars['path']['log'] . DIRECTORY_SEPARATOR . 'timetrex_cron_last_executed.log';
|
||||
@file_put_contents( $file_name, TTDate::getDate( 'DATE+TIME', time() ) . "\n" );
|
||||
}
|
||||
?>
|
Reference in New Issue
Block a user