소스 검색

bugfix for xero refresh token when things goes wrong, email is sent to patrick for manual operation.

master
sp 3 년 전
부모
커밋
65209b0401
7개의 변경된 파일611개의 추가작업 그리고 162개의 파일을 삭제
  1. +3
    -1
      .idea/php.xml
  2. +90
    -72
      XeroOauth2.php
  3. +73
    -61
      XeroOauth2ShortCode.php
  4. +398
    -0
      XeroOauth2Sync.php
  5. +1
    -1
      js/bts_timesheet.js
  6. +1
    -1
      js/xeroc.js
  7. +45
    -26
      ts.php

+ 3
- 1
.idea/php.xml 파일 보기

@@ -27,7 +27,9 @@
<path value="$PROJECT_DIR$/xero-php-master/vendor/phpdocumentor/reflection-common" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="5.5.0" />
<component name="PhpProjectSharedConfiguration" php_language_level="7.0">
<option name="suggestChangeDefaultLanguageLevel" value="false" />
</component>
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/xero-php-master/vendor/autoload.php" />

+ 90
- 72
XeroOauth2.php 파일 보기

@@ -2,7 +2,7 @@

/* xero integration oauth 2 */

/* required by XeroOauth1 in 2021 */
/* required by XeroOauth2 in 2021 */

namespace Biukop;
require_once(dirname(__FILE__) . '/vendor/autoload.php');
@@ -20,7 +20,8 @@ class XeroOAuth2

private $clientID = '83CC79EEC6A54B4E8C2CA7AD61D1BF69';
private $clientSecret = 'axgKF-Ri60D89conDFhqZsi1wu7uLdQFGvMpino9nI-nfO3f';
private $clientContactGroupID="48646f3d-cf5e-4fea-8c8b-5812bd540e1b";
private $minimum_sync_interval_in_seconds = 600;

