PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/wp/wp-content/plugins/hris-leave/includes/balance.php

https://bitbucket.org/akeda/bmw-id-hris
PHP | 368 lines | 227 code | 51 blank | 90 comment | 46 complexity | 59384f90e91fc358cba81562b5b9dc5b MD5 | raw file
  1. <?php
  2. /**
  3. * Leave balance.
  4. *
  5. * @package HRIS Leave
  6. * @since 0.1.0
  7. * @author Akeda Bagus <admin@gedex.web.id>
  8. */
  9. class HRIS_Leave_Balance implements HRIS_Leave_Component_Interface {
  10. /**
  11. *
  12. * @var array
  13. */
  14. private $_post_pre_deleted = array();
  15. /**
  16. * User meta key that stores leave balance.
  17. */
  18. const LEAVE_BALANCE_USER_META = 'leave_balance';
  19. public function load() {
  20. add_action( 'transition_post_status', array( $this, 'on_transition_status' ), 10, 3 );
  21. add_action( 'before_delete_post', array( $this, 'on_before_delete_post' ), 1 );
  22. add_action( 'deleted_post', array( $this, 'on_deleted_post' ), 1 );
  23. $post_type = HRIS_Leave_Post_Type::NAME;
  24. // Fills customized columns.
  25. add_action( 'manage_' . $post_type . '_posts_custom_column', array( $this, 'custom_columns_row' ), 99, 2 );
  26. // Saves leave balance into user meta.
  27. add_action( 'on_sanitizing_leave_balance_users', array( $this, 'on_sanitizing_leave_balance_users' ) );
  28. add_action( 'load-post-new.php', array( $this, 'show_remaining_leave_as_notice' ) );
  29. add_action( 'load-edit.php', array( $this, 'show_remaining_leave_as_notice' ) );
  30. }
  31. /**
  32. * Is a given leave a planned leave. Check its leave type term.
  33. *
  34. * @param mixed $post
  35. * @return bool
  36. */
  37. public function is_planned_leave( $post ) {
  38. $terms = get_the_terms( $post, HRIS_Leave_Type_Taxonomy::NAME );
  39. if ( ! $terms )
  40. return false;
  41. if ( is_wp_error( $terms ) )
  42. return false;
  43. $first_term = array_shift( $terms );
  44. // Get the meta
  45. $term_meta = get_option( 'taxonomy_' . $first_term->term_id );
  46. return ( isset( $term_meta['is_planned'] ) && $term_meta['is_planned'] );
  47. }
  48. /**
  49. * If leave is approved, increase number of approved of user meta leave balance.
  50. *
  51. * @param string $new_status Transition to this post status.
  52. * @param string $old_status Previous post status.
  53. * @param object $post Post data.
  54. */
  55. public function on_transition_status( $new_status, $old_status, $post ) {
  56. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
  57. return;
  58. if ( wp_is_post_revision( $post->ID ) )
  59. return;
  60. $excluded_status = array( 'auto-draft', 'draft', 'future', 'private', 'inherit', 'trash' );
  61. if ( in_array( $new_status, $excluded_status ) )
  62. return;
  63. // Unplanned leave?
  64. if ( ! $this->is_planned_leave( $post ) )
  65. return;
  66. $start_date = get_field( 'start_date', $post->ID );
  67. $strtime_start = strtotime( $start_date );
  68. $year = intval( date( 'Y', $strtime_start ) );
  69. $month_start = intval( date( 'm', $strtime_start ) );
  70. // If from another post status to approved then increases approved_per_month.
  71. // Please note that using this approach means that number of approved belongs
  72. // to year/month of start date. This is fine per BMW ID' request.
  73. if ( HRIS_Approval_Setting::APPROVED !== $old_status && HRIS_Approval_Setting::APPROVED === $new_status ) {
  74. $number_of_days = get_field( 'number_of_days', $post->ID );
  75. $this->increase_approved( $post->post_author, $year, $month_start, $number_of_days );
  76. }
  77. }
  78. /**
  79. * When a leave deleted, decrease number of approved.
  80. *
  81. * @action deleted_post
  82. * @param int $post_id Post ID
  83. * @return void
  84. */
  85. public function on_before_delete_post( $post_id ) {
  86. $post_obj = get_post( $post_id );
  87. if ( HRIS_Leave_Post_Type::NAME !== $post_obj->post_type )
  88. return;
  89. if ( HRIS_Approval_Setting::APPROVED !== $post_obj->post_status )
  90. return;
  91. // Unplanned leave?
  92. if ( ! $this->is_planned_leave( $post ) )
  93. return;
  94. $start_date = get_field( 'start_date', $post_id );
  95. $end_date = get_field( 'end_date', $post_id );
  96. $strtime_start = strtotime( $start_date );
  97. $strtime_end = strtotime( $end_date );
  98. $year = intval( date( 'Y', $strtime_start ) );
  99. $month_start = intval( date( 'm', $strtime_start ) );
  100. $month_end = intval( date( 'm', $strtime_end ) );
  101. if ( $month_end === $month_start ) {
  102. $days = hris_get_number_of_days( $start_date, $end_date );
  103. $month_start_days = $days['number_of_days'];
  104. $month_end_days = 0;
  105. } else {
  106. $days_start = hris_get_number_of_days( $start_date, sprintf( '%d-%d-31', $year, $month_start ) );
  107. $days_end = hris_get_number_of_days( sprintf( '%d-%d-01', $year, $month_end ), $end_date );
  108. $month_start_days = $days_start['number_of_days'];
  109. $month_end_days = $days_end['number_of_days'];
  110. }
  111. $user_id = $post_obj->post_author;
  112. $this->_post_pre_deleted[ $post_id ] = compact(
  113. 'user_id', 'year', 'month_start', 'month_end', 'month_start_days', 'month_end_days'
  114. );
  115. }
  116. public function on_deleted_post( $post_id ) {
  117. if ( isset( $this->_post_pre_deleted[ $post_id ] ) ) {
  118. /**
  119. * @var $user_id
  120. * @var $year
  121. * @var $month_start
  122. * @var $month_end
  123. * @var $month_start_days
  124. * @var $month_end_days
  125. */
  126. extract( $this->_post_pre_deleted[ $post_id ] );
  127. if ( $month_end === $month_start ) {
  128. $this->decrease_approved( $user_id, $year, $month_start, $month_start_days );
  129. } else {
  130. $this->decrease_approved( $user_id, $year, $month_start, $month_start_days );
  131. $this->decrease_approved( $user_id, $year, $month_end, $month_end_days );
  132. }
  133. unset( $this->_post_pre_deleted[ $post_id ] );
  134. }
  135. }
  136. /**
  137. * Saves leave balance into user meta too with nested array with following structure:
  138. *
  139. * ```
  140. * array(
  141. * 2013 => array(
  142. * 'balance' => 24,
  143. * 'approved_per_month' => array(
  144. * 1 => 2,
  145. * ...
  146. * 12 => 4,
  147. * )
  148. * )
  149. * )
  150. * ```
  151. *
  152. * @param array $val
  153. */
  154. public function on_sanitizing_leave_balance_users( $val ) {
  155. $default_balance = $val['default_annual_leave_balance'];
  156. $current_year = intval( date('Y') ); // @todo Use date helper from WordPress?
  157. $default_meta = array(
  158. $current_year => array(
  159. 'balance' => $default_balance,
  160. 'approved_per_month' => array_fill( 1, 12, 0 ),
  161. ),
  162. );
  163. foreach ( $val['leave_balance_users'] as $user_id => $balance ) {
  164. $meta_leave = get_user_meta( $user_id, self::LEAVE_BALANCE_USER_META, true );
  165. $balance = (double)$balance;
  166. if ( ! $meta_leave ) {
  167. $new_val = $default_meta;
  168. $new_val[ $current_year ]['balance'] = $balance;
  169. } else {
  170. if ( ! isset( $meta_leave[ $current_year ] ) ) {
  171. $meta_leave[ $current_year ] = array(
  172. 'balance' => $balance,
  173. 'approved_per_month' => array_fill( 1, 12, 0 ),
  174. );
  175. } else {
  176. $meta_leave[ $current_year ]['balance'] = $balance;
  177. }
  178. $new_val = $meta_leave;
  179. }
  180. update_user_meta( $user_id, self::LEAVE_BALANCE_USER_META, $new_val );
  181. }
  182. }
  183. public function custom_columns_row( $column, $post_id ) {
  184. if ( 'balance' === $column ) {
  185. $post_obj = get_post( $post_id );
  186. $year = intval( date( 'Y', strtotime( get_field( 'start_date', $post_id ) ) ) );
  187. $leave_balance = $this->get_leave_balance( $post_obj->post_author, $year );
  188. if ( isset( $leave_balance['approved_per_month'] ) && is_array( $leave_balance['approved_per_month'] ) ) {
  189. echo $this->get_remaining_leave( $post_obj->post_author, $year );
  190. } else {
  191. echo sprintf( __( 'The leave balance is not ready for %d.', 'hris-leave' ), $year );
  192. }
  193. }
  194. }
  195. /**
  196. * Show remaining leave inside notice in post-new.php
  197. *
  198. * @action load-post-new.php
  199. */
  200. public function show_remaining_leave_as_notice() {
  201. if ( ! isset( $_GET['post_type'] ) )
  202. return;
  203. if ( HRIS_Leave_Post_Type::NAME !== $_GET['post_type'] )
  204. return;
  205. $current_user = wp_get_current_user();
  206. if ( hris_check_user_role( 'administrator', $current_user->ID ) )
  207. return;
  208. wp_enqueue_style( 'leave-remaining-balance-notice', HRIS_LEAVE_URI . '/css/leave-remaining-balance-notice.css' );
  209. $total_approved = $this->get_remaining_leave( $current_user->ID, date( 'Y' ) );
  210. add_action( 'admin_notices', function() use( $total_approved ) {
  211. printf(
  212. '<div class="updated" id="leave-remaining-balance-notice"><p>%s</p></div>',
  213. sprintf( __( 'Your remaining leave balance for this year is <strong>%g</strong> days.', 'hris-leave' ), $total_approved )
  214. );
  215. } );
  216. }
  217. /**
  218. * Gets leave balance from particular user on a given year.
  219. *
  220. * @param int $user_id User ID
  221. * @param int $year
  222. * @param int $month
  223. * @return array
  224. */
  225. public function get_leave_balance( $user_id, $year, $month = null ) {
  226. $leave_balance = get_user_meta( $user_id, self::LEAVE_BALANCE_USER_META, true );
  227. $year = intval( $year );
  228. if ( ! $leave_balance )
  229. return null;
  230. if ( ! isset( $leave_balance[ $year ] ) )
  231. return null;
  232. if ( ! $month )
  233. return $leave_balance[ $year ];
  234. if ( ! isset( $leave_balance[ $year ]['approved_per_month'][ $month ] ) )
  235. return null;
  236. return $leave_balance[ $year ]['approved_per_month'][ $month ];
  237. }
  238. public function get_total_approved( $user_id, $year ) {
  239. $leave_balance = $this->get_leave_balance( $user_id, $year );
  240. if ( isset( $leave_balance['approved_per_month'] ) && is_array( $leave_balance['approved_per_month'] ) ) {
  241. $total_approved = 0;
  242. foreach ( $leave_balance['approved_per_month'] as $approved ) {
  243. $total_approved += $approved;
  244. }
  245. return $total_approved;
  246. }
  247. return 0;
  248. }
  249. public function get_remaining_leave( $user_id, $year ) {
  250. $leave_balance = $this->get_leave_balance( $user_id, $year );
  251. $balance = isset( $leave_balance['balance'] ) ? $leave_balance['balance'] : 0;
  252. $total_approved = $this->get_total_approved( $user_id, $year );
  253. $remaining = $balance - $total_approved;
  254. if ( $remaining >= 0 ) {
  255. return $remaining;
  256. }
  257. return 0;
  258. }
  259. /**
  260. * Increases total approved of a user for particlar year and month
  261. *
  262. * @param int $user_id User ID
  263. * @param int $year
  264. * @param int $month
  265. * @param int $number_of_days
  266. * @param string $operation Whether 'add', 'increase', 'decrease', 'sub', or 'subtract'
  267. */
  268. public function alter_approved( $user_id, $year, $month, $number_of_days, $operation = 'add' ) {
  269. $year = intval( $year );
  270. $month = intval( $month );
  271. $number_of_days = (double)$number_of_days;
  272. $setting = hris_get_component_from_module( 'leave', 'Setting' );
  273. $users_balance = $setting->get_setting( 'leave_balance_users' );
  274. $default_values = array(
  275. 'balance' => $users_balance[ $user_id ],
  276. 'approved_per_month' => array(
  277. $month => $number_of_days,
  278. ),
  279. );
  280. $meta_value = get_user_meta( $user_id, self::LEAVE_BALANCE_USER_META, true );
  281. if ( ! $meta_value ) {
  282. $meta_value = array(
  283. $year => $default_values,
  284. );
  285. } else {
  286. if ( ! isset( $meta_value[ $year ] ) ) {
  287. $meta_value[ $year ] = $default_values;
  288. } else if ( ! isset( $meta_value[ $year ]['approved_per_month'] ) ) {
  289. $meta_value[ $year ] = $default_values['approved_per_month'];
  290. } else if ( ! isset( $meta_value[ $year ]['approved_per_month'][ $month ] ) ) {
  291. $meta_value[ $year ]['approved_per_month'] = $default_values['approved_per_month'][ $month ];
  292. } else { // Increase.
  293. switch ( $operation ) {
  294. case 'decrease':
  295. case 'sub':
  296. case 'subtract':
  297. if ( (double) $meta_value[ $year ]['approved_per_month'][ $month ] >= (double) $number_of_days ) {
  298. $meta_value[ $year ]['approved_per_month'][ $month ] = (double)$meta_value[ $year ]['approved_per_month'][ $month ] - (double)$number_of_days;
  299. }
  300. break;
  301. case 'add':
  302. case 'increase':
  303. default:
  304. $meta_value[ $year ]['approved_per_month'][ $month ] = (double)$meta_value[ $year ]['approved_per_month'][ $month ] + (double)$number_of_days;
  305. break;
  306. }
  307. }
  308. }
  309. update_user_meta( $user_id, self::LEAVE_BALANCE_USER_META, $meta_value );
  310. }
  311. public function increase_approved( $user_id, $year, $month, $number_of_days ) {
  312. $this->alter_approved( $user_id, $year, $month, $number_of_days, 'increase' );
  313. }
  314. public function decrease_approved( $user_id, $year, $month, $number_of_days ) {
  315. $this->alter_approved( $user_id, $year, $month, $number_of_days, 'decrease' );
  316. }
  317. }