| } | } | ||||
| return ""; | return ""; | ||||
| } | } | ||||
| public function get_tos_price($ndis_code) | |||||
| { | |||||
| foreach ($this->tos as $r){ | |||||
| if ($ndis_code == $r->code) | |||||
| return (float) $r->price; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| public function get_tos_unit($ndis_code) | |||||
| { | |||||
| foreach ($this->tos as $r){ | |||||
| if ($ndis_code == $r->code) | |||||
| return $r->unit; | |||||
| } | |||||
| return ""; | |||||
| } | |||||
| } | } |
| // )); | // )); | ||||
| } | } | ||||
| public function refresh_remote(){ | |||||
| $this->get_remote_timesheets(); | |||||
| } | |||||
| private function get_remote_timesheets() | private function get_remote_timesheets() | ||||
| { | { | ||||
| $this->remote_timesheets = $this->xero->load('PayrollAU\\Timesheet') | $this->remote_timesheets = $this->xero->load('PayrollAU\\Timesheet') | ||||
| { | { | ||||
| $to_save=[]; | $to_save=[]; | ||||
| foreach ( $this->local_timesheets as $t){ | foreach ( $this->local_timesheets as $t){ | ||||
| $buddy = $this->get_buddy_timesheet_by_ts($t); | |||||
| if ($buddy != NULL && $buddy->getStatus() != "DRAFT"){ | |||||
| continue;//we encountered approved timesheet; | |||||
| } | |||||
| $t->setDirty('EmployeeID'); | $t->setDirty('EmployeeID'); | ||||
| $t->setDirty('StartDate'); | $t->setDirty('StartDate'); | ||||
| $t->setDirty('EndDate'); | $t->setDirty('EndDate'); | ||||
| ->setEndDate(new \DateTime($this->end_date)) | ->setEndDate(new \DateTime($this->end_date)) | ||||
| ->setStatus("DRAFT"); | ->setStatus("DRAFT"); | ||||
| foreach($ts->getTimesheetLines() as $line){ | |||||
| $eid = $line->getEarningsRateID(); | |||||
| $zeroline= $this->create_empty_timesheet_lines($eid); | |||||
| $empty->addTimesheetLine($zeroline); | |||||
| } | |||||
| if ( $ts->getStatus() == "DRAFT" ){//good, we can save it; | if ( $ts->getStatus() == "DRAFT" ){//good, we can save it; | ||||
| $to_save[] = $empty;//add it to save | $to_save[] = $empty;//add it to save | ||||
| }else{ | }else{ | ||||
| $this->xero->saveAll($to_save, false); | $this->xero->saveAll($to_save, false); | ||||
| } | } | ||||
| private function create_empty_timesheet_lines($EarningsRateID) | |||||
| { | |||||
| $line = new \XeroPHP\Models\PayrollAU\Timesheet\TimesheetLine($xero); | |||||
| $line->setEarningsRateID($EarningsRateID); | |||||
| for ($i=0; $i<14; $i++) | |||||
| $line->addNumberOfUnit(0); | |||||
| return $line; | |||||
| } | |||||
| private function get_timesheet_id_by_employee_id($id) | private function get_timesheet_id_by_employee_id($id) | ||||
| { | { | ||||
| foreach ($this->remote_timesheets as $ts) | foreach ($this->remote_timesheets as $ts) | ||||
| return $this->warning_timesheets; | return $this->warning_timesheets; | ||||
| } | } | ||||
| private function approve_all(){ | |||||
| public function approve_all(){ | |||||
| $to_save=[]; | $to_save=[]; | ||||
| foreach ( $this->local_timesheets as $t){ | |||||
| foreach ( $this->remote_timesheets as $t){ | |||||
| if($t->getStatus() == 'DRAFT'){ | |||||
| $t->setDirty('EmployeeID'); | $t->setDirty('EmployeeID'); | ||||
| $t->setDirty('StartDate'); | $t->setDirty('StartDate'); | ||||
| $t->setDirty('EndDate'); | $t->setDirty('EndDate'); | ||||
| $t->setDirty('TimesheetID'); | $t->setDirty('TimesheetID'); | ||||
| $t->setStatus('APPROVED'); | $t->setStatus('APPROVED'); | ||||
| $to_save[]=$t; | $to_save[]=$t; | ||||
| } | |||||
| } | } | ||||
| $this->xero->saveAll($to_save, false); | $this->xero->saveAll($to_save, false); | ||||
| } | } |
| @CHARSET "UTF-8"; | @CHARSET "UTF-8"; | ||||
| .blink_me { | |||||
| animation: blinker 0.3s linear infinite; | |||||
| } | |||||
| @keyframes blinker { | |||||
| 50% { | |||||
| opacity: 0; | |||||
| } | |||||
| } | |||||
| .container{ | .container{ | ||||
| width:100%; | width:100%; | ||||
| } | } | ||||
| #cstart, #cfinish, #paydate{ | |||||
| color: black; | |||||
| font-weight:900; | |||||
| #cstart, #cfinish, #paydate, | |||||
| #invoice_start, #invoice_finish{ | |||||
| width:100%; | width:100%; | ||||
| background-color: #EDEDED; | |||||
| border:none; | border:none; | ||||
| text-align: center; | |||||
| color: green; | |||||
| font-weight: 900; | |||||
| background: #f0fbff; | |||||
| font-size: 1.5em; | |||||
| } | } | ||||
| .hidden{ | .hidden{ | ||||
| display:none; | display:none; | ||||
| } | } | ||||
| padding:0px; | padding:0px; | ||||
| } | } | ||||
| table.staffhours, | |||||
| table.clientinvoice{ | |||||
| background-color:white; | |||||
| } | |||||
| div.invoice_button, | |||||
| table.hours{ | table.hours{ | ||||
| margin-bottom:0px; | margin-bottom:0px; | ||||
| } | } | ||||
| } | } | ||||
| .week1color { | |||||
| tr.DRAFT .week1color { | |||||
| color: black; | 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 */ | /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#ebe9f9+0,d8d0ef+50,cec7ec+51,c1bfea+100;Purple+3D+%231 */ | ||||
| } | } | ||||
| .week2color { | |||||
| tr.DRAFT .week2color { | |||||
| color: white; | color: white; | ||||
| /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#627d4d+0,1f3b08+100;Olive+3D */ | /* Permalink - use to edit and share this gradient: https://colorzilla.com/gradient-editor/#627d4d+0,1f3b08+100;Olive+3D */ | ||||
| background: -webkit-linear-gradient(top, #627d4d 0%,#1f3b08 100%); /* Chrome10-25,Safari5.1-6 */ | 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+ */ | 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 */ | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#627d4d', endColorstr='#1f3b08',GradientType=0 ); /* IE6-9 */ | ||||
| } | |||||
| } | |||||
| #staff tr.loading, | |||||
| #staff td.loading{ | |||||
| text-align:center; | |||||
| height:500px; | |||||
| } | |||||
| td.loading img{ | |||||
| margin-top:200px; | |||||
| } | |||||
| table.hours th, | |||||
| table.hours td{ | |||||
| text-align: center; | |||||
| width:7.14% ; | |||||
| } | |||||
| .mismatch { | |||||
| background-color:yellow; | |||||
| color:red; | |||||
| font-size:1.3em; | |||||
| font-weight:900; | |||||
| } | |||||
| td.invoice_nubmer{ | |||||
| text-align:center; | |||||
| } | |||||
| img.waiting_invoice_number{ | |||||
| height:32px; | |||||
| display:none; | |||||
| } | |||||
| .invoice_nameonly_row{ | |||||
| cursor:pointer; | |||||
| } | |||||
| .invoice_nameonly_row:hover{ | |||||
| background-color:yellow; | |||||
| } | |||||
| .invoice_detail_row th{ | |||||
| cursor:pointer; | |||||
| } | |||||
| .invoice_detail_row th:hover{ | |||||
| color:green; | |||||
| } | |||||
| .invoice_nameonly_row_dummy{ | |||||
| color:white; | |||||
| background-color: black; | |||||
| display:none; | |||||
| animation: blinker 0.3s linear infinite; | |||||
| } | |||||
| <tr id="invoice_{{client_login}}" class="invoice_detail_row"> | |||||
| <td class='sync_detail client_invoice' data-client-id='{{client_login}}'> | |||||
| <table id="p{{client_login}" data-client-login="{{client_login}}" class="clientinvoice"> | |||||
| <thead> | |||||
| <tr> | |||||
| <th class="client_invoice"><i class="vc_tta-icon fa fa-minus-square"></i> {{client_name}}</th> | |||||
| <th class="client_invoice">Service</th> | |||||
| <th class="client_invoice">Start</th> | |||||
| <th class="client_invoice">Finish</th> | |||||
| <th class="client_invoice">Hours</th> | |||||
| <th class="client_invoice">Unit Price</th> | |||||
| <th class="client_invoice">Staff</th> | |||||
| <th class="client_invoice">Price</th> | |||||
| <th class="client_invoice">Invoice Number</th> | |||||
| </tr> | |||||
| </thead> | |||||
| <tfoot> | |||||
| <tr> | |||||
| <td > | |||||
| </td> | |||||
| <td colspan="7"></td> | |||||
| <td class='invoice_button' rowspan="rowspan"> | |||||
| <div class="wpb_wrapper" {{#nojob}}style="visibility:hidden;" {{/nojob}}> | |||||
| <div class="vc_btn3-container vc_btn3-right invoice_button"> | |||||
| <a data-client-login="{{client_login}}" | |||||
| class="invoice_button vc_general vc_btn3 vc_btn3-size-md vc_btn3-shape-rounded vc_btn3-style-modern disable_vc_btn3-block vc_btn3-icon-left vc_btn3-color-success" | |||||
| href="" title="" target="_blank"><i | |||||
| class="vc_btn3-icon fa fa-refresh"></i> Create Invoice {{client_name}} | |||||
| </a> | |||||
| </div> | |||||
| </div> | |||||
| </td> | |||||
| </tr> | |||||
| </tfoot> | |||||
| <tbody class="invoiceitems"> | |||||
| {{#jobs}} | |||||
| <tr> | |||||
| <td></td> | |||||
| <td>{{{tos}}}</td> | |||||
| <td>{{start}}</td> | |||||
| <td>{{finish}}</td> | |||||
| <td>{{hours}}</td> | |||||
| <td>{{unitprice}}</td> | |||||
| <td>{{staff_name}}</td> | |||||
| <td>{{price}}</td> | |||||
| <td class='invoice_nubmer'> | |||||
| <img class="waiting_invoice_number" src="http://acaresydney.com.au/wp-content/plugins/ts/img/arrow.gif"> | |||||
| <img class="waiting_invoice_number" src="http://acaresydney.com.au/wp-content/plugins/ts/img/xero.png"> | |||||
| </td> | |||||
| </tr> | |||||
| {{/jobs}} | |||||
| </tbody> | |||||
| </table> | |||||
| </td> | |||||
| </tr> |
| {{#lines}} | {{#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> | |||||
| <tr class="{{Xero_Status}}"> | |||||
| <td rowspan='{{rowspan}}'><strong>{{staff_name}}</strong> <br>Total: {{local_total}} hours</td> | |||||
| <td>Earnings</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> | |||||
| </table> | |||||
| </td> | </td> | ||||
| <td style="vertical-align:bottom;">{{Xero_Status}}</td> | |||||
| <td rowspan='{{rowspan}}' style="vertical-align:bottom;">{{remote_total}} hours<br><strong>{{Xero_Status}}</strong></td> | |||||
| </tr> | </tr> | ||||
| {{#ratetype}} | |||||
| <tr> | |||||
| <td>{{rate_name}}</td> | |||||
| <td class=sync_detail> | |||||
| <table class='hours'> | |||||
| <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 {{xero_1_mismatch}}' id='b_0'>{{xero_1}}</td> | |||||
| <td class='xero week1color {{xero_2_mismatch}}' id='b_1'>{{xero_2}}</td> | |||||
| <td class='xero week1color {{xero_3_mismatch}}' id='b_2'>{{xero_3}}</td> | |||||
| <td class='xero week1color {{xero_4_mismatch}}' id='b_3'>{{xero_4}}</td> | |||||
| <td class='xero week1color {{xero_5_mismatch}}' id='b_4'>{{xero_5}}</td> | |||||
| <td class='xero week1color {{xero_6_mismatch}}' id='b_5'>{{xero_6}}</td> | |||||
| <td class='xero week1color {{xero_7_mismatch}}' id='b_6'>{{xero_7}}</td> | |||||
| <td class='xero week2color {{xero_8_mismatch}}' id='b_7'>{{xero_8}}</td> | |||||
| <td class='xero week2color {{xero_9_mismatch}}' id='b_8'>{{xero_9}}</td> | |||||
| <td class='xero week2color {{xero_10_mismatch}}' id='b_9'>{{xero_10}}</td> | |||||
| <td class='xero week2color {{xero_11_mismatch}}' id='b_10'>{{xero_11}}</td> | |||||
| <td class='xero week2color {{xero_12_mismatch}}' id='b_11'>{{xero_12}}</td> | |||||
| <td class='xero week2color {{xero_13_mismatch}}' id='b_12'>{{xero_13}}</td> | |||||
| <td class='xero week2color {{xero_14_mismatch}}' id='b_13'>{{xero_14}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </td> | |||||
| </tr> | |||||
| {{/ratetype}} | |||||
| {{/lines}} | {{/lines}} |
| <img src="#"> | <img src="#"> | ||||
| </div> | </div> | ||||
| <div class='statusbar titlebar_gradient'> | <div class='statusbar titlebar_gradient'> | ||||
| <a id='xeroc' href="/xeroc/" target="_blank"> | |||||
| <div class='xero'> | <div class='xero'> | ||||
| <i class="ticon ticon-times"></i> | <i class="ticon ticon-times"></i> | ||||
| </div> | </div> | ||||
| </a> | |||||
| <div class='wifi'> | <div class='wifi'> | ||||
| <i class="ticon ticon-times"></i> | <i class="ticon ticon-times"></i> | ||||
| </div> | </div> | ||||
| <a id='ndiscsv' href="/ndiscsv/" target="_blank"> | |||||
| <div class='csv'> | <div class='csv'> | ||||
| <i class="ticon ticon-times"></i> | <i class="ticon ticon-times"></i> | ||||
| </div> | </div> | ||||
| </a> | |||||
| <div class='wages'> | <div class='wages'> | ||||
| $ | $ | ||||
| <div name='number'> | <div name='number'> |
| /*! | |||||
| * jQuery scrollintoview() plugin and :scrollable selector filter | |||||
| * | |||||
| * Version 1.9.4 (06 April 2016) | |||||
| * Requires jQuery 1.4 or newer | |||||
| * | |||||
| * Copyright (c) 2011 Robert Koritnik | |||||
| * Licensed under the terms of the MIT license | |||||
| * http://www.opensource.org/licenses/mit-license.php | |||||
| */ | |||||
| !function(root, factory) { | |||||
| if (typeof define === 'function' && define.amd) { | |||||
| define(['jquery'], factory); | |||||
| } else if (typeof exports === 'object') { | |||||
| factory(require('jquery')); | |||||
| } else { | |||||
| factory(root.jQuery); | |||||
| } | |||||
| } | |||||
| (this, function($) { | |||||
| var converter = { | |||||
| vertical: { x: false, y: true }, | |||||
| horizontal: { x: true, y: false }, | |||||
| both: { x: true, y: true }, | |||||
| x: { x: true, y: false }, | |||||
| y: { x: false, y: true } | |||||
| }; | |||||
| var settings = { | |||||
| duration: "fast", | |||||
| direction: "both", | |||||
| viewPadding: 0 | |||||
| }; | |||||
| var rootrx = /^(?:html)$/i; | |||||
| // gets border dimensions | |||||
| var borders = function(domElement, styles) { | |||||
| styles = styles || (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(domElement, null) : domElement.currentStyle); | |||||
| var px = document.defaultView && document.defaultView.getComputedStyle ? true : false; | |||||
| var b = { | |||||
| top: (parseFloat(px ? styles.borderTopWidth : $.css(domElement, "borderTopWidth")) || 0), | |||||
| left: (parseFloat(px ? styles.borderLeftWidth : $.css(domElement, "borderLeftWidth")) || 0), | |||||
| bottom: (parseFloat(px ? styles.borderBottomWidth : $.css(domElement, "borderBottomWidth")) || 0), | |||||
| right: (parseFloat(px ? styles.borderRightWidth : $.css(domElement, "borderRightWidth")) || 0) | |||||
| }; | |||||
| return { | |||||
| top: b.top, | |||||
| left: b.left, | |||||
| bottom: b.bottom, | |||||
| right: b.right, | |||||
| vertical: b.top + b.bottom, | |||||
| horizontal: b.left + b.right | |||||
| }; | |||||
| }; | |||||
| var dimensions = function($element) { | |||||
| var elem = $element[0], | |||||
| isRoot = rootrx.test(elem.nodeName), | |||||
| $elem = isRoot ? $(window) : $element; | |||||
| return { | |||||
| border: isRoot ? { top: 0, left: 0, bottom: 0, right: 0 } : borders(elem), | |||||
| scroll: { | |||||
| top: $elem.scrollTop(), | |||||
| left: $elem.scrollLeft(), | |||||
| maxtop: elem.scrollHeight - elem.clientHeight, | |||||
| maxleft: elem.scrollWidth - elem.clientWidth | |||||
| }, | |||||
| scrollbar: isRoot | |||||
| ? { right: 0, bottom: 0 } | |||||
| : { | |||||
| right: $elem.innerWidth() - elem.clientWidth, | |||||
| bottom: $elem.innerHeight() - elem.clientHeight | |||||
| }, | |||||
| rect: isRoot ? { top: 0, left: 0, bottom: elem.clientHeight, right: elem.clientWidth } : elem.getBoundingClientRect() | |||||
| }; | |||||
| }; | |||||
| $.fn.extend({ | |||||
| scrollintoview: function(options) { | |||||
| /// <summary>Scrolls the first element in the set into view by scrolling its closest scrollable parent.</summary> | |||||
| /// <param name="options" type="Object">Additional options that can configure scrolling: | |||||
| /// duration (default: "fast") - jQuery animation speed (can be a duration string or number of milliseconds) | |||||
| /// direction (default: "both") - select possible scrollings ("vertical" or "y", "horizontal" or "x", "both") | |||||
| /// complete (default: none) - a function to call when scrolling completes (called in context of the DOM element being scrolled) | |||||
| /// </param> | |||||
| /// <return type="jQuery">Returns the same jQuery set that this function was run on.</return> | |||||
| options = $.extend({}, settings, options); | |||||
| options.direction = converter[typeof (options.direction) === "string" && options.direction.toLowerCase()] || converter.both; | |||||
| if (typeof options.viewPadding == "number") { | |||||
| options.viewPadding = { x: options.viewPadding, y: options.viewPadding }; | |||||
| } else if (typeof options.viewPadding == "object") { | |||||
| if (options.viewPadding.x == undefined) { | |||||
| options.viewPadding.x = 0; | |||||
| } | |||||
| if (options.viewPadding.y == undefined) { | |||||
| options.viewPadding.y = 0; | |||||
| } | |||||
| } | |||||
| var dirStr = ""; | |||||
| if (options.direction.x === true) dirStr = "horizontal"; | |||||
| if (options.direction.y === true) dirStr = dirStr ? "both" : "vertical"; | |||||
| var el = this.eq(0); | |||||
| var scroller = el.parent().closest(":scrollable(" + dirStr + ")"); | |||||
| // check if there's anything to scroll in the first place | |||||
| if (scroller.length > 0) { | |||||
| scroller = scroller.eq(0); | |||||
| var dim = { | |||||
| e: dimensions(el), | |||||
| s: dimensions(scroller) | |||||
| }; | |||||
| var rel = { | |||||
| top: dim.e.rect.top - (dim.s.rect.top + dim.s.border.top), | |||||
| bottom: dim.s.rect.bottom - dim.s.border.bottom - dim.s.scrollbar.bottom - dim.e.rect.bottom, | |||||
| left: dim.e.rect.left - (dim.s.rect.left + dim.s.border.left), | |||||
| right: dim.s.rect.right - dim.s.border.right - dim.s.scrollbar.right - dim.e.rect.right | |||||
| }; | |||||
| var animProperties = {}; | |||||
| // vertical scroll | |||||
| if (options.direction.y === true) { | |||||
| if (rel.top < 0) { | |||||
| animProperties.scrollTop = Math.max(0, dim.s.scroll.top + rel.top - options.viewPadding.y); | |||||
| } else if (rel.top > 0 && rel.bottom < 0) { | |||||
| animProperties.scrollTop = Math.min(dim.s.scroll.top + Math.min(rel.top, -rel.bottom) + options.viewPadding.y, dim.s.scroll.maxtop); | |||||
| } | |||||
| } | |||||
| // horizontal scroll | |||||
| if (options.direction.x === true) { | |||||
| if (rel.left < 0) { | |||||
| animProperties.scrollLeft = Math.max(0, dim.s.scroll.left + rel.left - options.viewPadding.x); | |||||
| } else if (rel.left > 0 && rel.right < 0) { | |||||
| animProperties.scrollLeft = Math.min(dim.s.scroll.left + Math.min(rel.left, -rel.right) + options.viewPadding.x, dim.s.scroll.maxleft); | |||||
| } | |||||
| } | |||||
| // scroll if needed | |||||
| if (!$.isEmptyObject(animProperties)) { | |||||
| var scrollExpect = {}, | |||||
| scrollListener = scroller; | |||||
| if (rootrx.test(scroller[0].nodeName)) { | |||||
| scroller = $("html,body"); | |||||
| scrollListener = $(window); | |||||
| } | |||||
| function animateStep(now, tween) { | |||||
| scrollExpect[tween.prop] = Math.floor(now); | |||||
| }; | |||||
| function onscroll(event) { | |||||
| $.each(scrollExpect, function(key, value) { | |||||
| if (Math.floor(scrollListener[key]()) != Math.floor(value)) { | |||||
| options.complete = null; // don't run complete function if the scrolling was interrupted | |||||
| scroller.stop('scrollintoview'); | |||||
| } | |||||
| }); | |||||
| } | |||||
| scrollListener.on('scroll', onscroll); | |||||
| scroller | |||||
| .stop('scrollintoview') | |||||
| .animate(animProperties, { duration: options.duration, step: animateStep, queue: 'scrollintoview' }) | |||||
| .eq(0) // we want function to be called just once (ref. "html,body") | |||||
| .queue('scrollintoview', function(next) { | |||||
| scrollListener.off('scroll', onscroll); | |||||
| $.isFunction(options.complete) && options.complete.call(scroller[0]); | |||||
| next(); | |||||
| }) | |||||
| scroller.dequeue('scrollintoview'); | |||||
| } else { | |||||
| // when there's nothing to scroll, just call the "complete" function | |||||
| $.isFunction(options.complete) && options.complete.call(scroller[0]); | |||||
| } | |||||
| } | |||||
| // return set back | |||||
| return this; | |||||
| } | |||||
| }); | |||||
| var scrollValue = { | |||||
| auto: true, | |||||
| scroll: true, | |||||
| visible: false, | |||||
| hidden: false | |||||
| }; | |||||
| var scroll = function(element, direction) { | |||||
| direction = converter[typeof (direction) === "string" && direction.toLowerCase()] || converter.both; | |||||
| var styles = (document.defaultView && document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, null) : element.currentStyle); | |||||
| var overflow = { | |||||
| x: scrollValue[styles.overflowX.toLowerCase()] || false, | |||||
| y: scrollValue[styles.overflowY.toLowerCase()] || false, | |||||
| isRoot: rootrx.test(element.nodeName) | |||||
| }; | |||||
| // check if completely unscrollable (exclude HTML element because it's special) | |||||
| if (!overflow.x && !overflow.y && !overflow.isRoot) { | |||||
| return false; | |||||
| } | |||||
| var size = { | |||||
| height: { | |||||
| scroll: element.scrollHeight, | |||||
| client: element.clientHeight | |||||
| }, | |||||
| width: { | |||||
| scroll: element.scrollWidth, | |||||
| client: element.clientWidth | |||||
| }, | |||||
| // check overflow.x/y because iPad (and possibly other tablets) don't dislay scrollbars | |||||
| scrollableX: function() { | |||||
| return (overflow.x || overflow.isRoot) && this.width.scroll > this.width.client; | |||||
| }, | |||||
| scrollableY: function() { | |||||
| return (overflow.y || overflow.isRoot) && this.height.scroll > this.height.client; | |||||
| } | |||||
| }; | |||||
| return direction.y && size.scrollableY() || direction.x && size.scrollableX(); | |||||
| }; | |||||
| $.expr[":"].scrollable = $.expr.createPseudo(function(direction) { | |||||
| return function(element) { | |||||
| return scroll(element, direction); | |||||
| }; | |||||
| }); | |||||
| }); |
| $( ".boundary_datepicker" ).datepicker("option", "dateFormat", "yy-mm-dd"); | $( ".boundary_datepicker" ).datepicker("option", "dateFormat", "yy-mm-dd"); | ||||
| } | } | ||||
| function loading() | |||||
| { | |||||
| return "<tr class='loading' ><td colspan=4 class='loading'><img src='"+bts().load_job_img +"'><br><h1>Sync to Xero</h1></td></tr>"; | |||||
| } | |||||
| function display_hour_lines(response) | function display_hour_lines(response) | ||||
| { | { | ||||
| $('#staff').html(); | |||||
| $('#staff').html(''); | |||||
| var temp = $('#bts_staff_hours_template').html(); | var temp = $('#bts_staff_hours_template').html(); | ||||
| var html = Mustache.render(temp, response); | var html = Mustache.render(temp, response); | ||||
| $('#staff').append(html); | $('#staff').append(html); | ||||
| } | } | ||||
| function get_timesheet_from_xero(){ | function get_timesheet_from_xero(){ | ||||
| $('#staff').html(loading()); | |||||
| $.post(bts().ajax_url, { // POST request | $.post(bts().ajax_url, { // POST request | ||||
| _ajax_nonce: bts().nonce, // nonce | _ajax_nonce: bts().nonce, // nonce | ||||
| action: "get_timesheet_from_xero", // action | action: "get_timesheet_from_xero", // action | ||||
| } | } | ||||
| function sync_timesheet_from_xero(){ | function sync_timesheet_from_xero(){ | ||||
| $('#staff').html(loading()); | |||||
| $.post(bts().ajax_url, { // POST request | $.post(bts().ajax_url, { // POST request | ||||
| _ajax_nonce: bts().nonce, // nonce | _ajax_nonce: bts().nonce, // nonce | ||||
| action: "get_timesheet_from_xero", // action | action: "get_timesheet_from_xero", // action | ||||
| sync: true, | sync: true, | ||||
| }).done(function(response){ | }).done(function(response){ | ||||
| set_payroll_calendar(response.payroll_calendar); | set_payroll_calendar(response.payroll_calendar); | ||||
| console.log("%o", response); | |||||
| display_hour_lines(response); | display_hour_lines(response); | ||||
| }).fail(function(){ | }).fail(function(){ | ||||
| console.warn('failed'); | console.warn('failed'); | ||||
| console.log('completed'); | console.log('completed'); | ||||
| }); | }); | ||||
| } | } | ||||
| function approve_all_timesheet() | |||||
| { | |||||
| $.post(bts().ajax_url, { // POST request | |||||
| _ajax_nonce: bts().nonce, // nonce | |||||
| action: "approve_all_timesheet", // action | |||||
| }).done(function(response){ | |||||
| if (response.status == 'success') | |||||
| alert("approve all succeed"); | |||||
| else | |||||
| alert(response.err); | |||||
| }).fail(function(){ | |||||
| alert("Network Error, cannot approve all"); | |||||
| }).always(function(){ | |||||
| console.log('completed'); | |||||
| }); | |||||
| } | |||||
| $('#sync_timesheet').click(function(){ | $('#sync_timesheet').click(function(){ | ||||
| sync_timesheet_from_xero(); | sync_timesheet_from_xero(); | ||||
| }); | }); | ||||
| $('#approve_all').click(function(){ | |||||
| approve_all_timesheet(); | |||||
| }); | |||||
| function display_invoice_items_test(response) | |||||
| { | |||||
| var template = $('#bts_client_invoice_template').html(); | |||||
| for (var i=1; i<10; i++){ | |||||
| data = { | |||||
| client_name: "Martin", | |||||
| jobs:[ | |||||
| { | |||||
| tos: "service a " + i, | |||||
| staff_name: "joe", | |||||
| start: "2019-07-01", | |||||
| finish: "2019-07-14", | |||||
| hours: i, | |||||
| price: 336, | |||||
| }, | |||||
| { | |||||
| tos: "service b " + i, | |||||
| staff_name: "joe dne", | |||||
| start: "2019-07-01", | |||||
| finish: "2019-07-14", | |||||
| hours: i, | |||||
| price: 16, | |||||
| } | |||||
| ] | |||||
| } | |||||
| html = Mustache.render(template, data); | |||||
| $('#clientinvoice').append(html); | |||||
| } | |||||
| } | |||||
| function display_invoice_items(selector, response) | |||||
| { | |||||
| var template = $('#bts_client_invoice_template').html(); | |||||
| html = Mustache.render(template, response); | |||||
| el = $(html); | |||||
| $(selector).after(el); | |||||
| //el.SlideDown(); | |||||
| el.show(); | |||||
| } | |||||
| function get_invoice_item(selector, client_id) | |||||
| { | |||||
| $.post(bts().ajax_url, { // POST request | |||||
| _ajax_nonce: bts().nonce, // nonce | |||||
| action: "get_invoice_item", // action | |||||
| client: client_id, | |||||
| start: get_invoice_start(), | |||||
| finish: get_invoice_finish(), | |||||
| }).done(function(response){ | |||||
| if (response.status == 'success'){ | |||||
| display_invoice_items(selector, response); | |||||
| $(selector).hide(); | |||||
| }else{ | |||||
| alert(response.err); | |||||
| } | |||||
| }).fail(function(){ | |||||
| alert("Network Error, cannot approve all"); | |||||
| }).always(function(){ | |||||
| console.log('completed'); | |||||
| }); | |||||
| } | |||||
| function create_invoice_number(client_id) | |||||
| { | |||||
| start_showing_invoice_request(client_id); | |||||
| $.post(bts().ajax_url, { // POST request | |||||
| _ajax_nonce: bts().nonce, // nonce | |||||
| action: "create_invoice_in_xero", // action | |||||
| client: client_id, | |||||
| start: get_invoice_start(), | |||||
| finish: get_invoice_finish(), | |||||
| }).done(function(response){ | |||||
| if (response.status == 'success'){ | |||||
| show_invoice_number(response); | |||||
| }else{ | |||||
| alert(response.err); | |||||
| } | |||||
| }).fail(function(){ | |||||
| alert("Network Error, cannot approve all"); | |||||
| }).always(function(){ | |||||
| console.log('completed'); | |||||
| }); | |||||
| } | |||||
| function start_showing_invoice_request(client_id) | |||||
| { | |||||
| $('td.invoice_nubmer img').show(); | |||||
| animate_into_top('#invoice_' + client_id); | |||||
| return; | |||||
| $('#invoice_' + client_id).scrollintoview({ | |||||
| duration: 2500, | |||||
| direction: "vertical", | |||||
| viewPadding: { y: 10 }, | |||||
| complete: function() { | |||||
| // highlight the element so user's focus gets where it needs to be | |||||
| } | |||||
| }); | |||||
| } | |||||
| function animate_into_top(el) | |||||
| { | |||||
| var offset = $(el).offset(); // Contains .top and .left | |||||
| offset.left -= 20; | |||||
| offset.top -= 20; | |||||
| $('html, body').animate({ | |||||
| scrollTop: offset.top, | |||||
| scrollLeft: offset.left | |||||
| },1000); | |||||
| } | |||||
| function show_invoice_number(response) | |||||
| { | |||||
| $('td.invoice_nubmer').html(response.invoice_number); | |||||
| $('td.invoice_button div').hide(); | |||||
| } | |||||
| function get_invoice_start() | |||||
| { | |||||
| return $('#invoice_start').attr('value'); | |||||
| } | |||||
| function get_invoice_finish() | |||||
| { | |||||
| return $('#invoice_finish').attr('value'); | |||||
| } | |||||
| $(document).on('click', 'td.client_nameonly', function(){ | |||||
| var id = $(this).attr('data-client-id'); | |||||
| $('#nameonly_' + id).hide(); | |||||
| $('#dummyui_' + id).show(); | |||||
| if( $('#invoice_' + id).length == 0 ){ | |||||
| get_invoice_item('#dummyui_' + id, id); | |||||
| }else{ | |||||
| $('#dummyui_' + id).hide(); | |||||
| $('#invoice_' + id).show(); | |||||
| } | |||||
| }); | |||||
| $(document).on('click', 'th.client_invoice', function(){ | |||||
| var id = $(this).closest('td.client_invoice').attr('data-client-id'); | |||||
| $('#nameonly_' + id).show(); | |||||
| $('#invoice_' + id).hide(); | |||||
| }); | |||||
| $(document).on('hide','#maintabs',function(){ | |||||
| alert('abc'); | |||||
| }); | |||||
| $(document).on("afterShow.vc.accordion", function(e, opt) { | |||||
| console.log("%o, %o", e, opt); | |||||
| if (e.target.hash =="#1565353205981-c3582e44-83d2"){ | |||||
| get_timesheet_from_xero(); | |||||
| } | |||||
| }) | |||||
| function format_date(date){ | |||||
| var dd = date.getDate(); | |||||
| var mm = date.getMonth() + 1; //January is 0! | |||||
| var yyyy = date.getFullYear(); | |||||
| if (dd < 10) { | |||||
| dd = '0' + dd; | |||||
| } | |||||
| if (mm < 10) { | |||||
| mm = '0' + mm; | |||||
| } | |||||
| return yyyy + '-' + mm + '-' +dd ; | |||||
| } | |||||
| function daysInMonth (month, year) { | |||||
| return new Date(year, month, 0).getDate(); | |||||
| } | |||||
| function setup_invoice_start_finish() | |||||
| { | |||||
| var date = new Date(); | |||||
| var firstDay = new Date(date.getFullYear(), | |||||
| date.getMonth(), 1); | |||||
| var lastDay = new Date(date.getFullYear(), | |||||
| date.getMonth(), daysInMonth(date.getMonth()+1, | |||||
| date.getFullYear())); | |||||
| $('#invoice_start').attr('value', format_date(firstDay)); | |||||
| $('#invoice_finish').attr('value', format_date(lastDay)); | |||||
| } | |||||
| $('#invoice_start').change(function(){ | |||||
| clear_all_invoice(); | |||||
| }); | |||||
| $('#invoice_finish').change(function(){ | |||||
| clear_all_invoice(); | |||||
| }); | |||||
| $(document).on('click', 'a.invoice_button', function(e){ | |||||
| e.stopPropagation(); | |||||
| var id = $(this).attr('data-client-login'); | |||||
| create_invoice_number(id); | |||||
| return false; | |||||
| }); | |||||
| function clear_all_invoice() | |||||
| { | |||||
| if ( $(".invoice_detail_row").length >0 ){ | |||||
| if (!confirm("Change Date will clear all invoice details")) | |||||
| return; | |||||
| } | |||||
| $(".invoice_detail_row").remove(); | |||||
| $(".invoice_nameonly_row").show(); | |||||
| } | |||||
| datebox(); | |||||
| setup_invoice_start_finish(); | |||||
| /*_____________________________________________*/ | /*_____________________________________________*/ | ||||
| }); | }); | ||||
| })(jQuery); | |||||
| })(jQuery); |
| 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')); | 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 | //user profile page | ||||
| add_shortcode( 'bts_user_name', array($this,'bts_user_name')); | add_shortcode( 'bts_user_name', array($this,'bts_user_name')); | ||||
| 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' )); | add_action('wp_ajax_get_timesheet_from_xero', array($this,'get_timesheet_from_xero' )); | ||||
| add_action('wp_ajax_approve_all_timesheet', array($this,'approve_all_timesheet' )); | |||||
| add_action('wp_ajax_get_invoice_item', array($this,'get_invoice_item' )); | |||||
| add_action('wp_ajax_create_invoice_in_xero', array($this,'create_invoice_in_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->xero->init_wp(); | $this->xero->init_wp(); | ||||
| //$abc = new AddrMap("01515b52-6936-46b2-a000-9ad4cd7a5b50", "0768db6d-e5f4-4b45-89a2-29f7e8d2953c"); | //$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"); | |||||
| //$abc = new AddrMap("122eb1d0-d8c4-4fc3-8bf8-b7825bee1a01", "0768db6d-e5f4-4b45-89a2-29f7e8d2953c"); | |||||
| $this->check_csv_download(); | |||||
| } | |||||
| private function check_csv_download() | |||||
| { | |||||
| $url = $_SERVER['REQUEST_URI']; | |||||
| $matches=[]; | |||||
| preg_match("/\/ndiscsv\/start-([^\/]+)\/finish-([^\/]+)\/?$/", $url, $matches); | |||||
| if ( $matches !=3 || $_SERVER['REQUEST_URI'] != $matches[0] ) | |||||
| return; | |||||
| $start = $matches[1]; | |||||
| $finish = $matches[2]; | |||||
| $filename="{$start}___{$finish}.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"); | |||||
| echo "fuck this file"; | |||||
| exit(); | |||||
| } | } | ||||
| 'feedback_card/([^/]+)/week-([^/]+)/?$' => 'index.php?pagename=feedback_card&bts_user_id=$matches[1]&bts_week_id=$matches[2]', | '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]', | '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; | $aRules = $aNewRules + $aRules; | ||||
| return $aRules; | return $aRules; | ||||
| wp_enqueue_style( 'bts_xeroc', plugins_url('css/xeroc.css', __FILE__)); | 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( '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('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; | global $wp_scripts; | ||||
| wp_enqueue_script('jquery-ui-datepicker'); | wp_enqueue_script('jquery-ui-datepicker'); | ||||
| public function bts_staff_hours_template($attr){ | public function bts_staff_hours_template($attr){ | ||||
| return $this->template('bts_staff_hours_template', 'bts_staff_hours_template.html'); | 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) | |||||
| { | |||||
| $result = ""; | |||||
| $users = $users = get_users(array('role' => 'client')); | |||||
| $row = <<<ZOT | |||||
| <tr id="nameonly_%s" data-client-id='%s' class="invoice_nameonly_row"> | |||||
| <td class="client_nameonly" data-client-id='%s'> | |||||
| <i class="vc_tta-icon fa fa-plus-square"></i> %s | |||||
| </td> | |||||
| </tr> | |||||
| <tr id="dummyui_%s" data-client-id='%s' class="invoice_nameonly_row_dummy"> | |||||
| <td class="client_nameonly" data-client-id='%s'> | |||||
| <i class="vc_tta-icon fa fa-plus-square"></i> %s is loading .... please wait... | |||||
| </td> | |||||
| </tr> | |||||
| ZOT; | |||||
| foreach ($users as $u) | |||||
| { | |||||
| $payment = get_user_meta($u->ID, 'payment', true); | |||||
| if( $payment != 'invoice' ) | |||||
| continue; //bypass | |||||
| $result .= sprintf($row, $u->user_login, $u->user_login, $u->user_login, $u->display_name, | |||||
| $u->user_login, $u->user_login, $u->user_login, $u->display_name); | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| //generate template based on html file | //generate template based on html file | ||||
| private function template($id, $file) | private function template($id, $file) | ||||
| { | { | ||||
| if ($sync){ | if ($sync){ | ||||
| $xx->set_local_timesheet($local_ts); | $xx->set_local_timesheet($local_ts); | ||||
| $xx->save_to_xero(); | $xx->save_to_xero(); | ||||
| $xx->refresh_remote(); | |||||
| } | } | ||||
| 'staff_name' => $this->get_user_name_by_login ($staff_login), | 'staff_name' => $this->get_user_name_by_login ($staff_login), | ||||
| 'staff_id' => $staff_login, | 'staff_id' => $staff_login, | ||||
| 'Xero_Status' => 'Empty', | 'Xero_Status' => 'Empty', | ||||
| 'rowspan' =>1, | |||||
| 'local_total' =>0, | |||||
| 'remote_total'=>0, | |||||
| 'ratetype' => [], | |||||
| ); | ); | ||||
| //for local | |||||
| $buddy = $xx->get_buddy_timesheets($staff_login, new \DateTime($start), new \DateTime($finish)); | |||||
| if ($buddy != NULL){ | |||||
| $item['Xero_Status'] = $buddy->getStatus(); | |||||
| }else{ | |||||
| $item['Xero_Status'] = "Not Exist"; | |||||
| } | |||||
| foreach($details as $rate => $hours){ | foreach($details as $rate => $hours){ | ||||
| $item['rate_name'] = $this->get_rate_name_by_id($rate); | |||||
| $item['rowspan'] ++; | |||||
| $ratetype_item=[]; | |||||
| $ratetype_item['rate_name'] = $this->get_rate_name_by_id($rate); | |||||
| //for local | |||||
| for ($i=1; $i<=14; $i++) | for ($i=1; $i<=14; $i++) | ||||
| { | { | ||||
| $item["local_$i"] = $hours[$i-1]; | |||||
| $ratetype_item["local_$i"] = $hours[$i-1]; | |||||
| $item['local_total'] += $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) | |||||
| //for remote | |||||
| if ( $buddy != NULL ) | |||||
| { | { | ||||
| if ( $rl->getEarningsRateID() == $rate){ | |||||
| for ($i=1; $i<=14; $i++) | |||||
| { | |||||
| $item["xero_$i"] = $rl->getNumberOfUnits()[$i-1]; | |||||
| } | |||||
| break;//we found it | |||||
| } | |||||
| $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); | $item = array_merge($item, $days); | ||||
| wp_send_json($response); | wp_send_json($response); | ||||
| } | } | ||||
| public function approve_all_timesheet() | |||||
| { | |||||
| check_ajax_referer('acaresydney'); | |||||
| //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); | |||||
| $xx->approve_all(); | |||||
| wp_send_json($response); | |||||
| } | |||||
| static public function get_user_name_by_login($login) | static public function get_user_name_by_login($login) | ||||
| { | { | ||||
| $user = get_user_by('login', $login); | $user = get_user_by('login', $login); | ||||
| return "Invalid Name"; | 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'=>'' | |||||
| ]; | |||||
| $now = new \DateTime(); | |||||
| $price = new NdisPrice($now->format("Y"));//current year; | |||||
| $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_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; | |||||
| } | |||||
| foreach ($summary as $key => $val) | |||||
| { | |||||
| $response['summary'][] = array( | |||||
| 'ndis' => $key, | |||||
| 'tos' => $price->get_tos_str($key), | |||||
| 'Hours'=> $val, | |||||
| ); | |||||
| } | |||||
| 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("<a href='https://go.xero.com/AccountsReceivable/Edit.aspx?InvoiceID=%s' target='_blank'>%s</a>", | |||||
| $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; | |||||
| $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_login' ORDER BY start"; | |||||
| $rows = $this->db->get_results($sql); | |||||
| $xero = $this->xero->get_xero_handle(); | |||||
| //crate invoice | |||||
| $invoice = new \XeroPHP\Models\Accounting\Invoice($xero); | |||||
| $contact= $xero->loadByGUID('Accounting\\Contact', $client_login); | |||||
| $now = new \DateTime(); | |||||
| $due = new \DateTime(); | |||||
| $due->modify("+14 days"); | |||||
| $invoice->setType("ACCREC") | |||||
| ->setStatus("DRAFT") | |||||
| ->setContact($contact) | |||||
| ->setDate($now) | |||||
| ->setDueDate($due); | |||||
| $to_save=[];//all invoices to save; | |||||
| $price = new NdisPrice($now->format("Y"));//current year; | |||||
| 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 \XeroPHP\Models\Accounting\Invoice\LineItem($xero); | |||||
| $lineItem->setDescription($description) | |||||
| ->setQuantity($quantity) | |||||
| ->setUnitAmount($unitprice) | |||||
| ->setAccountCode(220) | |||||
| ->setTaxType("EXEMPTOUTPUT"); | |||||
| $invoice->addLineItem($lineItem); | |||||
| } | |||||
| //prevent zero lineitems | |||||
| if ( count($invoice->getLineItems()) >0 ) | |||||
| $invoice->save(); | |||||
| 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), | |||||
| $job->tos, | |||||
| $job->start, | |||||
| $job->finish, | |||||
| $this->get_user_name_by_login($job->staff) | |||||
| ); | |||||
| return $description; | |||||
| } | |||||
| public function create_timesheet_from_db($start, $finish){ | public function create_timesheet_from_db($start, $finish){ | ||||
| $results = []; | $results = []; | ||||
| \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')); | \WP_CLI::add_command( 'produce_invoice', array($bb, 'produce_invoice')); | ||||
| } | } | ||||
| //$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"); | //$idx = $bb->convert_date_to_idx("2019-07-02 14:30:00", "2019-07-01", "2019-07-02"); | ||||
| //wp_send_json($idx); | //wp_send_json($idx); | ||||
| //$bb->create_timesheet_from_db("2019-07-01", "2019-07-14"); | //$bb->create_timesheet_from_db("2019-07-01", "2019-07-14"); |