|
|
|
@@ -1,495 +0,0 @@ |
|
|
|
<?php |
|
|
|
namespace Biukop; |
|
|
|
|
|
|
|
use \XeroPHP\Application\PrivateApplication; |
|
|
|
use \XeroPHP\Remote\Exception\RateLimitExceededException; |
|
|
|
use \XeroPHP\Remote\Exception\NotFoundException; |
|
|
|
|
|
|
|
class XeroOauth1 { |
|
|
|
private $xero; |
|
|
|
private $clientgroup="48646f3d-cf5e-4fea-8c8b-5812bd540e1b"; |
|
|
|
private $minimum_sync_interval_in_seconds = 600; |
|
|
|
|
|
|
|
private function default_config(){ |
|
|
|
$config = array( |
|
|
|
'oauth' => [ |
|
|
|
'callback' => 'http://acaresydney.com.au/', |
|
|
|
'consumer_key' => 'G6AJIRWTH0X3C5SVS3ETZXNFCMDNGG', |
|
|
|
'consumer_secret' => 'LP0PTSBCJLBB4CGYYKOHDXYF2NWXWD', |
|
|
|
'rsa_private_key' => 'file://' . dirname(__FILE__) . '/keys/privatekey.pem', |
|
|
|
], |
|
|
|
); |
|
|
|
return $config; |
|
|
|
} |
|
|
|
private function office_config(){ |
|
|
|
$office_config = [ |
|
|
|
'xero' => [ |
|
|
|
// 'payroll_version' =>'1.9', |
|
|
|
], |
|
|
|
'curl' => [ |
|
|
|
CURLOPT_USERAGENT =>'AcareSydneyWebOffice', |
|
|
|
], |
|
|
|
'oauth' => [ |
|
|
|
'callback' => 'http://acaresydney.com.au/', |
|
|
|
'consumer_key' => 'JE4LWKCJ6NHED30RFZWCT7WQYTS8JD', |
|
|
|
'consumer_secret' => 'JXVZAZWKGM7MLUZSVIMK7ZSJE9GNYQ', |
|
|
|
'rsa_private_key' => 'file://' . dirname(__FILE__) . '/keys/privatekey.pem', |
|
|
|
], |
|
|
|
]; |
|
|
|
return $office_config; |
|
|
|
} |
|
|
|
|
|
|
|
public function __construct(){ |
|
|
|
$this->xero = new PrivateApplication($this->office_config()); |
|
|
|
} |
|
|
|
|
|
|
|
public function get_xero_handle() |
|
|
|
{ |
|
|
|
return $this->xero; |
|
|
|
} |
|
|
|
|
|
|
|
public function getClients($contact_group_id){ |
|
|
|
$xero = $this->xero; |
|
|
|
$cg = $xero->loadByGUID("Accounting\\ContactGroup", $contact_group_id); |
|
|
|
$contacts = $cg->getContacts(); |
|
|
|
return $contacts; |
|
|
|
} |
|
|
|
|
|
|
|
public function getContact($id){ |
|
|
|
$user = $this->xero->loadByGUID("Accounting\\Contact",$id); |
|
|
|
return $user; |
|
|
|
} |
|
|
|
|
|
|
|
public function getEmployees(){ |
|
|
|
$employees = $this->xero->load("PayrollAU\\Employee") |
|
|
|
->where('EmployeeGroupName="Web-Employee"') |
|
|
|
->execute(); |
|
|
|
return $employees; |
|
|
|
} |
|
|
|
|
|
|
|
public function getEmployee($id){ |
|
|
|
$employee = $this->xero->loadByGUID("PayrollAU\\Employee",$id); |
|
|
|
return $employee; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// |
|
|
|
//sync users to wordpress system |
|
|
|
//does not work for too many users or employees |
|
|
|
public function sync_users($mininterval, $employeeonly, $clientsonly){ |
|
|
|
$this->usage(); |
|
|
|
$this->minimum_sync_interval_in_seconds = $mininterval; |
|
|
|
$msg="Sync users with minimum interval set to $mininterval \n"; |
|
|
|
$this->logConsole($msg); |
|
|
|
|
|
|
|
try{ |
|
|
|
if ($clientsonly){ |
|
|
|
$this->sync_clients(); |
|
|
|
}else if ($employeeonly){ |
|
|
|
$this->sync_employees(); |
|
|
|
}else{ |
|
|
|
$this->sync_clients(); |
|
|
|
$this->sync_employees(); |
|
|
|
} |
|
|
|
}catch(RateLimitExceededException $e){ |
|
|
|
$msg= "XeroOauth1 API rate limit exceeded, please try again later, existing sync within 600 seconds will by passed automatically\n"; |
|
|
|
$this->logConsole($msg); |
|
|
|
}catch(NotFoundException $e){ |
|
|
|
$msg= "XeroOauth1 API resource not found rate limit exceeded, please try again later, existing sync within 600 seconds will by passed automatically\n"; |
|
|
|
$this->logConsole($msg); |
|
|
|
} |
|
|
|
} |
|
|
|
private function sync_clients($add_new_only = false){ |
|
|
|
$contacts = $this->getClients($this->clientgroup); |
|
|
|
//add new first; |
|
|
|
$to_update = []; |
|
|
|
foreach ($contacts as $c){ |
|
|
|
$login = $c->getContactID(); |
|
|
|
$user = get_user_by('login', $login); |
|
|
|
if ($user === false){ |
|
|
|
$this->add_new_contact($c); |
|
|
|
}else{ |
|
|
|
$to_update[] = $c; // to update users later; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ($add_new_only) |
|
|
|
return; |
|
|
|
|
|
|
|
//sync existing |
|
|
|
foreach ($to_update as $c){ |
|
|
|
$msg = sprintf("SYNC Client name=[%s] {%s} \n", $c->getName(), $c->getContactID()); |
|
|
|
$this->logConsole($msg); |
|
|
|
$this->update_existing_contact($c); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private function sync_employees($add_new_only = false){ |
|
|
|
$employees = $this->getEmployees(); |
|
|
|
//add new staff |
|
|
|
foreach ( $employees as $e){ |
|
|
|
$login = $e->getEmployeeID(); |
|
|
|
$user = get_user_by('login', $login); |
|
|
|
if ($user === false){ |
|
|
|
$this->add_new_staff($e); |
|
|
|
}else{ |
|
|
|
$to_update[] = $e; // to update exiting users later; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ($add_new_only) |
|
|
|
return; |
|
|
|
|
|
|
|
foreach ( $to_update as $e){ |
|
|
|
$msg = sprintf("SYNC employee name=[%s %s] {%s} \n", $e->getFirstName(), $e->getLastName(), $e->getEmployeeID()); |
|
|
|
$this->logConsole($msg); |
|
|
|
$this->update_existing_staff($e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
$xero_contact = $this->getContact($login); |
|
|
|
$args = $this->xero_contact_profile($xero_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']); |
|
|
|
}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->is_too_close_to_sync($user)){ |
|
|
|
return; |
|
|
|
} |
|
|
|
$xero_contact = $this->getContact($login); |
|
|
|
$args = $this->xero_contact_profile($xero_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_updated($user->ID); |
|
|
|
} |
|
|
|
|
|
|
|
private function xero_contact_profile($c){ |
|
|
|
$args = [ |
|
|
|
'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_post_address($c), |
|
|
|
'role' => 'client', |
|
|
|
]; |
|
|
|
return $args; |
|
|
|
} |
|
|
|
|
|
|
|
private function get_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_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); |
|
|
|
|
|
|
|
$xero_employee = $this->getEmployee($login); |
|
|
|
$args = $this->xero_employee_profile($xero_employee); |
|
|
|
$id = wp_insert_user($args); |
|
|
|
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']); |
|
|
|
}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->is_too_close_to_sync($user)){ |
|
|
|
return; |
|
|
|
} |
|
|
|
if ($user != false) { |
|
|
|
$xero_employee = $this->getEmployee($login); |
|
|
|
$args = $this->xero_employee_profile($xero_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_updated($user->ID); |
|
|
|
} |
|
|
|
|
|
|
|
private function xero_employee_profile($e){ |
|
|
|
$args = [ |
|
|
|
'user_login' => $username = $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_address($e), |
|
|
|
'role' => 'staff', |
|
|
|
]; |
|
|
|
return $args; |
|
|
|
} |
|
|
|
|
|
|
|
private function get_employee_address($e){ |
|
|
|
// "HomeAddress": { |
|
|
|
// "AddressLine1": "16 Quist Avenue", |
|
|
|
// "City": "Lurnea", |
|
|
|
// "Region": "NSW", |
|
|
|
// "PostalCode": "2170", |
|
|
|
// "Country": "AUSTRALIA" |
|
|
|
// }, |
|
|
|
$addr = ""; |
|
|
|
$home = $e->getHomeAddress(); |
|
|
|
$addr .= $home->getAddressLine1() .","; |
|
|
|
$addr .= $home->getAddressLine2() .","; |
|
|
|
$addr .= $home->getCity() . ","; |
|
|
|
$addr .= $home->getRegion() . " "; |
|
|
|
$addr .= $home->getCountry() ." "; |
|
|
|
$addr .= $home->getPostalCode(); |
|
|
|
return $addr; |
|
|
|
} |
|
|
|
|
|
|
|
private function mark_updated($userid){ |
|
|
|
update_user_meta($userid, 'lastsync', time()); |
|
|
|
} |
|
|
|
|
|
|
|
private function get_last_sync($userid){ |
|
|
|
$lastsync = get_user_meta($userid, 'lastsync', true); |
|
|
|
return (int)($lastsync); |
|
|
|
} |
|
|
|
|
|
|
|
private function is_too_close_to_sync($user){ |
|
|
|
$userid = $user->ID; |
|
|
|
$lastsync = $this->get_last_sync($user->ID); |
|
|
|
$now = time(); |
|
|
|
$diff = $now - (int) $lastsync; |
|
|
|
if ($diff < $this->minimum_sync_interval_in_seconds){ |
|
|
|
$msg = sprintf("\tSKIP userid(%d),login=%s,display_name=%s,(lastsync=%d secs ago, mininterval=%d) \n", |
|
|
|
$user->ID, $user->user_login, $user->display_name, $diff, $this->minimum_sync_interval_in_seconds); |
|
|
|
$this->logConsole($msg); |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
private function logConsole($str){ |
|
|
|
//if is commandline |
|
|
|
if ( defined( 'WP_CLI' ) && WP_CLI ) { |
|
|
|
echo $str; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private function usage(){ |
|
|
|
$msg = "_____________________________________________\n"; |
|
|
|
$msg .= "run this command at public_html/, where wp-config.php exist \n"; |
|
|
|
$msg .= "wp sync_users --mininterval=6000 \n"; |
|
|
|
$msg .= "6000 means those users synced within 6000 seconds will be bypassed \n"; |
|
|
|
$msg .= "to sync everything \n"; |
|
|
|
$msg .= "wp sync_users --mininterval=0 [--clientsonly] [--employeeonly]\n"; |
|
|
|
$msg .= "but it may hit XERO rate limit, 60call/sec, 5000/day \n"; |
|
|
|
$msg .= "---------------------------------------------\n"; |
|
|
|
$this->logConsole($msg); |
|
|
|
} |
|
|
|
|
|
|
|
/* sync payitems to wp options */ |
|
|
|
public function init_wp(){ |
|
|
|
try{ |
|
|
|
$this->sync_payitem(); |
|
|
|
$this->add_new_client(); |
|
|
|
$this->add_new_employee(); |
|
|
|
$this->sync_payroll_calendar(); |
|
|
|
}catch(\XeroPHP\Remote\Exception $e){ |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private function add_new_client(){ |
|
|
|
if ($this->too_close_to_add_client()){ |
|
|
|
return; |
|
|
|
} |
|
|
|
update_option('bts_add_client_last_sync', time()); |
|
|
|
$this->sync_clients(true);//add new only; |
|
|
|
|
|
|
|
} |
|
|
|
private function add_new_employee(){ |
|
|
|
if ($this->too_close_to_add_employee()){ |
|
|
|
return; |
|
|
|
} |
|
|
|
update_option('bts_add_employee_last_sync', time()); |
|
|
|
$this->sync_employees(true);//add new only; |
|
|
|
} |
|
|
|
|
|
|
|
private function sync_payroll_calendar() |
|
|
|
{ |
|
|
|
if ($this->too_close_to_sync_payroll_calendar()){ |
|
|
|
return; |
|
|
|
} |
|
|
|
update_option('bts_pay_roll_calendar_last_sync', time()); |
|
|
|
|
|
|
|
$pc = $this->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'); |
|
|
|
$calendar["start"] = $start; |
|
|
|
$calendar["finish"] = $finish; |
|
|
|
$calendar["paydate"] = $paydate; |
|
|
|
|
|
|
|
update_option('bts_pay_roll_calendar', $calendar); |
|
|
|
} |
|
|
|
|
|
|
|
private function sync_payitem(){ |
|
|
|
if ($this->too_close_to_sync_payitem()){ |
|
|
|
return; |
|
|
|
} |
|
|
|
$payitems = $this->xero->load('PayrollAU\\PayItem')->execute(); |
|
|
|
$payitem_options = array(); |
|
|
|
foreach ($payitems[0]->getEarningsRates() as $e){ |
|
|
|
// "EarningsRateID": "34e17d08-237a-4ae2-8115-375d1ff8a9ed", |
|
|
|
// "Name": "Overtime Hours (exempt from super)", |
|
|
|
// "EarningsType": "OVERTIMEEARNINGS", |
|
|
|
// "RateType": "MULTIPLE", |
|
|
|
// "AccountCode": "477", |
|
|
|
// "Multiplier": 1.5, |
|
|
|
// "IsExemptFromTax": true, |
|
|
|
// "IsExemptFromSuper": true, |
|
|
|
// "AccrueLeave": false, |
|
|
|
// "IsReportableAsW1": true, |
|
|
|
// "UpdatedDateUTC": "2019-03-16T13:18:19+00:00", |
|
|
|
// "CurrentRecord": false |
|
|
|
if ($e->getCurrentRecord() == "true"){ |
|
|
|
$payitem_options[]= array( |
|
|
|
'EarningsRateID' => $e->getEarningsRateID(), |
|
|
|
'Name'=> $e->getName(), |
|
|
|
'EarningsType'=> $e->getEarningstype(), |
|
|
|
'RatePerUnit' => $e->getRatePerUnit(), |
|
|
|
'RateType' => $e->getRateType(), |
|
|
|
'AccountCode' => $e->getAccountCode(), |
|
|
|
"Multiplier"=> $e->getMultiplier(), |
|
|
|
"IsExemptFromTax" => $e->getIsExemptFromTax(), |
|
|
|
"IsExemptFromSuper"=> $e->getIsExemptFromSuper(), |
|
|
|
"AccrueLeave" => $e->getAccrueLeave(), |
|
|
|
"TypeOfUnits" => $e->getTypeOfUnits(), |
|
|
|
"CurrentRecord"=> $e->getCurrentRecord(), |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
update_option('bts_payitem_earnings_rate', $payitem_options); |
|
|
|
update_option('bts_payitem_last_sync', time()); |
|
|
|
} |
|
|
|
|
|
|
|
private function too_close_to_sync_payitem(){ |
|
|
|
$lastsync = get_option('bts_payitem_last_sync', 0); |
|
|
|
$now = time(); |
|
|
|
$diff = $now - (int) $lastsync; |
|
|
|
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(); |
|
|
|
$diff = $now - (int) $lastsync; |
|
|
|
return $diff < 3.0 * $this->minimum_sync_interval_in_seconds; //default 1.3 * 10 minutes |
|
|
|
} |
|
|
|
|
|
|
|
public function get_payroll_calendar() |
|
|
|
{ |
|
|
|
$id = "33dc7df5-3060-4d76-b4da-57c20685d77d"; //fortnightly |
|
|
|
$pc = $this->xero->loadByGUID('PayrollAU\\PayrollCalendar', $id); |
|
|
|
return $pc; |
|
|
|
} |
|
|
|
|
|
|
|
} |