| <?php | |||||
| namespace Biukop; | |||||
| class TimeSheet{ | |||||
| private $xero ; | |||||
| private $start_date; | |||||
| private $end_date; | |||||
| private $remote_timesheets; | |||||
| private $local_timesheets =[]; | |||||
| private $buddy_timesheets =[]; | |||||
| private $warning_timesheets =[]; | |||||
| public function __construct($xero, $end_date){ | |||||
| $this->xero = $xero; | |||||
| $this->end_date = $end_date; | |||||
| $this->cal_start(); //set start_date; | |||||
| $this->get_remote_timesheets(); | |||||
| } | |||||
| private function cal_start(){ | |||||
| $d = new \DateTime($this->end_date); | |||||
| $d->modify("-13 days"); | |||||
| $this->start_date = $d->format("Y-m-d"); | |||||
| // wp_send_json(array( | |||||
| // 'start'=> $this->start_date, | |||||
| // 'finish'=> $this->end_date, | |||||
| // )); | |||||
| } | |||||
| private function get_remote_timesheets() | |||||
| { | |||||
| $this->remote_timesheets = $this->xero->load('PayrollAU\\Timesheet') | |||||
| ->where('EndDate==DateTime.Parse("'. $this->end_date .'")') | |||||
| ->execute(); | |||||
| } | |||||
| public function get_xero_timesheet() | |||||
| { | |||||
| return $this->remote_timesheets; | |||||
| } | |||||
| public function get_local_timesheet() | |||||
| { | |||||
| return $this->local_timesheets; | |||||
| } | |||||
| public function set_local_timesheet($lines) | |||||
| { | |||||
| $this->local_timesheets = []; | |||||
| //convert $val to Timesheet format; | |||||
| foreach ($lines as $staff_login => $rateshours){ | |||||
| $ts = ""; | |||||
| if (array_key_exists($staff_login, $this->local_timesheets)){ | |||||
| $ts = $this->local_timesheets[$staff_login]; | |||||
| }else{ | |||||
| $ts = new \XeroPHP\Models\PayrollAU\Timesheet($this->xero); | |||||
| $ts->setEmployeeID($staff_login) | |||||
| ->setTimeSheetID($this->get_timesheet_id_by_employee_id($staff_login)) | |||||
| ->setStartDate(new \DateTime($this->start_date)) | |||||
| ->setEndDate(new \DateTime($this->end_date)) | |||||
| ->setStatus("DRAFT"); | |||||
| } | |||||
| //adding lines | |||||
| foreach ($rateshours as $rateid => $hours) | |||||
| { | |||||
| $ts_line = new \XeroPHP\Models\PayrollAU\Timesheet\TimesheetLine($this->xero); | |||||
| $ts_line->setEarningsRateID($rateid); | |||||
| for ($i=0; $i<14; $i++){ | |||||
| $ts_line->addNumberOfUnit($hours[$i]); | |||||
| } | |||||
| $ts->addTimesheetLine($ts_line); | |||||
| } | |||||
| //update this timesheet; | |||||
| $this->local_timesheets[$staff_login] = $ts; | |||||
| } | |||||
| } | |||||
| public function save_to_xero() | |||||
| { | |||||
| $to_save=[]; | |||||
| foreach ( $this->local_timesheets as $t){ | |||||
| $t->setDirty('EmployeeID'); | |||||
| $t->setDirty('StartDate'); | |||||
| $t->setDirty('EndDate'); | |||||
| $t->setDirty('TimesheetLines'); | |||||
| $t->setDirty('Status'); | |||||
| $t->setDirty('Hours'); | |||||
| $t->setDirty('TimesheetID'); | |||||
| $t->setStatus('DRAFT'); | |||||
| $to_save[]=$t; | |||||
| } | |||||
| //empty remote timesheet which are not available in local | |||||
| // | |||||
| //some of buddy timesheets might be removed from local already | |||||
| //we cannot delete it but we can set it to 0 hours | |||||
| // | |||||
| foreach ($this->remote_timesheets as $ts) | |||||
| { | |||||
| $staff_login = $ts->getEmployeeID(); | |||||
| if (!array_key_exists($staff_login, $this->local_timesheets)){//not found | |||||
| //we create empty timesheets for him/her | |||||
| $empty = new \XeroPHP\Models\PayrollAU\Timesheet($this->xero); | |||||
| $empty->setEmployeeID($staff_login) | |||||
| ->setTimeSheetID($ts->getTimesheetID()) | |||||
| ->setStartDate(new \DateTime($this->start_date)) | |||||
| ->setEndDate(new \DateTime($this->end_date)) | |||||
| ->setStatus("DRAFT"); | |||||
| if ( $ts->getStatus() == "DRAFT" ){//good, we can save it; | |||||
| $to_save[] = $empty;//add it to save | |||||
| }else{ | |||||
| $staff_name = \Biukop\AcareOffice::get_user_name_by_login($staff_login); | |||||
| $msg = sprintf("%s : %s is APPROVED, but needs to be empty it", | |||||
| $staff_name, | |||||
| $ts->getTimeSheetID()); | |||||
| \Biukop\AcareOffice::log($msg); | |||||
| } | |||||
| } | |||||
| } | |||||
| $this->xero->saveAll($to_save, false); | |||||
| } | |||||
| private function get_timesheet_id_by_employee_id($id) | |||||
| { | |||||
| foreach ($this->remote_timesheets as $ts) | |||||
| { | |||||
| $staff_login = $ts->getEmployeeID(); | |||||
| if($staff_login == $id){ | |||||
| return $ts->getTimesheetID(); | |||||
| } | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| public function warning_timesheet() | |||||
| { | |||||
| return $this->warning_timesheets; | |||||
| } | |||||
| private function approve_all(){ | |||||
| $to_save=[]; | |||||
| foreach ( $this->local_timesheets as $t){ | |||||
| $t->setDirty('EmployeeID'); | |||||
| $t->setDirty('StartDate'); | |||||
| $t->setDirty('EndDate'); | |||||
| $t->setDirty('TimesheetLines'); | |||||
| $t->setDirty('Status'); | |||||
| $t->setDirty('Hours'); | |||||
| $t->setDirty('TimesheetID'); | |||||
| $t->setStatus('APPROVED'); | |||||
| $to_save[]=$t; | |||||
| } | |||||
| $this->xero->saveAll($to_save, false); | |||||
| } | |||||
| public function get_buddy_timesheets($employee_id, $start, $end) | |||||
| { | |||||
| foreach ($this->remote_timesheets as $t){ | |||||
| if ( $t->getEmployeeID() == $employee_id && | |||||
| $t->getStartDate()->format('Y-m-d') == $start->format('Y-m-d') && | |||||
| $t->getEndDate()->format('Y-m-d') == $end->format('Y-m-d') ) | |||||
| { | |||||
| return $t; | |||||
| } | |||||
| } | |||||
| return NULL; //not found; | |||||
| } | |||||
| private function get_buddy_timesheet_by_ts($t) | |||||
| { | |||||
| $employee_id = $t->getEmployeeID(); | |||||
| $start = $t->getStartDate(); | |||||
| $end = $t->getEndDate(); | |||||
| return $this->get_buddy_timesheets($employee_id, $start, $end); | |||||
| } | |||||
| public function main(){ | |||||
| $this->warning_timesheets = []; | |||||
| $this->buddy_timesheets = []; | |||||
| $this->local_timesheets = $this->create_timesheet_from_db( | |||||
| new DateTime("2019-07-01"), | |||||
| new DateTime("2019-07-14") | |||||
| ); | |||||
| $length = count($this->local_timesheets); | |||||
| $to_save =[]; | |||||
| for ($i =0; $i < $length; $i++) | |||||
| { | |||||
| $me = $this->local_timesheets[$i]; | |||||
| $buddy = $this->get_buddy_timesheet_by_ts($me); | |||||
| $this->buddy_timesheets[$i] = $buddy; | |||||
| if ( $buddy != NULL ) { | |||||
| $timesheet_id = $buddy->getTimeSheetID(); | |||||
| $me->setTimeSheetID($timesheet_id); | |||||
| if ( $buddy->getStatus() != 'DRAFT'){ | |||||
| $this->warning_timesheets[]=$me; | |||||
| continue; | |||||
| }else{ | |||||
| $to_save[]=$me; | |||||
| } | |||||
| }else{ | |||||
| $to_save[]=$me; | |||||
| } | |||||
| } | |||||
| $this->xero->saveAlL($to_save, false); //false do not check GUID, always use POST; not PUT | |||||
| //$this->approve_all(); | |||||
| } | |||||
| } |
| $this->xero = new PrivateApplication($this->office_config()); | $this->xero = new PrivateApplication($this->office_config()); | ||||
| } | } | ||||
| public function get_xero_handle() | |||||
| { | |||||
| return $this->xero; | |||||
| } | |||||
| public function getClients($contact_group_id){ | public function getClients($contact_group_id){ | ||||
| $xero = $this->xero; | $xero = $this->xero; | ||||
| $cg = $xero->loadByGUID("Accounting\\ContactGroup", $contact_group_id); | $cg = $xero->loadByGUID("Accounting\\ContactGroup", $contact_group_id); | ||||
| } | } | ||||
| // | // | ||||
| //sync users to wordpress system | //sync users to wordpress system | ||||
| //does not work for too many users or employees | //does not work for too many users or employees | ||||
| /* sync payitems to wp options */ | /* sync payitems to wp options */ | ||||
| public function init_wp(){ | public function init_wp(){ | ||||
| $this->sync_payitem(); | |||||
| try{ | |||||
| $this->sync_payitem(); | |||||
| }catch(\XeroPHP\Remote\Exception $e){ | |||||
| } | |||||
| } | } | ||||
| private function sync_payitem(){ | private function sync_payitem(){ | ||||
| return $diff < $this->minimum_sync_interval_in_seconds; //default 10 minutes | return $diff < $this->minimum_sync_interval_in_seconds; //default 10 minutes | ||||
| } | } | ||||
| public function get_payroll_calendar() | |||||
| { | |||||
| $id = "33dc7df5-3060-4d76-b4da-57c20685d77d"; //fortnightly | |||||
| $pc = $this->xero->loadByGUID('PayrollAU\\PayrollCalendar', $id); | |||||
| return $pc; | |||||
| } | |||||
| } | } |
| background-color: white; | background-color: white; | ||||
| box-shadow: inset 0px 0px 2px dotted white; | box-shadow: inset 0px 0px 2px dotted white; | ||||
| border-radius: 100px; | border-radius: 100px; | ||||
| background-image: url(http://acaresydney.com.au/wp-content/uploads/2019/06/xero.png); | |||||
| background-image: url(../img/xero.png); | |||||
| background-size: 40px; | background-size: 40px; | ||||
| box-shadow: 0px 0px 10px white; | box-shadow: 0px 0px 10px white; | ||||
| } | } | ||||
| background-color: white; | background-color: white; | ||||
| box-shadow: inset 0px 0px 2px black; | box-shadow: inset 0px 0px 2px black; | ||||
| border-radius: 100px; | border-radius: 100px; | ||||
| background-image: url(http://acaresydney.com.au/wp-content/uploads/2019/06/wnet.png); | |||||
| background-image: url(../img/wnet.png); | |||||
| background-size: 40px; | background-size: 40px; | ||||
| box-shadow: 0px 0px 10px white; | box-shadow: 0px 0px 10px white; | ||||
| } | } | ||||
| background-color: white; | background-color: white; | ||||
| box-shadow: inset 0px 0px 2px black; | box-shadow: inset 0px 0px 2px black; | ||||
| border-radius: 100px; | border-radius: 100px; | ||||
| background-image: url(http://acaresydney.com.au/wp-content/uploads/2019/08/csv.png); | |||||
| background-image: url(../img/csv.png); | |||||
| background-size: 40px; | background-size: 40px; | ||||
| box-shadow: 0px 0px 10px white; | box-shadow: 0px 0px 10px white; | ||||
| } | } |
| @CHARSET "UTF-8"; | |||||
| .container{ | |||||
| width:100%; | |||||
| } | |||||
| #cstart, #cfinish, #paydate{ | |||||
| color: black; | |||||
| font-weight:900; | |||||
| width:100%; | |||||
| background-color: #EDEDED; | |||||
| border:none; | |||||
| } | |||||
| .hidden{ | |||||
| display:none; | |||||
| } | |||||
| td.sync_detail{ | |||||
| padding:0px; | |||||
| } | |||||
| table.hours{ | |||||
| margin-bottom:0px; | |||||
| } | |||||
| table.blueTable { | |||||
| border: 1px solid #1C6EA4; | |||||
| background-color: #EEEEEE; | |||||
| width: 100%; | |||||
| text-align: left; | |||||
| border-collapse: collapse; | |||||
| } | |||||
| table.blueTable td, table.blueTable th { | |||||
| border: 1px solid #AAAAAA; | |||||
| padding: 3px 2px; | |||||
| } | |||||
| table.blueTable tbody td { | |||||
| font-size: 13px; | |||||
| } | |||||
| table.blueTable tr:nth-child(even) { | |||||
| background: #D0E4F5; | |||||
| } | |||||
| table.blueTable thead { | |||||
| background: #1C6EA4; | |||||
| background: -moz-linear-gradient(top, #5592bb 0%, #327cad 66%, #1C6EA4 100%); | |||||
| background: -webkit-linear-gradient(top, #5592bb 0%, #327cad 66%, #1C6EA4 100%); | |||||
| background: linear-gradient(to bottom, #5592bb 0%, #327cad 66%, #1C6EA4 100%); | |||||
| border-bottom: 2px solid #444444; | |||||
| } | |||||
| table.blueTable thead th { | |||||
| font-size: 15px; | |||||
| font-weight: bold; | |||||
| color: #FFFFFF; | |||||
| border-left: 2px solid #D0E4F5; | |||||
| } | |||||
| table.blueTable thead th:first-child { | |||||
| border-left: none; | |||||
| } | |||||
| table.blueTable tfoot { | |||||
| font-size: 14px; | |||||
| font-weight: bold; | |||||
| color: #FFFFFF; | |||||
| background: #D0E4F5; | |||||
| background: -moz-linear-gradient(top, #dcebf7 0%, #d4e6f6 66%, #D0E4F5 100%); | |||||
| background: -webkit-linear-gradient(top, #dcebf7 0%, #d4e6f6 66%, #D0E4F5 100%); | |||||
| background: linear-gradient(to bottom, #dcebf7 0%, #d4e6f6 66%, #D0E4F5 100%); | |||||
| border-top: 2px solid #444444; | |||||
| } | |||||
| table.blueTable tfoot td { | |||||
| font-size: 14px; | |||||
| } | |||||
| table.blueTable tfoot .links { | |||||
| text-align: right; | |||||
| } | |||||
| table.blueTable tfoot .links a{ | |||||
| display: inline-block; | |||||
| background: #1C6EA4; | |||||
| color: #FFFFFF; | |||||
| padding: 2px 8px; | |||||
| border-radius: 5px; | |||||
| } | |||||
| .week1color { | |||||
| color: black; | |||||
| /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#ebe9f9+0,d8d0ef+50,cec7ec+51,c1bfea+100;Purple+3D+%231 */ | |||||
| background: #ebe9f9; /* Old browsers */ | |||||
| background: -moz-linear-gradient(top, #ebe9f9 0%, #d8d0ef 50%, #cec7ec 51%, #c1bfea 100%); /* FF3.6-15 */ | |||||
| background: -webkit-linear-gradient(top, #ebe9f9 0%,#d8d0ef 50%,#cec7ec 51%,#c1bfea 100%); /* Chrome10-25,Safari5.1-6 */ | |||||
| background: linear-gradient(to bottom, #ebe9f9 0%,#d8d0ef 50%,#cec7ec 51%,#c1bfea 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ | |||||
| filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ebe9f9', endColorstr='#c1bfea',GradientType=0 ); /* IE6-9 */ | |||||
| } | |||||
| .week2color { | |||||
| color: white; | |||||
| /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#627d4d+0,1f3b08+100;Olive+3D */ | |||||
| background: #627d4d; /* Old browsers */ | |||||
| background: -moz-linear-gradient(top, #627d4d 0%, #1f3b08 100%); /* FF3.6-15 */ | |||||
| background: -webkit-linear-gradient(top, #627d4d 0%,#1f3b08 100%); /* Chrome10-25,Safari5.1-6 */ | |||||
| background: linear-gradient(to bottom, #627d4d 0%,#1f3b08 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ | |||||
| filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#627d4d', endColorstr='#1f3b08',GradientType=0 ); /* IE6-9 */ | |||||
| } |
| {{#lines}} | |||||
| <tr> | |||||
| <td>{{staff_name}}</td> | |||||
| <td>{{rate_name}}</td> | |||||
| <td class=sync_detail> | |||||
| <table class='hours'> | |||||
| <thead id="p{{staff_id}}"> | |||||
| <th>{{days_1}}</th> | |||||
| <th>{{days_2}}</th> | |||||
| <th>{{days_3}}</th> | |||||
| <th>{{days_4}}</th> | |||||
| <th>{{days_5}}</th> | |||||
| <th>{{days_6}}</th> | |||||
| <th>{{days_7}}</th> | |||||
| <th>{{days_8}}</th> | |||||
| <th>{{days_9}}</th> | |||||
| <th>{{days_10}}</th> | |||||
| <th>{{days_11}}</th> | |||||
| <th>{{days_12}}</th> | |||||
| <th>{{days_13}}</th> | |||||
| <th>{{days_14}}</th> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class='local week1color' id='a_0'>{{local_1}}</td> | |||||
| <td class='local week1color' id='a_1'>{{local_2}}</td> | |||||
| <td class='local week1color' id='a_2'>{{local_3}}</td> | |||||
| <td class='local week1color' id='a_3'>{{local_4}}</td> | |||||
| <td class='local week1color' id='a_4'>{{local_5}}</td> | |||||
| <td class='local week1color' id='a_5'>{{local_6}}</td> | |||||
| <td class='local week1color' id='a_6'>{{local_7}}</td> | |||||
| <td class='local week2color' id='a_7'>{{local_8}}</td> | |||||
| <td class='local week2color' id='a_8'>{{local_9}}</td> | |||||
| <td class='local week2color' id='a_9'>{{local_10}}</td> | |||||
| <td class='local week2color' id='a_10'>{{local_11}}</td> | |||||
| <td class='local week2color' id='a_11'>{{local_12}}</td> | |||||
| <td class='local week2color' id='a_12'>{{local_13}}</td> | |||||
| <td class='local week2color' id='a_13'>{{local_14}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td colspan="14"> | |||||
| <div style="text-align: center">To Xero | |||||
| <span class="ticon ticon-arrow-down"></span> | |||||
| </div> | |||||
| </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class='xero week1color' id='b_0'>{{xero_1}}</td> | |||||
| <td class='xero week1color' id='b_1'>{{xero_2}}</td> | |||||
| <td class='xero week1color' id='b_2'>{{xero_3}}</td> | |||||
| <td class='xero week1color' id='b_3'>{{xero_4}}</td> | |||||
| <td class='xero week1color' id='b_4'>{{xero_5}}</td> | |||||
| <td class='xero week1color' id='b_5'>{{xero_6}}</td> | |||||
| <td class='xero week1color' id='b_6'>{{xero_7}}</td> | |||||
| <td class='xero week2color' id='b_7'>{{xero_8}}</td> | |||||
| <td class='xero week2color' id='b_8'>{{xero_9}}</td> | |||||
| <td class='xero week2color' id='b_9'>{{xero_10}}</td> | |||||
| <td class='xero week2color' id='b_10'>{{xero_11}}</td> | |||||
| <td class='xero week2color' id='b_11'>{{xero_12}}</td> | |||||
| <td class='xero week2color' id='b_12'>{{xero_13}}</td> | |||||
| <td class='xero week2color' id='b_13'>{{xero_14}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </td> | |||||
| <td style="vertical-align:bottom;">{{Xero_Status}}</td> | |||||
| </tr> | |||||
| {{/lines}} |
| (function ($) { | |||||
| $(function () { | |||||
| /*_____________________________________________*/ | |||||
| function test(){ | |||||
| var temp = $('#bts_staff_hours_template').html(); | |||||
| var lines = []; | |||||
| for (var i=1; i<10; i++){ | |||||
| var data = { | |||||
| staff_name: 'john', | |||||
| rate_name : 'some rate name', | |||||
| staff_id:"abc_"+ i, | |||||
| }; | |||||
| data.days={}; | |||||
| for (var j =1; j<=14; j++){ | |||||
| data['days_' + j] = j + '/July'; | |||||
| } | |||||
| lines.push(data); | |||||
| } | |||||
| var html = Mustache.render(temp, {lines:lines}); | |||||
| $('#staff').append(html); | |||||
| } | |||||
| function datebox(){ | |||||
| $( ".boundary_datepicker" ).datepicker(); | |||||
| $( ".boundary_datepicker" ).datepicker("option", "dateFormat", "yy-mm-dd"); | |||||
| } | |||||
| function display_hour_lines(response) | |||||
| { | |||||
| $('#staff').html(); | |||||
| var temp = $('#bts_staff_hours_template').html(); | |||||
| var html = Mustache.render(temp, response); | |||||
| $('#staff').append(html); | |||||
| } | |||||
| function set_payroll_calendar(cal) | |||||
| { | |||||
| $('#cstart').attr('value', cal.start); | |||||
| $('#cfinish').attr('value', cal.finish); | |||||
| $('#paydate').attr('value', cal.paydate); | |||||
| } | |||||
| function get_timesheet_from_xero(){ | |||||
| $.post(bts().ajax_url, { // POST request | |||||
| _ajax_nonce: bts().nonce, // nonce | |||||
| action: "get_timesheet_from_xero", // action | |||||
| sync: false, | |||||
| }).done(function(response){ | |||||
| set_payroll_calendar(response.payroll_calendar); | |||||
| console.log("%o", response); | |||||
| display_hour_lines(response); | |||||
| }).fail(function(){ | |||||
| console.warn('failed'); | |||||
| }).always(function(){ | |||||
| console.log('completed'); | |||||
| }); | |||||
| } | |||||
| function sync_timesheet_from_xero(){ | |||||
| $.post(bts().ajax_url, { // POST request | |||||
| _ajax_nonce: bts().nonce, // nonce | |||||
| action: "get_timesheet_from_xero", // action | |||||
| sync: true, | |||||
| }).done(function(response){ | |||||
| set_payroll_calendar(response.payroll_calendar); | |||||
| console.log("%o", response); | |||||
| display_hour_lines(response); | |||||
| }).fail(function(){ | |||||
| console.warn('failed'); | |||||
| }).always(function(){ | |||||
| console.log('completed'); | |||||
| }); | |||||
| } | |||||
| $('#sync_timesheet').click(function(){ | |||||
| sync_timesheet_from_xero(); | |||||
| }); | |||||
| /*_____________________________________________*/ | |||||
| }); | |||||
| })(jQuery); |
| add_shortcode( 'bts_staff_job_summary', array($this, 'bts_staff_job_summary')); | add_shortcode( 'bts_staff_job_summary', array($this, 'bts_staff_job_summary')); | ||||
| add_shortcode( 'bts_feedback_card', array($this, 'bts_feedback_card')); | add_shortcode( 'bts_feedback_card', array($this, 'bts_feedback_card')); | ||||
| add_shortcode( 'bb_timesheet_canvas', array($this, 'bb_timesheet_canvas')); | add_shortcode( 'bb_timesheet_canvas', array($this, 'bb_timesheet_canvas')); | ||||
| add_shortcode( 'bts_staff_hours_template', array($this, 'bts_staff_hours_template')); | |||||
| //user profile page | //user profile page | ||||
| add_action('wp_ajax_client_ack_job', array($this,'client_ack_job' )); | add_action('wp_ajax_client_ack_job', array($this,'client_ack_job' )); | ||||
| add_action('wp_ajax_nopriv_client_ack_job', array($this,'client_ack_job' )); | add_action('wp_ajax_nopriv_client_ack_job', array($this,'client_ack_job' )); | ||||
| add_action('wp_ajax_get_timesheet_from_xero', array($this,'get_timesheet_from_xero' )); | |||||
| // hook add_rewrite_rules function into rewrite_rules_array | // hook add_rewrite_rules function into rewrite_rules_array | ||||
| add_filter('rewrite_rules_array', array($this,'my_add_rewrite_rules')); | add_filter('rewrite_rules_array', array($this,'my_add_rewrite_rules')); | ||||
| $this->register_timesheet_js_css(); | $this->register_timesheet_js_css(); | ||||
| $this->register_task_js_css(); | $this->register_task_js_css(); | ||||
| $this->register_feedback_card_js_css(); | $this->register_feedback_card_js_css(); | ||||
| $this->register_xeroc_js_css(); | |||||
| } | } | ||||
| private function register_bts_js() | private function register_bts_js() | ||||
| { | { | ||||
| ) ); | ) ); | ||||
| } | } | ||||
| 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')); | |||||
| 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); | |||||
| } | |||||
| public function sync_users() | public function sync_users() | ||||
| { | { | ||||
| //dummy sync | //dummy sync | ||||
| return; | return; | ||||
| } | } | ||||
| public function 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); | |||||
| } | |||||
| } | |||||
| private function send_email_with_job_link($staff, $start, $finish) | private function send_email_with_job_link($staff, $start, $finish) | ||||
| { | { | ||||
| $message = file_get_contents(plugin_dir_path(__FILE__) . "/html/email_job.html"); | $message = file_get_contents(plugin_dir_path(__FILE__) . "/html/email_job.html"); | ||||
| return $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){ | public function bts_select_staff($attr){ | ||||
| $result = "<select> \n"; | $result = "<select> \n"; | ||||
| $staff = $this->get_people_by_role('staff'); | $staff = $this->get_people_by_role('staff'); | ||||
| { | { | ||||
| return file_get_contents(plugin_dir_path(__FILE__) . "/html/timesheet.html"); | 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'); | |||||
| } | |||||
| //generate template based on html file | //generate template based on html file | ||||
| private function template($id, $file) | private function template($id, $file) | ||||
| { | { | ||||
| $u = get_user_by('login', $staff); | $u = get_user_by('login', $staff); | ||||
| if ($this->is_staff($u)){ | if ($this->is_staff($u)){ | ||||
| $n = new UserJob($staff); | $n = new UserJob($staff); | ||||
| $resp = $n->list_jobs("$start 00:00:00", "$finish 23:59:59"); | |||||
| $resp = $n->list_jobs_by_staff("$start 00:00:00", "$finish 23:59:59"); | |||||
| if ($resp['status']=='success' && $resp['job_count'] >0 ){ | if ($resp['status']=='success' && $resp['job_count'] >0 ){ | ||||
| $msg = sprintf("Email to <strong>%s</strong> (with job=%d) \n", $u->user_email, $resp['job_count']); | $msg = sprintf("Email to <strong>%s</strong> (with job=%d) \n", $u->user_email, $resp['job_count']); | ||||
| $this->send_email_with_job_link($u, $start, $finish); | $this->send_email_with_job_link($u, $start, $finish); | ||||
| wp_send_json($response); | 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->xero->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->xero->get_xero_handle(), $finish); | |||||
| $local_ts = $this->create_timesheet_from_db($start, $finish); | |||||
| if ($sync){ | |||||
| $xx->set_local_timesheet($local_ts); | |||||
| $xx->save_to_xero(); | |||||
| } | |||||
| $days=[]; | |||||
| $d = new \DateTime($start); | |||||
| for ($i=1; $i<=14; $i++){ | |||||
| $days["days_$i"] = $d->format("d/F"); | |||||
| $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', | |||||
| ); | |||||
| //for local | |||||
| foreach($details as $rate => $hours){ | |||||
| $item['rate_name'] = $this->get_rate_name_by_id($rate); | |||||
| for ($i=1; $i<=14; $i++) | |||||
| { | |||||
| $item["local_$i"] = $hours[$i-1]; | |||||
| } | |||||
| } | |||||
| //for remote | |||||
| $buddy = $xx->get_buddy_timesheets($staff_login, new \DateTime($start), new \DateTime($finish)); | |||||
| if ( $buddy != NULL ) | |||||
| { | |||||
| $item['Xero_Status'] = $buddy->getStatus(); | |||||
| $remote_lines = $buddy->getTimesheetLines(); | |||||
| foreach($remote_lines as $rl) | |||||
| { | |||||
| if ( $rl->getEarningsRateID() == $rate){ | |||||
| for ($i=1; $i<=14; $i++) | |||||
| { | |||||
| $item["xero_$i"] = $rl->getNumberOfUnits()[$i-1]; | |||||
| } | |||||
| break;//we found it | |||||
| } | |||||
| } | |||||
| } | |||||
| $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); | |||||
| } | |||||
| 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"; | |||||
| } | |||||
| 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 $hours; | |||||
| } | |||||
| public function feedback_url() | public function feedback_url() | ||||
| { | { | ||||
| $users = get_users(array('role'=>'client')); | $users = get_users(array('role'=>'client')); | ||||
| foreach($users as $u){ | foreach($users as $u){ | ||||
| echo sprintf("%s:\t https://acaresydney.com.au/feedback_card/%s/\n", $u->display_name, $u->user_login); | |||||
| 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(); | $bb = new AcareOffice(); | ||||
| \WP_CLI::add_command( 'sync_users', array($bb, 'sync_user_cli')); | \WP_CLI::add_command( 'sync_users', array($bb, 'sync_user_cli')); | ||||
| \WP_CLI::add_command( 'email_jobs', array($bb, 'email_jobs')); | \WP_CLI::add_command( 'email_jobs', array($bb, 'email_jobs')); | ||||
| \WP_CLI::add_command( 'feedback_url', array($bb, 'feedback_url')); | \WP_CLI::add_command( 'feedback_url', array($bb, 'feedback_url')); | ||||
| \WP_CLI::add_command( 'produce_invoice', array($bb, 'produce_invoice')); | |||||
| } | } | ||||
| //$bb->class_loader(); | |||||
| //$bb->list_job_by_staff(); | |||||
| //$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"); |