timesheet source code
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

399 Zeilen
14KB

  1. <?php
  2. namespace Biukop;
  3. class XeroOauth2Sync
  4. {
  5. private $oauth2;
  6. private $clientContactGroupID="48646f3d-cf5e-4fea-8c8b-5812bd540e1b";
  7. private $minimum_sync_interval_in_seconds=600;
  8. public function __construct($oauth2)
  9. {
  10. $this->oauth2 = $oauth2;
  11. }
  12. /*
  13. * Sync users from Xero to WordPress
  14. *
  15. * Contact group: 48646f3d-cf5e-4fea-8c8b-5812bd540e1b "Clients -need carer"
  16. * Employees: All with "web-employee" or "skip_office_sync"
  17. *
  18. * Since Xero has API call rate limits 60 calls/ minute, we can only sync some of them
  19. *
  20. * Assuming sync_users were called at every 30 minutes, each time we retrieve
  21. * all clients, all employees and tries to update them;
  22. *
  23. *
  24. * Minimum API call quota cost,for each sync
  25. * getContacts retrieves all contacts 500+
  26. * getContactGroups retrieves all clients around 100
  27. * getEmployees need multiple request, each request get 100 employee at most.
  28. *
  29. * Each time (every sync) we use at least 4 API calls
  30. *
  31. * We then compare the lastsync user_meta value with the update time of each user
  32. * if lastsync is older than last update, then we do updates
  33. *
  34. */
  35. public function sync_users($mininterval=600, $employeeonly=false, $clientsonly=false){
  36. $this->usage();
  37. $this->minimum_sync_interval_in_seconds = $mininterval;
  38. $msg="Sync users with minimum interval set to $mininterval \n";
  39. $this->logConsole($msg);
  40. try{
  41. $this->sync_clients();
  42. $this->sync_employees();
  43. }catch(\XeroAPI\XeroPHP\ApiException $e){
  44. $msg= "Xero API exceiption encountered during Sync: " . $e->getMessage() ." true " . print_r($e,true);
  45. $this->logConsole($msg);
  46. }
  47. }
  48. private function get_last_sync($userid){
  49. $lastsync = get_user_meta($userid, 'lastsync', true);
  50. return (int)($lastsync);
  51. }
  52. private function mark_user_updated($userid, $timestamp){
  53. update_user_meta($userid, 'lastsync', $timestamp);
  54. }
  55. private function sync_clients(){
  56. $allClients = $this->oauth2->getClients(); // by calling oauth2, the cache HTML is updated.
  57. foreach ($allClients as $c) {
  58. $this->update_or_create_clients($c);
  59. }
  60. }
  61. private function sync_employees(){
  62. $allEmployees = $this->oauth2->getEmployees(); // by calling oauth2, the cache HTML is updated.
  63. $to_update=[];
  64. $to_test=[];
  65. $api = $this->oauth2->get_payroll_au_instance();
  66. foreach ($allEmployees as $e) {
  67. switch($e->getEmployeeGroupName()){
  68. case "Web-Employee":
  69. $this->update_or_create_employees($e);
  70. break;
  71. case "skip_office_sync":
  72. $this->update_or_create_employees($e);
  73. // $e->setEmployeeGroupName("Web-Employee");
  74. // $to_update[]= $e;
  75. // if ( count($to_test) <= 2 ){
  76. // $to_test[] = $e;
  77. // }
  78. break;
  79. default:
  80. // other employee such as duplicate we just bypass them
  81. ;
  82. }
  83. }
  84. }
  85. private function update_or_create_clients($contact) {
  86. $login = $contact->getContactId();
  87. if ( trim( $login) === "" ) {
  88. return; //invalid contact;
  89. }
  90. $user = get_user_by('login', $login);
  91. if ($user === false){
  92. $this->add_new_contact($contact);
  93. }else{
  94. $this->update_existing_contact($contact);
  95. }
  96. }
  97. private function update_or_create_employees($employee) {
  98. $login = $employee->getEmployeeID();
  99. if ( trim( $login) === "" ) {
  100. return; //invalid contact;
  101. }
  102. switch($employee->getEmployeeGroupName()){
  103. case "Web-Employee":
  104. case "skip_office_sync":
  105. break;
  106. default:
  107. // other employee such as duplicate we just bypass them
  108. return;
  109. }
  110. $user = get_user_by('login', $login);
  111. if ($user === false){
  112. $this->add_new_staff($employee);
  113. }else{
  114. $this->update_existing_staff($employee);
  115. }
  116. }
  117. public function getClients() {
  118. $contact_group_id = $this->clientContactGroupID;
  119. $apiAcc = $this->oauth2->get_accounting_instance();
  120. $result = $apiAcc->getContactGroupWithHttpInfo($this->oauth2->getTenantId(), $contact_group_id);
  121. $this->logConsole(print_r($result,true));
  122. $this->logConsole(print_r($result[2],true));
  123. $cg = $result[0]->getContactGroups();
  124. $allClients = $cg[0]->getContacts();
  125. $ifModifiedSince = new \DateTime();
  126. $recent = new \DateInterval("P30000D"); // 30,000 days, means everything
  127. $ifModifiedSince->sub($recent);
  128. $where=null;
  129. $order="UpdatedDateUTC DESC";
  130. $allContacts = $apiAcc->getContacts($this->oauth2->getTenantId(), $ifModifiedSince, $where, $order);
  131. $ret = [];
  132. foreach ( $allContacts as $ac ) {
  133. //search from within the group
  134. $found = false;
  135. $id = $ac->getContactID();
  136. foreach ($allClients as $client) {
  137. $clientID = $client->getContactID();
  138. if ( $clientID == $id ) {
  139. $found = true;
  140. break;
  141. }
  142. }
  143. if ( $found ) {
  144. $ret[] = $ac;
  145. }
  146. }
  147. return $ret;
  148. }
  149. public function getEmployees() {
  150. $api = $this->oauth2->get_payroll_au_instance();
  151. $xeroTenantId = $this->oauth2->xeroTenantId;
  152. //$xeroTenantId = "e23fd416-3b66-43e9-b908-97fbefa24eb8"; // demo company;
  153. //$xeroTenantId = "4e2521ae-83e6-4895-aa90-b20aa0825ce1"; // Acaresydney ;
  154. $ifModifiedSince = null;// date("M d Y H:i:s", strtotime("-30 days"));
  155. $where = "Status==\"ACTIVE\"";
  156. $order = "UpdatedDateUTC DESC"; // "null; // "EmailAddress%20DESC";
  157. $page = 1;
  158. $employees=[];
  159. do {
  160. $result = $api->getEmployees($xeroTenantId,$ifModifiedSince,$where,$order,$page);
  161. $thisPage = $result->getEmployees();
  162. $employees = array_merge($employees, $thisPage);
  163. $page++;
  164. }while (count($thisPage) == 100);
  165. return $employees;
  166. }
  167. public function get_client_post_address($client){
  168. $result = "";
  169. $addr = $this->get_client_address_by_type($client, 'POBOX');
  170. if ( $addr != false){
  171. if ($addr->getAddressLine1() != ""){
  172. $result .= $addr->getAddressLine1() . ";";
  173. }
  174. if ($addr->getAddressLine2() != ""){
  175. $result .= $addr->getAddressLine2() . ";";
  176. }
  177. if ($addr->getAddressLine3() != ""){
  178. $result .= $addr->getAddressLine3() . ";";
  179. }
  180. if ($addr->getAddressLine4() != ""){
  181. $result .= $addr->getAddressLine4() . ";";
  182. }
  183. if ($addr->getCity() != ""){
  184. $result .= $addr->getCity() . ";";
  185. }
  186. if ($addr->getPostalCode() != ""){
  187. $result .= $addr->getPostalCode() . ";";
  188. }
  189. }
  190. // echo "result for client is " . $result . "\n";
  191. return $result;
  192. }
  193. private function get_client_address_by_type($client, $t){
  194. $addr = false;
  195. foreach( $client->getAddresses() as $a){
  196. if( $a->getAddressType() == $t){
  197. $addr = $a;
  198. break;
  199. }
  200. }
  201. return $addr;
  202. }
  203. private function add_new_contact($contact){
  204. $login = $contact->getContactId();
  205. $user = get_user_by('login', $login);
  206. if ($user === false){
  207. $msg = sprintf("ADD Client name=[%s] {%s} \n", $contact->getName(), $contact->getContactId());
  208. $this->logConsole($msg);
  209. $args = $this->xero_contact_profile($contact);
  210. $id = wp_insert_user($args);
  211. if (! $id instanceof \WP_Error){
  212. $user = get_user_by('ID', $id);
  213. update_user_meta($user->ID, 'address', $args['address']);
  214. update_user_meta($user->ID, 'account', $args['account']);
  215. $this->mark_user_updated($login, $contact->getUpdatedDateUtcAsDate()->format('U'));
  216. }else{
  217. $msg = "==(Add Client failed)==";
  218. $this->logConsole($msg);
  219. $msg = sprintf("ADD Client name=[%s] {%s} Failed\n", $contact->getName(), $contact->getContactId());
  220. error_log("ACARE add client failed: $msg");
  221. }
  222. }
  223. }
  224. private function update_existing_contact($contact){
  225. $login = $contact->getContactID();
  226. $user = get_user_by('login', $login);
  227. if ($user !== false)
  228. {//update user - must be existing user;
  229. if (! $this->user_requires_update($user, $contact)){
  230. $this->logConsole("skip update client (lastSync is up-to-date) : " . $contact->getName() . "\n");
  231. return;
  232. }
  233. $args = $this->xero_contact_profile($contact);
  234. $args['ID'] = $user->ID;
  235. unset($args['user_pass']); //we don't change password
  236. wp_update_user($args);
  237. update_user_meta($user->ID, 'address', $args['address']);
  238. update_user_meta($user->ID, 'account', $args['account']);
  239. $this->mark_user_updated($user->ID, $contact->getUpdatedDateUtcAsDate()->format('U'));
  240. }
  241. }
  242. private function xero_contact_profile($c){
  243. return [
  244. 'user_login' => $username = $c->getContactId(),
  245. 'user_pass' => md5(uniqid(rand() + time(), true)),
  246. 'display_name' =>$c->getName(),
  247. 'user_email' => $c->getEmailAddress(),
  248. 'first_name' => $c->getFirstName(),
  249. 'last_name' => $c->getLastName(),
  250. 'nickname' => $c->getName(),
  251. 'account' => $c->getAccountNumber(),
  252. 'address'=> $this->get_client_post_address($c),
  253. 'role' => 'client',
  254. ];
  255. }
  256. private function user_requires_update($user, $contact) {
  257. $lastSync = $this->get_last_sync($user->ID);
  258. $updated = $contact->getUpdatedDateUtcAsDate()->format('U');;
  259. return $lastSync < $updated;
  260. }
  261. private function add_new_staff($employee)
  262. {
  263. $login = $employee->getEmployeeID();
  264. $user = get_user_by('login', $login);
  265. if ($user === false){
  266. $msg = sprintf("ADD employee name=[%s %s] {%s} \n",
  267. $employee->getFirstName(),
  268. $employee->getLastName(),
  269. $employee->getEmployeeID());
  270. $this->logConsole($msg);
  271. $args = $this->xero_employee_profile($employee);
  272. $this->logConsole(print_r($args, true));
  273. $id = wp_insert_user($args);
  274. $this->logConsole(print_r($id, true));
  275. if (! $id instanceof \WP_Error){
  276. $user = get_user_by('ID', $id);
  277. update_user_meta($user->ID, 'mobile', $args['mobile']);
  278. update_user_meta($user->ID, 'address', $args['address']);
  279. $this->mark_user_updated($user->ID, $employee->getUpdatedDateUtcAsDate()->format('U'));
  280. }else{
  281. $msg = "==(Add staff failed)==";
  282. $this->logConsole($msg);
  283. $msg = sprintf("ADD employee name=[%s %s] {%s} Failed\n",
  284. $employee->getFirstName(),
  285. $employee->getLastName(),
  286. $employee->getEmployeeID());
  287. error_log("ACARE add employee failed: $msg");
  288. }
  289. }
  290. }
  291. private function update_existing_staff($employee)
  292. {
  293. $login = $employee->getEmployeeID();
  294. $user = get_user_by('login', $login);
  295. if ($this->user_requires_update($user, $employee)){
  296. $this->logConsole("skip update EMPLOYEE (lastSync is up-to-date) : " .
  297. $employee->getFirstName() . " " . $employee->getLastName() . "\n");
  298. return;
  299. }
  300. if ($user != false) {
  301. $args = $this->xero_employee_profile($employee);
  302. $args['ID'] = $user->ID;
  303. unset($args['user_pass']);
  304. wp_update_user($args);
  305. update_user_meta($user->ID, 'mobile', $args['mobile']);
  306. update_user_meta($user->ID, 'address', $args['address']);
  307. $this->mark_user_updated($user->ID, $employee->getUpdatedDateUtcAsDate()->format('U'));
  308. }
  309. }
  310. private function xero_employee_profile($e){
  311. $args = [
  312. 'user_login' => $e->getEmployeeId(),
  313. 'user_pass' => md5(uniqid(rand() + time(), true)),
  314. 'display_name' =>$e->getFirstName() . " " . $e->getLastName(),
  315. 'user_email' => $e->getEmail(),
  316. 'first_name' => $e->getFirstName(),
  317. 'last_name' => $e->getLastName(),
  318. 'nickname' => $e->getFirstName(),
  319. 'mobile' => $e->getMobile(),
  320. 'address'=> $this->get_employee_home_address($e),
  321. 'role' => 'staff',
  322. ];
  323. return $args;
  324. }
  325. public function get_employee_home_address($e){
  326. // "HomeAddress": {
  327. // "AddressLine1": "16 Quist Avenue",
  328. // "City": "Lurnea",
  329. // "Region": "NSW",
  330. // "PostalCode": "2170",
  331. // "Country": "AUSTRALIA"
  332. // },
  333. $addr = "";
  334. $home = $e->getHomeAddress();
  335. if ( $home == null ) {
  336. return "";
  337. }
  338. $addr .= $home->getAddressLine1() .",";
  339. $addr .= $home->getAddressLine2() .",";
  340. $addr .= $home->getCity() . ",";
  341. $addr .= $home->getRegion() . " ";
  342. $addr .= $home->getCountry() ." ";
  343. $addr .= $home->getPostalCode();
  344. return $addr;
  345. }
  346. private function usage(){
  347. $msg = "_____________________________________________\n";
  348. $msg .= "run this command at public_html/, where wp-config.php exist \n";
  349. $msg .= "wp sync_users \n";
  350. $msg .= "but it may hit XERO rate limit, 60call/sec, 5000/day \n";
  351. $msg .= "---------------------------------------------\n";
  352. $this->logConsole($msg);
  353. }
  354. private function logConsole($str){
  355. //if is commandline
  356. if ( defined( 'WP_CLI' ) && WP_CLI ) {
  357. echo $str;
  358. }
  359. }
  360. }