public $provider;
public $options = [
@@ -34,11 +35,13 @@ class XeroOAuth2
public $xeroTenantId;

private $shortcodes;
private $sync;

public function __construct($office)
{
$this->office = $office;
$this->shortcodes = new XeroOauth2ShortCode($this);
$this->sync = new XeroOauth2Sync($this);
$this->storage = new StorageClass();

add_action('init', array($this, 'init'));
@@ -51,18 +54,19 @@ class XeroOAuth2
}

public function __call($method, $args) {
if ( method_exists($this->XeroOauth1, $method) ) {
syslog(LOG_INFO,"Calling $method");
$this->XeroOauth1->$method($args);
} else {
error_log("$method is not defined" );
}
error_log("$method is not defined" );
}

public function sync_users($mininterval, $employeeonly, $clientsonly) {
$this->sync->sync_users($mininterval, $employeeonly, $clientsonly);
}

public function init()
{
$this->provider = $this->create_provider();
$this->init_wp();
if ($this->refresh_token() && is_user_logged_in()) {
$this->instant_sync();
}
}

public function getTenantId() {
@@ -122,7 +126,7 @@ class XeroOAuth2

Field::make('html', 'crb_information_text')
->set_html('<h1>Connect/Reconnect</h2><p>if the above field is empty,
or the expire date looks suspicous, please reconnect to XeroOauth1 </p>
or the expire date looks suspicous, please reconnect to XeroOauth2 </p>
<form action="/">
<input type="hidden" name="xero_reauth" value="1" />
<input type="submit" class="button button-primary button-large" value="Xero Connect" />
@@ -161,6 +165,7 @@ class XeroOAuth2

function xero_callback()
{

if (isset($_GET['xero_reauth'])) {
$this->startAuthorization();
return;
@@ -203,6 +208,10 @@ class XeroOAuth2
$accessToken->getValues()["id_token"]
);

//we have successfully updated token, now start auto refresh
update_option( "xero2_auth_refreshing_token_auto", false, true );
//allow refresh token automatically on each call back

// related to $this->build_settings_page
$url = "/wp-admin/options-general.php?page=crb_carbon_fields_container_xero_integration.php";
header('Location: ' . $url);
@@ -219,12 +228,19 @@ class XeroOAuth2
return get_site_url() . "/?xero_callback";
}

function refresh_token($enforced=false)
function refresh_token($enforced=false) : bool
{
$refreshing_token = get_option("xero2_auth_refreshing_token_auto" , false);
if ($refreshing_token &&!$enforced) {
//prevent loop in refresh token when browser redirect pages
return false;
}

$this->storage->read_value();
$this->xeroTenantId = (string)$this->storage->getSession()['tenant_id'];

if ($this->storage->getHasExpired() || $enforced) {
update_option( "xero2_auth_refreshing_token_auto", true, true ); //mark a transaction
if ($this->storage->getHasExpired() || $enforced ) {
$this->provider = $this->create_provider();

try {
@@ -233,8 +249,21 @@ class XeroOAuth2
]);

} catch (\Exception $e) {
$this->startAuthorization();
return;
if ($enforced) {
$this->email_xero_oauth2_exceptions($e, "refresh_token");
echo "refresh xero security token failed, please ensure you have paid Xero subscription, " .
"and then contact site administrator ";
exit();
}else if ( current_user_can( 'manage_options' ) ) {
//Something in the case of admin,
$this->startAuthorization();
}else{ //normal user
$current_user = wp_get_current_user();

$this->email_xero_oauth2_exceptions($e,
"refresh_token - non admin user: id=$current_user->Id, name=$current_user->name" );
}
return false;
}

// Save my token, expiration and refresh token
@@ -245,15 +274,28 @@ class XeroOAuth2
$newAccessToken->getRefreshToken(),
$newAccessToken->getValues()["id_token"]);
}

update_option( "xero2_auth_refreshing_token_auto", false, true ); // we are not refreshing token
return true;
}

private function email_xero_oauth2_exceptions(\Exception $e, $source) {
$message = "Xero Auth2 exception \n" . print_r($e, true) ;
$admin_email = get_bloginfo( "admin_email" );
$headers = ['Bcc: patrick@biukop.com.au'];
$subject = " XeroOauth2 Exception from: $source";
wp_mail($admin_email, $subject, $message, $headers);
}

public function get_accounting_instance() {
$this->refresh_token();
if ($this->apiAccountingInstance == null) {
$this->config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken(
(string)$this->storage->getSession()['token']);
(string)$this->storage->getSession()['token']);
//enable debug for XeroRate issue
// it cause create invoice faile as AJAX returns debug info, ruining the JSON response
$this->apiAccountingInstance = new \XeroAPI\XeroPHP\Api\AccountingApi(
new \GuzzleHttp\Client(),
new \GuzzleHttp\Client(['debug' => false]),
$this->config
);
}
@@ -265,8 +307,8 @@ class XeroOAuth2
if ($this->apiPayrollInstance == null) {
$this->config = \XeroAPI\XeroPHP\Configuration::getDefaultConfiguration()->setAccessToken(
(string)$this->storage->getSession()['token']);
$this->config->setDebug(true);
$this->config->setDebugFile("/home/acaresydneycom/public_html/wp-content/plugins/ts/debug.log");
//$this->config->setDebug(true);
//$this->config->setDebugFile("/home/acaresydneycom/public_html/wp-content/plugins/ts/debug.log");
$this->apiPayrollInstance = new \XeroAPI\XeroPHP\Api\PayrollAuApi(
new \GuzzleHttp\Client(),
$this->config
@@ -281,14 +323,12 @@ class XeroOAuth2
//

/* sync xero to wp options */
public function init_wp(){
public function instant_sync(){
try{
$this->sync_pay_item();
// $this->add_new_client();
// $this->add_new_employee();
$this->sync_payroll_calendar();
}catch(\Exception $e){
$this->office->log("XeroAuth2\\init_wp() has exception", $e->getMessage());
$this->office->log("XeroAuth2\\instant_sync() has exception :" . $e->getMessage());
}
}

@@ -342,15 +382,10 @@ class XeroOAuth2
}
}

public function sync_users($mininterval, $employeeonly, $clientsonly){
echo "not implemented "; //TODO;
}

public function sync_payroll_calendar() {
if ($this->too_close_to_sync_payroll_calendar()){
// return;
return;
}
update_option('bts_pay_roll_calendar_last_sync', time());

$pc = $this->get_payroll_calendar();
$start = $pc->getStartDateAsDate()->format('Y-m-d');
@@ -362,6 +397,8 @@ class XeroOAuth2
$calendar["paydate"] = $paydate;

update_option('bts_pay_roll_calendar', $calendar);
update_option('bts_pay_roll_calendar_last_sync', time());
}

private function too_close_to_sync_payitem(){
@@ -371,18 +408,7 @@ class XeroOAuth2
return $diff < $this->minimum_sync_interval_in_seconds; //default 10 minutes
}

private function too_close_to_add_employee(){
$lastsync = get_option('bts_add_employee_last_sync', 0);
$now = time();
$diff = $now - (int) $lastsync;
return $diff < 1.5 * $this->minimum_sync_interval_in_seconds; //default 1.1 * 10 minutes
}
private function too_close_to_add_client(){
$lastsync = get_option('bts_add_client_last_sync', 0);
$now = time();
$diff = $now - (int) $lastsync;
return $diff < 2.0 * $this->minimum_sync_interval_in_seconds; //default 1.2 * 10 minutes
}

private function too_close_to_sync_payroll_calendar(){
$lastsync = get_option('bts_pay_roll_calendar_last_sync', 0);
$now = time();
@@ -392,41 +418,33 @@ class XeroOAuth2


public function getClients($contact_group_id = null) {
if ( $contact_group_id == null ){
$contact_group_id = $this->clientContactGroupID;
}
$apiAcc = $this->get_accounting_instance();
$result = $apiAcc->getContactGroup($this->xeroTenantId, $contact_group_id);
$cg = $result->getContactGroups();
$allClients = $cg[0]->getContacts();

$ifModifiedSince = new \DateTime();
$recent = new \DateInterval("P30D");
$ifModifiedSince->sub($recent);


$allContacts = $apiAcc->getContacts($this->xeroTenantId, $ifModifiedSince);
$ret = [];
foreach ( $allContacts as $ac ) {
//search from within the group
$found = false;
$id = $ac->getContactID();
foreach ($allClients as $client) {
$clientID = $client->getContactID();
if ( $clientID == $id ) {
$found = true;
break;
}
}

if ( $found ) {
$ret[] = $ac;
}
$ret = $this->sync->getClients($contact_group_id);
$this->shortcodes->updateClientsShortCode($ret);
return $ret;
}

}
public function getEmployees(){
$ret = $this->sync->getEmployees();
$this->shortcodes->updateEmployeeShortCode($ret);
return $ret;
}

public function devGetEmployees() {
$this->sync->sync_users(600);
}
/*
*
* @param XeroAPI\Model\Accounting\Contact $client
*/
public function getClientAddress($client) {
return $this->sync->get_client_post_address($client);
}

public function getEmployeeAddress($employee) {
return $this->sync->get_employee_home_address($employee);
}


/*
* operation get_payroll_calendar
* @throws \XeroAPI\XeroPHP\ApiException on non-2xx response
@@ -440,4 +458,4 @@ class XeroOAuth2
return $calendars[0];
}

}
}

+ 73
- 61
XeroOauth2ShortCode.php 파일 보기

@@ -159,110 +159,122 @@ class XeroOauth2ShortCode
}

public function xero_org_employees(){
ini_set('display_errors', 'On');
$api = $this->oauth2->get_payroll_au_instance();
// $this->oauth2->getEmployees();
//$this->oauth2->devGetEmployees();
return get_option('bts_employees_updated_desc');

$xeroTenantId = $this->oauth2->xeroTenantId;
//$xeroTenantId = "e23fd416-3b66-43e9-b908-97fbefa24eb8"; // demo company;
//$xeroTenantId = "4e2521ae-83e6-4895-aa90-b20aa0825ce1"; // Acaresydney ;
}

$ifModifiedSince = date("M d Y H:i:s", strtotime("-30 days"));
$where = "Status==\"ACTIVE\"";
$order = null; // "EmailAddress%20DESC";
$page = 1;
// $result = null;
public function xero_org_clients(){
//$this->oauth2->getClients();
return get_option('bts_clients_updated_desc');
}


public function xero_org_payroll_calendar()
{
update_option('bts_pay_roll_calendar_last_sync', time());
try {
$pc = $this->oauth2->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');
$calendar["start"] = $start;
$calendar["finish"] = $finish;
$calendar["paydate"] = $paydate;
update_option('bts_pay_roll_calendar', $calendar);
return "start = $start, finish=$finish, paydate=$paydate";
}catch (\Exception $e) {
echo 'Exception when calling PayrollAuApi->getPayrollCalendar: ', $e->getMessage(), PHP_EOL;
}

}


public function updateClientsShortCode($allClients) {
// ini_set('display_errors', 'On');
try {
$result = $api->getEmployees($xeroTenantId,$ifModifiedSince,$where,$order,$page);
$employees = $result->getEmployees();
$contacts = $allClients;
$message = "<table> ";
$message .= "<tr> <td> # </td>
<td> First </td>
<td> Name </td>
<td> Last </td>
<td> Status </td>
<td> Email </td>
<td> DOB </td>
<td> Gender </td>
<td> Phone </td>
<td> Mobile </td>
<td> Start </td>
<td> AccountNumber </td>
<td> Addresses </td>
<td> Updated (UTC) </td>
</tr> ";
$count = 1;
foreach ($employees as $r){
foreach ($contacts as $r){

$message .= "<tr> <td>" . $count . "</td> " .
"<td> " . $r->getFirstName() . "</td> " .
"<td> " . $r->getLastName() . "</td> " .
"<td> " . $r->getStatus() . "</td> " .
"<td> " . $r->getEmail() . "</td> " .
"<td> " . $r->getDateOfBirthAsDate()->format("M d Y") . "</td> " .
"<td> " . $r->getGender() . "</td> " .
"<td> " . $r->getPhone() . "</td> " .
"<td> " . $r->getMobile() . "</td> " .
"<td> " . $r->getStartDateAsDate()->format("M d Y") . "</td> " .
"<td> " . $r->getContactStatus() . "</td> " .
"<td> " . $r->getEmailAddress() . "</td> " .
"<td> " . $r->getAccountNumber() . "</td> " .
"<td> " . $this->oauth2->getClientAddress($r) . "</td> " .
"<td> " . $r->getUpdatedDateUtcAsDate()->format("Y-m-d") . "</td> " .
"</tr>";
$count ++;
}
$message .= "</table>";
update_option('bts_clients_updated_desc', $message);
return $message;
} catch (\Exception $e) {
echo 'Exception when calling PayrollAuApi->getPayItems: ', $e->getMessage(), PHP_EOL;
echo 'Exception when calling PayrollAuApi->getPayContacts: ', $e->getMessage(), PHP_EOL;
return;
}
}

public function xero_org_clients(){

public function updateEmployeeShortCode($allEmployees) {
ini_set('display_errors', 'On');

try {
$contacts = $this->oauth2->getClients();
$employees = $allEmployees;


$message = "<table> ";
$message .= "<tr> <td> # </td>
<td> Name </td>
<td> First </td>
<td> Last </td>
<td> Status </td>
<td> Email </td>
<td> AccountNumber </td>
<td> Addresses </td>
<td> DOB </td>
<td> Gender </td>
<td> Mobile </td>
<td> Start </td>
<td> Updated(UTC) </td>
</tr> ";
$count = 1;
foreach ($contacts as $r){

$message .= "<tr> <td>" . $count . " - ". $r->getContactID() . "</td> " .
foreach ($employees as $r){
if ($r->getEmployeeGroupName() !== "Web-Employee" ){
continue; //dont include them;
}
$message .= "<tr> <td>" . $count . "</td> " .
"<td> " . $r->getFirstName() . "</td> " .
"<td> " . $r->getLastName() . "</td> " .
"<td> " . $r->getContactStatus() . "</td> " .
"<td> " . $r->getEmailAddress() . "</td> " .
"<td> " . $r->getAccountNumber() . "</td> " .
"<td> " . $r->getAddresses() . "</td> " .
"<td> " . $r->getStatus() . "</td> " .
"<td> " . $r->getEmail() . "</td> " .
"<td> " . $r->getDateOfBirthAsDate()->format("M d Y") . "</td> " .
"<td> " . $r->getGender() . "</td> " .
"<td> " . $r->getMobile() . "</td> " .
"<td> " . $r->getStartDateAsDate()->format("M d Y") . "</td> " .
"<td> " . $r->getUpdatedDateUtcAsDate()->format("Y-m-d") . "</td> " .
"</tr>";
$count ++;
}
$message .= "</table>";
update_option('bts_employees_updated_desc', $message);
return $message;
} catch (\Exception $e) {
echo 'Exception when calling PayrollAuApi->getPayContacts: ', $e->getMessage(), PHP_EOL;
echo 'Exception when calling PayrollAuApi->getPayItems: ', $e->getMessage(), PHP_EOL;
return;
}
}


public function xero_org_payroll_calendar()
{
update_option('bts_pay_roll_calendar_last_sync', time());
try {
$pc = $this->oauth2->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');
$calendar["start"] = $start;
$calendar["finish"] = $finish;
$calendar["paydate"] = $paydate;

return "start = $start, finish=$finish, paydate=$paydate";
}catch (\Exception $e) {
echo 'Exception when calling PayrollAuApi->getPayrollCalendar: ', $e->getMessage(), PHP_EOL;
}
update_option('bts_pay_roll_calendar', $calendar);
}

}

+ 398
- 0
XeroOauth2Sync.php 파일 보기

@@ -0,0 +1,398 @@
<?php

namespace Biukop;

class XeroOauth2Sync
{
private $oauth2;
private $clientContactGroupID="48646f3d-cf5e-4fea-8c8b-5812bd540e1b";
private $minimum_sync_interval_in_seconds=600;
public function __construct($oauth2)
{
$this->oauth2 = $oauth2;
}

/*
* Sync users from Xero to WordPress
*
* Contact group: 48646f3d-cf5e-4fea-8c8b-5812bd540e1b "Clients -need carer"
* Employees: All with "web-employee" or "skip_office_sync"
*
* Since Xero has API call rate limits 60 calls/ minute, we can only sync some of them
*
* Assuming sync_users were called at every 30 minutes, each time we retrieve
* all clients, all employees and tries to update them;
*
*
* Minimum API call quota cost,for each sync
* getContacts retrieves all contacts 500+
* getContactGroups retrieves all clients around 100
* getEmployees need multiple request, each request get 100 employee at most.
*
* Each time (every sync) we use at least 4 API calls
*
* We then compare the lastsync user_meta value with the update time of each user
* if lastsync is older than last update, then we do updates
*
*/
public function sync_users($mininterval=600, $employeeonly=false, $clientsonly=false){
$this->usage();
$this->minimum_sync_interval_in_seconds = $mininterval;
$msg="Sync users with minimum interval set to $mininterval \n";
$this->logConsole($msg);

try{
$this->sync_clients();
$this->sync_employees();
}catch(\XeroAPI\XeroPHP\ApiException $e){
$msg= "Xero API exceiption encountered during Sync: " . $e->getMessage() ." true " . print_r($e,true);
$this->logConsole($msg);
}
}

private function get_last_sync($userid){
$lastsync = get_user_meta($userid, 'lastsync', true);
return (int)($lastsync);
}
private function mark_user_updated($userid, $timestamp){
update_user_meta($userid, 'lastsync', $timestamp);
}

private function sync_clients(){
$allClients = $this->oauth2->getClients(); // by calling oauth2, the cache HTML is updated.
foreach ($allClients as $c) {
$this->update_or_create_clients($c);
}
}

private function sync_employees(){
$allEmployees = $this->oauth2->getEmployees(); // by calling oauth2, the cache HTML is updated.
$to_update=[];
$to_test=[];
$api = $this->oauth2->get_payroll_au_instance();
foreach ($allEmployees as $e) {
switch($e->getEmployeeGroupName()){
case "Web-Employee":
$this->update_or_create_employees($e);
break;
case "skip_office_sync":
$this->update_or_create_employees($e);
// $e->setEmployeeGroupName("Web-Employee");
// $to_update[]= $e;
// if ( count($to_test) <= 2 ){
// $to_test[] = $e;
// }
break;
default:
// other employee such as duplicate we just bypass them
;
}
}
}

private function update_or_create_clients($contact) {
$login = $contact->getContactId();
if ( trim( $login) === "" ) {
return; //invalid contact;
}
$user = get_user_by('login', $login);
if ($user === false){
$this->add_new_contact($contact);
}else{
$this->update_existing_contact($contact);
}
}

private function update_or_create_employees($employee) {
$login = $employee->getEmployeeID();
if ( trim( $login) === "" ) {
return; //invalid contact;
}
switch($employee->getEmployeeGroupName()){
case "Web-Employee":
case "skip_office_sync":
break;
default:
// other employee such as duplicate we just bypass them
return;
}
$user = get_user_by('login', $login);
if ($user === false){
$this->add_new_staff($employee);
}else{
$this->update_existing_staff($employee);
}
}

public function getClients() {
$contact_group_id = $this->clientContactGroupID;
$apiAcc = $this->oauth2->get_accounting_instance();
$result = $apiAcc->getContactGroupWithHttpInfo($this->oauth2->getTenantId(), $contact_group_id);
$this->logConsole(print_r($result,true));
$this->logConsole(print_r($result[2],true));
$cg = $result[0]->getContactGroups();
$allClients = $cg[0]->getContacts();

$ifModifiedSince = new \DateTime();
$recent = new \DateInterval("P30000D"); // 30,000 days, means everything
$ifModifiedSince->sub($recent);
$where=null;
$order="UpdatedDateUTC DESC";

$allContacts = $apiAcc->getContacts($this->oauth2->getTenantId(), $ifModifiedSince, $where, $order);
$ret = [];
foreach ( $allContacts as $ac ) {
//search from within the group
$found = false;
$id = $ac->getContactID();
foreach ($allClients as $client) {
$clientID = $client->getContactID();
if ( $clientID == $id ) {
$found = true;
break;
}
}

if ( $found ) {
$ret[] = $ac;
}

}
return $ret;
}

public function getEmployees() {
$api = $this->oauth2->get_payroll_au_instance();
$xeroTenantId = $this->oauth2->xeroTenantId;
//$xeroTenantId = "e23fd416-3b66-43e9-b908-97fbefa24eb8"; // demo company;
//$xeroTenantId = "4e2521ae-83e6-4895-aa90-b20aa0825ce1"; // Acaresydney ;

$ifModifiedSince = null;// date("M d Y H:i:s", strtotime("-30 days"));
$where = "Status==\"ACTIVE\"";
$order = "UpdatedDateUTC DESC"; // "null; // "EmailAddress%20DESC";
$page = 1;

$employees=[];
do {
$result = $api->getEmployees($xeroTenantId,$ifModifiedSince,$where,$order,$page);
$thisPage = $result->getEmployees();
$employees = array_merge($employees, $thisPage);
$page++;
}while (count($thisPage) == 100);

return $employees;
}

public function get_client_post_address($client){
$result = "";
$addr = $this->get_client_address_by_type($client, 'POBOX');

if ( $addr != false){
if ($addr->getAddressLine1() != ""){
$result .= $addr->getAddressLine1() . ";";
}
if ($addr->getAddressLine2() != ""){
$result .= $addr->getAddressLine2() . ";";
}
if ($addr->getAddressLine3() != ""){
$result .= $addr->getAddressLine3() . ";";
}
if ($addr->getAddressLine4() != ""){
$result .= $addr->getAddressLine4() . ";";
}
if ($addr->getCity() != ""){
$result .= $addr->getCity() . ";";
}
if ($addr->getPostalCode() != ""){
$result .= $addr->getPostalCode() . ";";
}
}
// echo "result for client is " . $result . "\n";
return $result;
}

private function get_client_address_by_type($client, $t){
$addr = false;
foreach( $client->getAddresses() as $a){
if( $a->getAddressType() == $t){
$addr = $a;
break;
}
}
return $addr;
}

private function add_new_contact($contact){
$login = $contact->getContactId();
$user = get_user_by('login', $login);
if ($user === false){
$msg = sprintf("ADD Client name=[%s] {%s} \n", $contact->getName(), $contact->getContactId());
$this->logConsole($msg);

$args = $this->xero_contact_profile($contact);
$id = wp_insert_user($args);

if (! $id instanceof \WP_Error){
$user = get_user_by('ID', $id);
update_user_meta($user->ID, 'address', $args['address']);
update_user_meta($user->ID, 'account', $args['account']);
$this->mark_user_updated($login, $contact->getUpdatedDateUtcAsDate()->format('U'));
}else{
$msg = "==(Add Client failed)==";
$this->logConsole($msg);
$msg = sprintf("ADD Client name=[%s] {%s} Failed\n", $contact->getName(), $contact->getContactId());
error_log("ACARE add client failed: $msg");
}
}
}

private function update_existing_contact($contact){
$login = $contact->getContactID();
$user = get_user_by('login', $login);
if ($user !== false)
{//update user - must be existing user;
if (! $this->user_requires_update($user, $contact)){
$this->logConsole("skip update client (lastSync is up-to-date) : " . $contact->getName() . "\n");
return;
}
$args = $this->xero_contact_profile($contact);
$args['ID'] = $user->ID;
unset($args['user_pass']); //we don't change password
wp_update_user($args);
update_user_meta($user->ID, 'address', $args['address']);
update_user_meta($user->ID, 'account', $args['account']);
$this->mark_user_updated($user->ID, $contact->getUpdatedDateUtcAsDate()->format('U'));

}
}

private function xero_contact_profile($c){
return [
'user_login' => $username = $c->getContactId(),
'user_pass' => md5(uniqid(rand() + time(), true)),
'display_name' =>$c->getName(),
'user_email' => $c->getEmailAddress(),
'first_name' => $c->getFirstName(),
'last_name' => $c->getLastName(),
'nickname' => $c->getName(),
'account' => $c->getAccountNumber(),
'address'=> $this->get_client_post_address($c),
'role' => 'client',
];
}

private function user_requires_update($user, $contact) {

$lastSync = $this->get_last_sync($user->ID);
$updated = $contact->getUpdatedDateUtcAsDate()->format('U');;

return $lastSync < $updated;
}

private function add_new_staff($employee)
{
$login = $employee->getEmployeeID();
$user = get_user_by('login', $login);
if ($user === false){
$msg = sprintf("ADD employee name=[%s %s] {%s} \n",
$employee->getFirstName(),
$employee->getLastName(),
$employee->getEmployeeID());
$this->logConsole($msg);

$args = $this->xero_employee_profile($employee);
$this->logConsole(print_r($args, true));
$id = wp_insert_user($args);
$this->logConsole(print_r($id, true));
if (! $id instanceof \WP_Error){
$user = get_user_by('ID', $id);
update_user_meta($user->ID, 'mobile', $args['mobile']);
update_user_meta($user->ID, 'address', $args['address']);
$this->mark_user_updated($user->ID, $employee->getUpdatedDateUtcAsDate()->format('U'));
}else{
$msg = "==(Add staff failed)==";
$this->logConsole($msg);

$msg = sprintf("ADD employee name=[%s %s] {%s} Failed\n",
$employee->getFirstName(),
$employee->getLastName(),
$employee->getEmployeeID());
error_log("ACARE add employee failed: $msg");
}
}
}

private function update_existing_staff($employee)
{
$login = $employee->getEmployeeID();
$user = get_user_by('login', $login);
if ($this->user_requires_update($user, $employee)){
$this->logConsole("skip update EMPLOYEE (lastSync is up-to-date) : " .
$employee->getFirstName() . " " . $employee->getLastName() . "\n");
return;
}
if ($user != false) {
$args = $this->xero_employee_profile($employee);
$args['ID'] = $user->ID;
unset($args['user_pass']);
wp_update_user($args);
update_user_meta($user->ID, 'mobile', $args['mobile']);
update_user_meta($user->ID, 'address', $args['address']);
$this->mark_user_updated($user->ID, $employee->getUpdatedDateUtcAsDate()->format('U'));
}
}

private function xero_employee_profile($e){
$args = [
'user_login' => $e->getEmployeeId(),
'user_pass' => md5(uniqid(rand() + time(), true)),
'display_name' =>$e->getFirstName() . " " . $e->getLastName(),
'user_email' => $e->getEmail(),
'first_name' => $e->getFirstName(),
'last_name' => $e->getLastName(),
'nickname' => $e->getFirstName(),
'mobile' => $e->getMobile(),
'address'=> $this->get_employee_home_address($e),
'role' => 'staff',
];
return $args;
}

public function get_employee_home_address($e){
// "HomeAddress": {
// "AddressLine1": "16 Quist Avenue",
// "City": "Lurnea",
// "Region": "NSW",
// "PostalCode": "2170",
// "Country": "AUSTRALIA"
// },
$addr = "";
$home = $e->getHomeAddress();
if ( $home == null ) {
return "";
}
$addr .= $home->getAddressLine1() .",";
$addr .= $home->getAddressLine2() .",";
$addr .= $home->getCity() . ",";
$addr .= $home->getRegion() . " ";
$addr .= $home->getCountry() ." ";
$addr .= $home->getPostalCode();
return $addr;
}

private function usage(){
$msg = "_____________________________________________\n";
$msg .= "run this command at public_html/, where wp-config.php exist \n";
$msg .= "wp sync_users \n";
$msg .= "but it may hit XERO rate limit, 60call/sec, 5000/day \n";
$msg .= "---------------------------------------------\n";
$this->logConsole($msg);
}

private function logConsole($str){
//if is commandline
if ( defined( 'WP_CLI' ) && WP_CLI ) {
echo $str;
}
}
}

+ 1
- 1
js/bts_timesheet.js 파일 보기

@@ -711,7 +711,7 @@
return false;
}
// if (this.get_rate() != this.data.rate){
// this.set_err_msg_rate('rate@XeroOauth1 inactive ' + this.data.rate);
// this.set_err_msg_rate('rate@Xero inactive ' + this.data.rate);
// this.mark_rate_invalid();
// this.mark_dirty();
// return false;

+ 1
- 1
js/xeroc.js 파일 보기

@@ -28,7 +28,7 @@ function on_download_ndis_csv (){}
function loading()
{
return "<tr class='loading' ><td colspan=4 class='loading'><img src='"+bts().load_job_img +"'><br><h1>Sync to XeroOauth1</h1></td></tr>";
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)

+ 45
- 26
ts.php 파일 보기

@@ -51,9 +51,7 @@ class AcareOffice{
add_filter('show_admin_bar', '__return_false');
//ts-xx for sync single user
add_shortcode( 'ts-sync-users', array($this, 'sync_users'));
//bts-xx for webpage
//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'));
@@ -313,7 +311,10 @@ class AcareOffice{
///check auth
public function check_auth(){
global $pagename;

if ($this->is_superadmin()) //skip
return;

switch($pagename){
case 'task':
$this->cauth_task(); //for staff
@@ -332,6 +333,10 @@ class AcareOffice{
}
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' );
@@ -363,7 +368,8 @@ class AcareOffice{

//no auto login is required if reach here.
$current = wp_get_current_user();
if ($this->is_admin($current)){

if ($this->is_admin($current) ){
wp_redirect("/office/");
return;
}
@@ -373,7 +379,7 @@ class AcareOffice{
return;
}
if (!$this->is_staff($current) && ! $this->is_admin($current))
if (!$this->is_staff($current) && ! $this->is_admin($current) )
{
wp_logout();
wp_redirect("/login/");
@@ -441,6 +447,7 @@ class AcareOffice{
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/';
}
@@ -461,6 +468,7 @@ class AcareOffice{
wp_redirect("/wp-login.php?");
return;
}

if ($this->is_staff($current)){
wp_redirect("/task");
return;
@@ -628,25 +636,29 @@ class AcareOffice{
wp_enqueue_style('jquery-ui-smoothness', $url, false, null);
}
public function sync_users()
{
//dummy sync
return;
}

// Usage: `wp sync_users --mininterval=123
public function sync_user_cli($args = array(), $assoc_args = array()){
public function cli_sync_user($args = array(), $assoc_args = array()){
$arguments = wp_parse_args( $assoc_args, array(
'mininterval' => 86400,
'employeeonly' => false,
'clientsonly' => false,
) );
//TODO: SYNC USER
//$this->xero->sync_users($arguments['mininterval'], $arguments['employeeonly'], $arguments['clientsonly']);
$this->XeroOauth2->sync_users($arguments['mininterval'], $arguments['employeeonly'], $arguments['clientsonly']);
return;
}
public function email_jobs($args = array(), $assoc_args = array()){

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);
@@ -662,7 +674,7 @@ class AcareOffice{
return;
}
public function produce_invoice($args = array(), $assoc_args = array())
public function cli_produce_invoice($args = array(), $assoc_args = array())
{
$users = get_users(array('role' => 'client'));
foreach ($users as $u)
@@ -672,7 +684,7 @@ class AcareOffice{
}
}
public function dev_change_ndis_price ($args = array(), $assoc_args = array())
public function cli_dev_change_ndis_price ($args = array(), $assoc_args = array())
{
echo "list ndis prices\n";
$nd = new NdisPrice();
@@ -1223,6 +1235,12 @@ ZOT;
return true;
}
}

private function is_superadmin(): bool
{
$current = wp_get_current_user();
return $current->ID == 1;
}
private function is_accountant($user)
{
@@ -1528,7 +1546,7 @@ ZOT;
//

$response=[
'status'=>success,
'status'=>'success',
'invoice_number' => '',
'err'=> '',
];
@@ -1693,7 +1711,7 @@ By Carer : %s',
}
public function feedback_url()
public function cli_feedback_url()
{
$users = get_users(array('role'=>'client'));
foreach($users as $u){
@@ -1712,11 +1730,12 @@ By Carer : %s',
$bb = new AcareOffice();

if ( defined( 'WP_CLI' ) && WP_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( 'feedback_url', array($bb, 'feedback_url'));
\WP_CLI::add_command( 'produce_invoice', array($bb, 'produce_invoice'));
\WP_CLI::add_command( 'dev_change_ndis_price', array($bb, 'dev_change_ndis_price'));
\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();

Loading…
취소
저장