596 lines
26 KiB
PHP
596 lines
26 KiB
PHP
|
<?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".
|
||
|
*
|
||
|
********************************************************************************/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @package Core
|
||
|
*/
|
||
|
class Wage {
|
||
|
var $user_id = null;
|
||
|
var $pay_period_id = null;
|
||
|
var $advance = false;
|
||
|
|
||
|
var $user_date_total_arr = null;
|
||
|
|
||
|
var $user_obj = null;
|
||
|
var $user_tax_obj = null;
|
||
|
var $user_wage_obj = null;
|
||
|
var $user_pay_period_total_obj = null;
|
||
|
var $pay_stub_entry_account_link_obj = null;
|
||
|
|
||
|
var $pay_period_obj = null;
|
||
|
var $pay_period_schedule_obj = null;
|
||
|
|
||
|
var $labor_standard_obj = null;
|
||
|
var $holiday_obj = null;
|
||
|
|
||
|
/**
|
||
|
* Wage constructor.
|
||
|
* @param string $user_id UUID
|
||
|
* @param string $pay_period_id UUID
|
||
|
*/
|
||
|
function __construct( $user_id, $pay_period_id ) {
|
||
|
$this->user_id = $user_id;
|
||
|
$this->pay_period_id = $pay_period_id;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return null
|
||
|
*/
|
||
|
function getUser() {
|
||
|
return $this->user_id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return null
|
||
|
*/
|
||
|
function getPayPeriod() {
|
||
|
return $this->pay_period_id;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
function getAdvance() {
|
||
|
if ( isset( $this->advance ) ) {
|
||
|
return $this->advance;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $bool
|
||
|
* @return bool
|
||
|
*/
|
||
|
function setAdvance( $bool ) {
|
||
|
$this->advance = $bool;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//Because this class doesn't extend the original Factory class, we have to duplicate the getGenericObject() code here.
|
||
|
|
||
|
/**
|
||
|
* @return bool|null
|
||
|
*/
|
||
|
function getUserObject() {
|
||
|
if ( isset( $this->user_obj ) && is_object( $this->user_obj ) && $this->getUser() == $this->user_obj->getID() ) {
|
||
|
return $this->user_obj;
|
||
|
} else {
|
||
|
$lf = TTnew( 'UserListFactory' ); /** @var UserListFactory $lf */
|
||
|
$lf->getById( $this->getUser() );
|
||
|
if ( $lf->getRecordCount() == 1 ) {
|
||
|
$this->user_obj = $lf->getCurrent();
|
||
|
|
||
|
return $this->user_obj;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|null
|
||
|
*/
|
||
|
function getPayPeriodObject() {
|
||
|
if ( isset( $this->pay_period_obj ) && is_object( $this->pay_period_obj ) && $this->getPayPeriod() == $this->pay_period_obj->getID() ) {
|
||
|
return $this->pay_period_obj;
|
||
|
} else {
|
||
|
$lf = TTnew( 'PayPeriodListFactory' ); /** @var PayPeriodListFactory $lf */
|
||
|
$lf->getById( $this->getPayPeriod() );
|
||
|
if ( $lf->getRecordCount() == 1 ) {
|
||
|
$this->pay_period_obj = $lf->getCurrent();
|
||
|
|
||
|
return $this->pay_period_obj;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|null
|
||
|
*/
|
||
|
function getPayPeriodScheduleObject() {
|
||
|
$pay_period_schedule_id = TTUUID::getZeroID();
|
||
|
if ( is_object( $this->getPayPeriodObject() ) ) {
|
||
|
$pay_period_schedule_id = $this->getPayPeriodObject()->getPayPeriodSchedule();
|
||
|
}
|
||
|
if ( isset( $this->pay_period_schedule_obj ) && is_object( $this->pay_period_schedule_obj ) && $pay_period_schedule_id == $this->pay_period_schedule_obj->getID() ) {
|
||
|
return $this->pay_period_schedule_obj;
|
||
|
} else {
|
||
|
$lf = TTnew( 'PayPeriodScheduleListFactory' ); /** @var PayPeriodScheduleListFactory $lf */
|
||
|
$lf->getById( $pay_period_schedule_id );
|
||
|
if ( $lf->getRecordCount() == 1 ) {
|
||
|
$this->pay_period_schedule_obj = $lf->getCurrent();
|
||
|
|
||
|
return $this->pay_period_schedule_obj;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool|null
|
||
|
*/
|
||
|
function getPayStubEntryAccountLinkObject() {
|
||
|
$company_id = TTUUID::getZeroID();
|
||
|
if ( is_object( $this->getUserObject() ) ) {
|
||
|
$company_id = $this->getUserObject()->getCompany();
|
||
|
}
|
||
|
|
||
|
if ( isset( $this->pay_stub_entry_account_link_obj ) && is_object( $this->pay_stub_entry_account_link_obj ) && $company_id == $this->pay_stub_entry_account_link_obj->getCompany() ) {
|
||
|
return $this->pay_stub_entry_account_link_obj;
|
||
|
} else {
|
||
|
$lf = TTnew( 'PayStubEntryAccountLinkListFactory' ); /** @var PayStubEntryAccountLinkListFactory $lf */
|
||
|
$lf->getByCompanyId( $company_id );
|
||
|
if ( $lf->getRecordCount() == 1 ) {
|
||
|
$this->pay_stub_entry_account_link_obj = $lf->getCurrent();
|
||
|
|
||
|
return $this->pay_stub_entry_account_link_obj;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $seconds
|
||
|
* @param $rate
|
||
|
* @return int|string
|
||
|
*/
|
||
|
function getWage( $seconds, $rate ) {
|
||
|
if ( $seconds == '' || empty( $seconds ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ( $rate == '' || empty( $rate ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return bcmul( TTDate::getHours( $seconds ), $rate );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param object $user_wage_obj
|
||
|
* @return string
|
||
|
*/
|
||
|
function getMaximumPayPeriodWage( $user_wage_obj ) {
|
||
|
if ( is_object( $user_wage_obj ) && is_object( $this->getPayPeriodScheduleObject() ) && $this->getPayPeriodScheduleObject()->getAnnualPayPeriods() > 0 ) {
|
||
|
$maximum_pay_period_wage = bcdiv( $user_wage_obj->getAnnualWage(), $this->getPayPeriodScheduleObject()->getAnnualPayPeriods() );
|
||
|
Debug::text( 'Absolute Maximum Pay Period (NO Advance): Wage: ' . $maximum_pay_period_wage . ' User Wage ID: ' . $user_wage_obj->getId() . ' Annual Wage: ' . $user_wage_obj->getAnnualWage() . ' Annual Pay Periods: ' . $this->getPayPeriodScheduleObject()->getAnnualPayPeriods(), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
} else {
|
||
|
Debug::text( 'WARNING: Pay Period Schedule does not exist, or annual pay periods is 0...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$maximum_pay_period_wage = 0;
|
||
|
}
|
||
|
|
||
|
return $maximum_pay_period_wage;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return string
|
||
|
*/
|
||
|
function getPayStubAmendmentEarnings() {
|
||
|
//Get pay stub amendments here.
|
||
|
$psalf = TTnew( 'PayStubAmendmentListFactory' ); /** @var PayStubAmendmentListFactory $psalf */
|
||
|
|
||
|
if ( $this->getAdvance() == true ) {
|
||
|
//For advances, any PS amendment effective BEFORE the advance end date is considered in full.
|
||
|
//Any AFTER the advance end date, is considered half.
|
||
|
|
||
|
//$pay_period_end_date = $this->getPayPeriodObject()->getAdvanceEndDate();
|
||
|
$advance_pos_sum = $psalf->getAmountSumByUserIdAndTypeIdAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 10, true, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getAdvanceEndDate() );
|
||
|
Debug::text( 'Pay Stub Amendment Advance Earnings: ' . $advance_pos_sum, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$full_pos_sum = $psalf->getAmountSumByUserIdAndTypeIdAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 10, true, $this->getPayPeriodObject()->getAdvanceEndDate(), $this->getPayPeriodObject()->getEndDate() );
|
||
|
Debug::text( 'Pay Stub Amendment Full Earnings: ' . $full_pos_sum, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
//Take the full amount of PS amendments BEFORE the advance end date, and half of any AFTER the advance end date.
|
||
|
//$pos_sum = $advance_pos_sum + ($full_pos_sum / 2);
|
||
|
$pos_sum = bcadd( $advance_pos_sum, bcdiv( $full_pos_sum, 2 ) );
|
||
|
} else {
|
||
|
$pos_sum = $psalf->getAmountSumByUserIdAndTypeIdAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 10, true, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate() );
|
||
|
}
|
||
|
//$neg_sum = $psalf->getAmountSumByUserIdAndTypeIdAndTaxExemptAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 20, FALSE, TRUE, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate() )*-1;
|
||
|
|
||
|
Debug::text( 'Pay Stub Amendment Total Earnings: ' . $pos_sum, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return $pos_sum;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return string
|
||
|
*/
|
||
|
function getPayStubAmendmentDeductions() {
|
||
|
//Get pay stub amendments here.
|
||
|
$psalf = TTnew( 'PayStubAmendmentListFactory' ); /** @var PayStubAmendmentListFactory $psalf */
|
||
|
|
||
|
if ( $this->getAdvance() == true ) {
|
||
|
//For advances, any PS amendment effective BEFORE the advance end date is considered in full.
|
||
|
//Any AFTER the advance end date, is considered half.
|
||
|
|
||
|
//$pay_period_end_date = $this->getPayPeriodObject()->getAdvanceEndDate();
|
||
|
$advance_neg_sum = $psalf->getAmountSumByUserIdAndTypeIdAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 20, true, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getAdvanceEndDate() );
|
||
|
Debug::text( 'Pay Stub Amendment Advance Deductions: ' . $advance_neg_sum, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$full_neg_sum = $psalf->getAmountSumByUserIdAndTypeIdAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 20, true, $this->getPayPeriodObject()->getAdvanceEndDate(), $this->getPayPeriodObject()->getEndDate() );
|
||
|
Debug::text( 'Pay Stub Amendment Full Deductions: ' . $full_neg_sum, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
//Take the full amount of PS amendments BEFORE the advance end date, and half of any AFTER the advance end date.
|
||
|
//$neg_sum = $advance_neg_sum + ($full_neg_sum / 2);
|
||
|
$neg_sum = bcadd( $advance_neg_sum, bcdiv( $full_neg_sum, 2 ) );
|
||
|
} else {
|
||
|
//$pay_period_end_date =
|
||
|
$neg_sum = $psalf->getAmountSumByUserIdAndTypeIdAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 20, true, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate() );
|
||
|
}
|
||
|
//$neg_sum = $psalf->getAmountSumByUserIdAndTypeIdAndTaxExemptAndAuthorizedAndStartDateAndEndDate( $this->getUser(), 20, FALSE, TRUE, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate() )*-1;
|
||
|
|
||
|
|
||
|
Debug::text( 'Pay Stub Amendment Total Deductions: ' . $neg_sum, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return bcmul( $neg_sum, -1 );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return int
|
||
|
*/
|
||
|
function getRawGrossWage() {
|
||
|
$wage = 0;
|
||
|
|
||
|
$udt_arr = $this->getUserDateTotalArray();
|
||
|
if ( isset( $udt_arr['entries'] ) && count( $udt_arr['entries'] ) > 0 ) {
|
||
|
foreach ( $udt_arr['entries'] as $udt ) {
|
||
|
if ( isset( $udt['amount'] ) ) {
|
||
|
$wage += $udt['amount'];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Debug::text( 'Raw Gross Wage: ' . $wage, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return $wage;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return int
|
||
|
*/
|
||
|
function getGrossWage() {
|
||
|
|
||
|
$wage = $this->getRawGrossWage();
|
||
|
|
||
|
Debug::text( 'Gross Wage (NOT incl amendments) $' . $wage, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return $wage;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array|bool|null
|
||
|
*/
|
||
|
function getUserDateTotalArray() {
|
||
|
if ( isset( $this->user_date_total_arr ) ) {
|
||
|
return $this->user_date_total_arr;
|
||
|
}
|
||
|
|
||
|
//If the user date total array isn't set, set it now, and return its value.
|
||
|
return $this->setUserDateTotalArray();
|
||
|
//return FALSE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array|bool
|
||
|
*/
|
||
|
function setUserDateTotalArray() {
|
||
|
$calculate_salary = false;
|
||
|
|
||
|
$dock_absence_time = 0;
|
||
|
$paid_absence_time = 0;
|
||
|
$dock_absence_amount = 0;
|
||
|
$paid_absence_amount = 0;
|
||
|
$prev_wage_effective_date = 0;
|
||
|
$paid_absence_amount_arr = [];
|
||
|
$reduce_salary_absence_amount_arr = [];
|
||
|
$salary_regular_time = [];
|
||
|
$dock_absence_amount_arr = [];
|
||
|
$ret_arr = [];
|
||
|
|
||
|
//Loop through unique UserDateTotal rows... Adding entries to pay stubs.
|
||
|
$udtlf = TTnew( 'UserDateTotalListFactory' ); /** @var UserDateTotalListFactory $udtlf */
|
||
|
$udtlf->getByUserIdAndPayPeriodIdAndEndDate( $this->getUser(), $this->getPayPeriod(), $this->getPayPeriodObject()->getEndDate() );
|
||
|
if ( $udtlf->getRecordCount() > 0 ) {
|
||
|
foreach ( $udtlf as $udt_obj ) {
|
||
|
Debug::text( 'User Total Row... Object Type: ' . $udt_obj->getObjectType() . ' PayCode ID: ' . $udt_obj->getPayCode() . ' Amount: ' . $udt_obj->getTotalTimeAmount() . ' Hourly Rate: ' . $udt_obj->getColumn( 'hourly_rate' ) . ' Pay Code Type: ' . $udt_obj->getColumn( 'pay_code_type_id' ) . ' User Wage ID: ' . $udt_obj->getColumn( 'user_wage_id' ), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
if ( $udt_obj->getColumn( 'pay_code_type_id' ) == 10 ) { //Paid
|
||
|
if ( $udt_obj->getObjectType() == 25 ) { //Absence
|
||
|
Debug::text( 'User Total Row... Absence Time: ' . $udt_obj->getTotalTime(), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
if ( is_object( $udt_obj->getPayCodeObject() )
|
||
|
&& ( $udt_obj->getPayCodeObject()->getType() == 10 || $udt_obj->getPayCodeObject()->getType() == 12 )
|
||
|
&& $udt_obj->getPayCodeObject()->getPayStubEntryAccountID() != '' ) { //Paid
|
||
|
Debug::text( 'Paid Absence Time: ' . $udt_obj->getTotalTime(), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$pay_stub_entry = $udt_obj->getPayCodeObject()->getPayStubEntryAccountID();
|
||
|
$total_time = $udt_obj->getTotalTime();
|
||
|
$rate = $udt_obj->getColumn( 'hourly_rate' );
|
||
|
$amount = $udt_obj->getTotalTimeAmount();
|
||
|
|
||
|
//Debug::text('Paid Absence Info: '. $udt_obj->getTotalTime(), __FILE__, __LINE__, __METHOD__, 10);
|
||
|
Debug::text( 'cPay Stub Entry Account ID: ' . $pay_stub_entry . ' Amount: ' . $amount . ' Rate: ' . $rate, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$paid_absence_time = bcadd( $paid_absence_time, $udt_obj->getTotalTime() );
|
||
|
$paid_absence_amount = bcadd( $paid_absence_amount, $amount );
|
||
|
|
||
|
//Make sure we add the amount below. Incase there are two or more
|
||
|
//entries for a paid absence in the same user_wage_id on one pay stub.
|
||
|
if ( !isset( $paid_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] ) ) {
|
||
|
$paid_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] = 0;
|
||
|
}
|
||
|
$paid_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] = bcadd( $paid_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )], $amount );
|
||
|
|
||
|
//Some paid absences are over and above employees salary, so we need to track them separately.
|
||
|
//So we only reduce the salary of the amount of regular paid absences, not "Paid (Above Salary)" absences.
|
||
|
if ( !isset( $reduce_salary_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] ) ) {
|
||
|
$reduce_salary_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] = 0;
|
||
|
}
|
||
|
if ( $udt_obj->getColumn( 'pay_code_type_id' ) == 10 ) {
|
||
|
$reduce_salary_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] = bcadd( $reduce_salary_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )], $amount );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
//Check if they are a salary user...
|
||
|
//Use WORKED time to calculate regular time. Not just regular time.
|
||
|
//user_wage_id is only needed for default wages, so it doesn't take into account pay formulas at all.
|
||
|
if ( $udt_obj->getColumn( 'user_wage_type_id' ) > 10 ) { //Salaried
|
||
|
//Salary
|
||
|
Debug::text( 'Strict Salary Wage: Reduce Regular Pay By: Dock Time: ' . $dock_absence_time . ' and Paid Absence: ' . $paid_absence_time, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$calculate_salary = true;
|
||
|
|
||
|
if ( !isset( $salary_regular_time[$udt_obj->getColumn( 'user_wage_id' )] ) ) {
|
||
|
$salary_regular_time[$udt_obj->getColumn( 'user_wage_id' )] = 0;
|
||
|
}
|
||
|
|
||
|
//Only include regular time units in salary calculation.
|
||
|
if ( $udt_obj->getObjectType() == 20 ) { //Regular Time
|
||
|
$salary_regular_time[$udt_obj->getColumn( 'user_wage_id' )] += $udt_obj->getTotalTime();
|
||
|
}
|
||
|
} else {
|
||
|
//Hourly
|
||
|
Debug::text( 'Hourly Wage', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$pay_stub_entry = $udt_obj->getPayCodeObject()->getPayStubEntryAccountId();
|
||
|
$total_time = $udt_obj->getTotalTime();
|
||
|
$rate = $udt_obj->getColumn( 'hourly_rate' );
|
||
|
$amount = $udt_obj->getTotalTimeAmount();
|
||
|
Debug::text( 'aPay Stub Entry Account ID: ' . $pay_stub_entry . ' Amount: ' . $amount .' Rate: '. $rate, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
}
|
||
|
} else if ( $udt_obj->getColumn( 'pay_code_type_id' ) == 12 ) { //Paid Above
|
||
|
//This is typically but not always overtime/premium time.
|
||
|
if ( is_object( $udt_obj->getPayCodeObject() ) && $udt_obj->getColumn( 'hourly_rate' ) != 0 ) {
|
||
|
Debug::text( 'Paid (Above Salary) Time... Rate: ' . $udt_obj->getColumn( 'hourly_rate' ), __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$pay_stub_entry = $udt_obj->getPayCodeObject()->getPayStubEntryAccountId();
|
||
|
$total_time = $udt_obj->getTotalTime();
|
||
|
$rate = $udt_obj->getColumn( 'hourly_rate' );
|
||
|
$amount = $udt_obj->getTotalTimeAmount();
|
||
|
Debug::text( 'bPay Stub Entry Account ID: ' . $pay_stub_entry . ' Amount: ' . $amount . ' Rate: ' . $rate, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
} else {
|
||
|
Debug::text( ' NOT Paid Time Policy...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
// //We shouldn't do anything with UNPAID pay codes. If there needs to be Tax/Deduction calculations based on unpaid timesheet information, they should be marked as "PAID" but have a $0 hourly rate instead.
|
||
|
// //PayStubFactory will still accept $0 amount entries, allow calculations to be made on them, then remove them at the last second though.
|
||
|
// } elseif ( $udt_obj->getColumn('pay_code_type_id') == 20 ) { //UnPaid
|
||
|
// //Pass through even unpaid time to pay stubs, so Tax/Deduction records can base calculations on just units/hours if needed to create other earnings amounts.
|
||
|
// if ( is_object( $udt_obj->getPayCodeObject() ) ) {
|
||
|
// Debug::text('UnPaid Time... Total Time: '. $udt_obj->getTotalTime() .' Rate: '. $udt_obj->getColumn('hourly_rate'), __FILE__, __LINE__, __METHOD__, 10);
|
||
|
// $pay_stub_entry = $udt_obj->getPayCodeObject()->getPayStubEntryAccountId();
|
||
|
// $total_time = $udt_obj->getTotalTime();
|
||
|
// $rate = $udt_obj->getColumn('hourly_rate');
|
||
|
// $amount = $udt_obj->getTotalTimeAmount();
|
||
|
// Debug::text('cPay Stub Entry Account ID: '. $pay_stub_entry .' Amount: '. $amount .' Rate: '. $rate, __FILE__, __LINE__, __METHOD__, 10);
|
||
|
// } else {
|
||
|
// Debug::text(' NOT UDT Object...', __FILE__, __LINE__, __METHOD__, 10);
|
||
|
// }
|
||
|
} else if ( $udt_obj->getColumn( 'pay_code_type_id' ) == 30 ) { //Dock
|
||
|
$dock_absence_time = bcadd( $dock_absence_time, $udt_obj->getTotalTime() );
|
||
|
$rate = $udt_obj->getColumn( 'hourly_rate' );
|
||
|
$amount = $this->getWage( $udt_obj->getTotalTime(), $rate );
|
||
|
$dock_absence_amount = bcadd( $dock_absence_amount, $amount );
|
||
|
|
||
|
//Make sure we account for multiple dock absence policies, for the same wage entry in the same pay period.
|
||
|
if ( isset( $dock_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] ) ) {
|
||
|
$dock_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] += $amount;
|
||
|
} else {
|
||
|
$dock_absence_amount_arr[$udt_obj->getColumn( 'user_wage_id' )] = $amount;
|
||
|
}
|
||
|
|
||
|
Debug::text( 'DOCK Absence Time.. Adding: ' . $udt_obj->getTotalTime() . ' Total: ' . $dock_absence_time . ' Rate: ' . $rate, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
unset( $rate );
|
||
|
}
|
||
|
|
||
|
if ( isset( $pay_stub_entry ) && $pay_stub_entry != '' ) {
|
||
|
Debug::text( 'zPay Stub Entry Account ID: ' . $pay_stub_entry . ' Amount: ' . $amount, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$ret_arr['entries'][] = [
|
||
|
'user_wage_id' => $udt_obj->getColumn( 'user_wage_id' ),
|
||
|
'pay_stub_entry' => $pay_stub_entry,
|
||
|
'total_time' => $total_time,
|
||
|
'amount' => $amount,
|
||
|
'rate' => $rate,
|
||
|
'description' => null,
|
||
|
];
|
||
|
}
|
||
|
unset( $pay_stub_entry, $amount, $total_time, $rate );
|
||
|
}
|
||
|
|
||
|
if ( $calculate_salary == true ) {
|
||
|
//When the employee is salary and their wage changes in the middle of the pay period and they don't have any regular time worked
|
||
|
//in any one of the periods that either wage is effective, the period without any regular time was not being paid before.
|
||
|
//Therefore we moved the salary calcuations to the very end and if there is any regular time in the entire pay period
|
||
|
//we simply loop through all salaried wages and calculate the pro-rated amounts. Even if no regular time exists in one of the wage periods.
|
||
|
Debug::text( 'Calculating Salary...', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
//Get all wages that apply in this period so we can determine pro-rating for salaries.
|
||
|
$uwlf = TTNew( 'UserWageListFactory' ); /** @var UserWageListFactory $uwlf */
|
||
|
$uwlf->getDefaultWageGroupByUserIdAndStartDateAndEndDate( $this->getUser(), $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate() ); //ORDER BY effective_date desc
|
||
|
if ( $uwlf->getRecordCount() > 0 ) {
|
||
|
foreach ( $uwlf as $uw_obj ) {
|
||
|
$description = null;
|
||
|
if ( $uw_obj->getType() != 10 ) {
|
||
|
if ( isset( $dock_absence_amount_arr[$uw_obj->getID()] ) ) {
|
||
|
$dock_absence_wage = abs( $dock_absence_amount_arr[$uw_obj->getID()] ); //Make sure the dock absence wage is always a positive, since we subtract is below.
|
||
|
} else {
|
||
|
$dock_absence_wage = 0;
|
||
|
}
|
||
|
if ( isset( $reduce_salary_absence_amount_arr[$uw_obj->getID()] ) ) {
|
||
|
$paid_absence_wage = abs( $reduce_salary_absence_amount_arr[$uw_obj->getID()] ); //Make sure the dock absence wage is always a positive, since we subtract is below.
|
||
|
} else {
|
||
|
$paid_absence_wage = 0;
|
||
|
}
|
||
|
Debug::text( 'Wage ID: ' . $uw_obj->getID() . ' Dock Absence Wage: ' . $dock_absence_wage . ' Paid Absence Wage: ' . $paid_absence_wage, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
$maximum_wage_salary = UserWageFactory::proRateSalary( $this->getMaximumPayPeriodWage( $uw_obj ), $uw_obj->getEffectiveDate(), $prev_wage_effective_date, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate(), $this->getUserObject()->getHireDate(), $this->getUserObject()->getTerminationDate() );
|
||
|
|
||
|
$amount = bcsub( $maximum_wage_salary, bcadd( $dock_absence_wage, $paid_absence_wage ) );
|
||
|
//Include time if we have it, otherwise use 0.
|
||
|
$total_time = ( isset( $salary_regular_time[$uw_obj->getID()] ) ) ? $salary_regular_time[$uw_obj->getID()] : 0; //Dont minus dock/paid absence time. Because its already not included.
|
||
|
$rate = null;
|
||
|
$pay_stub_entry = $this->getPayStubEntryAccountLinkObject()->getRegularTime();
|
||
|
unset( $dock_absence_wage, $paid_absence_wage );
|
||
|
|
||
|
$salary_dates = UserWageFactory::proRateSalaryDates( $uw_obj->getEffectiveDate(), $prev_wage_effective_date, $this->getPayPeriodObject()->getStartDate(), $this->getPayPeriodObject()->getEndDate(), $this->getUserObject()->getHireDate(), $this->getUserObject()->getTerminationDate() );
|
||
|
if ( is_array( $salary_dates ) && isset( $salary_dates['percent'] ) && $salary_dates['percent'] < 100 ) {
|
||
|
$description = TTi18n::getText( 'Prorate Salary' ) . ': ' . TTDate::getDate( 'DATE', $salary_dates['start_date'] ) . ' - ' . TTDate::getDate( 'DATE', $salary_dates['end_date'] ) . ' (' . $salary_dates['percent'] . '%)';
|
||
|
}
|
||
|
|
||
|
if ( isset( $pay_stub_entry ) && $pay_stub_entry != '' ) {
|
||
|
Debug::text( ' Pay Stub Entry Account ID: ' . $pay_stub_entry . ' Amount: ' . $amount, __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
$ret_arr['entries'][] = [
|
||
|
'user_wage_id' => $udt_obj->getColumn( 'user_wage_id' ),
|
||
|
'pay_stub_entry' => $pay_stub_entry,
|
||
|
'total_time' => $total_time,
|
||
|
'amount' => $amount,
|
||
|
'rate' => $rate,
|
||
|
'description' => $description,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
|
||
|
unset( $pay_stub_entry, $amount, $total_time, $rate );
|
||
|
}
|
||
|
|
||
|
//Must go outside the $uw_obj->getType() != 10 check, so we can properly switch from Salary to Hourly in the middle of a PP.
|
||
|
$prev_wage_effective_date = $uw_obj->getEffectiveDate();
|
||
|
}
|
||
|
}
|
||
|
unset( $uwlf, $uw_obj );
|
||
|
}
|
||
|
|
||
|
//Compact entries to minimize line items on pay stubs unless absolutely necessary.
|
||
|
if ( isset( $ret_arr['entries'] ) ) {
|
||
|
$ret_arr['entries'] = $this->compactUserDateTotalPayStubEntries( $ret_arr['entries'] );
|
||
|
}
|
||
|
} else {
|
||
|
Debug::text( 'NO UserDate Total entries found.', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
}
|
||
|
|
||
|
$ret_arr['other']['paid_absence_time'] = $paid_absence_time;
|
||
|
$ret_arr['other']['dock_absence_time'] = $dock_absence_time;
|
||
|
|
||
|
$ret_arr['other']['paid_absence_amount'] = $paid_absence_amount;
|
||
|
$ret_arr['other']['dock_absence_amount'] = $dock_absence_amount;
|
||
|
|
||
|
if ( empty( $ret_arr ) == false ) {
|
||
|
Debug::Arr( $ret_arr, 'UserDateTotal Array', __FILE__, __LINE__, __METHOD__, 10 );
|
||
|
|
||
|
return $this->user_date_total_arr = $ret_arr;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Compact wage entries that have the same: user_wage_id, pay_stub_entry, rate and description.
|
||
|
* This is primarily to avoid having multiple Regular Time entries for each meal/break policy that they have, which can be confusing for some employees.
|
||
|
* @param $array
|
||
|
* @return array
|
||
|
*/
|
||
|
function compactUserDateTotalPayStubEntries( $array ) {
|
||
|
$retarr = [];
|
||
|
if ( is_array( $array ) ) {
|
||
|
foreach( $array as $entry_arr ) {
|
||
|
$compact_key = md5( $entry_arr['user_wage_id'] . $entry_arr['pay_stub_entry'] . Misc::MoneyRound( $entry_arr['rate'], 4 ) . $entry_arr['description'] );
|
||
|
if ( isset( $retarr[$compact_key] ) ) {
|
||
|
//Found matching Wage/PayStubEntry/Rate/Description, simply add total_time and amount to it, rather than create a new entry.
|
||
|
$retarr[$compact_key]['total_time'] = bcadd( $retarr[$compact_key]['total_time'], $entry_arr['total_time'] );
|
||
|
$retarr[$compact_key]['amount'] = bcadd( $retarr[$compact_key]['amount'], $entry_arr['amount'] );
|
||
|
} else {
|
||
|
$retarr[$compact_key] = $entry_arr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return array_values( $retarr );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
?>
|