'default_schedule', //20 => 'schedule_amendment', //30 => 'shift_amendment', //40 => 'pay_stub_amendment', //52 => 'request_vacation', //54 => 'request_missed_punch', //56 => 'request_edit_punch', //58 => 'request_absence', //59 => 'request_schedule', 90 => 'timesheet', 200 => 'expense', //50 => 'request', //request_other 1010 => 'request_punch', 1020 => 'request_punch_adjust', 1030 => 'request_absence', 1040 => 'request_schedule', 1100 => 'request_other', ]; break; case 'columns': $retval = [ '-1010-created_by' => TTi18n::gettext( 'Name' ), '-1020-created_date' => TTi18n::gettext( 'Date' ), '-1030-authorized' => TTi18n::gettext( 'Authorized' ), //'-1100-object_type' => TTi18n::gettext('Object Type'), //'-2020-updated_by' => TTi18n::gettext('Updated By'), //'-2030-updated_date' => TTi18n::gettext('Updated Date'), ]; break; case 'list_columns': $retval = Misc::arrayIntersectByKey( $this->getOptions( 'default_display_columns' ), Misc::trimSortPrefix( $this->getOptions( 'columns' ) ) ); break; case 'default_display_columns': //Columns that are displayed by default. $retval = [ 'created_by', 'created_date', 'authorized', ]; break; } return $retval; } /** * @param $data * @return array */ function _getVariableToFunctionMap( $data ) { $variable_function_map = [ 'id' => 'ID', 'object_type_id' => 'ObjectType', 'object_type' => false, 'object_id' => 'Object', 'authorized' => 'Authorized', 'deleted' => 'Deleted', ]; return $variable_function_map; } /** * @return bool */ function getCurrentUserObject() { return $this->getGenericObject( 'UserListFactory', $this->getCurrentUser(), 'user_obj' ); } /** * Stores the current user in memory, so we can determine if its the employee verifying, or a superior. * @return mixed */ function getCurrentUser() { return $this->getGenericTempDataValue( 'current_user_id' ); } /** * @param string $value UUID * @return bool */ function setCurrentUser( $value ) { $value = trim( $value ); return $this->setGenericTempDataValue( 'current_user_id', $value ); } /** * @return array|bool|null */ function getHierarchyArray() { if ( is_array( $this->hierarchy_arr ) ) { return $this->hierarchy_arr; } else { $user_id = $this->getCurrentUser(); if ( is_object( $this->getObjectHandler() ) ) { $this->getObjectHandler()->getByID( $this->getObject() ); $current_obj = $this->getObjectHandler()->getCurrent(); $object_user_id = $current_obj->getUser(); if ( TTUUID::isUUID( $object_user_id ) && $object_user_id != TTUUID::getZeroID() && $object_user_id != TTUUID::getNotExistID() ) { $ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */ $company_id = $ulf->getById( $object_user_id )->getCurrent()->getCompany(); Debug::Text( ' Authorizing User ID: ' . $user_id .' Object User ID: ' . $object_user_id . ' Company ID: ' . $company_id, __FILE__, __LINE__, __METHOD__, 10 ); $hlf = TTnew( 'HierarchyListFactory' ); /** @var HierarchyListFactory $hlf */ $this->hierarchy_arr = $hlf->getHierarchyParentByCompanyIdAndUserIdAndObjectTypeID( $company_id, $object_user_id, $this->getObjectType(), false ); Debug::Arr( $this->hierarchy_arr, ' Hierarchy Arr: ', __FILE__, __LINE__, __METHOD__, 10 ); return $this->hierarchy_arr; } else { Debug::Text( ' Could not find Object User ID: ' . $user_id, __FILE__, __LINE__, __METHOD__, 10 ); } } else { Debug::Text( ' ERROR: No ObjectHandler defined...', __FILE__, __LINE__, __METHOD__, 10 ); } } return false; } /** * @return array|bool */ function getHierarchyChildLevelArray() { $retval = []; $user_id = $this->getCurrentUser(); $parent_arr = $this->getHierarchyArray(); if ( is_array( $parent_arr ) && count( $parent_arr ) > 0 ) { $next_level = false; foreach ( $parent_arr as $level_parent_arr ) { if ( in_array( $user_id, $level_parent_arr ) ) { $next_level = true; continue; } if ( $next_level == true ) { //Debug::Arr( $level_parent_arr, ' Child: Level: '. $level, __FILE__, __LINE__, __METHOD__, 10 ); $retval = array_merge( $retval, $level_parent_arr ); //Append from all levels. } } } if ( count( $retval ) > 0 ) { return $retval; } return false; } /** * @param bool $force * @return bool|mixed */ function getHierarchyCurrentLevelArray( $force = false ) { $retval = false; $user_id = $this->getCurrentUser(); $parent_arr = $this->getHierarchyArray(); if ( is_array( $parent_arr ) && count( $parent_arr ) > 0 ) { $next_level = false; foreach ( $parent_arr as $level_parent_arr ) { if ( in_array( $user_id, $level_parent_arr ) ) { $next_level = true; if ( $force == false ) { continue; } } if ( $next_level == true ) { //Current level is alway one level lower, as this often gets called after the level has been changed. $retval = $level_parent_arr; //Debug::Arr( $level_parent_arr, ' Current: Level: ' . $level, __FILE__, __LINE__, __METHOD__, 10 ); break; } } if ( $next_level == true && $retval == false ) { //Current level was the top and only level. $retval = $level_parent_arr; //Debug::Arr( $level_parent_arr, ' Current: Level: ' . $level, __FILE__, __LINE__, __METHOD__, 10 ); } } return $retval; } /** * @return array|bool|mixed */ function getHierarchyParentLevelArray() { $retval = false; $user_id = TTUUID::castUUID( $this->getCurrentUser() ); $parent_arr = array_reverse( (array)$this->getHierarchyArray() ); if ( is_array( $parent_arr ) && count( $parent_arr ) > 0 ) { $next_level = false; foreach ( $parent_arr as $level_parent_arr ) { if ( is_array( $level_parent_arr ) && in_array( $user_id, $level_parent_arr ) ) { $next_level = true; continue; } //Since this loops in reverse, always assume the first element is the parent for cases where a subordinate may be submitting the object (ie: request) and it needs to go to the direct superiors. if ( $next_level == true ) { //Debug::Arr( $level_parent_arr, ' Parents: Level: '. $level, __FILE__, __LINE__, __METHOD__, 10 ); $retval = $level_parent_arr; break; } } //If we get here without finding a parent, use the lowest lower parents by default. if ( $next_level == false ) { reset( $parent_arr ); $retval = $parent_arr[key( $parent_arr )]; } } return $retval; } /** * This will return false if it can't find a hierarchy, or if its at the top level (1) and can't find a higher level. * @return bool|int|string */ function getNextHierarchyLevel() { $retval = false; $user_id = $this->getCurrentUser(); $parent_arr = $this->getHierarchyArray(); if ( is_array( $parent_arr ) && count( $parent_arr ) > 0 ) { foreach ( $parent_arr as $level => $level_parent_arr ) { if ( in_array( $user_id, $level_parent_arr ) ) { break; } $retval = $level; } } if ( $retval < 1 ) { Debug::Text( ' ERROR, hierarchy level goes past 1... This shouldnt happen...', __FILE__, __LINE__, __METHOD__, 10 ); $retval = false; } return $retval; } /** * @param string $company_id UUID * @param string $user_id UUID * @param int $hierarchy_type_id * @return int|mixed */ static function getInitialHierarchyLevel( $company_id, $user_id, $hierarchy_type_id ) { $hierarchy_highest_level = 99; if ( $company_id != '' && $user_id != '' && $hierarchy_type_id > 0 ) { $hlf = TTnew( 'HierarchyListFactory' ); /** @var HierarchyListFactory $hlf */ $hierarchy_arr = $hlf->getHierarchyParentByCompanyIdAndUserIdAndObjectTypeID( $company_id, $user_id, $hierarchy_type_id, false ); if ( isset( $hierarchy_arr ) && is_array( $hierarchy_arr ) ) { //Debug::Arr( $hierarchy_arr, ' aUser ID ' . $user_id . ' Type ID: ' . $hierarchy_type_id . ' Array: ', __FILE__, __LINE__, __METHOD__, 10 ); //See if current user is in superior list, if so, start at one level up in the hierarchy, unless its level 1. foreach ( $hierarchy_arr as $level => $superior_user_ids ) { if ( in_array( $user_id, $superior_user_ids, true ) == true ) { Debug::Text( ' Found user in superior list at level: ' . $level, __FILE__, __LINE__, __METHOD__, 10 ); $i = $level; while ( isset( $hierarchy_arr[$i] ) ) { if ( $i != 1 ) { Debug::Text( ' Removing lower level: ' . $i, __FILE__, __LINE__, __METHOD__, 10 ); unset( $hierarchy_arr[$i] ); } $i++; } } } //Debug::Arr( $hierarchy_arr, ' bUser ID ' . $user_id . ' Type ID: ' . $hierarchy_type_id . ' Array: ', __FILE__, __LINE__, __METHOD__, 10 ); $hierarchy_arr = array_keys( $hierarchy_arr ); $hierarchy_highest_level = end( $hierarchy_arr ); } } Debug::Text( ' Returning initial hierarchy level to: ' . $hierarchy_highest_level, __FILE__, __LINE__, __METHOD__, 10 ); return $hierarchy_highest_level; } /** * @return bool */ function isValidParent() { $user_id = $this->getCurrentUser(); $parent_arr = $this->getHierarchyArray(); if ( is_array( $parent_arr ) && count( $parent_arr ) > 0 ) { krsort( $parent_arr ); foreach ( $parent_arr as $level_parent_arr ) { if ( in_array( $user_id, $level_parent_arr ) ) { return true; } } } Debug::Text( ' Authorizing User is not a parent of the object owner: ', __FILE__, __LINE__, __METHOD__, 10 ); return false; } /** * @return bool */ function isFinalAuthorization() { $user_id = $this->getCurrentUser(); $parent_arr = $this->getHierarchyArray(); if ( is_array( $parent_arr ) && count( $parent_arr ) > 0 ) { //Check that level 1 parent exists if ( isset( $parent_arr[1] ) && in_array( $user_id, $parent_arr[1] ) ) { Debug::Text( ' Final Authorization!', __FILE__, __LINE__, __METHOD__, 10 ); return true; } } Debug::Text( ' NOT Final Authorization!', __FILE__, __LINE__, __METHOD__, 10 ); return false; } /** * Checks to see if the currently logged in user is the only superior in the hierarchy at the current level. * This would normally be paired with a isFinalAuthorization() check as well. * @return bool */ function isCurrentUserOnlySuperior() { $hierarchy_current_level_user_ids = $this->getHierarchyCurrentLevelArray(); if ( count( $hierarchy_current_level_user_ids ) == 1 && in_array( $this->getCurrentUser(), $hierarchy_current_level_user_ids ) ) { return true; } return false; } /** * @return null|object */ function getObjectHandler() { if ( is_object( $this->obj_handler ) ) { return $this->obj_handler; } else { switch ( $this->getObjectType() ) { case 90: //TimeSheet $this->obj_handler = TTnew( 'PayPeriodTimeSheetVerifyListFactory' ); break; case 200: $this->obj_handler = TTnew( 'UserExpenseListFactory' ); break; case 50: //Requests case 1010: case 1020: case 1030: case 1040: case 1100: $this->obj_handler = TTnew( 'RequestListFactory' ); break; } return $this->obj_handler; } } /** * @return bool|int */ function getObjectType() { return $this->getGenericDataValue( 'object_type_id' ); } /** * @param $value * @return bool */ function setObjectType( $value ) { $value = (int)trim( $value ); return $this->setGenericDataValue( 'object_type_id', $value ); } /** * @return bool|mixed */ function getObject() { return $this->getGenericDataValue( 'object_id' ); } /** * @param string $value UUID * @return bool */ function setObject( $value ) { $value = TTUUID::castUUID( $value ); return $this->setGenericDataValue( 'object_id', $value ); } /** * @return bool */ function getAuthorized() { return $this->fromBool( $this->getGenericDataValue( 'authorized' ) ); } /** * @param $value * @return bool */ function setAuthorized( $value ) { return $this->setGenericDataValue( 'authorized', $this->toBool( $value ) ); } /** * @return bool */ function clearHistory() { Debug::text( 'Clearing Authorization History For Type: ' . $this->getObjectType() . ' ID: ' . $this->getObject(), __FILE__, __LINE__, __METHOD__, 10 ); if ( $this->getObjectType() === false || $this->getObject() === false ) { Debug::text( 'Clearing Authorization History FAILED!', __FILE__, __LINE__, __METHOD__, 10 ); return false; } $alf = TTnew( 'AuthorizationListFactory' ); /** @var AuthorizationListFactory $alf */ $alf->getByObjectTypeAndObjectId( $this->getObjectType(), $this->getObject() ); foreach ( $alf as $authorization_obj ) { $authorization_obj->setDeleted( true ); $authorization_obj->Save(); } return true; } /** * @return object */ function getObjectHandlerObject() { if ( is_object( $this->obj_handler_obj ) ) { return $this->obj_handler_obj; } else { //Get user_id of object. $this->getObjectHandler()->getByID( $this->getObject() ); $this->obj_handler_obj = $this->getObjectHandler()->getCurrent(); // if ( method_exists( $this->obj_handler_obj, 'setCurrentUser' ) AND $this->obj_handler_obj->getCurrentUser() != $this->getCurrentUser() ) { //Required for authorizing TimeSheets from MyAccount -> TimeSheet Authorization. // $this->obj_handler_obj->setCurrentUser( $this->getCurrentUser() ); // } return $this->obj_handler_obj; } } /** * @return boolean */ function setObjectHandlerStatus() { $is_final_authorization = $this->isFinalAuthorization(); $this->obj_handler_obj = $this->getObjectHandlerObject(); if ( $this->getAuthorized() === true ) { if ( $is_final_authorization === true ) { //If no other superiors exist in the hierarchy and we are at the top level, assume its authorized. if ( $this->getCurrentUser() != $this->obj_handler_obj->getUser() || $this->isCurrentUserOnlySuperior() == true ) { Debug::Text( ' Approving Authorization... Final Authorizing Object: ' . $this->getObject() . ' - Type: ' . $this->getObjectType(), __FILE__, __LINE__, __METHOD__, 10 ); $this->obj_handler_obj->setAuthorizationLevel( 1 ); $this->obj_handler_obj->setStatus( 50 ); //Active/Authorized $this->obj_handler_obj->setAuthorized( true ); } else { Debug::Text( ' Currently logged in user is authorizing (or submitting as new) their own request, when other superiors exist in the hierarchy, not authorizing...', __FILE__, __LINE__, __METHOD__, 10 ); } } else { Debug::text( ' Approving Authorization, moving to next level up...', __FILE__, __LINE__, __METHOD__, 10 ); $current_level = $this->obj_handler_obj->getAuthorizationLevel(); if ( $current_level > 1 ) { //Highest level is 1, so no point in making it less than that. //Get the next level above the current user doing the authorization, in case they have dropped down a level or two. $next_level = $this->getNextHierarchyLevel(); if ( $next_level !== false && $next_level < $current_level ) { Debug::text( ' Current Level: ' . $current_level . ' Moving Up To Level: ' . $next_level, __FILE__, __LINE__, __METHOD__, 10 ); $this->obj_handler_obj->setAuthorizationLevel( $next_level ); } } unset( $current_level, $next_level ); } } else { Debug::text( ' Declining Authorization...', __FILE__, __LINE__, __METHOD__, 10 ); $this->obj_handler_obj->setStatus( 55 ); //'AUTHORIZATION DECLINED' $this->obj_handler_obj->setAuthorized( false ); } return true; } /** * @return array|bool */ function getUserAuthorizationIds() { $object_handler_user_id = $this->getObjectHandlerObject()->getUser(); //Object handler (request) user_id. $is_final_authorization = $this->isFinalAuthorization(); $authorization_level = $this->getObjectHandlerObject()->getAuthorizationLevel(); //This is the *new* level, not the old level. $hierarchy_current_level_arr = $this->getHierarchyCurrentLevelArray(); Debug::Arr( $hierarchy_current_level_arr, ' Authorization Level: ' . $authorization_level . ' Authorized: ' . (int)$this->getAuthorized() . ' Is Final Auth: ' . (int)$is_final_authorization . ' Object Handler User ID: ' . $object_handler_user_id, __FILE__, __LINE__, __METHOD__, 10 ); if ( $this->getAuthorized() == true && $authorization_level == 0 ) { //Final authorization has taken place //Notify original submittor and all lower level superiors? $user_ids = $this->getHierarchyChildLevelArray(); if ( is_a( $this->getObjectHandlerObject(), 'PayPeriodTimeSheetVerify' ) ) { //is_a() will match on plugin class names too because it also checks the parent class name. //Check to see what type of timesheet verification is required, if its superior only, don't notify the employee to avoid confusion. if ( $this->getObjectHandlerObject()->getVerificationType() != 30 ) { $user_ids[] = $object_handler_user_id; } else { Debug::text( ' TimeSheetVerification for superior only, dont motify employee...', __FILE__, __LINE__, __METHOD__, 10 ); } } else { $user_ids[] = $object_handler_user_id; } //Debug::Arr($user_ids , ' aAuthorization Level: '. $authorization_level .' Authorized: '. (int)$this->getAuthorized() .' Child: ' , __FILE__, __LINE__, __METHOD__, 10); } else { //Debug::Text(' bAuthorization Level: '. $authorization_level .' Authorized: '. (int)$this->getAuthorized(), __FILE__, __LINE__, __METHOD__, 10); //Final authorization has *not* yet taken place if ( $this->getObjectHandlerObject()->getStatus() == 55 ) { //Declined //Authorization declined. Notify original submittor and all lower level superiors? $user_ids = $this->getHierarchyChildLevelArray(); $user_ids[] = $object_handler_user_id; //Debug::Arr($user_ids , ' b1Authorization Level: '. $authorization_level .' Authorized: '. (int)$this->getAuthorized() .' Child: ', __FILE__, __LINE__, __METHOD__, 10); } else if ( $is_final_authorization == true && $this->getCurrentUser() == $object_handler_user_id && $this->getAuthorized() == true && $authorization_level == 1 ) { //Subordinate who is also a superior at the top and only level of the hierarchy is submitting a request. $user_ids = $this->getHierarchyCurrentLevelArray( true ); //Force to real current level. //Debug::Arr($user_ids , ' b2Authorization Level: '. $authorization_level .' Authorized: '. (int)$this->getAuthorized() .' Child: ', __FILE__, __LINE__, __METHOD__, 10); } else { //Authorized at a middle level, notify current level superiors only so they know its waiting on them. $user_ids = $this->getHierarchyParentLevelArray(); //Debug::Arr($user_ids , ' b3Authorization Level: '. $authorization_level .' Authorized: '. (int)$this->getAuthorized() .' Parent: ', __FILE__, __LINE__, __METHOD__, 10); } } if( isset( $user_ids ) && !empty( $user_ids ) ) { //Remove the current authorizing user from the array, as they don't need to be notified as they are performing the action. $user_ids = array_diff( (array)$user_ids, [ $this->getCurrentUser() ] ); //CurrentUser is currently logged in user. //remove duplicate user_ids $user_ids = array_unique( $user_ids ); return $user_ids; } return []; } /** * @return bool */ function sendNotificationAuthorization( ) { Debug::Text( 'getNotificationData: ', __FILE__, __LINE__, __METHOD__, 10 ); $user_ids = $this->getUserAuthorizationIds(); if ( empty ( $user_ids ) ) { return false; } //Get initiator user from User Object so we can include more information in the message. if ( is_object( $this->getCurrentUserObject() ) ) { $u_obj = $this->getCurrentUserObject(); } else { Debug::Text( 'From object does not exist: ' . $this->getCurrentUser(), __FILE__, __LINE__, __METHOD__, 10 ); return false; } foreach ( $user_ids as $user_id ) { //Grab each users preferences as they can be custom to them and their language etc. $ulf = TTnew( 'UserListFactory' ); /** @var UserListFactory $ulf */ $ulf->getById( $user_id ); if ( $ulf->getRecordCount() == 1 ) { $user_to_obj = $ulf->getCurrent(); if ( is_object( $user_to_obj ) ) { $user_to_pref_obj = $user_to_obj->getUserPreferenceObject(); /** @var UserPreferenceFactory $user_to_pref_obj */ $user_to_pref_obj->setDateTimePreferences(); TTi18n::setLanguage( $user_to_pref_obj->getLanguage() ); TTi18n::setCountry( $user_to_obj->getCountry() ); TTi18n::setLocale(); } else { return false; } } else { Debug::Text( 'ERROR: User does not exist: ' . $user_id, __FILE__, __LINE__, __METHOD__, 10 ); return false; } $object_handler_user_obj = $this->getObjectHandlerObject()->getUserObject(); //Object handler (request) user_id. $status_label = TTi18n::ucfirst( TTi18n::strtolower( Option::getByKey( $this->getObjectHandlerObject()->getStatus(), Misc::trimSortPrefix( $this->getObjectHandlerObject()->getOptions( 'status' ) ) ) ) ); //PENDING, AUTHORIZED, DECLINED $title_short = '#object_type# '. TTi18n::gettext( 'by' ) .' #object_employee_first_name# #object_employee_last_name# #status#.'; $title_long = '#object_type# '. TTi18n::gettext( 'by' ) .' #object_employee_first_name# #object_employee_last_name# #status# '. TTi18n::gettext( 'for' ) .' #date#'; switch ( $this->getObjectType() ) { case 90: //TimeSheet $object_type = TTi18n::getText( 'TimeSheet' ); $notification_object_type = 90; if ( $this->getAuthorized() == true && $this->getObjectHandlerObject()->getAuthorizationLevel() == 0 ) { // Timesheet has been verified link back to timesheet. $link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=TimeSheet'; // If timesheet belongs to user being notified set type as timesheet_verify timesheet_authorize for supervisors. if ( $object_handler_user_obj->getId() === $user_to_obj->getId() ) { $notification_type = 'timesheet_verify'; } else { $notification_type = 'timesheet_authorize'; } } else { $link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=TimeSheetAuthorization&a=view&id=' . $this->getObject() . '&tab=TimeSheetVerification'; $notification_type = 'timesheet_authorize'; } $display_date = TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getPayPeriodObject()->getEndDate() ); $body_short = TTi18n::getText( 'Pay Period' ) . ': ' . TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getPayPeriodObject()->getStartDate() ) . ' -> ' . TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getPayPeriodObject()->getEndDate() ); break; case 200: //Expense $object_type = TTi18n::getText( 'Expense' ); $notification_object_type = 110; if ( $this->getAuthorized() == true && $this->getObjectHandlerObject()->getAuthorizationLevel() == 0 ) { // Expense has been authorized link back to original expense and not the authorization view. $link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=LoginUserExpense&a=view&id=' . $this->getObject() . '&tab=Expense'; // If expense belongs to user being notified set type as expense_verify else expense_authorize for supervisors. if ( $object_handler_user_obj->getId() === $user_to_obj->getId() ) { $notification_type = 'expense_verify'; } else { $notification_type = 'expense_authorize'; } } else { $link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=ExpenseAuthorization&a=edit&id=' . $this->getObject() . '&tab=Expense'; $notification_type = 'expense_authorize'; } $display_date = TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getIncurredDate() ); //Check if its a custom unit or just dollars so the message can be formatted properly for each. if ( is_object( $this->getObjectHandlerObject()->getExpensePolicyObject() ) && $this->getObjectHandlerObject()->getExpensePolicyObject()->getType() == 30 ) { //30=Per Unit $body_short = $this->getObjectHandlerObject()->getGrossAmount() . ' ' . $this->getObjectHandlerObject()->getExpensePolicyObject()->getUnitName() . ' ' . TTi18n::getText( 'incurred on' ) . ': ' . TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getIncurredDate() ); } else { $body_short = '$' . $this->getObjectHandlerObject()->getGrossAmount() . ' ' . TTi18n::getText( 'incurred on' ) . ': ' . TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getIncurredDate() ); } //Add the reimbursable amount so its clear to the end-user. $body_short .= "\n". TTi18n::getText( 'Reimbursable Amount' ) .': $'. $this->getObjectHandlerObject()->getReimburseAmount(); break; case 50: //Requests case 1010: case 1020: case 1030: case 1040: case 1100: $object_type = TTi18n::getText( 'Request' ); $notification_object_type = 50; if ( $this->getAuthorized() == true && $this->getObjectHandlerObject()->getAuthorizationLevel() == 0 ) { // Request has been authorized link back to original request and not the authorization view. $link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=Request&a=view&id=' . $this->getObject() . '&tab=Request'; // If request belongs to user being notified set type as request else request_authorize for supervisors. if ( $object_handler_user_obj->getId() === $user_to_obj->getId() ) { $notification_type = 'request'; } else { $notification_type = 'request_authorize'; } } else { $link = Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() . 'html5/#!m=RequestAuthorization&a=view&id=' . $this->getObject() . '&tab=Request'; $notification_type = 'request_authorize'; } $display_date = TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getDateStamp() ); $body_short = Option::getByKey( $this->getObjectHandlerObject()->getType(), Misc::trimSortPrefix( $this->getObjectHandlerObject()->getOptions( 'type' ) ) ) . ' ' . TTi18n::getText( 'on' ) . ' ' . TTDate::getDate( 'DATE', $this->getObjectHandlerObject()->getDateStamp() ); break; } //Define title_short/body variables here. $search_arr = [ '#object_type#', '#object_type_long_description#', '#status#', '#date#', '#current_employee_first_name#', '#current_employee_last_name#', '#object_employee_first_name#', '#object_employee_last_name#', '#object_employee_default_branch#', '#object_employee_default_department#', '#object_employee_group#', '#object_employee_title#', '#company_name#', '#url#', ]; $replace_arr = Misc::escapeHTML( [ $object_type, $body_short, $status_label, $display_date, $u_obj->getFirstName(), $u_obj->getLastName(), $object_handler_user_obj->getFirstName(), $object_handler_user_obj->getLastName(), ( is_object( $object_handler_user_obj->getDefaultBranchObject() ) ) ? $object_handler_user_obj->getDefaultBranchObject()->getName() : null, ( is_object( $object_handler_user_obj->getDefaultDepartmentObject() ) ) ? $object_handler_user_obj->getDefaultDepartmentObject()->getName() : null, ( is_object( $object_handler_user_obj->getGroupObject() ) ) ? $object_handler_user_obj->getGroupObject()->getName() : null, ( is_object( $object_handler_user_obj->getTitleObject() ) ) ? $object_handler_user_obj->getTitleObject()->getName() : null, ( is_object( $object_handler_user_obj->getCompanyObject() ) ) ? $object_handler_user_obj->getCompanyObject()->getName() : null, ( Misc::getURLProtocol() . '://' . Misc::getHostName() . Environment::getDefaultInterfaceBaseURL() ), ] ); $title_short = str_replace( $search_arr, $replace_arr, $title_short ); $title_long = str_replace( $search_arr, $replace_arr, $title_long ); $body_short = str_replace( $search_arr, $replace_arr, $body_short ); //$body_long = TTi18n::gettext( '*DO NOT REPLY TO THIS EMAIL - PLEASE USE THE LINK BELOW INSTEAD*' ) . "\n\n"; $body_long = '#object_type# '. TTi18n::gettext( 'by' ) .' #object_employee_first_name# #object_employee_last_name# #status#'. "\n"; $body_long .= ( $replace_arr[1] != '' ) ? '#object_type_long_description#' . "\n" : null; $body_long .= "\n"; $body_long .= ( $replace_arr[8] != '' ) ? TTi18n::gettext( 'Default Branch' ) . ': #object_employee_default_branch#' . "\n" : null; $body_long .= ( $replace_arr[9] != '' ) ? TTi18n::gettext( 'Default Department' ) . ': #object_employee_default_department#' . "\n" : null; $body_long .= ( $replace_arr[10] != '' ) ? TTi18n::gettext( 'Group' ) . ': #object_employee_group#' . "\n" : null; $body_long .= ( $replace_arr[11] != '' ) ? TTi18n::gettext( 'Title' ) . ': #object_employee_title#' . "\n" : null; $body_long .= TTi18n::gettext( 'Link' ) . ': ' . APPLICATION_NAME . ' ' . TTi18n::gettext( 'Login' ) . '' . "\n"; $body_long .= NotificationFactory::addEmailFooter( ( ( is_object( $object_handler_user_obj->getCompanyObject() ) ) ? $object_handler_user_obj->getCompanyObject()->getName() : null ) ); $body_long = '
' . str_replace( $search_arr, $replace_arr, $body_long ) . '
'; $notification_data = [ 'object_id' => $this->getObject(), 'user_id' => $user_id, 'type_id' => $notification_type, 'object_type_id' => $notification_object_type, 'title_short' => $title_short, 'title_long' => $title_long, 'body_short' => $body_short, 'body_long_html' => $body_long, //For emails 'payload' => [ 'link' => $link ], ]; Notification::sendNotification( $notification_data ); } //reset datetime and tti8n preferences to current user $user_pref_obj = $u_obj->getUserPreferenceObject(); /** @var UserPreferenceFactory $user_pref_obj */ $user_pref_obj->setDateTimePreferences(); TTi18n::setLanguage( $user_pref_obj->getLanguage() ); TTi18n::setCountry( $u_obj->getCountry() ); TTi18n::setLocale(); return true; } function markRelatedNotificationsAsRead() { $request_object_type_to_notification_object_type_map = [ 90 => 90, //'timesheet', 200 => 110, //'expense', //50 => 'request', //request_other 1010 => 50, //'request_punch', 1020 => 50, //'request_punch_adjust', 1030 => 50, //'request_absence', 1040 => 50, //'request_schedule', 1100 => 50, //'request_other', ]; if ( isset( $request_object_type_to_notification_object_type_map[$this->getObjectType()] ) ) { $notification_object_type_id = $request_object_type_to_notification_object_type_map[$this->getObjectType()]; if ( $this->isFinalAuthorization() == true ) { //If its a final authorization, mark notification as read for *all* notifications/users at any level. NotificationFactory::updateStatusByObjectIdAndObjectTypeId( $notification_object_type_id, $this->getObject() ); //Mark any notifications linked to these exceptions as read. } else { //If its a superior at a low level, only mark notifications as read for any other superior at the same level. $hierarchy_current_level_user_ids = $this->getHierarchyCurrentLevelArray(); NotificationFactory::updateStatusByObjectIdAndObjectTypeId( $notification_object_type_id, $this->getObject(), $hierarchy_current_level_user_ids ); //Mark any notifications linked to these exceptions as read. } } return true; } /** * Used by Request/TimeSheetVerification/Expense when initially saving a record to notify the immediate superiors, rather than using the message notification. * @param string $current_user_id UUID * @param int $object_type_id * @param string $object_id UUID * @return bool */ static function sendNotificationAuthorizationOnInitialObjectSave( $current_user_id, $object_type_id, $object_id ) { $authorization_obj = TTNew( 'AuthorizationFactory' ); /** @var AuthorizationFactory $authorization_obj */ $authorization_obj->setObjectType( $object_type_id ); $authorization_obj->setObject( $object_id ); $authorization_obj->setCurrentUser( $current_user_id ); $authorization_obj->setAuthorized( true ); $authorization_obj->sendNotificationAuthorization(); } /** * @return bool */ function isUnique() { $ph = [ 'object_type' => (int)$this->getObjectType(), 'object_id' => TTUUID::castUUID( $this->getObject() ), 'authorized' => (int)$this->getAuthorized(), 'created_by' => TTUUID::castUUID( $this->getCreatedBy() ), ]; $query = 'select id from ' . $this->getTable() . ' where object_type_id = ? AND object_id = ? AND authorized = ? AND created_by = ?'; $id = $this->db->GetOne( $query, $ph ); Debug::Arr( $id, 'Unique Authorization: ' . $id, __FILE__, __LINE__, __METHOD__, 10 ); if ( $id === false ) { return true; } else { if ( $id == $this->getId() ) { return true; } } return false; } /** * @param bool $ignore_warning * @return bool */ function Validate( $ignore_warning = true ) { // // BELOW: Validation code moved from set*() functions. // // Object Type $this->Validator->inArrayKey( 'object_type', $this->getObjectType(), TTi18n::gettext( 'Object Type is invalid' ), $this->getOptions( 'object_type' ) ); // Object ID $this->Validator->isResultSetWithRows( 'object', ( is_object( $this->getObjectHandler() ) ) ? $this->getObjectHandler()->getByID( $this->getObject() ) : false, TTi18n::gettext( 'Object ID is invalid' ) ); //Prevent duplicate authorizations by the same person. // This may cause problems if the hierarchy is changed and the same superior needs to authorize the request again though? // By definition this should never happen at the final authorization level, so someone higher up in the hierarchy could always drop down and authorize it during the transition. if ( $this->getDeleted() == false ) { if ( $this->Validator->getValidateOnly() == false && $this->isUnique() == false ) { $this->Validator->isTrue( 'object', false, TTi18n::gettext( 'Record has already been authorized/declined by you' ) ); } } // // ABOVE: Validation code moved from set*() functions. // if ( $this->getDeleted() === false && $this->isFinalAuthorization() === false && $this->isValidParent() === false ) { //FYI: This error may occur on timesheet authorization if the timesheet cannot be verified because pending requests or critical severity exceptions exist. Though it should display a proper validation message to that affect instead. $this->Validator->isTrue( 'parent', false, TTi18n::gettext( 'Employee authorizing this object is not a superior in the hierarchy that controls it' ) ); return false; } $this->setObjectHandlerStatus(); if ( $this->getDeleted() == false && is_object( $this->getObjectHandlerObject() ) && $this->getObjectHandlerObject()->isValid() == false ) { Debug::text( ' ObjectHandler Validation Failed, pass validation errors up the chain...', __FILE__, __LINE__, __METHOD__, 10 ); $this->Validator->merge( $this->getObjectHandlerObject()->Validator ); } return true; } /** * @return bool */ function preSave() { //Debug::Text(' Calling preSave!: ', __FILE__, __LINE__, __METHOD__, 10); $this->StartTransaction(); return true; } /** * @return bool */ function postSave() { if ( $this->getDeleted() == false ) { if ( is_object( $this->getObjectHandlerObject() ) && $this->getObjectHandlerObject()->isValid() == true ) { Debug::text( ' Object Valid...', __FILE__, __LINE__, __METHOD__, 10 ); //Return true if object saved correctly. $retval = $this->getObjectHandlerObject()->Save( false ); if ( $this->getObjectHandlerObject()->isValid() == false ) { Debug::text( ' Object postSave validation FAILED!', __FILE__, __LINE__, __METHOD__, 10 ); $this->Validator->merge( $this->getObjectHandlerObject()->Validator ); } else { Debug::text( ' Object postSave validation SUCCESS!', __FILE__, __LINE__, __METHOD__, 10 ); $this->markRelatedNotificationsAsRead(); //Mark existing notifications as read before new ones are sent. $this->sendNotificationAuthorization(); } if ( $retval === true ) { $this->CommitTransaction(); return true; } else { $this->FailTransaction(); } } else { //Always fail the transaction if we get this far. //This stops authorization entries from being inserted. $this->FailTransaction(); } $this->CommitTransaction(); //preSave() starts the transaction return false; } $this->CommitTransaction(); //preSave() starts the transaction return true; } /** * @param $data * @return bool */ function setObjectFromArray( $data ) { if ( is_array( $data ) ) { $variable_function_map = $this->getVariableToFunctionMap(); foreach ( $variable_function_map as $key => $function ) { if ( isset( $data[$key] ) ) { $function = 'set' . $function; switch ( $key ) { default: if ( method_exists( $this, $function ) ) { $this->$function( $data[$key] ); } break; } } } $this->setCreatedAndUpdatedColumns( $data ); return true; } return false; } /** * @param null $include_columns * @param bool $permission_children_ids * @return array */ function getObjectAsArray( $include_columns = null, $permission_children_ids = false ) { $data = []; $variable_function_map = $this->getVariableToFunctionMap(); if ( is_array( $variable_function_map ) ) { foreach ( $variable_function_map as $variable => $function_stub ) { if ( $include_columns == null || ( isset( $include_columns[$variable] ) && $include_columns[$variable] == true ) ) { $function = 'get' . $function_stub; switch ( $variable ) { case 'object_type': Debug::text( ' Object Type...', __FILE__, __LINE__, __METHOD__, 10 ); $data[$variable] = Option::getByKey( $this->getObjectType(), $this->getOptions( $variable ) ); break; default: if ( method_exists( $this, $function ) ) { $data[$variable] = $this->$function(); } break; } } } $this->getPermissionColumns( $data, $this->getColumn( 'user_id' ), $this->getCreatedBy(), $permission_children_ids, $include_columns ); $this->getCreatedAndUpdatedColumns( $data, $include_columns ); } return $data; } /** * @param $log_action * @return bool */ function addLog( $log_action ) { if ( $this->getAuthorized() === true ) { $authorized = TTi18n::getText( 'True' ); } else { $authorized = TTi18n::getText( 'False' ); } return TTLog::addEntry( $this->getId(), $log_action, TTi18n::getText( 'Authorization Object Type' ) . ': ' . ucwords( str_replace( '_', ' ', Option::getByKey( $this->getObjectType(), $this->getOptions( 'object_type' ) ) ) ) . ' ' . TTi18n::getText( 'Authorized' ) . ': ' . $authorized, null, $this->getTable() ); } } ?>