timesheet source code
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

394 líneas
14KB

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