timesheet source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

505 lines
18KB

  1. <?php
  2. /**
  3. * Plugin Name: Acare Advanced Office
  4. * Plugin URI: http://biukop.com.au/acaresydney/timesheets
  5. * Description: Advanced Office system, timesheet, Payroll for AcareSydney
  6. * Version: 2.1
  7. * Author: Biukop Intelligence
  8. * Author URI: http://biukop.com.au/
  9. */
  10. namespace Biukop;
  11. use XeroPHP\Models\Accounting\Address;
  12. require_once(dirname(__FILE__) . '/autoload.php');
  13. require_once (ABSPATH . 'wp-includes/pluggable.php');
  14. class AcareOffice{
  15. private $nonce; //for ajax verification
  16. private $pages = array('time-sheets', 'user-list');
  17. private $acaresydney_userid = 0;
  18. private $xero ;
  19. private $db;
  20. private $table_name;
  21. private $addr_table;
  22. public function __construct() {
  23. add_option( "acare_ts_db_version", "1.0" );
  24. register_activation_hook( __FILE__, array($this, 'db_install') );
  25. add_action('init', array($this, 'class_loader'));
  26. add_action('wp', array($this, 'check_auth'));
  27. add_action('wp_enqueue_scripts', array($this, 'register_js_css'), 99);
  28. add_filter('show_admin_bar', '__return_false');
  29. //ts-xx for sync single user
  30. add_shortcode( 'ts-sync-users', array($this, 'sync_users'));
  31. //bts-xx for webpage
  32. add_shortcode( 'bts_staff_item', array($this, 'bts_staff_item'));
  33. add_shortcode( 'bts_client_item', array($this, 'bts_client_item'));
  34. add_shortcode( 'bts_job_item', array($this, 'bts_job_item'));
  35. add_shortcode( 'bts_rate_options', array($this, 'bts_rate_options'));
  36. add_shortcode( 'bts_select_staff', array($this, 'bts_select_staff'));
  37. add_shortcode( 'bts_select_client', array($this, 'bts_select_client'));
  38. add_shortcode( 'bts_type_of_service', array($this, 'bts_type_of_service'));
  39. //user profile page
  40. add_shortcode( 'bts_user_name', array($this,'bts_user_name'));
  41. add_action('wp_ajax_list_staff', array($this,'list_staff' ));
  42. add_action('wp_ajax_list_client', array($this,'list_client' ));
  43. add_action('wp_ajax_save_job', array($this,'save_job' ));
  44. add_action('wp_ajax_list_job', array($this,'list_job' ));
  45. add_action('wp_ajax_delete_job', array($this,'delete_job' ));
  46. add_action('wp_ajax_earnings_rate', array($this,'get_payitem_earnings_rate' ));
  47. add_action('wp_ajax_nopriv_earnings_rate', array($this,'get_payitem_earnings_rate' ));
  48. // hook add_rewrite_rules function into rewrite_rules_array
  49. add_filter('rewrite_rules_array', array($this,'my_add_rewrite_rules'));
  50. // hook add_query_vars function into query_vars
  51. add_filter('query_vars', array($this,'add_query_vars'));
  52. global $wpdb;
  53. $this->db = $wpdb;
  54. $this->table_name = $wpdb->prefix . 'acare_ts';
  55. $this->addr_table = $wpdb->prefix . 'acare_addr_distance';
  56. $this->ndis_table = $wpdb->prefix . 'acare_ndis_price';
  57. }
  58. /**
  59. * Autoload the custom theme classes
  60. */
  61. public function class_loader()
  62. {
  63. // Create a new instance of the autoloader
  64. $loader = new \Psr4AutoloaderClass();
  65. // Register this instance
  66. $loader->register();
  67. // Add our namespace and the folder it maps to
  68. $loader->addNamespace('\XeroPHP', dirname(__FILE__) . '/xero-php-master/src/XeroPHP');
  69. $loader->addNamespace('\Biukop', dirname(__FILE__) . '/' );
  70. $this->xero = new Xero();
  71. $this->xero->init_wp();
  72. //$abc = new AddrMap("01515b52-6936-46b2-a000-9ad4cd7a5b50", "0768db6d-e5f4-4b45-89a2-29f7e8d2953c");
  73. $abc = new AddrMap("122eb1d0-d8c4-4fc3-8bf8-b7825bee1a01", "0768db6d-e5f4-4b45-89a2-29f7e8d2953c");
  74. }
  75. //init database
  76. public function db_install () {
  77. global $wpdb;
  78. $charset_collate = $wpdb->get_charset_collate();
  79. //table name: timesheets jobs
  80. $table_name = $this->table_name;
  81. $sql = "CREATE TABLE $table_name (
  82. id INT NOT NULL AUTO_INCREMENT,
  83. tos VARCHAR(45) NULL,
  84. start DATETIME NULL,
  85. finish DATETIME NULL,
  86. rate VARCHAR(45) NULL,
  87. staff VARCHAR(45) NULL,
  88. client VARCHAR(45) NULL,
  89. ack TINYINT(4) NULL,
  90. rating INT(4) NULL DEFAULT 0,
  91. PRIMARY KEY (id)
  92. ) $charset_collate;";
  93. //addr distance
  94. $addr_table = $this->addr_table;
  95. $sql_addr = "CREATE TABLE $addr_table (
  96. id INT NOT NULL AUTO_INCREMENT,
  97. origin VARCHAR(1024) NULL,
  98. destination VARCHAR(1024) NULL,
  99. response VARCHAR(40960) NULL,
  100. distance INT NULL,
  101. PRIMARY KEY (id)
  102. ) $charset_collate;";
  103. $ndis_table = $this->ndis_table;
  104. $sql_ndis_price = "
  105. CREATE TABLE $ndis_table (
  106. code VARCHAR(45) NOT NULL,
  107. name VARCHAR(45) NULL,
  108. level INT NULL,
  109. unit VARCHAR(45) NULL,
  110. price FLOAT NULL,
  111. year INT NOT NULL,
  112. PRIMARY KEY (code, year)
  113. )$charset_collate;";
  114. //create database
  115. require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
  116. dbDelta( $sql );
  117. dbDelta( $sql_addr);
  118. dbDelta( $sql_ndis_price);
  119. }
  120. //
  121. //query var
  122. public function add_query_vars($aVars) {
  123. $aVars[] = "bts_user_id"; // represents the name of the product category as shown in the URL
  124. return $aVars;
  125. }
  126. //for customer profile and broker trans
  127. public function my_add_rewrite_rules($aRules) {
  128. $aNewRules = array('user/([^/]+)/?$' => 'index.php?pagename=user&bts_user_id=$matches[1]');
  129. $aRules = $aNewRules + $aRules;
  130. return $aRules;
  131. }
  132. //
  133. //
  134. ///check auth
  135. public function check_auth(){
  136. global $pagename;
  137. //echo $pagename;
  138. }
  139. ///
  140. // enqueue / register css /js
  141. //
  142. public function register_js_css() {
  143. $this->nonce = wp_create_nonce('acaresydney');
  144. $this->acaresydney_userid = get_query_var( 'acaresydney_userid' ) ;
  145. $this->register_bts_js();
  146. $this->register_timesheet_js_css();
  147. }
  148. private function register_bts_js()
  149. {
  150. wp_enqueue_style( 'bts', plugins_url('css/ts.css', __FILE__));
  151. wp_enqueue_script('bts', plugins_url('js/ts.js', __FILE__), array('jquery', 'jquery-ui-core'));
  152. wp_localize_script( 'bts', 'bts1', array(
  153. 'ajax_url' => admin_url( 'admin-ajax.php' ),
  154. 'nonce' => $this->nonce, // It is common practice to comma after
  155. 'display_name' => wp_get_current_user()->display_name,
  156. 'anonymous' => !is_user_logged_in(),
  157. 'me'=> get_current_user_id(),
  158. 'userid'=> $this->acaresydney_userid,
  159. 'load_user_img'=> plugins_url('img/loading_user.gif', __FILE__),
  160. 'load_job_img'=> plugins_url('img/loading_job.gif', __FILE__),
  161. 'earnings_rate'=> get_option('bts_payitem_earnings_rate'),
  162. 'high_pay_keywords' => ['sat ', 'sun ', 'high ', 'public holiday'], //space is important
  163. ) );
  164. }
  165. private function register_timesheet_js_css(){
  166. global $pagename;
  167. if ($pagename != 'time-sheets'){
  168. return;
  169. }
  170. wp_enqueue_style( 'bts_ts', plugins_url('css/bts_timesheet.css', __FILE__));
  171. wp_enqueue_script( 'bts_ts', plugins_url('js/bts_timesheet.js', __FILE__), array( 'jquery' , 'bts' ));
  172. wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery'));
  173. }
  174. public function sync_users()
  175. {
  176. //dummy sync
  177. return;
  178. }
  179. // Usage: `wp sync_users --mininterval=123
  180. public function sync_user_cli($args = array(), $assoc_args = array()){
  181. $arguments = wp_parse_args( $assoc_args, array(
  182. 'mininterval' => 600,
  183. ) );
  184. $this->xero->sync_users($arguments['mininterval']);
  185. return;
  186. }
  187. public function bts_staff_item($attr){
  188. return $this->template('staff_item', 'staff.html');
  189. }
  190. public function bts_client_item($attr){
  191. return $this->template('client_item', 'client.html');
  192. }
  193. public function bts_job_item($attr){
  194. $html =$this->template('job_item', 'job.html');
  195. //$html = str_replace('[bts-tos-options]', $this->bts_tos_options([]), $html);
  196. $html = do_shortcode($html);
  197. return $html;
  198. }
  199. public function bts_rate_options($attr){
  200. $result = "<select> \n";
  201. $options = get_option('bts_payitem_earnings_rate');
  202. foreach($options as $o){
  203. $result.=sprintf("<option value='%s'> $%3.2f-%s</option>",
  204. $o['EarningsRateID'], $o['RatePerUnit'], $o['Name']);
  205. }
  206. $result .="</select>";
  207. return $result;
  208. }
  209. public function bts_select_staff($attr){
  210. $result = "<select> \n";
  211. $staff = $this->get_people_by_role('staff');
  212. foreach ($staff as $u){
  213. $result .= sprintf("<option value=%s> %s</option>", $u->user_login, $u->first_name . " " . $u->last_name);
  214. }
  215. $result .="</select>";
  216. return $result;
  217. }
  218. public function bts_select_client($attr){
  219. $result = "<select> \n";
  220. $staff = $this->get_people_by_role('client');
  221. foreach ($staff as $u){
  222. $result .= sprintf("<option value=%s> %s</option>", $u->user_login, $u->first_name . " " . $u->last_name);
  223. }
  224. $result .="</select>";
  225. return $result;
  226. }
  227. public function bts_type_of_service($attr){
  228. $n = new NdisPrice(2019);
  229. return $n->get_html();
  230. }
  231. public function bts_user_name($attr)
  232. {
  233. //echo '<div class="vcex-module vcex-heading vcex-heading-bottom-border-w-color wpb_animate_when_almost_visible wpb_bounceInUp bounceInUp bts_user_name aligncenter wpb_start_animation animated" style="width:100%;"><span class="vcex-heading-inner clr">A hahah</span></div>;';
  234. $content = '[vc_row wpex_bg_overlay="color" wpex_bg_overlay_color="#ffffff"][vc_column][vcex_heading text="Heading13331" style="bottom-border-w-color" css_animation="flipInY" icon="fa fa-handshake-o" inner_bottom_border_color="#000000" width="100%;"][/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row]';
  235. echo do_shortcode($content);
  236. }
  237. //generate template based on html file
  238. private function template($id, $file)
  239. {
  240. $text = '<script id="' . $id .'" type="text/x-biukop-template">';
  241. $text .= file_get_contents(plugin_dir_path(__FILE__) . "/html/$file");
  242. $text .= '</script>';
  243. return $text;
  244. }
  245. function list_staff(){
  246. check_ajax_referer('acaresydney');
  247. // Handle the ajax request
  248. $response = array(
  249. 'status' =>'error',
  250. 'users' => [],
  251. );
  252. //search all users that are staff
  253. $staffq = new \WP_User_Query(array('role'=>'staff','meta_key'=>'first_name', 'orderby'=>'meta_value', 'order'=>'ASC'));
  254. $staff = $staffq->get_results();
  255. if (! empty($staff)){
  256. $response['status'] = 'success';
  257. foreach( $staff as $s){
  258. $response['users'][] = array(
  259. 'login' => $s->user_login,
  260. 'firstname'=> $s->first_name,
  261. 'lastname'=> $s->last_name,
  262. 'mobile'=> get_user_meta($s->ID, 'mobile', true),
  263. 'email'=> $s->user_email,
  264. 'wages'=> 0,
  265. 'hour' => 0 ,
  266. 'OT' => 0 ,
  267. 'petrol'=> 0 ,
  268. 'rating'=> 0,
  269. 'unconfirmedjob'=> 0,
  270. );
  271. }
  272. }
  273. wp_send_json($response);
  274. wp_die();
  275. }
  276. function list_client(){
  277. check_ajax_referer('acaresydney');
  278. // Handle the ajax request
  279. $response = array(
  280. 'status' =>'error',
  281. 'users' => [],
  282. );
  283. //search all users that are staff
  284. $clientq = new \WP_User_Query(array('role'=>'client', 'meta_key'=>'first_name', 'orderby'=>'meta_value', 'order'=>'ASC'));
  285. $client = $clientq->get_results();
  286. if (! empty($client)){
  287. $response['status'] = 'success';
  288. foreach( $client as $s){
  289. $response['users'][] = array(
  290. 'login' => $s->user_login,
  291. 'firstname'=> $s->first_name,
  292. 'lastname'=> $s->last_name,
  293. 'mobile'=> get_user_meta($s->ID, 'mobile', true),
  294. 'email'=> $s->user_email,
  295. 'account'=> get_user_meta($s->ID, 'account', true),
  296. 'address' => get_user_meta($s->ID, 'address', true),
  297. 'rating'=> 0,
  298. 'unconfirmedjob'=> 0,
  299. );
  300. }
  301. }
  302. wp_send_json($response);
  303. wp_die();
  304. }
  305. private function get_people_by_role($role){
  306. //search all users that are staff
  307. $staffq = new \WP_User_Query(array('role'=>$role, 'meta_key'=>'first_name', 'orderby'=>'meta_value', 'order'=>'ASC'));
  308. $staff = $staffq->get_results();
  309. return $staff;
  310. }
  311. //ajax get earnings rates
  312. function get_payitem_earnings_rate()
  313. {
  314. $response= array(
  315. 'status' => 'success',
  316. 'options'=> get_option('bts_payitem_earnings_rate'),
  317. );
  318. wp_send_json($response);
  319. }
  320. //ajax job CRUD
  321. function save_job()
  322. {
  323. check_ajax_referer('acaresydney');
  324. $r = $_POST['record'];
  325. $response = array();
  326. $d = array(
  327. 'tos' => $r['tos'],
  328. 'start' => $r['start'],
  329. 'finish' => $r['finish'],
  330. 'rate' => $r['rate'],
  331. 'staff' => $r['staff'],
  332. 'client' => $r['client'],
  333. 'ack' => $r['ack']=='true'?1:0,
  334. 'rating'=>$r['rating'],
  335. );
  336. //this is an update
  337. if ( isset($r['id']) && trim($r['id']) !='' && is_numeric($r['id'])){
  338. $response['isNew'] = false; //add or update?
  339. $result = $this->db->update($this->table_name, $d, array('id' =>$r['id']));
  340. if ($result !== false && $this->db->last_error == ''){
  341. $d['id'] = $r['id'];
  342. $response['status'] = 'success';
  343. //do data type conversion, string to int
  344. $response['newdata'] = $this->get_ts_record($r['id']);
  345. $response['errors'] = array(); //empty array
  346. }else{
  347. $response['status'] = 'error';
  348. $repsonse['errors'] = array(
  349. 'db' => "network database error" . $this->db->last_error,
  350. );
  351. }
  352. }else{
  353. $response['isNew'] = true;
  354. $result = $this->db->insert($this->table_name, $d);
  355. $lastid = $this->db->insert_id;
  356. if ($result != false && $this->db->last_error == ''){
  357. $response['status'] = 'success';
  358. $response['newdata'] = $this->get_ts_record($lastid);
  359. }else{
  360. $response['status'] = 'error';
  361. $response['errors'] = array(
  362. 'db' => 'network database error ' . $this->db->last_error,
  363. );
  364. }
  365. }
  366. wp_send_json($response);
  367. wp_die();
  368. }
  369. private function get_ts_record($id){
  370. $sql = "SELECT * FROM $this->table_name WHERE id=%d";
  371. $row = $this->db->get_row($this->db->prepare ($sql, array($id)));
  372. $response = [];
  373. if ($row != null){
  374. $response = array(
  375. 'id' => (int)$row->id,
  376. 'tos' => $row->tos,
  377. 'start' => $row->start,
  378. 'finish' => $row->finish,
  379. 'rate' => $row->rate,
  380. 'staff' => $row->staff,
  381. 'client' => $row->client,
  382. 'ack' => (int)$row->ack,
  383. 'rating' =>(int) $row->rating,
  384. );
  385. }
  386. return $response;
  387. }
  388. //ajax delete job
  389. function delete_job(){
  390. check_ajax_referer('acaresydney');
  391. $id = $_POST['jobid'];
  392. $result = $this->db->delete($this->table_name, array('id'=> $id));
  393. $response=array(
  394. 'status' => 'success',
  395. 'id' => $id,
  396. 'action'=> 'delete',
  397. 'error' => '',
  398. );
  399. if ($result == 1){
  400. wp_send_json($response);
  401. }else{
  402. $response['status'] = 'error';
  403. $response['error'] = $this->db->last_error;
  404. wp_send_json($response);
  405. }
  406. wp_die();
  407. }
  408. //ajax browse job with different filters
  409. function list_job(){
  410. check_ajax_referer('acaresydney');
  411. $start = $_POST['start'];
  412. $finish = $_POST['finish'];
  413. $response = array(
  414. 'status'=>'success',
  415. 'jobs' => [],
  416. );
  417. $sql = "SELECT * FROM $this->table_name WHERE start>='%s' and start <='%s' order by start ASC ,staff ASC";
  418. $jobs = $this->db->get_results($this->db->prepare ($sql, array($start, $finish)));
  419. if (! empty($jobs)){
  420. $response['status'] = 'success';
  421. foreach( $jobs as $s){
  422. $response['jobs'][] = array(
  423. 'id' => $s->id,
  424. 'tos' => $s->tos,
  425. 'start'=> $s->start,
  426. 'finish'=> $s->finish,
  427. 'rate'=> $s->rate,
  428. 'staff'=> $s->staff,
  429. 'client'=> $s->client,
  430. 'ack' => $s->ack,
  431. 'rating' =>$s->rating,
  432. );
  433. }
  434. }
  435. wp_send_json($response);
  436. wp_die();
  437. }
  438. }
  439. $bb = new AcareOffice();
  440. if ( defined( 'WP_CLI' ) && WP_CLI ) {
  441. \WP_CLI::add_command( 'sync_users', array($bb, 'sync_user_cli'));
  442. }