PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.4.3
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.4.3
3.0.3 3.0.2 3.0.1 trunk 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.3.0 2.3.1 2.3.10 2.3.11 2.3.12 2.3.13 2.3.14 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.7.91 2.7.92 2.7.93 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0
commercebird / admin / includes / Actions / Ajax / ExactOnlineAjax.php
commercebird / admin / includes / Actions / Ajax Last commit date
AcfAjax.php 1 year ago ExactOnlineAjax.php 1 year ago SettingsAjax.php 1 year ago ZohoCRMAjax.php 1 year ago ZohoInventoryAjax.php 1 year ago index.php 1 year ago
ExactOnlineAjax.php
1066 lines
1 <?php
2
3 namespace CommerceBird\Admin\Actions\Ajax;
4
5 use CommerceBird\Admin\Actions\Sync\ExactOnlineSync;
6 use CommerceBird\Admin\Actions\Ajax\SettingsAjax;
7 use CommerceBird\Admin\Connectors\CommerceBird;
8 use CommerceBird\Admin\Traits\AjaxRequest;
9 use CommerceBird\Admin\Traits\LogWriter;
10 use CommerceBird\Admin\Traits\OptionStatus;
11 use CommerceBird\Admin\Traits\Singleton;
12 use WC_Webhook;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17 final class ExactOnlineAjax {
18 use Singleton;
19 use LogWriter;
20 use AjaxRequest;
21 use OptionStatus;
22
23 private const FORMS = [
24 'product' => [
25 'importProducts',
26 ],
27 'order' => [ 'range' ],
28 'customer' => [
29 'importCustomers',
30 ],
31 'webhooks' => [
32 'enable_StockPosition',
33 'enable_Item',
34 ],
35 'sync_settings' => [
36 'allowedInvoicesStores',
37 'allowedOrdersStores',
38 'allowedMandateStores',
39 'errors_notifications_email'
40 ],
41 'general_settings' => [
42 'divisionId',
43 'cost_center',
44 'cost_unit',
45 'email_layout',
46 'invoice_style',
47 'journal',
48 'products_gla',
49 'send_invoice',
50 'shipping_cost_label',
51 'shipping_gla',
52 'warehouse'
53 ],
54 'tax_mapping' => [ 'taxMapping' ],
55 'payment_status_mapping' => [
56 'paymentStatusMapping',
57 ]
58 ];
59 private const ACTIONS = [
60 'save_woo_webhooks' => 'create_woo_webhooks',
61 'save_sync_order_via_cron' => 'sync_order',
62 'save_exact_online_connect' => 'connect_exact_online',
63 'get_exact_online_connect' => 'get_exact_connection',
64 'save_exact_online_cost_center' => 'cost_center_save',
65 'save_exact_online_cost_unit' => 'cost_unit_save',
66 'save_exact_online_gl_account' => 'gl_account_save',
67 'save_exact_online_payment_status' => 'get_payment_save',
68 'map_exact_online_product' => 'product_map',
69 'map_exact_online_customer' => 'customer_map',
70 'map_exact_online_order' => 'order_map',
71 'export_exact_online_order' => 'order_export',
72 'get_exact_online_webhooks' => 'webhooks_get',
73 'save_exact_online_webhooks' => 'webhooks_save',
74 'reset_exact_online_connect' => 'reset_mapping',
75 'get_exact_online_sync_settings' => 'get_exact_sync_settings',
76 'save_exact_online_sync_settings' => 'save_exact_sync_settings',
77 'get_exact_online_general_settings' => 'get_exact_general_settings',
78 'save_exact_online_general_settings' => 'save_exact_general_settings',
79 'get_exact_online_cost_center' => 'cost_center_get',
80 'get_exact_online_cost_unit' => 'cost_unit_get',
81 'get_exact_online_gl_accounts' => 'gl_account_get',
82 'get_exact_online_journals' => 'journals_get',
83 'get_exact_online_warehouses' => 'warehouses_get',
84 'get_exact_online_invoice_layouts' => 'invoice_layouts_get',
85 'get_exact_online_tax_mapping' => 'get_exact_tax_mapping',
86 'save_exact_online_tax_mapping' => 'save_exact_tax_mapping',
87 'get_exact_online_vat_codes' => 'get_exact_vat_codes',
88 'get_exact_online_payment_status_mapping' => 'get_exact_payment_status_mapping',
89 'save_exact_online_payment_status_mapping' => 'save_exact_payment_status_mapping',
90 'get_exact_online_payment_conditions' => 'get_exact_payment_conditions',
91 'get_wc_payment_methods' => 'wc_payment_gateways',
92 ];
93 private const OPTIONS = [
94 'connect' => [
95 'exact_domain' => 'commercebird-exact-online-domain',
96 'woo_webhook_status' => 'commercebird-woo-webhook-status',
97 ],
98 'cost_center' => 'commercebird-exact-online-cost-center',
99 'cost_unit' => 'commercebird-exact-online-cost-unit',
100 'gl_accounts' => 'commercebird-exact-online-gl-accounts',
101 ];
102
103 private const SOURCE = 'exact';
104
105 public function __construct() {
106 $this->load_actions();
107 add_action( 'cmbird_exact_online_sync_orders', array( $this, 'sync_via_cron' ) );
108 }
109 /**
110 * Load actions for the class.
111 *
112 * @return void
113 */
114 public function create_woo_webhooks() {
115 $this->verify( [ 'token', 'override' ] );
116 if ( ! isset( $this->data['token'] ) || empty( $this->data['token'] ) ) {
117 $this->errors['message'] = __( 'Token is required', 'commercebird' );
118 }
119
120 // Check if webhook status has value in option then don't create webhook and response with status.
121 $status = get_option( self::OPTIONS['connect']['woo_webhook_status'] );
122 if ( empty( $this->data['override'] ) && isset( $status ) && ! empty( $status ) ) {
123 $this->response['message'] = __( $status, 'commercebird' );
124 } else {
125 $response = ( new CommerceBird() )->create_woo_webbhook();
126 if ( empty( $response ) ) {
127 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
128 } else {
129 if ( $response['code'] === 200 ) {
130 // Update the option with the webhook status
131 update_option( self::OPTIONS['connect']['woo_webhook_status'], $response['message'] );
132 $this->response['message'] = __( 'Webhook created', 'commercebird' );
133 } else {
134 $this->errors['message'] = __( $response['message'], 'commercebird' );
135 }
136 }
137 }
138 $this->serve();
139 }
140
141
142 /**
143 * Sync orders from Exact Online.
144 *
145 * @return void
146 */
147 public function sync_order(): void {
148 $this->verify( array( 'sync' ) );
149 if ( $this->data['sync'] ) {
150 if ( ! wp_next_scheduled( 'cmbird_exact_online_sync_orders' ) ) {
151 wp_schedule_event( time(), 'hourly', 'cmbird_exact_online_sync_orders' );
152 }
153 } else {
154 wp_clear_scheduled_hook( 'cmbird_exact_online_sync_orders' );
155 }
156 update_option( 'cmbird_exact_online_sync_orders', (bool) $this->data['sync'] );
157 $this->response = [
158 'success' => true,
159 'message' => __( 'Synced', 'commercebird' ),
160 ];
161 $this->serve();
162 }
163
164 public function get_payment_save(): void {
165 $this->verify();
166 $this->get_payment_status();
167 }
168
169 /**
170 * Get Payment status from Exact Online. This is used to update the payment status of the order.
171 *
172 * @return void
173 */
174 public function get_payment_status(): void {
175 // $fd = fopen( __DIR__ . '/get_payment_status.log', 'a+' );
176
177 $start_date = gmdate( 'Y-m-d H:i:s', strtotime( '-200 days' ) );
178 $end_date = gmdate( 'Y-m-d H:i:s', strtotime( '-5 days' ) );
179
180 // Get all orders with the included statuses
181 $orders = wc_get_orders(
182 [
183 'status' => 'wc-on-hold',
184 'limit' => -1,
185 'date_created' => $start_date . '...' . $end_date,
186 'return' => 'ids',
187 ]
188 );
189 if ( empty( $orders ) ) {
190 $this->response = [
191 'success' => false,
192 'message' => __( 'No Invoice orders found', 'commercebird' ),
193 ];
194 $this->serve();
195 }
196 foreach ( $orders as $order_id ) {
197 // send each order_id to wc action scheduler to process if not scheduled
198 if ( ! as_has_scheduled_action( 'cmbird_payment_status', [ $order_id ] ) ) {
199 as_schedule_single_action(
200 time(),
201 'cmbird_payment_status',
202 [
203 $order_id,
204 ]
205 );
206 } else {
207 continue;
208 }
209 }
210 // fclose( $fd );
211
212 // Schedule the event to run weekly starting next week
213 if ( ! wp_next_scheduled( 'cmbird_eo_get_payment_statuses' ) ) {
214 $seven_days_in_future = time() + 7 * DAY_IN_SECONDS;
215 wp_schedule_event( $seven_days_in_future, 'weekly', 'cmbird_eo_get_payment_statuses' );
216 }
217 $this->response = [
218 'success' => true,
219 'message' => __( 'Payment status updated', 'commercebird' ),
220 ];
221 $this->serve();
222 }
223
224 private function process_orders( $range ): bool {
225 $result = 0;
226 $orders = ( new CommerceBird() )->order( $range );
227 if ( is_string( $orders ) ) {
228 $this->response = [
229 'success' => false,
230 'message' => $orders,
231 ];
232 $this->serve();
233 }
234 $chunked = array_chunk( $orders['orders'], 20 );
235 foreach ( $chunked as $chunked_order ) {
236 $result = as_schedule_single_action(
237 time(),
238 'cmbird_sync_eo',
239 [
240 'orders',
241 wp_json_encode( $chunked_order ),
242 false,
243 ]
244 );
245 if ( empty( $result ) ) {
246 break;
247 }
248 }
249 return $result > 0;
250 }
251
252 public function order_map() {
253 $this->verify( self::FORMS['order'] );
254 if ( empty( $this->data ) || empty( $this->data['range'] ) ) {
255 $this->response['success'] = false;
256 $this->response['message'] = __( 'Select dates', 'commercebird' );
257 $this->serve();
258 }
259 $result = $this->process_orders(
260 [
261 'start_date' => gmdate(
262 'Y-m-d\TH:i:s.000\Z',
263 strtotime( $this->data['range'][0] )
264 ),
265 'end_date' => gmdate(
266 'Y-m-d\TH:i:s.000\Z',
267 strtotime( $this->data['range'][1] )
268 ),
269 ],
270 );
271 $this->response['success'] = $result > 0;
272 $this->response['data'] = $this->data['range'];
273 $this->response['message'] = __( 'Mapped', 'commercebird' );
274 $this->serve();
275 }
276
277 public function export_order( $start_date_raw, $end_date_raw ) {
278 // $fd = fopen( __DIR__ . '/export_order.log', 'a+' );
279
280 $start_date = gmdate( 'Y-m-d H:i:s', $start_date_raw );
281 $end_date = gmdate( 'Y-m-d H:i:s', $end_date_raw );
282 // Define the order statuses to exclude
283 $exclude_statuses = [ 'wc-failed', 'wc-pending', 'wc-on-hold', 'wc-cancelled' ];
284 $posts_per_page = 20;
285 $paged = 1;
286
287 do {
288 // Query to get orders
289 $args = [
290 'date_created' => $start_date . '...' . $end_date,
291 'status' => array_diff( array_keys( wc_get_order_statuses() ), $exclude_statuses ),
292 'limit' => $posts_per_page,
293 'paged' => $paged,
294 'orderby' => 'date',
295 'order' => 'ASC',
296 'return' => 'ids',
297 ];
298 $orders = wc_get_orders( $args );
299
300 // Loop through orders and add customer note
301 foreach ( $orders as $order_id ) {
302 $order = wc_get_order( $order_id );
303 $order->set_status( $order->get_status() );
304 $order->save();
305 }
306
307 // Increment the offset for the next batch
308 ++$paged;
309 } while ( ! empty( $orders ) );
310 // fclose( $fd );
311 }
312
313 public function order_export() {
314 $this->verify( self::FORMS['order'] );
315 if ( empty( $this->data ) || empty( $this->data['range'] ) ) {
316 $this->response['success'] = false;
317 $this->response['message'] = __( 'Select dates', 'commercebird' );
318 $this->serve();
319 }
320 // Set the date range to last 30 days
321 $start_date = strtotime( $this->data['range'][0] );
322 $end_date = strtotime( $this->data['range'][1] );
323 $this->export_order( $start_date, $end_date );
324 $this->response['success'] = true;
325 $this->response['message'] = __( 'Exported', 'commercebird' );
326 $this->serve();
327 }
328
329 public function product_map() {
330 $this->verify( self::FORMS['product'] );
331 $products = ( new CommerceBird() )->products();
332 if ( is_string( $products ) ) {
333 $this->response = [
334 'success' => false,
335 'message' => $products,
336 ];
337 $this->serve();
338 }
339 $chunked = array_chunk( $products['items'], 100 );
340 foreach ( $chunked as $index => $chunked_products ) {
341 $transient_key = 'cmbird_product_chunk_' . time() . '_' . $index;
342 set_transient( $transient_key, $chunked_products, HOUR_IN_SECONDS );
343 // Wrap the arguments in an array
344 as_schedule_single_action(
345 time(),
346 'cmbird_process_product_chunk',
347 [
348 [
349 'transient_key' => $transient_key,
350 'import_products' => (bool) $this->data['importProducts'],
351 ],
352 ]
353 );
354 }
355 // handle more pages if response contains next_page_url
356 if ( ! empty( $products['next_page_url'] ) ) {
357 $next_page = $products['next_page_url'];
358 $this->handle_more_pages( $next_page, 'product', 'customs/exact/bulk-items' );
359 }
360 $this->response['message'] = __( 'Items are being mapped in the background. You can visit other tabs :).', 'commercebird' );
361 $this->serve();
362 }
363
364 private function handle_more_pages( $next_page, $type, $endpoint, $params = [] ) {
365 // Add the pagination param
366 $params['__next'] = $next_page;
367
368 $response = ( new CommerceBird() )->request( $endpoint, 'GET', [], $params );
369
370 if ( is_string( $response ) || $response['code'] !== 200 ) {
371 $this->response = [
372 'success' => false,
373 'message' => is_string( $response ) ? $response : $response['message'],
374 ];
375 $this->serve();
376 }
377
378 $result = $response['data'];
379 $next_page = $result['next_page_url'] ?? null;
380
381 switch ( $type ) {
382 case 'product':
383 if ( empty( $result['items'] ) ) {
384 $this->response = [
385 'success' => false,
386 'message' => __( 'No more products found', 'commercebird' ),
387 ];
388 $this->serve();
389 }
390
391 $chunked = array_chunk( $result['items'], 100 );
392 foreach ( $chunked as $index => $chunked_products ) {
393 // sleep for 0.1 seconds to avoid hitting the API limit
394 usleep( 100000 );
395 $transient_key = 'cmbird_product_chunk_' . time() . '_' . $index;
396 set_transient( $transient_key, $chunked_products, HOUR_IN_SECONDS );
397
398 as_schedule_single_action(
399 time() + 10,
400 'cmbird_process_product_chunk',
401 [
402 [
403 'transient_key' => $transient_key,
404 'import_products' => (bool) $this->data['importProducts'],
405 ],
406 ]
407 );
408 }
409 break;
410
411 case 'customer':
412 if ( empty( $result['customers'] ) ) {
413 $this->response = [
414 'success' => false,
415 'message' => __( 'No more customers found', 'commercebird' ),
416 ];
417 $this->serve();
418 }
419
420 $chunked = array_chunk( $result['customers'], 100 );
421 foreach ( $chunked as $index => $chunked_customers ) {
422 // sleep for 0.1 seconds to avoid hitting the API limit
423 usleep( microseconds: 100000 );
424 $transient_key = 'cmbird_customer_chunk_' . time() . '_' . $index;
425 set_transient( $transient_key, $chunked_customers, HOUR_IN_SECONDS );
426
427 as_schedule_single_action(
428 time() + 10,
429 'cmbird_process_customer_chunk',
430 [
431 [
432 'transient_key' => $transient_key,
433 'import_customers' => (bool) $this->data['importCustomers'],
434 ],
435 ]
436 );
437 }
438 break;
439
440 case 'order':
441 $sync = new ExactOnlineSync();
442 $sync->sync( 'order', $result['orders'], (bool) $this->data['importOrders'] );
443 break;
444 }
445
446 // 🔁 Recurse if there is a next page
447 if ( $next_page ) {
448 $this->handle_more_pages( $next_page, $type, $endpoint, $params );
449 }
450 }
451
452 public function customer_map() {
453 $this->verify( self::FORMS['customer'] );
454 $response = ( new CommerceBird() )->customer();
455 if ( is_string( $response ) ) {
456 $this->response = [
457 'success' => false,
458 'message' => $response,
459 ];
460 $this->serve();
461 }
462 if ( empty( $response['customers'] ) ) {
463 $this->response = [
464 'success' => false,
465 'message' => __( 'No customers found', 'commercebird' ),
466 ];
467 $this->serve();
468 }
469 $chunked = array_chunk( $response['customers'], 100 );
470 foreach ( $chunked as $index => $chunked_customers ) {
471 $transient_key = 'cmbird_customer_chunk_' . time() . '_' . $index;
472 set_transient( $transient_key, $chunked_customers, HOUR_IN_SECONDS );
473 // Wrap the arguments in an array
474 as_schedule_single_action(
475 time(),
476 'cmbird_process_customer_chunk',
477 [
478 [
479 'transient_key' => $transient_key,
480 'import_customers' => (bool) $this->data['importCustomers'],
481 ],
482 ]
483 );
484 }
485 // handle more pages if response meta contains next
486 if ( ! empty( $response['next_page_url'] ) ) {
487 $next_page = $response['next_page_url'];
488 $this->handle_more_pages( $next_page, 'customer', 'customs/exact/bulk-customers' );
489 }
490 $this->response['message'] = __( 'Items are being mapped in background. You can visit other tabs :).', 'commercebird' );
491 $this->serve();
492 }
493
494 public function reset_mapping() {
495 $this->verify();
496 // Check if resetAll is true (sent from Vue)
497 $reset_all = isset( $this->data['reset'] ) && $this->data['reset'] == 1;
498 // if ( ! $reset_all ) {
499 // delete_option(self::OPTIONS['connect']['token']);
500 // $this->response['message'] = __( 'Token removed', 'commercebird' );
501 // $this->serve();
502 // }
503 global $wpdb;
504 // Delete all transients related to products and customers
505 $transients = [ 'cmbird_product_chunk_', 'cmbird_customer_chunk_' ];
506 foreach ( $transients as $transient ) {
507 $wpdb->query(
508 $wpdb->prepare(
509 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
510 $transient . '%'
511 )
512 );
513 }
514 // Delete all product and customer meta keys
515 $wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key = 'eo_item_id'" );
516 $wpdb->query( "DELETE FROM {$wpdb->prefix}usermeta WHERE meta_key IN ('eo_account_id', 'eo_contact_id', 'eo_gl_account')" );
517 // return response.
518 $this->response['message'] = __( 'Mapping reset completed', 'commercebird' );
519 $this->serve();
520 }
521
522 public function cost_center_get_options() {
523 return get_option( self::OPTIONS['cost_center'], [] );
524 }
525
526 public function cost_unit_get_options() {
527 return get_option( self::OPTIONS['cost_unit'], [] );
528 }
529
530 public function gl_account_get_options() {
531 return get_option( self::OPTIONS['gl_accounts'], [] );
532 }
533
534
535 public function cost_center_save() {
536 $this->verify();
537 $response = ( new CommerceBird() )->cost_centers();
538 if ( empty( $response ) ) {
539 $this->errors['message'] = __( 'Cost centers not found', 'commercebird' );
540 } else {
541 $centers = array_map(
542 function ($item) {
543 return "{$item['Code']}-{$item['Description']}";
544 },
545 $response['data']
546 );
547 update_option( self::OPTIONS['cost_center'], $centers );
548 // remove each meta if it does not exists in the cost centers.
549 global $wpdb;
550 if ( ! empty( $centers ) ) {
551 // Sanitize and prepare the units for the SQL query
552 $placeholders = implode( ',', array_fill( 0, count( $centers ), '%s' ) );
553
554 // Prepare the query string
555 $query = "DELETE FROM {$wpdb->prefix}wc_orders_meta
556 WHERE meta_key = 'costcenter'
557 AND meta_value NOT IN ($placeholders)";
558 $wpdb->query( $wpdb->prepare( $query, ...$centers ) ); // phpcs:ignore
559 // also remove from postmeta table.
560 $query2 = "DELETE FROM {$wpdb->prefix}postmeta
561 WHERE meta_key = 'costcenter'
562 AND meta_value NOT IN ($placeholders)";
563 $wpdb->query( $wpdb->prepare( $query2, ...$centers ) ); // phpcs:ignore
564 } else {
565 // If $units is empty, delete all meta keys with 'costunit'
566 $wpdb->query(
567 "DELETE FROM {$wpdb->prefix}wc_orders_meta
568 WHERE meta_key = 'costcenter'"
569 );
570 }
571 $this->response['message'] = __( 'Cost centers saved', 'commercebird' );
572 }
573 $this->serve();
574 }
575
576 public function cost_unit_save() {
577 $this->verify();
578 $response = ( new CommerceBird() )->cost_units();
579 if ( empty( $response ) ) {
580 $this->errors['message'] = __( 'Cost units not found', 'commercebird' );
581 } else {
582 $units = array_map(
583 function ($item) {
584 return "{$item['Code']}-{$item['Description']}";
585 },
586 $response['data']
587 );
588 update_option( self::OPTIONS['cost_unit'], $units );
589 // remove each meta if it does not exists in the units.
590 global $wpdb;
591 if ( ! empty( $units ) ) {
592 // Sanitize and prepare the units for the SQL query
593 $placeholders = implode( ',', array_fill( 0, count( $units ), '%s' ) );
594
595 // Prepare the query string
596 $query = "DELETE FROM {$wpdb->prefix}wc_orders_meta
597 WHERE meta_key = 'costunit'
598 AND meta_value NOT IN ($placeholders)";
599 $wpdb->query( $wpdb->prepare( $query, ...$units ) ); // phpcs:ignore
600 // also remove from postmeta table
601 $query2 = "DELETE FROM {$wpdb->prefix}postmeta
602 WHERE meta_key = 'costunit'
603 AND meta_value NOT IN ($placeholders)";
604 $wpdb->query( $wpdb->prepare( $query2, ...$units ) ); // phpcs:ignore
605 } else {
606 // If $units is empty, delete all meta keys with 'costunit'
607 $wpdb->query(
608 "DELETE FROM {$wpdb->prefix}wc_orders_meta
609 WHERE meta_key = 'costunit'"
610 );
611 }
612 $this->response['message'] = __( 'Cost units saved', 'commercebird' );
613 }
614 $this->serve();
615 }
616
617 public function gl_account_save() {
618 $this->verify();
619 $response = ( new CommerceBird() )->gl_accounts();
620 if ( empty( $response ) ) {
621 $this->errors['message'] = __( 'GL accounts not found', 'commercebird' );
622 } else {
623 $accounts = array_map(
624 function ($item) {
625 return "{$item['ID']} : {$item['Description']}";
626 },
627 $response['data']
628 );
629 update_option( self::OPTIONS['gl_accounts'], $accounts );
630 $this->response['message'] = __( 'GL accounts saved', 'commercebird' );
631 }
632 $this->serve();
633 }
634
635 public function sync_via_cron() {
636 $start_date = strtotime( '-1 day' );
637 $end_date = strtotime( 'now' );
638
639 $continue = $this->process_orders(
640 [
641 'start_date' => $start_date,
642 'end_date' => $end_date,
643 ]
644 );
645
646 if ( $continue ) {
647 $this->export_order( $start_date, $end_date );
648 }
649 }
650
651 public function connect_exact_online() {
652 $this->verify( [ 'exact_domain' ] );
653 if ( ! isset( $this->data['exact_domain'] ) || empty( $this->data['exact_domain'] ) ) {
654 $this->errors['message'] = __( 'Please select exact domain', 'commercebird' );
655 } else {
656 update_option( self::OPTIONS['connect']['exact_domain'], $this->data['exact_domain'] );
657 $response = ( new CommerceBird() )->connect_exact( $this->data['exact_domain'] );
658 error_log( 'Exact Online Connect Response: ' . print_r( $response, true ) );
659 if ( empty( $response ) ) {
660 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
661 } else {
662
663 if ( $response['code'] === 200 ) {
664 $this->response['message'] = __( 'Account generared', 'commercebird' );
665 $this->response['auth_url'] = $response['data']['auth_url'];
666 $this->response['message'] = __( 'Saved', 'commercebird' );
667 } else {
668 $this->errors['message'] = __( $response['message'], 'commercebird' );
669 }
670 }
671 }
672
673 $this->serve();
674 }
675
676 public function get_exact_connection() {
677 $this->verify();
678 $exact_domain = get_option( self::OPTIONS['connect']['exact_domain'], '' );
679 $token = SettingsAjax::instance()->get_token();
680 if ( ! empty( $exact_domain ) && ! empty( $token ) ) {
681 $response = ( new CommerceBird() )->get_exact_divisions();
682 if ( empty( $response ) ) {
683 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
684 } else {
685 $this->response['message'] = __( $response['message'], 'commercebird' );
686 if ( $response['code'] === 200 ) {
687 $this->response['connection'] = $response['data'];
688 }
689 }
690 $this->response['exact_domain'] = $exact_domain;
691 $this->response['token'] = $token;
692 }
693 $this->serve();
694 }
695
696
697 /**
698 * @description Retrieves the general account settings.
699 **/
700 public function get_exact_sync_settings() {
701 $this->verify();
702 $response = ( new CommerceBird() )->get_account_settings();
703 if ( empty( $response ) ) {
704 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
705 } else {
706 $this->response['message'] = __( $response['message'], 'commercebird' );
707 if ( $response['code'] === 200 ) {
708 $this->response['settings'] = $response['data'];
709 }
710 }
711 $this->serve();
712 }
713
714 /**
715 * @description Function to save the general account settings.
716 */
717 public function save_exact_sync_settings() {
718 $this->verify( self::FORMS['sync_settings'] );
719
720 $settings = $this->data;
721
722 if ( empty( $settings ) ) {
723 $this->errors['message'] = __( 'Invalid sync settings payload.', 'commercebird' );
724 $this->serve();
725 }
726
727 $response = ( new CommerceBird() )->save_account_settings( $settings );
728
729 if ( empty( $response ) ) {
730 $this->errors['message'] = __( 'Unable to save settings.', 'commercebird' );
731 } else {
732 $this->response['message'] = __( $response['message'], 'commercebird' );
733 if ( ! empty( $response['code'] ) && $response['code'] === 200 ) {
734 $this->response['success'] = true;
735 $this->response['settings'] = $response['data'] ?? [];
736 }
737 }
738
739 $this->serve();
740 }
741
742 /**
743 * @description Retrieves mapped general settings for exact.
744 **/
745 public function get_exact_general_settings() {
746 $this->verify();
747 $response = ( new CommerceBird() )->exact_general_settings();
748 if ( empty( $response ) ) {
749 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
750 } else {
751 $this->response['message'] = __( $response['message'], 'commercebird' );
752 if ( $response['code'] === 200 ) {
753 $this->response['settings'] = $response['data'];
754 }
755 }
756 $this->serve();
757 }
758
759 /**
760 * @description Function to save the mapped general settings.
761 */
762 public function save_exact_general_settings() {
763 $this->verify( self::FORMS['general_settings'] );
764
765 $settings = $this->data;
766
767 if ( empty( $settings ) ) {
768 $this->errors['message'] = __( 'Invalid general settings payload.', 'commercebird' );
769 $this->serve();
770 }
771
772 $response = ( new CommerceBird() )->save_exact_general_settings( $settings );
773
774 if ( empty( $response ) ) {
775 $this->errors['message'] = __( 'Unable to save settings.', 'commercebird' );
776 } else {
777 $this->response['message'] = __( $response['message'], 'commercebird' );
778 if ( ! empty( $response['code'] ) && $response['code'] === 200 ) {
779 $this->response['success'] = true;
780 $this->response['settings'] = $response['data'] ?? [];
781 }
782 }
783
784 $this->serve();
785 }
786
787 /**
788 * @description Retrieves Journals list from exact.
789 **/
790 public function journals_get() {
791 $this->verify();
792 $response = ( new CommerceBird() )->journals();
793 if ( empty( $response ) ) {
794 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
795 } else {
796 $this->response['message'] = __( $response['message'], 'commercebird' );
797 if ( $response['code'] === 200 ) {
798 $this->response['settings'] = $response['data'];
799 }
800 }
801 $this->serve();
802 }
803
804 /**
805 * @description Retrieves warehoused from exact.
806 **/
807 public function warehouses_get() {
808 $this->verify();
809 $response = ( new CommerceBird() )->exact_warehouses();
810 if ( empty( $response ) ) {
811 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
812 } else {
813 $this->response['message'] = __( $response['message'], 'commercebird' );
814 if ( $response['code'] === 200 ) {
815 $this->response['settings'] = $response['data'];
816 }
817 }
818 $this->serve();
819 }
820
821 /**
822 * @description Retrieves invoice layouts from exact.
823 **/
824 public function invoice_layouts_get() {
825 $this->verify();
826 $response = ( new CommerceBird() )->invoice_layouts();
827 if ( empty( $response ) ) {
828 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
829 } else {
830 $this->response['message'] = __( $response['message'], 'commercebird' );
831 if ( $response['code'] === 200 ) {
832 $this->response['settings'] = $response['data'];
833 }
834 }
835 $this->serve();
836 }
837
838 /**
839 * @description Retrieves invoice layouts from exact.
840 **/
841 public function gl_account_get() {
842 $this->verify();
843 $response = ( new CommerceBird() )->gl_accounts();
844 if ( empty( $response ) ) {
845 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
846 } else {
847 $this->response['message'] = __( $response['message'], 'commercebird' );
848 if ( $response['code'] === 200 ) {
849 $this->response['settings'] = $response['data'];
850 }
851 }
852 $this->serve();
853 }
854
855 /**
856 * @description Retrieves cost centers for exact.
857 **/
858 public function cost_center_get() {
859 $this->verify();
860 $response = ( new CommerceBird() )->cost_centers();
861 if ( empty( $response ) ) {
862 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
863 } else {
864 $this->response['message'] = __( $response['message'], 'commercebird' );
865 if ( $response['code'] === 200 ) {
866 $this->response['settings'] = $response['data'];
867 }
868 }
869 $this->serve();
870 }
871
872 /**
873 * @description Retrieves cost units for exact.
874 **/
875 public function cost_unit_get() {
876 $this->verify();
877 $response = ( new CommerceBird() )->cost_units();
878 if ( empty( $response ) ) {
879 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
880 } else {
881 $this->response['message'] = __( $response['message'], 'commercebird' );
882 if ( $response['code'] === 200 ) {
883 $this->response['settings'] = $response['data'];
884 }
885 }
886 $this->serve();
887 }
888
889 /**
890 * Sets the webhooks settings and updates the status.
891 *
892 * @return void
893 */
894 public function webhooks_save(): void {
895 $this->verify( self::FORMS['webhooks'] );
896 $form_data = $this->data;
897 foreach ( $form_data as $topic => $is_active ) {
898 $webhooks[] = [
899 'topic' => $topic,
900 'status' => $is_active ? 'active' : 'inactive',
901 ];
902 }
903 $response = ( new CommerceBird() )->subscribe_exact_webhooks( $webhooks );
904 // error_log( print_r( $response, true ) );
905 $this->option_status_update( $this->data );
906 $this->response = [ 'message' => 'Saved!' ];
907 $this->serve();
908 }
909
910 /**
911 * Retrieves the wehbhook settings.
912 *
913 * @return void
914 */
915 public function webhooks_get(): void {
916 $this->verify();
917 $this->response = $this->option_status_get( self::FORMS['webhooks'] );
918 $this->serve();
919 }
920
921 /**
922 * @description Retrieves exact vat codes.
923 **/
924 public function get_exact_vat_codes() {
925 $this->verify();
926 $response = ( new CommerceBird() )->get_exact_vat_codes();
927 if ( empty( $response ) ) {
928 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
929 } else {
930 $this->response['message'] = __( $response['message'], 'commercebird' );
931 if ( $response['code'] === 200 ) {
932 $this->response['settings'] = $response['data'];
933 }
934 }
935 $this->serve();
936 }
937
938 /**
939 * @description Retrieves mapped tax settings.
940 **/
941 public function get_exact_tax_mapping() {
942 $this->verify();
943 $response = ( new CommerceBird() )->get_exact_tax_mapping();
944 if ( empty( $response ) ) {
945 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
946 } else {
947 $this->response['message'] = __( $response['message'], 'commercebird' );
948 if ( $response['code'] === 200 ) {
949 $this->response['taxMapping'] = $response['data'];
950 }
951 }
952 $this->serve();
953 }
954
955 /**
956 * @description Function to save the mapped taxes.
957 */
958 public function save_exact_tax_mapping() {
959 $this->verify( self::FORMS['tax_mapping'] );
960 $settings = $this->data['taxMapping'] ?? [];
961 if ( empty( $settings ) ) {
962 $this->errors['message'] = __( 'Invalid tax mapping payload.', 'commercebird' );
963 $this->serve();
964 }
965
966 $response = ( new CommerceBird() )->save_exact_tax_mapping( $settings );
967
968 if ( empty( $response ) ) {
969 $this->errors['message'] = __( 'Unable to save tax mapping.', 'commercebird' );
970 } else {
971 $this->response['message'] = __( $response['message'], 'commercebird' );
972 if ( ! empty( $response['code'] ) && $response['code'] === 200 ) {
973 $this->response['success'] = true;
974 $this->response['settings'] = $response['data'] ?? [];
975 }
976 }
977
978 $this->serve();
979 }
980
981 /**
982 * @description Retrieves exact payment conditions.
983 **/
984 public function get_exact_payment_conditions() {
985 $this->verify();
986 $response = ( new CommerceBird() )->get_exact_payment_conditions();
987 if ( empty( $response ) ) {
988 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
989 } else {
990 $this->response['message'] = __( $response['message'], 'commercebird' );
991 if ( $response['code'] === 200 ) {
992 $this->response['payment_conditions'] = $response['data'];
993 }
994 }
995 $this->serve();
996 }
997 /**
998 * @description Retrieves mapped payment status mapping.
999 **/
1000 public function get_exact_payment_status_mapping() {
1001 $this->verify();
1002 $response = ( new CommerceBird() )->get_exact_payment_status_mapping();
1003 if ( empty( $response ) ) {
1004 $this->errors['message'] = __( 'Unable to connect', 'commercebird' );
1005 } else {
1006 $this->response['message'] = __( $response['message'], 'commercebird' );
1007 if ( $response['code'] === 200 ) {
1008 $this->response['paymentStatusMapping'] = $response['data'];
1009 }
1010 }
1011 $this->serve();
1012 }
1013
1014 /**
1015 * @description Function to save the mapped taxes.
1016 */
1017 public function save_exact_payment_status_mapping() {
1018 $this->verify( self::FORMS['payment_status_mapping'] );
1019 $settings = $this->data['paymentStatusMapping'] ?? [];
1020 if ( empty( $settings ) ) {
1021 $this->errors['message'] = __( 'Invalid payment status mapping payload.', 'commercebird' );
1022 $this->serve();
1023 }
1024
1025 $response = ( new CommerceBird() )->save_exact_payment_status_mapping( $settings );
1026
1027 if ( empty( $response ) ) {
1028 $this->errors['message'] = __( 'Unable to save payment status mapping.', 'commercebird' );
1029 } else {
1030 $this->response['message'] = __( $response['message'], 'commercebird' );
1031 if ( ! empty( $response['code'] ) && $response['code'] === 200 ) {
1032 $this->response['success'] = true;
1033 $this->response['paymentStatusMapping'] = $response['data'] ?? [];
1034 }
1035 }
1036
1037 $this->serve();
1038 }
1039
1040 /**
1041 * @description Function to get the payment Methods.
1042 **/
1043 public function wc_payment_gateways() {
1044 $gateways = array();
1045 $available_gateways = WC()->payment_gateways->payment_gateways();
1046 // $available_gateways = $wc_gateways->get_available_payment_gateways();
1047 foreach ( $available_gateways as $gateway_id => $gateway ) {
1048 if ( 'yes' !== $gateway->enabled ) {
1049 continue; // skip disabled gateways
1050 }
1051 $gateways[] = array(
1052 'id' => $gateway_id,
1053 'title' => $gateway->get_title(),
1054 'description' => $gateway->get_description(),
1055 'method_title' => $gateway->method_title,
1056 'enabled' => $gateway->enabled,
1057 'supports' => $gateway->supports,
1058 );
1059 }
1060 $this->response['success'] = true;
1061 $this->response['gateways'] = $gateways ?? [];
1062 $this->serve();
1063 }
1064
1065 }
1066