setup_db_name(); $this->class_loader(); $this->apiv1 = new Apiv1($this, $this->job_table); $this->XeroOauth2 = new XeroOauth2($this); //$this->check_csv_download(); add_option( "acare_ts_db_version", "1.0" ); register_activation_hook( __FILE__, array($this, 'db_install') ); //add_action('init', array($this, 'class_loader')); add_action('init', array($this, 'check_csv_download')); add_action('wp', array($this, 'check_auth')); add_action('wp_enqueue_scripts', array($this, 'register_js_css'), 99); add_filter('show_admin_bar', '__return_false'); //bts-xx for webpage add_shortcode( 'bts_staff_item', array($this, 'bts_staff_item')); add_shortcode( 'bts_client_item', array($this, 'bts_client_item')); add_shortcode( 'bts_job_item', array($this, 'bts_job_item')); add_shortcode( 'bts_rate_options', array($this, 'bts_rate_options')); add_shortcode( 'bts_select_staff', array($this, 'bts_select_staff')); add_shortcode( 'bts_select_client', array($this, 'bts_select_client')); add_shortcode( 'bts_type_of_service', array($this, 'bts_type_of_service')); add_shortcode( 'bts_staff_job_summary', array($this, 'bts_staff_job_summary')); add_shortcode( 'bts_feedback_card', array($this, 'bts_feedback_card')); add_shortcode( 'bb_timesheet_canvas', array($this, 'bb_timesheet_canvas')); add_shortcode( 'bts_staff_hours_template', array($this, 'bts_staff_hours_template')); add_shortcode( 'bts_client_invoice_template', array($this, 'bts_client_invoice_template')); add_shortcode( 'bts_csv_template', array($this, 'bts_csv_template')); add_shortcode( 'bts_invoiced_client', array($this, 'bts_invoiced_client')); //user profile page add_shortcode( 'bts_user_name', array($this,'bts_user_name')); $this->ajax_hook('list_staff'); $this->ajax_hook('list_client'); $this->ajax_hook('list_tos'); $this->ajax_hook('save_job'); $this->ajax_hook('list_job'); $this->ajax_hook('delete_job'); $this->ajax_hook('email_job'); $this->ajax_hook('email_feedback_url'); $this->ajax_hook('earnings_rate'); $this->ajax_hook('list_job_by_staff'); $this->ajax_hook('staff_ack_job'); $this->ajax_hook('list_job_by_client'); $this->ajax_hook('client_ack_job'); $this->ajax_hook('get_timesheet_from_xero'); $this->ajax_hook('approve_all_timesheet'); $this->ajax_hook('get_invoice_item'); $this->ajax_hook('create_invoice_in_xero'); // hook add_rewrite_rules function into rewrite_rules_array add_filter('rewrite_rules_array', array($this,'my_add_rewrite_rules')); // hook add_query_vars function into query_vars add_filter('query_vars', array($this,'add_query_vars')); } private function ajax_hook($code, $admin_only = false) { add_action("wp_ajax_$code", array($this,$code )); if (!$admin_only) { add_action("wp_ajax_nopriv_$code", array($this,$code)); } } private function setup_db_name() { global $wpdb; $this->db = $wpdb; $this->table_name = $wpdb->prefix . 'acare_ts'; //for backward compatability; $this->job_table = $wpdb->prefix . 'acare_ts'; $this->allowance_table = $wpdb->prefix . 'acare_allowance'; $this->addr_table = $wpdb->prefix . 'acare_addr_distance'; $this->ndis_table = $wpdb->prefix . 'acare_ndis_price'; } /** * Autoload the custom theme classes */ public function class_loader() { // Create a new instance of the autoloader $loader = new \Psr4AutoloaderClass(); // Register this instance $loader->register(); // Add our namespace and the folder it maps to // $loader->addNamespace('\XeroPHP', dirname(__FILE__) . '/xero-php-master/src/XeroPHP'); $loader->addNamespace('\Biukop', dirname(__FILE__) . '/' ); //$abc = new AddrMap("01515b52-6936-46b2-a000-9ad4cd7a5b50", "0768db6d-e5f4-4b45-89a2-29f7e8d2953c"); //$abc = new AddrMap("122eb1d0-d8c4-4fc3-8bf8-b7825bee1a01", "0768db6d-e5f4-4b45-89a2-29f7e8d2953c"); } public function check_csv_download() { $method = $_SERVER['REQUEST_METHOD']; if ( $method != "POST") return; global $wpdb; $url = $_SERVER['REQUEST_URI']; if ($url != "/ndiscsv/"){ return; } $clients= $_POST['clients']; $start = $_POST['start'];; $finish = $_POST['finish'];; $filename="n" . time() . ".csv"; header("Expires: 0"); header("Cache-Control: no-cache, no-store, must-revalidate"); header('Cache-Control: pre-check=0, post-check=0, max-age=0', false); header("Pragma: no-cache"); header("Content-type: text/csv"); header("Content-Disposition:attachment; filename=$filename"); header("Content-Type: application/force-download"); //readfile(dirname(__FILE__) . "/img/circle.png"); $clients_in = "'" . implode("','", $clients) . "'"; $sql = "SELECT * from $this->table_name WHERE client in ($clients_in) and start >='$start 00:00:00' and start<='$finish 23:59:59'"; $results = $wpdb->get_results($sql); $csv_header = "RegistrationNumber,NDISNumber,SupportsDeliveredFrom,SupportsDeliveredTo,SupportNumber,ClaimReference,Quantity,Hours,UnitPrice,GSTCode,AuthorisedBy,ParticipantApproved,InKindFundingProgram,ClaimType,CancellationReason\n"; echo $csv_header; foreach($results as $r) { echo $this->ndis_csv_line($r); } //echo $sql; exit(); } private function get_ndis_price() {//help to ensure ndis_price is only build once per call if ( ! $this->ndis_price instanceof NdisPrice ) $this->ndis_price = new NdisPrice(); return $this->ndis_price; } private function ndis_csv_line($record) { $str = ""; $price = $price = $this->get_ndis_price(); $registration = $this->acare_ndis_registration; $ndisnumber = $this->get_client_ndis_account($record->client); $date = new \DateTime($record->start); $start = $date->format("Y-m-d"); $date = new \Datetime($record->finish); $finish = $date->format("Y-m-d"); $quantity = $this->get_job_hours($record->start, $record->finish); $hours = $this->get_job_hours_hh_mm($record->start, $record->finish); $unitprice = $this->get_ndis_price()->get_tos_price($record->tos); $authorizedby="helen"; $participant_approved = ""; $GST = $this->get_client_GST($record->client); $in_kind_program = $this->get_client_in_kind_program($record->client); $ClaimType = "";// standard; $CancellationReason=""; $SupportNumber = $this->get_ndis_price()->get_tos_ndis_code($record->tos); return "$registration,$ndisnumber,$start,$finish,$SupportNumber,REC_{$record->id},$quantity,$hours,$unitprice,$GST,$authorizedby,$participant_approved,$in_kind_program,$ClaimType,$CancellationReason\n"; } private function get_client_ndis_account($client) { $user = get_user_by('login', $client); return get_user_meta($user->ID,'account',true); } private function get_client_in_kind_program($client) { $user = get_user_by('login', $client); return get_user_meta($user->ID,'in_kind_prog',true); } private function get_client_GST($client) { $user = get_user_by('login', $client); $str = get_user_meta($user->ID,'gst',true); if ($str == "") return "P2"; return $str; } //init database public function db_install () { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); //table name: timesheets jobs $table_name = $this->table_name; $sql = "CREATE TABLE $table_name ( id INT NOT NULL AUTO_INCREMENT, tos VARCHAR(45) NULL, start DATETIME NULL, finish DATETIME NULL, rate VARCHAR(45) NULL, staff VARCHAR(45) NULL, client VARCHAR(45) NULL, ack TINYINT(4) NULL, rating INT(4) NULL DEFAULT 0, PRIMARY KEY (id) ) $charset_collate;"; //addr distance $addr_table = $this->addr_table; $sql_addr = "CREATE TABLE $addr_table ( id INT NOT NULL AUTO_INCREMENT, origin VARCHAR(1024) NULL, destination VARCHAR(1024) NULL, response VARCHAR(40960) NULL, distance INT NULL, PRIMARY KEY (id) ) $charset_collate;"; $ndis_table = $this->ndis_table; $sql_ndis_price = " CREATE TABLE $ndis_table ( code VARCHAR(45) NOT NULL, name VARCHAR(45) NULL, level INT NULL, unit VARCHAR(45) NULL, price FLOAT NULL, year INT NOT NULL, PRIMARY KEY (code, year) )$charset_collate;"; //create database require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); dbDelta( $sql_addr); dbDelta( $sql_ndis_price); } // //query var public function add_query_vars($aVars) { $aVars[] = "bts_user_id"; // represents the name of the product category as shown in the URL $aVars[] = "bts_week_id"; // represents the name of the product category as shown in the URL $aVars[] = "bts_job_start"; // represents the name of the product category as shown in the URL $aVars[] = "bts_job_finish"; // represents the name of the product category as shown in the URL return $aVars; } //for customer profile and broker trans public function my_add_rewrite_rules($aRules) { $aNewRules = array( 'user/([^/]+)/?$' => 'index.php?pagename=user&bts_user_id=$matches[1]', 'task/week-([^/]+)/?$' => 'index.php?pagename=task&bts_week_id=$matches[1]', 'task/start-([^/]+)/finish-([^/]+)/?$' => 'index.php?pagename=task&bts_job_start=$matches[1]&bts_job_finish=$matches[2]', 'task/([^/]+)/?$' => 'index.php?pagename=task&bts_user_id=$matches[1]', 'task/([^/]+)/week-([^/]+)/?$' => 'index.php?pagename=task&bts_user_id=$matches[1]&bts_week_id=$matches[2]', 'task/([^/]+)/start-([^/]+)/finish-([^/]+)/?$' => 'index.php?pagename=task&bts_user_id=$matches[1]&bts_job_start=$matches[2]&bts_job_finish=$matches[3]', 'feedback_card/week-([^/]+)/?$' => 'index.php?pagename=feedback_card&bts_week_id=$matches[1]', 'feedback_card/start-([^/]+)/finish-([^/]+)/?$' => 'index.php?pagename=feedback_card&bts_job_start=$matches[1]&bts_job_finish=$matches[2]', 'feedback_card/([^/]+)/?$' => 'index.php?pagename=feedback_card&bts_user_id=$matches[1]', 'feedback_card/([^/]+)/week-([^/]+)/?$' => 'index.php?pagename=feedback_card&bts_user_id=$matches[1]&bts_week_id=$matches[2]', 'feedback_card/([^/]+)/start-([^/]+)/finish-([^/]+)/?$' => 'index.php?pagename=feedback_card&bts_user_id=$matches[1]&bts_job_start=$matches[2]&bts_job_finish=$matches[3]', 'ndiscsv/start-([^/]+)/finish-([^/]+)/?$' => 'index.php?pagename=ndiscsv&bts_job_start=$matches[1]&bts_job_finish=$matches[2]', ); $aRules = $aNewRules + $aRules; return $aRules; } // // ///check auth public function check_auth(){ global $pagename; if ($this->is_superadmin()) //skip return; switch($pagename){ case 'task': $this->cauth_task(); //for staff break; case 'time-sheets': case 'office': $this->cauth_time_sheet(); //for admin break; case 'xeroc': $this->cauth_xero_sync(); //for accountant break; case 'feedback_card': $this->cauth_feedback_card(); //for client break; } } private function cauth_task(){ if ($this->is_superadmin()) //skip return; $login = get_query_var( 'bts_user_id' ); $this->bts_job_start = get_query_var( 'bts_job_start' ); $this->bts_job_finish = get_query_var( 'bts_job_finish' ); $this->bts_week_id = get_query_var('bts_week_id'); $redirect_url = $this->get_redirect_url_for_task(); // wp_send_json(array( // 'week'=> $week, // 'userid'=>$login, // 'job_start' => $this->bts_job_start, // 'job_finish' => $this->bts_job_finish, // 'redirect' => $redirect_url, // )); if ($login != "")//perform autologin, and redirect { $staff = get_user_by('login', $login); if ($this->is_staff($staff)){//is valid staff; $current = wp_get_current_user(); if($current->ID != $staff->ID){ wp_logout(); wp_set_current_user($staff->ID, $staff->display_name); //this is a must wp_set_auth_cookie($staff->ID, true);//only with this, wordpress calls login + redirect and lost week-%d } } wp_redirect($redirect_url); return; } //no auto login is required if reach here. $current = wp_get_current_user(); if ($this->is_admin($current) ){ wp_redirect("/office/"); return; } if ($this->is_accountant($current)){ wp_redirect("/xeroc/"); return; } if (!$this->is_staff($current) && ! $this->is_admin($current) ) { wp_logout(); wp_redirect("/login/"); return; } } private function cauth_feedback_card(){ $login = get_query_var( 'bts_user_id' ); $this->bts_job_start = get_query_var( 'bts_job_start' ); $this->bts_job_finish = get_query_var( 'bts_job_finish' ); $this->bts_week_id = get_query_var('bts_week_id'); $redirect_url = $this->get_redirect_url_for_feedback_card(); if ($login != "")//perform autologin, and redirect { $client = get_user_by('login', $login); if ($this->is_client($client)){//is valid client; $current = wp_get_current_user(); if($current->ID != $client->ID){ wp_logout(); wp_set_current_user($client->ID, $client->display_name); //this is a must wp_set_auth_cookie($client->ID, true);//only with this, wordpress calls login + redirect and lost week-%d } } wp_redirect($redirect_url); return; } //no auto login is required if reach here. $current = wp_get_current_user(); if ($this->is_admin($current)){ wp_redirect("/office/"); return; } if (!$this->is_client($current) && ! $this->is_admin($current)) { wp_logout(); wp_redirect("/login/"); return; } } private function get_week_id() { $week = get_query_var( 'bts_week_id' ); $week_id = intval($week); if ($week_id == 0 || $week_id >53 ||$week_id < 1) return $this->get_current_week_id(); else return $week; } private function get_current_week_id() { $now = new \DateTime(); $week = $now->format("W"); return $week; } private function get_redirect_url_for_task() { if ($this->bts_week_id != "") return "/task/week-" . $this->bts_week_id . "/"; if ($this->bts_job_start!="" && $this->bts_job_finish !="") return "/task/start-" . $this->bts_job_start . "/finish-" .$this->bts_job_finish . "/"; return '/task/'; } private function get_redirect_url_for_feedback_card() { if ($this->bts_week_id != "") return "/feedback_card/week-" . $this->bts_week_id . "/"; if ($this->bts_job_start!="" && $this->bts_job_finish !="") return "/feedback_card/start-" . $this->bts_job_start . "/finish-" .$this->bts_job_finish . "/"; return '/feedback_card/'; } private function cauth_time_sheet() { $current = wp_get_current_user(); if ($current->ID == 0 ) { //visitor not logged in wp_redirect("/wp-login.php?"); return; } if ($this->is_staff($current)){ wp_redirect("/task"); return; } if ($this->is_accountant($current)){ wp_redirect("/xeroc"); return; } if ($this->is_admin($current)){ //proceed return; } if ($this->is_client($current)){ wp_redirect("/service"); return; } //everything else wp_redirect("/?invalid-access"); } private function cauth_xero_sync() { $current = wp_get_current_user(); if ($this->is_admin($current) ||$this->is_accountant($current) ){ //proceed return; } wp_logout(); wp_redirect("/login/"); } /// // enqueue / register css /js // public function register_js_css() { $this->nonce = wp_create_nonce('acaresydney'); $this->bts_user_id = get_query_var( 'bts_user_id' ) ; $this->register_bts_js(); $this->register_timesheet_js_css(); $this->register_office_js_css(); $this->register_task_js_css(); $this->register_feedback_card_js_css(); $this->register_xeroc_js_css(); } private function register_bts_js() { wp_enqueue_style( 'bts', plugins_url('css/ts.css', __FILE__)); wp_enqueue_script('bts', plugins_url('js/ts.js', __FILE__), array('jquery', 'jquery-ui-core')); wp_localize_script( 'bts', 'bts1', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => $this->nonce, // It is common practice to comma after 'display_name' => wp_get_current_user()->display_name, 'anonymous' => !is_user_logged_in(), 'me'=> get_current_user_id(), 'userid'=> $this->bts_user_id, 'load_user_img'=> plugins_url('img/loading_user.gif', __FILE__), 'load_job_img'=> plugins_url('img/loading_job.gif', __FILE__), 'driving' => "259ee8e8-a1e5-42e4-9c14-517543ecdc4b", 'high_pay_keywords' => ['sat ', 'sun ', 'high ', 'public holiday'], //space is important 'pay_calendar'=> get_option('bts_pay_roll_calendar'), ) ); } private function register_timesheet_js_css(){ global $pagename; if ($pagename != 'time-sheets'){ return; } wp_enqueue_style( 'bts_ts', plugins_url('css/bts_timesheet.css', __FILE__)); wp_enqueue_script( 'bts_ts', plugins_url('js/bts_timesheet.js', __FILE__), array( 'jquery' , 'bts' )); wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery')); global $wp_scripts; wp_enqueue_script('jquery-ui-datepicker'); // get registered script object for jquery-ui //$ui = $wp_scripts->query('jquery-ui-core'); // tell WordPress to load the Smoothness theme from Google CDN //$protocol = is_ssl() ? 'https' : 'http'; // $url = "$protocol://ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.min.css"; $url = plugins_url('jquery-ui-1.11.4.theme/jquery-ui.min.css', __FILE__); wp_enqueue_style('jquery-ui-smoothness', $url, false, null); } private function register_office_js_css(){ global $pagename; if ($pagename != 'office'){ return; } wp_enqueue_style( 'bts_office', plugins_url('css/bts_office.css', __FILE__)); wp_enqueue_script( 'bts_office', plugins_url('js/bts_office.js', __FILE__), array( 'jquery' , 'bts' )); wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery')); global $wp_scripts; wp_enqueue_script('jquery-ui-datepicker'); // get registered script object for jquery-ui //$ui = $wp_scripts->query('jquery-ui-core'); // tell WordPress to load the Smoothness theme from Google CDN //$protocol = is_ssl() ? 'https' : 'http'; // $url = "$protocol://ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.min.css"; $url = plugins_url('jquery-ui-1.11.4.theme/jquery-ui.min.css', __FILE__); wp_enqueue_style('jquery-ui-smoothness', $url, false, null); } private function register_task_js_css(){ global $pagename; if ($pagename != 'task'){ return; } $this->bts_job_start = get_query_var( 'bts_job_start' ); $this->bts_job_finish = get_query_var( 'bts_job_finish' ); $this->bts_week_id = get_query_var('bts_week_id'); wp_enqueue_style( 'bts_task', plugins_url('css/bts_task.css', __FILE__)); wp_enqueue_script( 'bts_task', plugins_url('js/bts_task.js', __FILE__), array( 'jquery' , 'bts' )); wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery')); wp_localize_script('bts_task','bts_task1',array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce('bts_task'), 'week_id' => $this->bts_week_id, 'bts_job_start' => $this->bts_job_start, 'bts_job_finish' => $this->bts_job_finish, ) ); } private function register_feedback_card_js_css() { global $pagename; if ($pagename != 'feedback_card'){ return; } $this->bts_job_start = get_query_var( 'bts_job_start' ); $this->bts_job_finish = get_query_var( 'bts_job_finish' ); $this->bts_week_id = get_query_var('bts_week_id'); wp_enqueue_style( 'feedback_card', plugins_url('css/feedback_card.css', __FILE__)); wp_enqueue_script( 'feedback_card', plugins_url('js/feedback_card.js', __FILE__), array( 'jquery' , 'bts' )); wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery')); wp_localize_script('feedback_card','feedback_card',array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce('feedback_card'), 'week_id' => $this->bts_week_id, 'bts_job_start' => $this->bts_job_start, 'bts_job_finish' => $this->bts_job_finish, ) ); } private function register_xeroc_js_css(){ global $pagename; if ($pagename != 'xeroc'){ return; } wp_enqueue_style( 'bts_xeroc', plugins_url('css/xeroc.css', __FILE__)); wp_enqueue_script( 'bts_xeroc', plugins_url('js/xeroc.js', __FILE__), array( 'jquery' , 'bts' )); wp_enqueue_script('mustache', plugins_url('js/mustache.min.js', __FILE__), array('jquery')); wp_enqueue_script('scrollintoview', plugins_url('js/scrollintoview.js', __FILE__), array('jquery')); global $wp_scripts; wp_enqueue_script('jquery-ui-datepicker'); $url = plugins_url('jquery-ui-1.11.4.theme/jquery-ui.min.css', __FILE__); wp_enqueue_style('jquery-ui-smoothness', $url, false, null); } // Usage: `wp sync_users --mininterval=123 public function cli_sync_user($args = array(), $assoc_args = array()){ $arguments = wp_parse_args( $assoc_args, array( 'mininterval' => 86400, 'employeeonly' => false, 'clientsonly' => false, ) ); $this->XeroOauth2->sync_users($arguments['mininterval'], $arguments['employeeonly'], $arguments['clientsonly']); return; } public function cli_refresh_token($args = array(), $assoc_args = array()){ $this->XeroOauth2->refresh_token(true); $date = new \DateTime("now", new \DateTimeZone('Australia/Sydney') ); $strDate = $date->format('Y-m-d H:i:s'); echo "refresh_token at: $strDate Sydney time \n"; echo "accessToken: " . $this->XeroOauth2->storage->getAccessToken() . "\n"; echo "refreshToken: " . $this->XeroOauth2->storage->getRefreshToken() . "\n" ; echo "expires: " . $this->XeroOauth2->storage->tokenExpiresHumanRedable() . "\n" ; } public function cli_email_jobs($args = array(), $assoc_args = array()){ $users = get_users(array('role' => 'staff')); foreach ($users as $u){ $n = new UserJob($u->user_login); $resp = $n->list_jobs_by_staff("2019-07-22 00:00:00", "2019-07-28 23:59:59"); if ($resp['status']=='success' && $resp['job_count'] >0 ){ if( $u->user_login != "9aa3308e-cc19-4c21-a110-f2c6abec4337" ) continue; $msg = sprintf("Staff = %s, Login=%s, email=%s Job=%d\n", $u->display_name, $u->user_login, $u->user_email, $resp['job_count']); echo $msg; $this->send_email_with_job_link($u, "2019-07-22", "2019-07-28"); } } return; } public function cli_produce_invoice($args = array(), $assoc_args = array()) { $users = get_users(array('role' => 'client')); foreach ($users as $u) { $pay = get_user_meta($u->id, 'payment',true); echo sprintf("%s: %s\n", $u->display_name, $pay); } } public function cli_dev_change_ndis_price ($args = array(), $assoc_args = array()) { echo "list ndis prices\n"; $nd = new NdisPrice(); echo "get tos array\n"; $tosarray = $nd->get_tos_array(); $map = array(); foreach($tosarray as $item){ $map[$item->code] = $item->id; if ( $item->year ==20200325) { $newid = $nd->get_id_by_tos($item->code, 20200701); if ($newid != 0){ echo "UPDATE wp1m_acare_ts SET tos=$newid WHERE tos= $item->id and id > 0 and start>='2020-07-01 00:00:00'; \n" ; } } //echo "UPDATE wp1m_acare_ts SET tos_id=$item->id WHERE tos='$item->code' and id > 0; \n"; } return; $sql = "SELECT * FROM $this->table_name WHERE start>='2020-03-25 00:00:00' order by start ASC ,staff ASC "; echo $sql . "\n"; $jobs = $this->db->get_results($sql); foreach ($jobs as $job) { echo "id= ". $job->id . " tos= ". $job->tos . " start= " . $job->start . "\n"; } } private function send_email_with_job_link($staff, $start, $finish) { $message = file_get_contents(plugin_dir_path(__FILE__) . "/html/email_job.html"); $message = str_ireplace("{{display_name}}", $staff->display_name, $message); $message = str_ireplace("{{user_login}}", $staff->user_login, $message); $message = str_ireplace("{{job_start}}", $start, $message); $message = str_ireplace("{{job_finish}}", $finish, $message); $headers = ['Bcc: patrick@biukop.com.au, timesheet@acaresydney.com.au']; $subject = $staff->display_name . " Job arrangement $start ~ $finish"; //wp_mail("sp@lawipac.com", $subject, $message, $headers); //wp_mail("timesheet@acaresydney.com.au", $subject, $message, $headers); wp_mail($staff->user_email, $subject, $message, $headers); } private function send_email_feedback_url($client) { $message = file_get_contents(plugin_dir_path(__FILE__) . "/html/email_feedback_url.html"); $message = str_ireplace("{{display_name}}", $client->display_name, $message); $message = str_ireplace("{{user_login}}", $client->user_login, $message); $headers = ['Bcc: helenwang41@hotmail.com, timesheet@acaresydney.com.au']; $subject = $client->display_name . " Feedback Link"; wp_mail( "helen@acaresydney.com.au", $subject, $message, $headers); //wp_mail( "patrick@biukop.com.au", $subject, $message, $headers); } public function bts_staff_item($attr){ return $this->template('staff_item', 'staff.html'); } public function bts_client_item($attr){ return $this->template('client_item', 'client.html'); } public function bts_job_item($attr){ $html =$this->template('job_item', 'job.html'); //$html = str_replace('[bts-tos-options]', $this->bts_tos_options([]), $html); $html = do_shortcode($html); return $html; } public function bts_rate_options($attr){ $result = ""; return $result; } private function get_rate_name_by_id($id) { $options = get_option('bts_payitem_earnings_rate'); foreach($options as $o){ if ( $o['EarningsRateID'] == $id ) return sprintf("$%3.2f-%s", $o['RatePerUnit'], $o['Name']); } } public function bts_select_staff($attr){ $result = ""; return $result; } public function bts_select_client($attr){ $result = ""; return $result; } public function bts_type_of_service($attr){ $n = new NdisPrice(2019); return $n->get_html(); } public function bts_user_name($attr) { $user = wp_get_current_user(); return $user->display_name; } public function bts_staff_job_summary($attr) { $result =" If there is more than one job, please click on 'confirm' to make sure it is included in your payment. "; return $result; } public function bts_feedback_card($attr) { return $this->template('bts_feedback_card', 'feedback_card.html'); } public function bb_timesheet_canvas($attr) { return file_get_contents(plugin_dir_path(__FILE__) . "/html/timesheet.html"); } public function bts_staff_hours_template($attr){ return $this->template('bts_staff_hours_template', 'bts_staff_hours_template.html'); } public function bts_client_invoice_template($attr){ return $this->template('bts_client_invoice_template', 'bts_client_invoice_template.html'); } public function bts_csv_template($attr){ return $this->template('bts_csv_template', 'bts_csv_template.html'); } public function bts_invoiced_client($attr) { $attr = shortcode_atts([ 'preferred' => 'true', ], $attr); $result = ""; $users = $users = get_users(array('role' => 'client')); $row = << %s %s is loading .... please wait... ZOT; foreach ($users as $u) { $payment = get_user_meta($u->ID, 'payment', true); if ( $attr['preferred'] == 'true' ){ if( $payment == 'invoice' ){ $invoice_preferred = "invoice_preferred"; $result .= sprintf($row, $u->user_login, $u->user_login, $invoice_preferred, $u->user_login, $u->display_name, $u->user_login, $u->user_login, $invoice_preferred, $u->user_login, $u->display_name); } }else{ $invoice_preferred = ""; if( $payment != 'invoice' ){ $result .= sprintf($row, $u->user_login, $u->user_login, $invoice_preferred, $u->user_login, $u->display_name, $u->user_login, $u->user_login, $invoice_preferred, $u->user_login, $u->display_name); } } } return $result; } //generate template based on html file private function template($id, $file) { $text = ''; return $text; } function list_staff(){ check_ajax_referer('acaresydney'); // Handle the ajax request $response = array( 'status' =>'error', 'users' => [], ); //search all users that are staff $staffq = new \WP_User_Query(array('role'=>'staff','meta_key'=>'first_name', 'orderby'=>'meta_value', 'order'=>'ASC')); $staff = $staffq->get_results(); if (! empty($staff)){ $response['status'] = 'success'; foreach( $staff as $s){ $response['users'][] = array( 'login' => $s->user_login, 'firstname'=> $s->first_name, 'lastname'=> $s->last_name, 'display_name' => str_replace("/", "/ ", $s->display_name), 'mobile'=> get_user_meta($s->ID, 'mobile', true), 'email'=> $s->user_email, 'wages'=> 0, 'hour' => 0 , 'OT' => 0 , 'petrol'=> 0 , 'rating'=> 0, 'unconfirmedjob'=> 0, ); } } wp_send_json($response); wp_die(); } function list_client(){ check_ajax_referer('acaresydney'); $user = wp_get_current_user(); // Handle the ajax request $response = array( 'status' =>'error', 'users' => [], 'role' => $user, ); //search all users that are staff $clientq = new \WP_User_Query(array('role'=>'client', 'meta_key'=>'first_name', 'orderby'=>'meta_value', 'order'=>'ASC')); $client = $clientq->get_results(); if (! empty($client)){ $response['status'] = 'success'; foreach( $client as $s){ $data_item = array( 'login' => $s->user_login, 'firstname'=> $s->first_name, 'lastname'=> $s->last_name, 'display_name'=> $s->display_name, 'mobile'=> get_user_meta($s->ID, 'mobile', true), 'email'=> $s->user_email, 'account'=> get_user_meta($s->ID, 'account', true), 'address' => get_user_meta($s->ID, 'address', true), 'payment'=>get_user_meta($s->ID, 'payment', true), 'rating'=> 0, 'unconfirmedjob'=> 0, ); $response['users'][] = $data_item; } } wp_send_json($response); wp_die(); } //ajax function list_tos() { check_ajax_referer('acaresydney'); // Handle the ajax request $response = array( 'status' =>'success', 'tos' => [], ); $price = $this->get_ndis_price(); $response['tos']= $price->get_tos_array(); wp_send_json($response); } private function get_people_by_role($role){ //search all users that are staff $staffq = new \WP_User_Query(array('role'=>$role, 'meta_key'=>'first_name', 'orderby'=>'meta_value', 'order'=>'ASC')); $staff = $staffq->get_results(); return $staff; } //ajax get earnings rates function earnings_rate() { $response= array( 'status' => 'success', 'options'=> get_option('bts_payitem_earnings_rate'), ); wp_send_json($response); } //ajax job CRUD function save_job() { check_ajax_referer('acaresydney'); $r = $_POST['record']; $response = array(); $d = array( 'tos' => $r['tos'], 'start' => $r['start'], 'finish' => $r['finish'], 'rate' => $r['rate'], 'staff' => $r['staff'], 'client' => $r['client'], 'ack' => $r['ack']=='true'?1:0, 'rating'=>$r['rating'], ); //this is an update if ( isset($r['id']) && trim($r['id']) !='' && is_numeric($r['id'])){ $response['isNew'] = false; //add or update? $result = $this->db->update($this->table_name, $d, array('id' =>$r['id'])); if ($result !== false && $this->db->last_error == ''){ $d['id'] = $r['id']; $response['status'] = 'success'; //do data type conversion, string to int $response['newdata'] = $this->get_ts_record($r['id']); $response['errors'] = array(); //empty array }else{ $response['status'] = 'error'; $repsonse['errors'] = array( 'db' => "network database error" . $this->db->last_error, ); } }else{ $response['isNew'] = true; $result = $this->db->insert($this->table_name, $d); $lastid = $this->db->insert_id; if ($result != false && $this->db->last_error == ''){ $response['status'] = 'success'; $response['newdata'] = $this->get_ts_record($lastid); }else{ $response['status'] = 'error'; $response['errors'] = array( 'db' => 'network database error ' . $this->db->last_error, ); } } wp_send_json($response); wp_die(); } private function get_ts_record($id){ $sql = "SELECT * FROM $this->table_name WHERE id=%d"; $row = $this->db->get_row($this->db->prepare ($sql, array($id))); $response = []; if ($row != null){ $response = array( 'id' => (int)$row->id, 'tos' => $row->tos, 'start' => $row->start, 'finish' => $row->finish, 'rate' => $row->rate, 'staff' => $row->staff, 'client' => $row->client, 'ack' => (int)$row->ack, 'rating' =>(int) $row->rating, ); } return $response; } //ajax delete job function delete_job(){ check_ajax_referer('acaresydney'); $id = $_POST['jobid']; $result = $this->db->delete($this->table_name, array('id'=> $id)); $response=array( 'status' => 'success', 'id' => $id, 'action'=> 'delete', 'error' => '', ); if ($result == 1){ wp_send_json($response); }else{ $response['status'] = 'error'; $response['error'] = $this->db->last_error; wp_send_json($response); } wp_die(); } //ajax email staff their job arrangement function email_job() { check_ajax_referer('acaresydney'); $staff = $_POST['staff']; $start = $_POST['start']; $finish = $_POST['finish']; $response=array( 'status' => 'success', 'staff' => $staff, 'start' => $start, 'finish' => $finish, 'error' => '', 'sent' => false, 'emailstatus'=>"Bypass (no job)", ); $u = get_user_by('login', $staff); if ($this->is_staff($u)){ $n = new UserJob($staff); $resp = $n->list_jobs_by_staff("$start 00:00:00", "$finish 23:59:59"); if ($resp['status']=='success' && $resp['job_count'] >0 ){ $msg = sprintf("Email to %s (with job=%d) \n", $u->user_email, $resp['job_count']); $this->send_email_with_job_link($u, $start, $finish); $response['sent'] = true; $response['emailstatus'] = $msg; } } wp_send_json($response); } //ajax email feedback url function email_feedback_url() { check_ajax_referer('acaresydney'); $client = $_POST['client']; $response=array( 'status' => 'success', 'error' => '', 'sent' => false, 'emailstatus' =>'not sent', ); $u = get_user_by('login', $client); if ($this->is_client($u)){ $status = $this->send_email_feedback_url($u); $response['emailstatus'] = $status; } wp_send_json($response); } //ajax browse job with different filters function list_job(){ check_ajax_referer('acaresydney'); $start = $_POST['start'] . " 00:00:00"; $finish = $_POST['finish']. " 23:59:59"; $response = array( 'status'=>'success', 'jobs' => [], ); $sql = "SELECT * FROM $this->table_name WHERE start>='%s' and start <='%s' order by start ASC ,staff ASC"; $query = $this->db->prepare ($sql, array($start, $finish)); $response['sql'] = $query; $jobs = $this->db->get_results($query); $calendar = get_option('bts_pay_roll_calendar'); if (! empty($jobs)){ $response['status'] = 'success'; foreach( $jobs as $s){ $item = array( 'id' => $s->id, 'tos' => $s->tos, 'start'=> $s->start, 'finish'=> $s->finish, 'rate'=> $s->rate, 'staff'=> $s->staff, 'client'=> $s->client, 'ack' => $s->ack, 'rating' =>$s->rating, ); $response['jobs'][] = $item; } } wp_send_json($response); wp_die(); } public function list_job_by_staff() { check_ajax_referer('acaresydney'); $start = $_POST['start']; $finish = $_POST['finish']; //$start="2019-07-01 00:00:00"; //$finish="2019-07-14 23:59:59"; $user = wp_get_current_user();// should be staff; if ( $this->is_staff($user) || $this->is_admin($user) ){ $n = new UserJob($user->user_login); $response = $n->list_jobs_by_staff($start, $finish); wp_send_json($response); }else{ $response = array( 'status' => 'error', 'errmsg' => 'invalid access', 'user' => $user, ); wp_send_json($response); } wp_die(); } public function list_job_by_client() { check_ajax_referer('acaresydney'); $start = $_POST['start']; $finish = $_POST['finish']; //$start="2019-07-01 00:00:00"; //$finish="2019-07-14 23:59:59"; $user = wp_get_current_user();// should be staff; if ( $this->is_client($user) || $this->is_admin($user) ){ $n = new UserJob($user->user_login); $response = $n->list_jobs_by_client($start, $finish); wp_send_json($response); }else{ $response = array( 'status' => 'error', 'errmsg' => 'invalid access', 'user' => $user, ); wp_send_json($response); } wp_die(); } private function is_staff($user) { return ($user->ID !=0 && in_array('staff', $user->roles)); } private function is_client($user) { return ($user->ID !=0 && in_array('client', $user->roles)); } private function is_admin($user) { $allowed_roles = array('administrator', 'admin'); if( array_intersect($allowed_roles, $user->roles ) ) { return true; } } private function is_superadmin(): bool { $current = wp_get_current_user(); return $current->ID == 1; } private function is_accountant($user) { return ($user->ID !=0 && in_array('accountant', $user->roles)); } public function staff_ack_job() { check_ajax_referer('acaresydney'); $jobs = $_POST['jobs']; $response = array( 'status'=>'success', 'jobs'=>$jobs, ); $yes=[]; $no=[]; foreach($jobs as $job){ if ( $job['ack'] == "true") $yes[] =(int) $job['id']; else $no[] = (int) $job['id']; } $err = $this->ack_multiple_job($yes, $no); if ($this->db->last_error !='') { $response['status']= 'error'; $response['err_msg']= $err; } $response['yes'] = $yes; $response['no'] = $no; wp_send_json($response); wp_die(); } public function ack_multiple_job($yes, $no) { $str_yes_ids = implode(",", $yes); $str_no_ids = implode(",", $no); $err = ""; if (count($yes) >0 ){ $sql = "UPDATE $this->table_name SET ack=1 WHERE id IN ( $str_yes_ids) ; "; $r = $this->db->get_results($sql); $err = $this->db->last_error; } if (count($no) >0 ){ $sql = "UPDATE $this->table_name SET ack=0 WHERE id IN ( $str_no_ids) ; "; $r = $this->db->get_results($sql); $err .= $this->db->last_error; } return $err; } public function client_ack_job() { check_ajax_referer('acaresydney'); $job_id = $_POST['job_id']; $rating = $_POST['rating']; $response = array( 'status'=>'success', ); $sql= "UPDATE $this->table_name SET rating=%d WHERE id = %d ; "; $sql= $this->db->prepare($sql, array($rating, $job_id)); $result = $this->db->get_results($sql); $response['rating'] = (int) $rating; if ($this->db->last_error !='') { $response['status']= 'error'; $response['err_msg']= $this->db->last_error; $response['rating'] = 0; } wp_send_json($response); } public function get_timesheet_from_xero() { check_ajax_referer('acaresydney'); //check if we need sync $sync = $_POST['sync']; if ($sync != "true") $sync = false; else $sync = true; //set up payroll calendar $pc = $this->XeroOauth2->get_payroll_calendar(); $start = $pc->getStartDateAsDate()->format('Y-m-d'); $finish = new \DateTime($start); $finish = $finish->modify("+13 days")->format('Y-m-d'); $paydate = $pc->getPaymentDateAsDate()->format('Y-m-d'); //prepare response $response = array( 'status' => 'success', 'payroll_calendar' => array( 'start' => $start, 'finish' => $finish, 'paydate'=> $paydate, ), ); $xx = new \Biukop\XeroOauth2Timesheet($this->XeroOauth2, $finish); $local_ts = $this->create_timesheet_from_db($start, $finish); if ($sync){ $xx->set_local_timesheet($local_ts); $xx->save_to_xero(); $xx->refresh_remote(); } $days=[]; $d = new \DateTime($start); for ($i=1; $i<=14; $i++){ $days["days_$i"] = $d->format("d/M"); $d->modify("+1 day"); } $lines=[]; foreach ($local_ts as $staff_login => $details) { $item = array( 'staff_name' => $this->get_user_name_by_login ($staff_login), 'staff_id' => $staff_login, 'Xero_Status' => 'Empty', 'rowspan' =>1, 'local_total' =>0, 'remote_total'=>0, 'ratetype' => [], ); $buddy = $xx->get_buddy_timesheets($staff_login, $start, $finish); if ($buddy != NULL){ $item['Xero_Status'] = $buddy->getStatus(); }else{ $item['Xero_Status'] = "Not Exist"; } foreach($details as $rate => $hours){ $item['rowspan'] ++; $ratetype_item=[]; $ratetype_item['rate_name'] = $this->get_rate_name_by_id($rate); //for local for ($i=1; $i<=14; $i++) { $ratetype_item["local_$i"] = $hours[$i-1]; $ratetype_item["local_$i"] = number_format($ratetype_item["local_$i"], 2); $item['local_total'] += $hours[$i-1]; } $item['local_total'] = number_format($item['local_total'], 2); //for remote if ( $buddy != NULL ) { $remote_lines = $buddy->getTimesheetLines(); foreach($remote_lines as $rl) { if ( $rl->getEarningsRateID() == $rate){ for ($i=1; $i<=14; $i++) { $ratetype_item["xero_$i"] = $rl->getNumberOfUnits()[$i-1]; $item['remote_total'] += $rl->getNumberOfUnits()[$i-1]; if ($ratetype_item["xero_$i"] != $ratetype_item["local_$i"]){ $ratetype_item["xero_{$i}_mismatch"] = "mismatch"; } } break;//we found it } } } $item['ratetype'][] = $ratetype_item; } $item = array_merge($item, $days); $lines[]=$item; } $ts = json_decode(file_get_contents(dirname(__FILE__) . "/sample/timesheets.json")); $response['ts'] = $ts; $response['lines'] = $lines; wp_send_json($response); } public function approve_all_timesheet() { check_ajax_referer('acaresydney'); //set up payroll calendar $pc = $this->XeroOauth2->get_payroll_calendar(); $start = $pc->getStartDate()->format('Y-m-d'); $finish = new \DateTime($start); $finish = $finish->modify("+13 days")->format('Y-m-d'); $paydate = $pc->getPaymentDate()->format('Y-m-d'); //prepare response $response = array( 'status' => 'success', 'payroll_calendar' => array( 'start' => $start, 'finish' => $finish, 'paydate'=> $paydate, ), ); $xx = new \Biukop\TimeSheet($this->XeroOauth2, $finish); $xx->approve_all(); wp_send_json($response); } static public function get_user_name_by_login($login) { $user = get_user_by('login', $login); if ($user->ID !=0 ) return $user->display_name; else return "Invalid Name"; } //ajax public function get_invoice_item() { check_ajax_referer('acaresydney'); $client = $_POST['client']; //$client = "8cb3d205-6cdc-4187-ae39-9216923dd86d"; $start = $_POST['start']; //2019-07-01'; $finish= $_POST['finish'];//2019-07-31'; $sql = "SELECT * from $this->table_name WHERE tos != '00_000_0000_0_0' and start>='$start 00:00:00' and start<='$finish 23:59:59' and client='$client' ORDER BY start"; $rows = $this->db->get_results($sql); $response=[ 'status' =>'success', 'client_login' => $client, 'client_name' => $this->get_user_name_by_login($client), 'jobs'=>[], 'err'=>'' ]; $price = $this->get_ndis_price(); $summary=[];// by ndis code foreach($rows as $r){ $quantity = $this->get_job_hours($r->start, $r->finish); $unit = $price->get_tos_unit($r->tos); if ($unit != "Hour") { $quantity = 1; } $unitprice = $price->get_tos_price($r->tos); $description = $this->get_job_description_for_invoice($price, $r); $data = [ 'tos' => $price->get_tos_full_str($r->tos), 'start' => $r->start, 'finish' => $r->finish, 'hours' => $quantity, 'unitprice'=> $price->get_tos_price($r->tos), 'staff_name' => $this->get_user_name_by_login($r->staff), 'price' => sprintf("%.2f", $quantity * $unitprice), ]; $response['jobs'][] = $data; $summary[$r->tos] += $quantity; } if (count($response['jobs']) ==0) { $response['nojob'] = true; } $response['overall_total'] = 0; foreach ($summary as $key => $val) { $unitprice = $price->get_tos_price($key); $tos_total = $val * $price->get_tos_price($key); $response['overall_total'] += $tos_total; $response['summary'][] = array( 'ndis' => $price->get_tos_ndis_code($key), 'tos' => $price->get_tos_full_str($key), 'unitprice'=> $unitprice, 'Hours'=> sprintf("%0.2f", $val), 'tos_total'=> sprintf("%0.2f", $tos_total), ); } if (count($summary) > 0){ $response['has_summary'] = true; $response['overall_total'] = sprintf("%0.2f", $response['overall_total'] ); } wp_send_json($response); } public function create_invoice_in_xero() { check_ajax_referer('acaresydney'); $client = $_POST['client']; //$client = "8cb3d205-6cdc-4187-ae39-9216923dd86d"; $start = $_POST['start']; //2019-07-01'; $finish= $_POST['finish'];//2019-07-31'; // $response=[ 'status'=>'success', 'invoice_number' => '', 'err'=> '', ]; try{ $invoice = $this->create_invoice_by_client($client, $start, $finish); $response['invoice_number'] = sprintf("%s", $invoice->getInvoiceId(), $invoice->getInvoiceNumber()); }catch(\Exception $e){ $response['status'] = 'error'; $response['err'] = "XERO Invoice Error"; } wp_send_json($response); } public function create_invoice_by_client($client_login, $start, $finish) { $user = get_user_by('login', $client_login); if ( !$this->is_client($user) ) return NULL; // $payment = get_user_meta($user->ID, "payment", true); // if ($payment != "invoice") // return NULL; // tos ==1 is code 00_000_0000_0_0 = NOT NDIS $sql = "SELECT * from $this->table_name WHERE tos != 1 and start>='$start 00:00:00' and start<='$finish 23:59:59' and client='$client_login' ORDER BY start"; $rows = $this->db->get_results($sql); $api = $this->XeroOauth2->get_accounting_instance(); //crate invoice $invoice = new \XeroAPI\XeroPHP\Models\Accounting\Invoice; $contacts = $api->getContact($this->XeroOauth2->getTenantId(), $client_login); $now = new \DateTime(); $due = new \DateTime(); $due->modify("+14 days"); $invoice->setType("ACCREC"); $invoice->setStatus("DRAFT"); $invoice->setContact($contacts[0]); $invoice->setDate($now); $invoice->setDueDate($due); $price = new NdisPrice(2019);//always 2019 until its being changed; $lineItems=[]; foreach($rows as $r){ $quantity = $this->get_job_hours($r->start, $r->finish); $unit = $price->get_tos_unit($r->tos); if ($unit != "Hour") { $quantity = 1; } $unitprice = $price->get_tos_price($r->tos); $description = $this->get_job_description_for_invoice($price, $r); $lineItem = new \XeroAPI\XeroPHP\Models\Accounting\LineItem; $lineItem->setDescription($description); $lineItem->setQuantity($quantity); $lineItem->setUnitAmount($unitprice); $lineItem->setAccountCode(220); $lineItem->setTaxType("EXEMPTOUTPUT"); $lineItems[]= $lineItem; } $invoice->setLineItems($lineItems); //prevent zero lineitems if ( count($invoice->getLineItems()) >0 ){ $to_save[] = $invoice; $invoices = new \XeroAPI\XeroPHP\Models\Accounting\Invoices; $invoices->setInvoices($to_save); $result = $api->createInvoices($this->XeroOauth2->getTenantId(), $invoices); $createdInvoices = $result->getInvoices(); return $createdInvoices[0]; } return $invoice; } public function get_job_description_for_invoice($price, $job) { $description = sprintf( '%s [NDIS code: %s] Time: %s - %s By Carer : %s', $price->get_tos_str($job->tos), $price->get_tos_ndis_code($job->tos), $job->start, $job->finish, $this->get_user_name_by_login($job->staff) ); return $description; } public function create_timesheet_from_db($start, $finish){ $results = []; $sql = "SELECT * from $this->table_name WHERE start>='$start 00:00:00' and start<='$finish 23:59:59'"; $rows = $this->db->get_results($sql); foreach ($rows as $r){ if (!array_key_exists($r->staff, $results)){ $results[$r->staff] = []; } if (!array_key_exists($r->rate, $results[$r->staff])){ $results[$r->staff][$r->rate] = []; for ($i=0; $i<14; $i++){ $results[$r->staff][$r->rate][$i] = 0; //14 days init to 0; } } $idx = $this->convert_date_to_idx($r->start, $start, $finish); if ($idx >=0 && $idx <=13){ $hours = $this->get_job_hours($r->start, $r->finish); $results[$r->staff][$r->rate][$idx] += $hours; //$results[$r->staff][$r->id] = $hours; //$results[$r->staff][$r->id . '_index'] = $idx; }else{ $msg = sprintf("ACARE_TS_ERR: found invalid job index for job id=%d, on %s, idx is %d, start=%s, finish=%s\n", $r->id, date('Y-m-d'), $idx, $start, $finish); $this->log($msg); } } //wp_send_json($results); return $results; } //convert date (no time) to index, 0 day is $start, 13day is finish, -1 is not found public function convert_date_to_idx($date, $start, $finish) {//start, finish format must be yyyy-mm-dd $idx = -1; $cur = new \DateTime($date); $cur->setTime(0,0,0);//clear time; $s = new \DateTime($start); $s->setTime(0,0,0); $f = new \DateTime($finish); $f->setTime(0,0,0); if ( $s <= $cur && $cur <=$f ){ $datediff = date_diff($s,$cur); $idx = $datediff->days; } return $idx; } private function get_job_hours($start, $finish) { $hours = 0; $s = strtotime($start); $f = strtotime($finish); $diff = $f- $s; $hours = ($diff * 1.0 / 3600); //can be float; return sprintf('%0.2f', $hours); } private function get_job_hours_hh_mm($start, $finish) { $hours = 0; $s = strtotime($start); $f = strtotime($finish); $diff = $f- $s; $hours = floor($diff * 1.0 / 3600); //down to integer $minutes = round( (($diff * 1.0) % 3600) / 60) ; //round to integer; if ($minutes <10) $minutes = "0$minutes"; return "$hours:$minutes"; } public function cli_feedback_url() { $users = get_users(array('role'=>'client')); foreach($users as $u){ echo sprintf("Hi %s:\n\nPlease rate our service, https://acaresydney.com.au/feedback_card/%s/\n\nAcareSydney\n\n", $u->display_name, $u->user_login); } } static public function log($msg){ openlog("ACARE_TS", LOG_PID | LOG_PERROR, LOG_SYSLOG); //something wrong, we dont touch it, but give some error here syslog(LOG_WARNING, $msg); closelog(); } } $bb = new AcareOffice(); if ( defined( 'WP_CLI' ) && WP_CLI ) { \WP_CLI::add_command( 'sync_users', array($bb, 'cli_sync_user')); \WP_CLI::add_command( 'refresh_token', array($bb, 'cli_refresh_token')); // \WP_CLI::add_command( 'email_jobs', array($bb, 'cli_email_jobs')); // \WP_CLI::add_command( 'feedback_url', array($bb, 'cli_feedback_url')); // \WP_CLI::add_command( 'produce_invoice', array($bb, 'cli_produce_invoice')); // \WP_CLI::add_command( 'dev_change_ndis_price', array($bb, 'cli_dev_change_ndis_price')); } //$bb->class_loader(); //$bb->create_invoice_by_client("8cb3d205-6cdc-4187-ae39-9216923dd86d", "2019-07-01", "2019-07-31"); //$idx = $bb->convert_date_to_idx("2019-07-02 14:30:00", "2019-07-01", "2019-07-02"); //wp_send_json($idx); //$bb->create_timesheet_from_db("2019-07-01", "2019-07-14");