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; } } }