PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.4.5
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.4.5
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 / ZohoCRMAjax.php
commercebird / admin / includes / Actions / Ajax Last commit date
AcfAjax.php 1 year ago ExactOnlineAjax.php 11 months ago SettingsAjax.php 11 months ago ZohoCRMAjax.php 11 months ago ZohoInventoryAjax.php 11 months ago index.php 1 year ago
ZohoCRMAjax.php
775 lines
1 <?php
2
3 namespace CommerceBird\Admin\Actions\Ajax;
4
5 use CMBIRD_Auth_Zoho;
6 use CMBIRD_API_Handler_Zoho;
7 use CommerceBird\Admin\Actions\Sync\ZohoCRMSync;
8 use CommerceBird\Admin\Template;
9 use CommerceBird\Admin\Traits\AjaxRequest;
10 use CommerceBird\Admin\Traits\OptionStatus;
11 use CommerceBird\Admin\Traits\Singleton;
12 use CommerceBird\Admin\Traits\LogWriter;
13 use WC_Tax;
14 use Throwable;
15
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20 /**
21 * Initializes the Zoho CRM class.
22 * @since 1.0.0
23 * @return void
24 */
25 final class ZohoCRMAjax {
26
27 use Singleton;
28 use AjaxRequest;
29 use OptionStatus;
30 use LogWriter;
31
32
33
34 private const FORMS = array(
35 // 'connect' => array(
36 // 'token',
37 // ),
38 'product' => array(
39 'importProducts',
40 ),
41 'order' => array( 'range' ),
42 'order_settings' => array(
43 'syncAsDeals',
44 ),
45 'order_field_mapping' => array(
46 'dealFields',
47 ),
48 'order_status_stage_mapping' => array(
49 'mapping',
50 ),
51 'customer' => array(
52 'importCustomers',
53 ),
54 'fields' => array(
55 'form',
56 'module',
57 ),
58 'tax' => array(
59 'selectedTaxRates',
60 )
61 );
62 private const ACTIONS = array(
63 'get_zcrm_connect' => 'connection_get',
64 'save_zcrm_connect' => 'connection_set',
65 'reset_zcrm_connect' => 'connection_reset',
66 'is_zcrm_connected' => 'connection_done',
67 'import_zcrm_product' => 'product_import',
68 'map_zcrm_product' => 'product_map',
69 'map_zcrm_customer' => 'customer_map',
70 'map_zcrm_order' => 'order_map',
71 'export_zcrm_order' => 'order_export',
72 'get_zcrm_order_settings' => 'order_settings_get',
73 'save_zcrm_order_settings' => 'order_settings_save',
74 'get_zcrm_deal_fields' => 'deal_fields_get',
75 'get_zcrm_deal_field_mapping' => 'deal_field_mapping_get',
76 'save_zcrm_deal_field_mapping' => 'deal_field_mapping_save',
77 'get_wc_order_statuses' => 'wc_order_statuses_get',
78 'get_zcrm_deal_stages' => 'deal_stages_get',
79 'get_order_status_stage_mapping' => 'order_status_stage_mapping_get',
80 'save_order_status_stage_mapping' => 'order_status_stage_mapping_save',
81 'refresh_zcrm_fields' => 'refresh_zcrm_fields',
82 'zcrm_get_custom_fields' => 'zcrm_get_custom_fields',
83 'zcrm_save_custom_fields' => 'zcrm_save_custom_fields',
84 'zcrm_reset_custom_fields' => 'zcrm_reset_custom_fields',
85 'zcrm_fields' => 'get_zcrm_fields',
86 'zcrm_handle_code' => 'handle_code',
87 'get_zcrm_tax' => 'tax_get',
88 'save_zcrm_tax' => 'tax_set',
89 'reset_zcrm_tax' => 'tax_reset',
90 'get_wc_taxes' => 'wc_tax_collect',
91 'get_zcrm_taxes' => 'zoho_tax_rates_collect',
92 'get_zcrm_layouts' => 'layouts_get',
93 'save_zcrm_layout' => 'layout_save',
94 'get_zcrm_selected_layout' => 'layout_get_selected',
95 );
96 private const OPTIONS = array(
97 // 'connect' => array(
98 // 'token' => 'commercebird-exact-online-token',
99 // ),
100 'zcrm_sales_orders_fields' => 'zcrm_sales_orders_fields',
101 'zcrm_contacts_fields' => 'zcrm_contacts_fields',
102 'zcrm_products_fields' => 'zcrm_products_fields',
103 'zcrm_sales_order_layout' => 'zcrm_sales_order_layout',
104 );
105
106 public function __construct() {
107 $this->load_actions();
108 }
109
110 /**
111 * Sets the connection for the Zoho crm API.
112 */
113 public function connection_set(): void {
114 $this->verify(
115 array(
116 'account_domain',
117 'client_id',
118 'client_secret',
119 'redirect_uri',
120 ),
121 );
122 try {
123 $crm_url = sprintf( 'https://www.zohoapis.%s/', $this->data['account_domain'] );
124 update_option( 'cmbird_zoho_crm_domain', $this->data['account_domain'] );
125 update_option( 'cmbird_zoho_crm_cid', $this->data['client_id'] );
126 update_option( 'cmbird_zoho_crm_cs', $this->data['client_secret'] );
127 update_option( 'cmbird_zoho_crm_url', $crm_url );
128 update_option( 'cmbird_authorization_redirect_uri', $this->data['redirect_uri'] );
129 $redirect = esc_url_raw(
130 'https://accounts.zoho.'
131 . $this->data['account_domain']
132 . '/oauth/v2/auth?response_type=code&client_id='
133 . $this->data['client_id'] . '&scope=ZohoCRM.users.ALL,ZohoCRM.bulk.ALL,ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.org.ALL,profile.userphoto.READ,ZohoFiles.files.CREATE&redirect_uri='
134 . $this->data['redirect_uri'] . '&prompt=consent&access_type=offline&state='
135 . wp_create_nonce( Template::NAME )
136 );
137 $this->response = array(
138 'redirect' => $redirect,
139 'message' => 'We are redirecting you to zoho. please wait...',
140 );
141 } catch (Throwable $throwable) {
142 $this->errors = array( 'message' => $throwable->getMessage() );
143 }
144
145 $this->serve();
146 }
147
148 /**
149 * Retrieves the connection details.
150 *
151 * @return void
152 */
153 public function connection_get(): void {
154 $this->verify();
155 $this->response['account_domain'] = get_option( 'cmbird_zoho_crm_domain' );
156 $this->response['client_id'] = get_option( 'cmbird_zoho_crm_cid' );
157 $this->response['client_secret'] = get_option( 'cmbird_zoho_crm_cs' );
158 $this->response['crm_url'] = get_option( 'cmbird_zoho_crm_url' );
159 $this->response['redirect_uri'] = get_option( 'cmbird_authorization_redirect_uri' );
160 $this->serve();
161 }
162
163 /**
164 * Handles the oAuth code.
165 */
166 public function handle_code(): void {
167 $this->verify();
168 if ( array_key_exists( 'code', $this->request ) ) {
169 $class_functions = new CMBIRD_Auth_Zoho();
170 $code = $this->request['code'];
171 try {
172 $access_token = $class_functions->get_zoho_access_token( $code, 'zoho_crm' );
173 if ( array_key_exists( 'access_token', $access_token ) ) {
174 update_option( 'cmbird_zoho_crm_auth_code', $code );
175 update_option( 'cmbird_zoho_crm_access_token', $access_token['access_token'] );
176 update_option( 'cmbird_zoho_crm_refresh_token', $access_token['refresh_token'] );
177 update_option( 'cmbird_zoho_crm_timestamp', strtotime( gmdate( 'Y-m-d H:i:s' ) ) + $access_token['expires_in'] );
178 $this->response = (array) $access_token;
179 } else {
180 $this->errors = (array) $access_token;
181 }
182 } catch (Throwable $throwable) {
183 $this->errors = array( 'message' => $throwable->getMessage() );
184 }
185 }
186 $this->serve();
187 }
188
189 /**
190 * Resets the connection by deleting specific options from the database.
191 */
192 public function connection_reset(): void {
193 $this->verify();
194 try {
195 $options = array(
196 'cmbird_zoho_crm_domain',
197 'cmbird_zoho_crm_cid',
198 'cmbird_zoho_crm_cs',
199 'cmbird_zoho_crm_url',
200 'cmbird_zoho_crm_access_token',
201 );
202 foreach ( $options as $zi_option ) {
203 delete_option( $zi_option );
204 }
205 $this->response = array( 'message' => 'Reset successfully!' );
206 } catch (Throwable $throwable) {
207 $this->errors = array( 'message' => $throwable->getMessage() );
208 }
209
210 $this->serve();
211 }
212
213
214 public function connection_done() {
215 $this->verify();
216 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
217 $url = $zoho_crm_url . 'crm/v7/org';
218 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
219 $json = $execute_curl_call_handle->execute_curl_call_get( $url );
220 if ( is_wp_error( $json ) ) {
221 $this->errors = array( 'message' => $json->get_error_message() );
222 } elseif ( empty( $json ) ) {
223 $this->errors = array( 'message' => 'We lost connection with zoho. please refresh page.' );
224 } else {
225 $this->response = ! empty( $json->org ) ? (array) $json->org[0] : [];
226 // schedule a wp cron to refresh the token after one hour from now.
227 if ( ! wp_next_scheduled( 'zcrm_refresh_token' ) ) {
228 wp_schedule_event( time() + 3600, 'hourly', 'zcrm_refresh_token' );
229 } else {
230 // unschedule first and reschedule.
231 wp_unschedule_event( wp_next_scheduled( 'zcrm_refresh_token' ), 'zcrm_refresh_token' );
232 wp_schedule_event( time() + 3600, 'hourly', 'zcrm_refresh_token' );
233 }
234 }
235 $this->serve();
236 }
237
238 /**
239 * Get Zoho CRM custom fields.
240 * @since 1.0.0
241 * @return void
242 */
243 public function refresh_zcrm_fields() {
244 $module = isset( $_GET['module'] ) ? sanitize_text_field( wp_unslash( $_GET['module'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
245 if ( empty( $module ) ) {
246 $this->errors['message'] = 'Module name is required.';
247 } else {
248 $fields = ( new ZohoCRMSync() )->get_custom_fields( $module );
249 if ( is_wp_error( $fields ) ) {
250 $this->errors['message'] = $fields->get_error_message();
251 } else {
252 $option_name = 'zcrm_' . strtolower( $module ) . '_fields';
253 update_option( self::OPTIONS[ $option_name ], $fields );
254 $this->response = array( 'message' => 'Refresh successfully!' );
255 $this->response['fields'] = $fields;
256 }
257 $this->serve();
258 }
259 }
260
261 /**
262 * Get Zoho CRM fields from wordpress database.
263 */
264 public function get_zcrm_fields() {
265 $module = isset( $_GET['module'] ) ? sanitize_text_field( wp_unslash( $_GET['module'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
266 if ( empty( $module ) ) {
267 $this->errors['message'] = 'Module name is required.';
268 } else {
269 $fields = get_option( 'zcrm_' . strtolower( $module ) . '_fields', array() );
270 $this->response['fields'] = $fields;
271 $this->serve();
272 }
273 }
274
275 /**
276 * Retrieves the fields and serves the response.
277 *
278 * @return object
279 */
280 public function zcrm_get_custom_fields(): void {
281
282 $module = isset( $_GET['module'] ) ? sanitize_text_field( wp_unslash( $_GET['module'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
283
284 if ( empty( $module ) ) {
285 $this->errors['message'] = 'Module name is required.';
286 } else {
287 $this->verify();
288 $option_name = 'zcrm_' . strtolower( $module ) . '_custom_fields';
289 $this->response['form'] = get_option( $option_name, array() );
290 $this->serve();
291 }
292 }
293
294 /**
295 * Sets the custom fields for the form.
296 *
297 * @return void
298 */
299 public function zcrm_save_custom_fields(): void {
300 $this->verify( self::FORMS['fields'] );
301 $module = isset( $this->data['module'] ) ? $this->data['module'] : '';
302 if ( empty( $module ) ) {
303 $this->errors['message'] = 'Module name is required.';
304 } else {
305 try {
306 $option_name = 'zcrm_' . strtolower( $module ) . '_custom_fields';
307 update_option( $option_name, $this->data['form'] );
308 $this->response = array( 'message' => 'saved' );
309 } catch (Throwable $throwable) {
310 $this->errors = array( 'message' => $throwable->getMessage() );
311 }
312 $this->serve();
313 }
314 }
315
316 /**
317 * Resets the fields.
318 *
319 * @return void
320 */
321 public function zcrm_reset_custom_fields(): void {
322 $module = isset( $_GET['module'] ) ? sanitize_text_field( wp_unslash( $_GET['module'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
323 if ( empty( $module ) ) {
324 $this->errors['message'] = 'Module name is required.';
325 } else {
326 $this->verify();
327 $option_name = 'zcrm_' . strtolower( $module ) . '_custom_fields';
328 delete_option( $option_name );
329 $this->response = array( 'message' => 'Reset successfully!' );
330 $this->serve();
331 }
332 }
333
334 /**
335 * Export Orders to Zoho CRM.
336 *
337 * @param int $start_date_raw The start date.
338 * @param int $end_date_raw The end date.
339 */
340 public function export_order( $start_date_raw, $end_date_raw ) {
341 // $fd = fopen( __DIR__ . '/export_order.log', 'a+' );
342
343 $start_date = gmdate( 'Y-m-d H:i:s', $start_date_raw );
344 $end_date = gmdate( 'Y-m-d H:i:s', $end_date_raw );
345 // Define the order statuses to exclude
346 $exclude_statuses = array( 'wc-failed', 'wc-cancelled', 'wc-checkout-draft' );
347 $posts_per_page = 20;
348 $paged = 1;
349
350 do {
351 // Query to get orders
352 $args = array(
353 'date_created' => $start_date . '...' . $end_date,
354 'status' => array_diff( array_keys( wc_get_order_statuses() ), $exclude_statuses ),
355 'limit' => $posts_per_page,
356 'paged' => $paged,
357 'orderby' => 'date',
358 'order' => 'ASC',
359 'return' => 'ids',
360 );
361 $orders = wc_get_orders( $args );
362 // Loop through orders and schedule them for Zoho CRM sync
363 foreach ( $orders as $order_id ) {
364 // Use WC Action Scheduler to sync the order to Zoho CRM
365 $existing_schedule = as_has_scheduled_action( 'sync_zcrm_order', array( $order_id ) );
366 if ( ! $existing_schedule ) {
367 as_schedule_single_action( time(), 'sync_zcrm_order', array( $order_id ) );
368 }
369 }
370 // Increment the offset for the next batch
371 ++$paged;
372 } while ( ! empty( $orders ) );
373 // fclose( $fd );
374 }
375
376 public function order_export() {
377 $this->verify( self::FORMS['order'] );
378 if ( empty( $this->data ) || empty( $this->data['range'] ) ) {
379 $this->response['success'] = false;
380 $this->response['message'] = __( 'Select dates', 'commercebird' );
381 $this->serve();
382 }
383 // Set the date range to last 30 days
384 $start_date = strtotime( $this->data['range'][0] );
385 $end_date = strtotime( $this->data['range'][1] );
386 $zoho_crm_access_token = get_option( 'cmbird_zoho_crm_access_token' );
387 if ( empty( $zoho_crm_access_token ) ) {
388 $this->response['success'] = false;
389 $this->response['message'] = __( 'Zoho CRM is not Connected.', 'commercebird' );
390 $this->serve();
391 }
392 $this->export_order( $start_date, $end_date );
393 $this->response['success'] = true;
394 $this->response['message'] = __( 'Exported', 'commercebird' );
395 $this->serve();
396 }
397
398 /**
399 * Retrieves an array of all tax rates from WooCommerce.
400 *
401 * @return array An array of all tax rates.
402 */
403 public function wc_taxes(): array {
404 $wc_tax_array = array();
405 $tax_classes = WC_Tax::get_tax_classes(); // Retrieve all tax classes.
406 if ( ! in_array( '', $tax_classes ) ) { // Make sure "Standard rate" (empty class name) is present.
407 array_unshift( $tax_classes, '' );
408 }
409
410 foreach ( $tax_classes as $tax_class ) { // For each tax class, get all rates.
411 $taxes = WC_Tax::get_rates_for_tax_class( $tax_class );
412
413 foreach ( $taxes as $key => $tax ) {
414 $taxarray = (array) $tax;
415 $taxarray['id'] = $key;
416 $wc_tax_array[] = $taxarray;
417 }
418 }
419
420 return $wc_tax_array;
421 }
422 /**
423 * Executes the wc_tax_collect function.
424 *
425 * This function verifies the current state, retrieves the taxes using the
426 * wc_taxes method, and serves the response.
427 */
428 public function wc_tax_collect(): void {
429 $this->verify();
430 $this->response = $this->wc_taxes();
431 $this->serve();
432 }
433
434 /**
435 * Retrieves the tax information.
436 *
437 * @return void
438 */
439 public function tax_get(): void {
440 $this->verify();
441 $this->response = array();
442 foreach ( $this->wc_taxes() as $tax ) {
443 $this->response['selectedTaxRates'][] = $tax['id'] . '^^' . get_option( 'cmbird_zoho_crm_tax_rate_' . $tax['id'] );
444 }
445 $this->serve();
446 }
447
448
449 /**
450 * Saves tax rates to the database.
451 */
452 public function tax_set(): void {
453 $this->verify( self::FORMS['tax'] );
454 $rates = array_filter( $this->data['selectedTaxRates'] );
455 try {
456 foreach ( $rates as $value ) {
457 $valarray = explode( '^^', $value );
458 update_option( 'cmbird_zoho_crm_tax_rate_' . $valarray[0], $valarray[1] );
459 }
460 $this->response = array( 'message' => 'Saved' );
461 } catch (Throwable $throwable) {
462 $this->errors = array( 'message' => $throwable->getMessage() );
463 }
464
465 $this->serve();
466 }
467
468 /**
469 * Resets the tax settings.
470 *
471 * This function verifies the tax settings and then deletes the options
472 * related to the tax rates
473 *
474 * @return void
475 */
476 public function tax_reset(): void {
477 $this->verify();
478 foreach ( $this->wc_taxes() as $tax ) {
479 delete_option( 'cmbird_zoho_crm_tax_rate_' . $tax['id'] );
480 }
481 $this->response = array( 'message' => 'Reset successfully!' );
482 $this->serve();
483 }
484
485 /**
486 * Retrieves the Zoho tax rates for the organization.
487 *
488 * This function verifies the request, retrieves the organization ID and URL
489 * from the options, constructs the URL for the API request, and executes
490 * the cURL call to fetch the tax rates. If the response contains the
491 * 'taxes' key, it sets the response to the tax rates. Otherwise, it sets
492 * the errors to a default message.
493 */
494 public function zoho_tax_rates_collect(): void {
495 $this->verify();
496 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
497 $url = $zoho_crm_url . 'crm/v7/org/taxes';
498 try {
499 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
500 $json = (array) $execute_curl_call_handle->execute_curl_call_get( $url );
501
502 // error_log('Data: ' . print_r($response, true));
503 if (
504 is_array( $json ) &&
505 isset( $json['org_taxes'] ) &&
506 isset( $json['org_taxes']->taxes )
507 ) {
508 $this->response = $json['org_taxes']->taxes;
509 } else {
510 $this->errors = array( 'message' => 'Something went wrong!' );
511 }
512 } catch (Throwable $throwable) {
513 $this->errors = array( 'message' => $throwable->getMessage() );
514 }
515 $this->serve();
516 }
517
518 /**
519 * Get Zoho CRM Sales Order layouts.
520 */
521 public function layouts_get(): void {
522 $this->verify();
523 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
524 $url = $zoho_crm_url . 'crm/v7/settings/layouts?module=Sales_Orders';
525 try {
526 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
527 $json = (array) $execute_curl_call_handle->execute_curl_call_get( $url );
528
529 if (
530 is_array( $json ) &&
531 isset( $json['layouts'] ) &&
532 is_array( $json['layouts'] )
533 ) {
534 // Format the layouts for frontend dropdown
535 $formatted_layouts = array();
536 foreach ( $json['layouts'] as $layout ) {
537 if ( isset( $layout->id ) && isset( $layout->name ) ) {
538 $formatted_layouts[] = array(
539 'id' => $layout->id,
540 'name' => $layout->name,
541 'api_name' => $layout->api_name ?? '',
542 'status' => $layout->status ?? '',
543 );
544 }
545 }
546 $this->response = $formatted_layouts;
547 } else {
548 $this->errors = array( 'message' => 'Unable to fetch layouts!' );
549 }
550 } catch (Throwable $throwable) {
551 $this->errors = array( 'message' => $throwable->getMessage() );
552 }
553 $this->serve();
554 }
555
556 /**
557 * Save selected Zoho CRM Sales Order layout.
558 */
559 public function layout_save(): void {
560 $this->verify( array( 'layout_id' ) );
561 if ( empty( $this->data['layout_id'] ) ) {
562 $this->errors = array( 'message' => 'Layout ID is required!' );
563 $this->serve();
564 return;
565 }
566
567 $updated = update_option( self::OPTIONS['zcrm_sales_order_layout'], sanitize_text_field( $this->data['layout_id'] ) );
568
569 if ( $updated ) {
570 $this->response = array( 'message' => 'Layout saved successfully!' );
571 } else {
572 $this->errors = array( 'message' => 'Failed to save layout!' );
573 }
574 $this->serve();
575 }
576
577 /**
578 * Get selected Zoho CRM Sales Order layout.
579 */
580 public function layout_get_selected(): void {
581 $this->verify();
582 $saved_layout = get_option( self::OPTIONS['zcrm_sales_order_layout'], '' );
583 $this->response = array( 'selected_layout' => $saved_layout );
584 $this->serve();
585 }
586
587 /**
588 * Get order settings for Zoho CRM sync.
589 */
590 public function order_settings_get(): void {
591 $this->verify();
592 $sync_as_deals = get_option( 'cmbird_zcrm_sync_as_deals', false );
593 $this->response = array( 'syncAsDeals' => (bool) $sync_as_deals );
594 $this->serve();
595 }
596
597 /**
598 * Save order settings for Zoho CRM sync.
599 */
600 public function order_settings_save(): void {
601 $this->verify( self::FORMS['order_settings'] );
602 try {
603 $sync_as_deals = isset( $this->data['syncAsDeals'] ) ? (bool) $this->data['syncAsDeals'] : false;
604 update_option( 'cmbird_zcrm_sync_as_deals', $sync_as_deals );
605 $this->response = array(
606 'success' => true,
607 'message' => __( 'Order settings saved successfully!', 'commercebird' )
608 );
609 } catch (\Throwable $throwable) {
610 $this->errors = array( 'message' => $throwable->getMessage() );
611 }
612 $this->serve();
613 }
614
615 /**
616 * Get Zoho CRM Deals fields.
617 */
618 public function deal_fields_get(): void {
619 $this->verify();
620 $refresh = isset( $this->data['refresh'] ) && $this->data['refresh'];
621
622 $fields = get_option( 'zcrm_deals_fields', array() );
623
624 // If refresh is requested or no fields exist, try to refresh them from Zoho
625 if ( $refresh || empty( $fields ) ) {
626 $sync = new ZohoCRMSync();
627 $fresh_fields = $sync->get_all_fields( 'Deals' );
628 if ( ! is_wp_error( $fresh_fields ) ) {
629 // Convert to simple key-value format, excluding hardcoded fields
630 $formatted_fields = array();
631 foreach ( $fresh_fields as $field_key => $field_label ) {
632 // Skip hardcoded fields (Deal_Name, Amount, Currency)
633 if ( in_array( $field_key, array( 'Deal_Name', 'Amount', 'Currency' ) ) ) {
634 continue;
635 }
636 $formatted_fields[ $field_key ] = $field_label;
637 }
638
639 // Add empty option at the beginning
640 $formatted_fields = array_merge( array( '' => 'Select a field...' ), $formatted_fields );
641
642 update_option( 'zcrm_deals_fields', $formatted_fields );
643 $fields = $formatted_fields;
644 }
645 } else {
646 // Ensure empty option exists
647 if ( ! isset( $fields[''] ) ) {
648 $fields = array_merge( array( '' => 'Select a field...' ), $fields );
649 }
650 }
651
652 $this->response = array( 'fields' => $fields );
653 $this->serve();
654 }
655
656 /**
657 * Get deal field mapping settings.
658 */
659 public function deal_field_mapping_get(): void {
660 $this->verify();
661 $mapping = get_option( 'cmbird_zcrm_deal_field_mapping', array() );
662 $this->response = array( 'dealFields' => $mapping );
663 $this->serve();
664 }
665
666 /**
667 * Save deal field mapping settings.
668 */
669 public function deal_field_mapping_save(): void {
670 $this->verify( self::FORMS['order_field_mapping'] );
671 try {
672 $deal_fields = isset( $this->data['dealFields'] ) ? $this->data['dealFields'] : array();
673 update_option( 'cmbird_zcrm_deal_field_mapping', $deal_fields );
674 $this->response = array(
675 'success' => true,
676 'message' => __( 'Deal field mapping saved successfully!', 'commercebird' )
677 );
678 } catch (\Throwable $throwable) {
679 $this->errors = array( 'message' => $throwable->getMessage() );
680 }
681 $this->serve();
682 }
683
684 /**
685 * Get WooCommerce order statuses.
686 */
687 public function wc_order_statuses_get(): void {
688 $this->verify();
689 $order_statuses = wc_get_order_statuses();
690 $formatted_statuses = array();
691
692 // Add empty option
693 $formatted_statuses[''] = 'Select order status...';
694
695 foreach ( $order_statuses as $status_key => $status_label ) {
696 $formatted_statuses[ $status_key ] = $status_label;
697 }
698
699 $this->response = array( 'statuses' => $formatted_statuses );
700 $this->serve();
701 }
702
703 /**
704 * Get Zoho Deal stages.
705 */
706 public function deal_stages_get(): void {
707 $this->verify();
708 $cached_stages = get_option( 'zcrm_deal_stages', array() );
709
710 // Check if we have cached stages and they're not empty
711 if ( ! empty( $cached_stages ) && is_array( $cached_stages ) && count( $cached_stages ) > 1 ) {
712 $this->response = array( 'stages' => $cached_stages );
713 $this->serve();
714 return;
715 }
716
717 try {
718 // Directly get the Stage field pick list values without calling get_all_fields
719 $zoho_crm_url = get_option( 'cmbird_zoho_crm_url' );
720 $url = $zoho_crm_url . 'crm/v7/settings/fields?module=Deals';
721 $execute_curl_call_handle = new \CMBIRD_API_Handler_Zoho();
722 $json = $execute_curl_call_handle->execute_curl_call_get( $url );
723
724 $stage_options = array( '' => 'Select stage...' );
725
726 if ( ! is_wp_error( $json ) && ! empty( $json->fields ) ) {
727 foreach ( $json->fields as $field ) {
728 if ( $field->api_name === 'Stage' && ! empty( $field->pick_list_values ) ) {
729 foreach ( $field->pick_list_values as $option ) {
730 $stage_options[ $option->actual_value ] = $option->display_value;
731 }
732 break;
733 }
734 }
735 }
736
737 // Cache the stages
738 update_option( 'zcrm_deal_stages', $stage_options );
739 $this->response = array( 'stages' => $stage_options );
740 } catch (\Exception $e) {
741 $this->response = array( 'stages' => array( '' => 'Select stage...' ) );
742 }
743
744 $this->serve();
745 }
746
747 /**
748 * Get order status to stage mapping settings.
749 */
750 public function order_status_stage_mapping_get(): void {
751 $this->verify();
752 $mapping = get_option( 'cmbird_zcrm_order_status_stage_mapping', array() );
753 $this->response = array( 'mapping' => $mapping );
754 $this->serve();
755 }
756
757 /**
758 * Save order status to stage mapping settings.
759 */
760 public function order_status_stage_mapping_save(): void {
761 $this->verify( self::FORMS['order_status_stage_mapping'] );
762 try {
763 $mapping = isset( $this->data['mapping'] ) ? $this->data['mapping'] : array();
764 update_option( 'cmbird_zcrm_order_status_stage_mapping', $mapping );
765 $this->response = array(
766 'success' => true,
767 'message' => __( 'Order status to stage mapping saved successfully!', 'commercebird' )
768 );
769 } catch (\Throwable $throwable) {
770 $this->errors = array( 'message' => $throwable->getMessage() );
771 }
772 $this->serve();
773 }
774 }
775