PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.8.2
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.8.2
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 / includes / classes / zoho-inventory / class-product.php
commercebird / includes / classes / zoho-inventory Last commit date
class-cmbird-categories-zi.php 4 months ago class-cmbird-image-zi.php 5 months ago class-import-items.php 3 months ago class-import-price-list.php 9 months ago class-multi-currency.php 7 months ago class-order-sync.php 3 months ago class-product.php 4 months ago class-users-contact.php 5 months ago index.php 1 year ago
class-product.php
1149 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * Handles export and synchronization of WooCommerce products with Zoho Inventory.
9 *
10 * Provides methods for syncing simple, variable, and bundle products, as well as updating product data between WooCommerce and Zoho Inventory.
11 */
12 class CMBIRD_Products_ZI_Export {
13
14 /**
15 * Configuration options for Zoho Inventory and plugin settings.
16 *
17 * @var array
18 */
19 private $config;
20
21 /**
22 * Constructor for CMBIRD_Products_ZI_Export.
23 *
24 * Initializes configuration options for Zoho Inventory and plugin settings.
25 */
26 public function __construct() {
27 $this->config = array(
28 'ProductZI' => array(
29 'OID' => get_option( 'cmbird_zoho_inventory_oid' ),
30 'APIURL' => get_option( 'cmbird_zoho_inventory_url' ),
31 'Domain' => get_option( 'cmbird_zoho_inventory_domain' ),
32 ),
33 'Settings' => array(
34 'disable_description' => get_option( 'cmbird_zoho_disable_description_sync_status' ),
35 'disable_name' => get_option( 'cmbird_zoho_disable_name_sync_status' ),
36 'disable_price' => get_option( 'cmbird_zoho_disable_price_sync_status' ),
37 'disable_stock' => get_option( 'cmbird_zoho_disable_stock_sync_status' ),
38 'enable_accounting_stock' => get_option( 'cmbird_zoho_enable_accounting_stock_status' ),
39 'enable_location_stock' => get_option( 'cmbird_zoho_enable_locationstock_status' ),
40 'zoho_location_id' => get_option( 'cmbird_zoho_location_id_status' ),
41 'disable_image' => get_option( 'cmbird_zoho_disable_image_sync_status' ),
42 ),
43 );
44 }
45
46 /**
47 * Prepares products for Zoho Inventory sync.
48 *
49 * @param array $product_ids List of WooCommerce product IDs to sync.
50 *
51 * @return void
52 */
53 public function cmbird_zi_products_prepare_sync( $product_ids ) {
54 $rate_limit = get_option( 'cmbird_zoho_rate_limit_exceeded' );
55 if ( is_array( $product_ids ) ) {
56 // schedule the unsynced products for tomorrow via action scheduler.
57 if ( $rate_limit ) {
58 $timestamp = strtotime( 'tomorrow' );
59 // set the zoho rate limit option exceeded to false tomorrow if not scheduled yet.
60 if ( ! wp_next_scheduled( 'cmbird_common' ) ) {
61 wp_schedule_single_event( $timestamp, 'cmbird_common' );
62 }
63 // Get all scheduled actions with the specific hook name.
64 $action_ids = as_get_scheduled_actions(
65 array(
66 'hook' => 'sync_zi_product_cron',
67 'status' => ActionScheduler_Store::STATUS_PENDING,
68 'per_page' => -1,
69 )
70 );
71 // Loop through each action and reschedule it.
72 if ( ! empty( $action_ids ) ) {
73 foreach ( $action_ids as $action_id ) {
74 // Fetch the action by ID.
75 $action = as_get_scheduled_action( $action_id );
76 // If the action is valid and exists, reschedule it.
77 if ( $action ) {
78 // Reschedule the action to run tomorrow.
79 as_schedule_single_action( $timestamp, 'sync_zi_product_cron', $action->get_args(), 'ActionScheduler' );
80 // Cancel the old action to avoid duplicates.
81 as_unschedule_action( 'sync_zi_product_cron', $action->get_args(), $action_id );
82 }
83 }
84 }
85 } else {
86 foreach ( $product_ids as $product_id ) {
87 $this->cmbird_zi_product_sync( $product_id );
88 }
89 }
90 }
91 }
92
93 /**
94 * Starting point to sync Product to Zoho Inventory. If product type is other than simple, pass the product_id to different function
95 *
96 * @param integer $post_id - Product id.
97 * @return string|void - item_id of Zoho Inventory.
98 */
99 public function cmbird_zi_product_sync( $post_id ) {
100
101 // $fd = fopen( __DIR__ . '/product_class.txt', 'a+' );
102 $item_id = '';
103
104 if ( is_array( $post_id ) ) {
105 $product_id = intval( $post_id['0'] );
106 $post_id = $product_id;
107 }
108
109 if ( 'draft' === get_post_status( $post_id ) ) {
110 return;
111 }
112
113 $product = wc_get_product( $post_id );
114 if ( ! $product ) {
115 return;
116 }
117 if ( $product->is_type( 'bundle' ) ) {
118 // fwrite($fd,PHP_EOL.'Inside bundle: ');
119 $item_id = $this->zi_bundle_product_to_zoho( $post_id );
120 return $item_id;
121 } elseif ( $product->is_type( 'variable' ) ) {
122 // fwrite( $fd, PHP_EOL . 'Inside variable: ' );
123 $this->cmbird_zi_variation_product_to_zoho( $post_id );
124 } elseif ( $product->is_type( 'variation' ) ) {
125 return;
126 } else {
127 // fwrite($fd,PHP_EOL.'Inside Regular: ');
128 // Simple product.
129 $rate = $product->get_regular_price();
130 $pre_name = $product->get_name();
131 $name = sanitize_text_field( $pre_name );
132 // remove any remaining emoji/4‑byte characters that sanitize_text_field doesn't strip.
133 $name = preg_replace( '/[\x{1F300}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u', '', $name );
134 $stock_quantity = $product->get_stock_quantity();
135 $in_stock = ( $stock_quantity > 0 ) ? $stock_quantity : 0;
136 // fwrite($fd,PHP_EOL.'$product->get_stock_quantity() : '.$product->get_stock_quantity());
137 $in_stock_rate = $in_stock * (int) $rate;
138 $sku = $product->get_sku();
139 $tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class() );
140 $tax_id_key = '';
141 foreach ( $tax_rates as $tax_key => $tax_value ) {
142 $tax_id_key = $tax_key;
143 break;
144 }
145 $tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
146 $tax_id = explode( '##', $tax_option )[0];
147
148 $zi_status = ( 'publish' === get_post_status( $post_id ) ) ? 'active' : 'inactive';
149 // request data for adding/updating value to zoho.
150 $zi_disable_item_name_sync = get_option( 'cmbird_zoho_disable_name_sync_status' );
151 $zoho_item_id = get_post_meta( $post_id, 'zi_item_id', true );
152
153 $zidata = '';
154 if ( empty( $zoho_item_id ) || 'true' !== $zi_disable_item_name_sync ) {
155 $zidata .= '"name" : "' . $name . '",';
156 }
157
158 if ( $product->is_virtual( 'yes' ) ) {
159 $zidata .= '"product_type" : "service",';
160 $zidata .= '"item_type" : "sales",';
161 } else {
162 $zidata .= '"product_type" : "goods",';
163 $zidata .= '"item_type" : "inventory",';
164 }
165
166 $zidata .= '"sku" : "' . $sku . '",';
167 // $zidata .= '"unit" : "pcs",';
168 $zidata .= '"status" : "' . $zi_status . '",';
169 // Initial stock update only if item sync for first time.
170 if ( empty( $zoho_item_id ) ) {
171 $zidata .= '"initial_stock" : ' . $in_stock . ',';
172 $zidata .= '"initial_stock_rate" : "' . $in_stock_rate . '",';
173 }
174 $zidata .= '"rate" : "' . $rate . '",';
175 $domain = isset( $this->config['ProductZI']['Domain'] ) ? $this->config['ProductZI']['Domain'] : '';
176 if ( 'in' !== strtolower( $domain ) && $tax_id ) {
177 $zidata .= '"tax_id" : "' . $tax_id . '",';
178 } elseif ( 'in' === strtolower( $domain ) ) {
179 // For India domain, find GST and IGST tax IDs dynamically, but only if tax rate > 0%.
180 $intra_tax_id = ''; // For SGST&CGST (intra-state).
181 $inter_tax_id = ''; // For IGST (inter-state).
182 $has_non_zero_rate = false;
183 $tax_rate_percent = 0;
184
185 // First, check if we have any non-zero tax rates and get the rate percentage.
186 foreach ( $tax_rates as $tax_key => $tax_value ) {
187 if ( isset( $tax_value['rate'] ) && floatval( $tax_value['rate'] ) > 0 ) {
188 $has_non_zero_rate = true;
189 $tax_rate_percent = floatval( $tax_value['rate'] );
190 break; // Use the first non-zero rate found.
191 }
192 }
193
194 // If we have non-zero rates, find the corresponding Zoho tax IDs for the same rate.
195 if ( $has_non_zero_rate ) {
196 // Look for tax options that match this rate percentage.
197 global $wpdb;
198 $all_tax_options = $wpdb->get_results(
199 "SELECT option_name, option_value
200 FROM {$wpdb->options}
201 WHERE option_name LIKE 'cmbird_zoho_inventory_tax_rate_%'"
202 );
203
204 foreach ( $all_tax_options as $option ) {
205 $option_str = $option->option_value;
206 // Example: "2946885000000035251##SGST&CGST12##tax_group##12".
207 $parts = explode( '##', $option_str );
208 if ( isset( $parts[1] ) && isset( $parts[3] ) ) {
209 $option_rate = floatval( $parts[3] );
210
211 // Only consider options with the same tax rate percentage.
212 if ( $option_rate === $tax_rate_percent ) {
213 // Check for intra-state tax (SGST&CGST combined):
214 // Look for "GST" without "IGST", or explicit "SGST" and "CGST" mentions.
215 if ( ( false !== stripos( $parts[1], 'GST' ) && false === stripos( $parts[1], 'IGST' ) ) ||
216 ( false !== stripos( $parts[1], 'SGST' ) && false !== stripos( $parts[1], 'CGST' ) ) ) {
217 $intra_tax_id = $parts[0];
218 }
219 // Check for IGST (inter-state) - contains "IGST".
220 if ( false !== stripos( $parts[1], 'IGST' ) ) {
221 $inter_tax_id = $parts[0];
222 }
223 }
224 }
225 }
226
227 // Add both intra and inter tax preferences if we found the tax IDs.
228 $item_tax_preferences = array();
229 if ( ! empty( $intra_tax_id ) ) {
230 $item_tax_preferences[] = array(
231 'tax_id' => $intra_tax_id,
232 'tax_specification' => 'intra',
233 );
234 }
235 if ( ! empty( $inter_tax_id ) ) {
236 $item_tax_preferences[] = array(
237 'tax_id' => $inter_tax_id,
238 'tax_specification' => 'inter',
239 );
240 }
241 if ( ! empty( $item_tax_preferences ) ) {
242 $zidata .= '"item_tax_preferences" : ' . wp_json_encode( $item_tax_preferences ) . ',';
243 }
244 } else {
245 $zidata .= '"is_taxable" : false,';
246 $zidata .= '"tax_exemption_code" : "exempt",';
247 }
248 }
249 // $zidata .= '"image_name" : "' . $image . '",';
250
251 // Get cost_price from meta data.
252 $cost_price = $product->get_meta( '_cogs_total_value' );
253 if ( ! empty( $cost_price ) && is_numeric( $cost_price ) ) {
254 $zidata .= '"purchase_rate" : "' . $cost_price . '",';
255 }
256
257 $dimensions = (object) array();
258 $dimensions->length = $product->get_length();
259 $dimensions->width = $product->get_width();
260 $dimensions->height = $product->get_height();
261 $dimensions->weight = $product->get_weight();
262 $zidata .= '"package_details" : ' . wp_json_encode( $dimensions ) . '';
263
264 // Send category only if category ID available.
265 $zi_category_id = $this->cmbird_zi_get_prod_updated_category( $post_id );
266 if ( $zi_category_id ) {
267 $zidata .= ',"category_id" : "' . $zi_category_id . '"';
268 }
269
270 // save the request body for debugging purposes.
271 $zi_body_request = '{' . $zidata . '}';
272 update_post_meta( $post_id, 'zi_product_body_request', $zi_body_request );
273
274 // $zidata .= '"image_type" : "' . $ext . '"';
275 if ( ! empty( $zoho_item_id ) && ctype_digit( $zoho_item_id ) ) {
276 // fwrite($fd,PHP_EOL.'Inside Update: ');
277 $this->cmbird_zi_product_put( $post_id, $zoho_item_id, $zidata );
278 } else {
279 // fwrite($fd,PHP_EOL.'Inside Create ');
280 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
281 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
282
283 $data = array(
284 'JSONString' => '{' . $zidata . '}',
285 'organization_id' => $zoho_inventory_oid,
286 );
287 $url = $zoho_inventory_url . 'inventory/v1/items';
288
289 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
290 $json = $execute_curl_call_handle->execute_curl_call_post( $url, $data );
291
292 $errmsg = $json->message;
293 update_post_meta( $post_id, 'zi_product_errmsg', $errmsg );
294
295 $code = $json->code;
296 // fwrite($fd,PHP_EOL.'JSON Response : '.print_r($json,true));
297 // Check if the the given sku has product at zoho inventory.
298 if ( '1001' === $code || 1001 === $code ) {
299 // fwrite($fd,PHP_EOL.'Inside SKU Check');
300 $sku_check = str_replace( ' ', '+', $sku );
301 $url = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $sku_check . '&organization_id=' . $zoho_inventory_oid;
302 $get_request = $execute_curl_call_handle->execute_curl_call_get( $url );
303
304 if ( '0' === $get_request->code || 0 === $get_request->code ) {
305 $item_id = '';
306 $matching_item = null;
307
308 foreach ( $get_request->items as $zoho_item ) {
309 if ( $zoho_item->sku === $sku ) {
310 // SKU matched.
311 $matching_item = $zoho_item;
312 break;
313 }
314 }
315
316 // If SKU check didn't find a match, perform name check.
317 if ( ! $matching_item ) {
318 $item_name_check = str_replace( ' ', '+', $name );
319 $url = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $item_name_check . '&organization_id=' . $zoho_inventory_oid;
320 $get_request = $execute_curl_call_handle->execute_curl_call_get( $url );
321
322 if ( '0' === $get_request->code || 0 === $get_request->code ) {
323 foreach ( $get_request->items as $zoho_item ) {
324 if ( $zoho_item->name === $name ) {
325 // Name matched.
326 $matching_item = $zoho_item;
327 break;
328 }
329 }
330 }
331 }
332
333 if ( $matching_item ) {
334 $code = 0;
335 $json->item = $matching_item;
336 update_post_meta( $post_id, 'zi_product_errmsg', 'Product "' . $matching_item->name . '" is mapped successfully with Zoho' );
337 }
338 }
339 }
340 // fwrite($fd,PHP_EOL.'After SKU Check : code '.$code);
341 if ( '0' === $code || 0 === $code ) {
342 foreach ( $json->item as $key => $value ) {
343 if ( 'item_id' === $key ) {
344 $item_id = $value;
345 }
346 if ( 'purchase_account_id' === $key ) {
347 $purchase_account_id = $value;
348 }
349 if ( 'account_id' === $key ) {
350 $account_id = $value;
351 }
352 if ( 'account_name' === $key ) {
353 $account_name = $value;
354 }
355 if ( 'inventory_account_id' === $key ) {
356 $inventory_account_id = $value;
357 }
358 if ( 'category_id' === $key && ! empty( $value ) ) {
359 update_post_meta( $post_id, 'zi_category_id', $value );
360 }
361 }
362 update_post_meta( $post_id, 'zi_item_id', $item_id );
363 update_post_meta( $post_id, 'zi_purchase_account_id', $purchase_account_id );
364 update_post_meta( $post_id, 'zi_account_id', $account_id );
365 update_post_meta( $post_id, 'zi_account_name', $account_name );
366 update_post_meta( $post_id, 'zi_inventory_account_id', $inventory_account_id );
367 }
368 }
369 }
370 return $item_id;
371 }
372
373 /**
374 * Function to update zoho item if already exists.
375 *
376 * @param number $proid - product number.
377 * @param number $item_id - zoho item id.
378 * @param mixed $pdt3 - Zoho item object for post request.
379 * @return string
380 */
381 public function cmbird_zi_product_put( $proid, $item_id, $pdt3 = '' ) {
382 // $fd = fopen(__DIR__.'/product_class.txt','a+');
383 // fwrite($fd,PHP_EOL.'Inside update : ');
384 $errmsg = '';
385 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
386 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
387
388 $url = $zoho_inventory_url . 'inventory/v1/items/' . $item_id;
389 // fwrite($fd,PHP_EOL.'JSON Data : '.'{' . $pdt3 . '}');
390 $data = array(
391 'JSONString' => '{' . $pdt3 . '}',
392 'organization_id' => $zoho_inventory_oid,
393 );
394
395 // if pdt3 is empty then do GET call, else do PUT call.
396 if ( empty( $pdt3 ) ) {
397 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
398 $json = $execute_curl_call_handle->execute_curl_call_get( $url );
399 $code = $json->code;
400 $errmsg = $json->message;
401 } else {
402 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
403 $json = $execute_curl_call_handle->execute_curl_call_put( $url, $data );
404 $code = $json->code;
405 $errmsg = $json->message;
406 }
407
408 if ( 0 === $code || '0' === $code ) {
409 $product = wc_get_product( $proid );
410 // if type is not simple then return.
411 if ( ! $product->is_type( 'simple' ) ) {
412 return $errmsg;
413 }
414 $item = $json->item;
415 // update price.
416 $zi_disable_itemprice_sync = $this->config['Settings']['disable_price'];
417 if ( ! empty( $item->rate ) && ! $zi_disable_itemprice_sync ) {
418 $product->set_regular_price( $item->rate );
419 $sale_price = $product->get_sale_price();
420 if ( empty( $sale_price ) ) {
421 $product->set_price( $item->rate );
422 }
423 }
424 // To check status of stock sync option.
425 $zi_disable_stock_sync = $this->config['Settings']['disable_stock'];
426 if ( ! $zi_disable_stock_sync && isset( $item->available_for_sale_stock ) ) {
427 $stock = '';
428 // Update stock.
429 $accounting_stock = $this->config['Settings']['enable_accounting_stock'];
430 // Sync from specific location check.
431 $zi_enable_locationstock = $this->config['Settings']['enable_location_stock'];
432 if ( $zi_enable_locationstock && isset( $item->locations ) ) {
433 $locations = $item->locations;
434 $location_id = $this->config['Settings']['zoho_location_id'];
435 foreach ( $locations as $location ) {
436 if ( $location->location_id === $location_id ) {
437 if ( $accounting_stock ) {
438 $stock = $location->location_available_for_sale_stock;
439 } else {
440 $stock = $location->location_actual_available_for_sale_stock;
441 }
442 }
443 }
444 } elseif ( $accounting_stock ) {
445 $stock = $item->available_for_sale_stock;
446 } else {
447 $stock = $item->actual_available_for_sale_stock;
448 }
449
450 if ( is_numeric( $stock ) ) {
451 $product->set_manage_stock( true );
452 $product->set_stock_quantity( number_format( $stock, 0, '.', '' ) );
453 if ( $stock > 0 ) {
454 $product->set_stock_status( 'instock' );
455 } else {
456 $backorder_status = $product->backorders_allowed();
457 $status = ( 'yes' === $backorder_status ) ? 'onbackorder' : 'outofstock';
458 $product->set_stock_status( $status );
459 }
460 }
461 }
462 $product->save();
463 update_post_meta( $proid, 'zi_product_errmsg', $errmsg );
464 } else {
465 update_post_meta( $proid, 'zi_product_errmsg', $errmsg );
466 }
467 // fclose($fd);
468 return $errmsg;
469 }
470
471 protected function cmbird_zi_bundle_product_data_zoho( $bundle_id ) {
472 // $fd = fopen(__DIR__ . '/cmbird_zi_bundle_product_data_zoho.txt', 'w+');
473
474 $bundled_product = new WC_Product_Bundle( $bundle_id );
475 $bundle_childs = $bundled_product->get_bundled_items();
476
477 // Allow Bundle Product.
478 $child_array = array();
479 foreach ( $bundle_childs as $child ) {
480 $parent_product = $child->product;
481 $child_id = $child->product_id;
482 $meta_value = WC_PB_DB::get_bundled_item_meta( $child_id, 'quantity_max' );
483 $zi_child_ids = array(); // Array to store zi_child_ids.
484
485 if ( $parent_product->is_type( 'variable' ) ) {
486 $meta_data = WC_PB_DB::get_bundled_item_meta( $child_id, 'allowed_variations' );
487
488 foreach ( $meta_data as $meta ) {
489 if ( $meta->meta_key === 'allowed_variations' ) {
490 $serialized_value = $meta->meta_value;
491 $deserialized_value = maybe_unserialize( $serialized_value );
492
493 if ( is_array( $deserialized_value ) ) {
494 foreach ( $deserialized_value as $variation_id ) {
495 $zi_variation_id = get_post_meta( $variation_id, 'zi_item_id', true );
496 if ( $zi_variation_id ) {
497 $zi_child_ids[] = $zi_variation_id;
498 }
499 }
500 }
501 }
502 }
503 } else {
504 $zi_child_id = get_post_meta( $child_id, 'zi_item_id', true );
505 if ( $zi_child_id ) {
506 $zi_child_ids[] = $zi_child_id;
507 } else {
508 // sync the child product to zoho inventory if not synced already and get the item id.
509 $zi_child_id = $this->cmbird_zi_product_sync( $child_id );
510 $zi_child_ids[] = $zi_child_id;
511 }
512 }
513
514 foreach ( $zi_child_ids as $zi_child_id ) {
515 $json_child = (object) array(
516 'item_id' => $zi_child_id,
517 'quantity' => $meta_value[0]->meta_value,
518 );
519 array_push( $child_array, $json_child );
520 }
521 }
522 $child_items = $child_array;
523
524 // fclose($fd);
525 return $child_items;
526 }
527
528 /**
529 * Create a bundle product on Zoho Inventory.
530 *
531 * @param int $post_id The ID of the product to be created on Zoho Inventory.
532 *
533 * @return string The item ID of the created bundle product on Zoho Inventory.
534 */
535 protected function zi_bundle_product_to_zoho( $post_id ) {
536 // $fd = fopen(__DIR__ . '/zi_bundle_product_to_zoho.txt', 'w+');
537
538 $item = wc_get_product( $post_id );
539 if ( $item->is_type( 'bundle' ) ) {
540
541 $child_items = $this->cmbird_zi_bundle_product_data_zoho( $post_id );
542 }
543
544 $price_r = $item->get_regular_price();
545 $price_s = $item->get_sale_price();
546
547 if ( $price_s ) {
548 $rate = round( $price_s, 2 );
549 } else {
550 $rate = round( $price_r, 2 );
551 }
552 // $rate = 500;
553 // $proid = $item->ID;
554 $pre_name = $item->get_name();
555 $name = sanitize_text_field( $pre_name );
556 // remove any remaining 4‑byte Unicode characters (covers all emojis).
557 $name = preg_replace( '/[\x{10000}-\x{10FFFF}]/u', '', $name );
558 $sku = $item->get_sku();
559 $stock_quantity = $item->get_stock_quantity();
560 $in_stock = ( $stock_quantity > 0 ) ? $stock_quantity : 0;
561 $in_stock_rate = ( $in_stock * $rate );
562
563 $product_type = 'goods';
564 $item_type = 'inventory';
565 $tax_rates = WC_Tax::get_base_tax_rates( $item->get_tax_class() );
566 $tax_id_key = '';
567 foreach ( $tax_rates as $tax_key => $tax_value ) {
568 $tax_id_key = $tax_key;
569 break;
570 }
571 $tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
572 $tax_id = explode( '##', $tax_option )[0];
573 if ( ! empty( $tax_rates ) ) {
574 $tax_rate = reset( $tax_rates );
575 }
576
577 $pdt1 = '"name" : "' . $name . '","mapped_items":' . wp_json_encode( $child_items ) . ', "product_type" : "' . $product_type . '","tax_id" : "' . $tax_id . '","rate" : "' . $rate . '","sku" : "' . $sku . '","item_type" : "' . $item_type . '"';
578 // If zoho category id is not mapped to product, then assign mapped product category with zoho.
579
580 // $zi_category_id = $this->cmbird_zi_get_prod_updated_category($post_id);
581 // if ($zi_category_id) {.
582 // $pdt1 .= ',"category_id" : "' . $zi_category_id . '"';
583 // }.
584
585 $zoho_item_id = get_post_meta( $post_id, 'zi_item_id', true );
586 if ( empty( $zoho_item_id ) ) {
587 $pdt1 .= ',"initial_stock" : ' . $in_stock . ',';
588 $pdt1 .= '"initial_stock_rate" : "' . $in_stock_rate . '"';
589 }
590
591 // Dimensions data append to update call.
592 $dimensions = (object) array();
593 $dimensions->length = $item->get_length();
594 $dimensions->width = $item->get_width();
595 $dimensions->height = $item->get_height();
596 $dimensions->weight = $item->get_weight();
597 $pdt1 .= ',"package_details" : ' . wp_json_encode( $dimensions ) . ',';
598
599 // save the request body for debugging purposes.
600 $zi_body_request = '{' . $pdt1 . '}';
601 update_post_meta( $post_id, 'zi_product_body_request', $zi_body_request );
602
603 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
604 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
605
606 if ( $zoho_item_id && ctype_digit( $zoho_item_id ) ) {
607 $url_p = $zoho_inventory_url . 'inventory/v1/compositeitems/' . $zoho_item_id;
608 } else {
609 $url_p = $zoho_inventory_url . 'inventory/v1/compositeitems';
610 }
611
612 $data_p = array(
613 'JSONString' => '{' . $pdt1 . '}',
614 'organization_id' => $zoho_inventory_oid,
615 );
616
617 // fwrite($fd, PHP_EOL . 'data_p : ' . print_r($data_p, true));
618
619 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
620
621 if ( $zoho_item_id && ctype_digit( $zoho_item_id ) ) {
622
623 $json = $execute_curl_call_handle->execute_curl_call_put( $url_p, $data_p );
624 $errmsg = $json->message;
625 update_post_meta( $post_id, 'zi_product_errmsg', $errmsg );
626 } else {
627
628 $json = $execute_curl_call_handle->execute_curl_call_post( $url_p, $data_p );
629
630 $code = $json->code;
631 $errmsg = $json->message;
632 update_post_meta( $post_id, 'zi_product_errmsg', $errmsg );
633 if ( '1001' == $code || 1001 == $code ) {
634 $sku_check = str_replace( ' ', '+', $sku );
635 $url = $zoho_inventory_url . 'inventory/v1/compositeitems/?search_text=' . $sku_check . '&organization_id=' . $zoho_inventory_oid;
636 $get_request = $execute_curl_call_handle->execute_curl_call_get( $url );
637 if ( '0' === $get_request->code || 0 === $get_request->code ) {
638 $item_id = '';
639 foreach ( $get_request->composite_items as $zoho_composite ) {
640 // fwrite($fd,PHP_EOL.'ZOHO Item : '.print_r($zoho_item, true));
641 if ( $zoho_composite->sku === $sku ) {
642 $code = 0;
643 $json->composite_item = $zoho_composite;
644 update_post_meta( $post_id, 'zi_product_errmsg', 'Product "' . $zoho_composite->name . '" is mapped successfully with Zoho' );
645 break;
646 }
647 }
648 }
649 }
650 if ( '0' === $code || 0 === $code ) {
651 foreach ( $json->composite_item as $key => $value ) {
652
653 if ( 'composite_item_id' === $key ) {
654 $item_id = $value;
655 }
656 if ( 'purchase_account_id' === $key ) {
657 $purchase_account_id = $value;
658 }
659 if ( 'account_id' === $key ) {
660 $account_id = $value;
661 }
662 if ( 'account_name' === $key ) {
663 $account_name = $value;
664 }
665 if ( 'inventory_account_id' === $key ) {
666 $inventory_account_id = $value;
667 }
668 if ( 'category_id' === $key && ! empty( $value ) ) {
669 update_post_meta( $post_id, 'zi_category_id', $value );
670 }
671 }
672 update_post_meta( $post_id, 'zi_item_id', $item_id );
673 update_post_meta( $post_id, 'zi_purchase_account_id', $purchase_account_id );
674 update_post_meta( $post_id, 'zi_account_id', $account_id );
675 update_post_meta( $post_id, 'zi_account_name', $account_name );
676 update_post_meta( $post_id, 'zi_inventory_account_id', $inventory_account_id );
677 }
678 }
679 // fclose($fd);
680 return $item_id;
681 }
682
683 /**
684 * Pushes the variation product to Zoho Inventory.
685 *
686 * @param int $post_id The post ID of the product.
687 *
688 * @return void
689 */
690 protected function cmbird_zi_variation_product_to_zoho( $post_id ) {
691 // $fd = fopen( __DIR__ . '/cmbird_zi_variation_product_to_zoho.txt', 'a+' );
692
693 $product = wc_get_product( $post_id );
694 if ( ! $product ) {
695 return;
696 }
697 $zoho_group_id = $product->get_meta( 'zi_item_id', true );
698 if ( ! empty( $zoho_group_id ) ) {
699 return;
700 }
701
702 // Check if the the given sku has product in zoho inventory.
703 $sku = $product->get_sku();
704 // use the product name if sku is empty, as Zoho search can match on name as well.
705 if ( empty( $sku ) ) {
706 $sku = $product->get_name();
707 }
708 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
709 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
710 $sku_check = str_replace( ' ', '+', $sku );
711 $url = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $sku_check . '&filter_by=Status.Active&organization_id=' . $zoho_inventory_oid;
712 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
713 $get_request = $execute_curl_call_handle->execute_curl_call_get( $url );
714
715 // Guard against transport/errors and ensure Zoho returned a successful response with items.
716 if ( ! is_wp_error( $get_request ) && 0 === (int) $get_request->code && isset( $get_request->items ) && is_array( $get_request->items ) ) {
717 // build lookups keyed by SKU for quick matching.
718 // A Zoho item may carry the SKU in either the `sku` field or the `name` field.
719 $zoho_items_by_sku = array();
720 $zoho_group_by_sku = array();
721 foreach ( $get_request->items as $zi_item ) {
722 // Collect candidate SKU values: prefer explicit sku field, fall back to name.
723 $candidates = array();
724 if ( ! empty( $zi_item->sku ) ) {
725 $candidates[] = $zi_item->sku;
726 }
727 if ( ! empty( $zi_item->name ) ) {
728 $candidates[] = $zi_item->name;
729 }
730 foreach ( $candidates as $candidate ) {
731 $sku_lc = strtolower( trim( $candidate ) );
732 $zoho_items_by_sku[ $sku_lc ] = $zi_item->item_id;
733 if ( ! empty( $zi_item->group_id ) ) {
734 $zoho_group_by_sku[ $sku_lc ] = (int) $zi_item->group_id;
735 }
736 }
737 }
738 // Match each Woo variation's SKU to a Zoho item. Save item_id on the variation and,
739 // from the first match, capture the group_id to save on the parent product.
740 $child_ids = $product->get_children();
741 $matched_group_id = null;
742 foreach ( $child_ids as $child_id ) {
743 $variation = wc_get_product( $child_id );
744 if ( ! $variation ) {
745 continue;
746 }
747 $var_sku_lc = strtolower( trim( (string) $variation->get_sku() ) );
748 if ( $var_sku_lc && isset( $zoho_items_by_sku[ $var_sku_lc ] ) ) {
749 $variation->update_meta_data( 'zi_item_id', $zoho_items_by_sku[ $var_sku_lc ] );
750 $variation->save();
751 // Capture group_id from the first matched item to store on the parent.
752 if ( null === $matched_group_id && isset( $zoho_group_by_sku[ $var_sku_lc ] ) ) {
753 $matched_group_id = $zoho_group_by_sku[ $var_sku_lc ];
754 }
755 }
756 }
757 if ( $matched_group_id ) {
758 $product->update_meta_data( 'zi_item_id', $matched_group_id );
759 $product->update_meta_data( 'zi_product_errmsg', 'Variation products are mapped with Zoho item group successfully' );
760 $product->save();
761 return;
762 }
763 }
764
765 $pre_name = $product->get_title();
766 $name = sanitize_text_field( $pre_name );
767 // remove any 4‑byte Unicode characters (covers all emojis).
768 $name = preg_replace( '/[\x{10000}-\x{10FFFF}]/u', '', $name );
769
770 $tax_rates = WC_Tax::get_base_tax_rates( $product->get_tax_class() );
771 $tax_id_key = '';
772 foreach ( $tax_rates as $tax_key => $tax_value ) {
773 $tax_id_key = $tax_key;
774 break;
775 }
776 $tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
777 $tax_id = explode( '##', $tax_option )[0];
778 $zi_category_id = $this->cmbird_zi_get_prod_updated_category( $post_id );
779
780 $zidata = '"group_name" : "' . $name . '", "tax_id" : "' . $tax_id . '","category_id" : "' . $zi_category_id . '",';
781
782 // attributes.
783 $attributes = $product->get_attributes();
784 // fwrite($fd, PHP_EOL . 'ATTRIBUTES : ' . print_r($attributes, true));
785
786 $attribute_name1 = '';
787 $attribute_name2 = '';
788 $attribute_name3 = '';
789 foreach ( $attributes as $attribute ) {
790 if ( ! empty( $attribute ) ) {
791 $attrname1 = $attribute->get_name();
792 $attrname = sanitize_text_field( $attrname1 );
793 if ( ! empty( $attrname ) && $attribute['variation'] ) {
794 if ( empty( $attribute_name1 ) ) {
795 $attribute_name1 = $attrname;
796 } elseif ( empty( $attribute_name2 ) ) {
797 $attribute_name2 = $attrname;
798 } elseif ( empty( $attribute_name3 ) ) {
799 $attribute_name3 = $attrname;
800 }
801 }
802 }
803 }
804 if ( ! empty( $attribute_name1 ) ) {
805 $zidata .= '"attribute_name1": "' . $attribute_name1 . '",';
806 }
807 if ( ! empty( $attribute_name2 ) ) {
808 $zidata .= '"attribute_name2": "' . $attribute_name2 . '",';
809 }
810 if ( ! empty( $attribute_name3 ) ) {
811 $zidata .= '"attribute_name3": "' . $attribute_name3 . '",';
812 }
813
814 $available_variations = $product->get_children();
815 // If there is attributes variations then append that data to server.
816 $items = array();
817 if ( count( $available_variations ) > 0 ) {
818 foreach ( $available_variations as $child_id ) {
819
820 $product_variable = wc_get_product( $child_id );
821 $items[] = $this->cmbird_zi_variants_products( $product_variable, $child_id, $attribute_name1, $attribute_name2, $attribute_name3 );
822 }
823 }
824
825 // get category id.
826 // $zi_category_id = $this->cmbird_zi_get_prod_updated_category($post_id);
827 // if ($zi_category_id) {.
828 // $zidata .= '"category_id" : "' . $zi_category_id . '",';
829 // }.
830
831 $zidata .= '"items" :[' . implode( ',', $items ) . ']';
832 // save the request body for debugging purposes.
833 $zi_body_request = rtrim( $zidata, ',' );
834 $product->update_meta_data( 'zi_product_body_request', '{' . $zi_body_request . '}' );
835 $product->save();
836
837 $data = array(
838 'JSONString' => '{' . $zidata . '}',
839 );
840
841 // fwrite($fd, PHP_EOL . 'ZI Data JSON : ' . '{' . print_r($data, true) . '}');
842 // fclose($fd);
843
844 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
845 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
846
847 if ( ! empty( $zoho_inventory_oid ) ) {
848 $url = $zoho_inventory_url . 'inventory/v1/itemgroups?organization_id=' . $zoho_inventory_oid;
849
850 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
851 $json = $execute_curl_call_handle->execute_curl_call_post( $url, $data );
852
853 $errmsg = $json->message;
854 $product->update_meta_data( 'zi_product_errmsg', $errmsg );
855 $product->save();
856 $code = $json->code;
857 if ( '0' === $code || 0 === $code ) {
858
859 // This item will keep the copy of zoho item_id with respect to product.
860 // name as key synced to zoho.
861 $child_items = array();
862 foreach ( $json->item_group as $key => $value ) {
863 if ( 'group_id' === $key ) {
864 $group_id = $value;
865 }
866
867 if ( 'items' === $key ) {
868 foreach ( $value as $key2 => $val2 ) {
869 // Only map child items by SKU — skip entries without SKU.
870 if ( ! empty( $val2->sku ) ) {
871 $child_items[ strtolower( trim( $val2->sku ) ) ] = $val2->item_id;
872 }
873 }
874 }
875 }
876 if ( ! empty( $group_id ) ) {
877 $product->update_meta_data( 'zi_item_id', $group_id );
878 $product->save();
879 }
880
881 foreach ( $available_variations as $child_id ) {
882 $product_variable = wc_get_product( $child_id );
883
884 $var_sku = trim( (string) $product_variable->get_sku() );
885 if ( $var_sku && isset( $child_items[ strtolower( $var_sku ) ] ) ) {
886 $variation = wc_get_product( $child_id );
887 if ( $variation ) {
888 $variation->update_meta_data( 'zi_item_id', $child_items[ strtolower( $var_sku ) ] );
889 $variation->save();
890 }
891 }
892 }
893 }
894 }
895 // End New Variable Product.
896 }
897
898 /**
899 * Function to sync variations of a product.
900 *
901 * @param object $product_variable - WooCommerce product object of a variation.
902 * @param int $post_id - Post ID of a variation product.
903 * @param string $attr1 - Attribute 1 name.
904 * @param string $attr2 - Attribute 2 name.
905 * @param string $attr3 - Attribute 3 name.
906 * @return string $item_id - Zoho item id of a variation product.
907 */
908 protected function cmbird_zi_variants_products( $product_variable, $post_id, $attr1 = '', $attr2 = '', $attr3 = '' ) {
909 // $fd = fopen(__DIR__.'/variations_products.txt','a+');
910 // fwrite($fd,PHP_EOL.'-------------------------------');
911 // fwrite($fd,PHP_EOL.'$attr1 : '.$attr1.' | $attr2 : '.$attr2.' | $attr3 : '.$attr3.' $post_id : '.$post_id);
912 // Sync Attributes of Variable Products.
913
914 $attributes = $product_variable->get_variation_attributes();
915 // fwrite($fd,PHP_EOL.'$variation_attributes : '.print_r($attributes,true));
916 $arrtibute_string = '';
917 foreach ( array( $attr1, $attr2, $attr3 ) as $idx => $attr ) {
918 if ( empty( $attr ) ) {
919 continue;
920 }
921 // Use sanitize_title() so the key matches WooCommerce's internal storage for
922 // both taxonomy attributes (pa_*) and custom/text attributes.
923 $attr_key = 'attribute_' . sanitize_title( $attr );
924 $raw_val = $attributes[ $attr_key ] ?? '';
925 // For taxonomy attributes get_variation_attributes() returns the term slug.
926 // Resolve it to the human-readable name before sending to Zoho.
927 if ( '' !== $raw_val && taxonomy_exists( $attr ) ) {
928 $term = get_term_by( 'slug', $raw_val, $attr );
929 if ( $term ) {
930 $raw_val = $term->name;
931 }
932 }
933 if ( '' !== $raw_val ) {
934 $option_num = $idx + 1;
935 $arrtibute_string .= '"attribute_option_name' . $option_num . '": "' . str_replace( '"', '', $raw_val ) . '",';
936 }
937 }
938 // fwrite($fd,PHP_EOL.'$arrtibute_string : '.$arrtibute_string);
939 // fclose($fd);
940 // Use product object meta accessor instead of direct postmeta.
941 $zoho_item_id = $product_variable->get_meta( 'zi_item_id', true );
942 $pname = '';
943 foreach ( $product_variable->get_variation_attributes() as $taxonomy => $terms_slug ) {
944
945 $pname .= $terms_slug;
946 }
947
948 $vname = $product_variable->get_name();
949 $name = sanitize_text_field( $vname );
950 // remove any 4‑byte Unicode characters (covers emojis).
951 $name = preg_replace( '/[\x{10000}-\x{10FFFF}]/u', '', $name );
952 $rate = $product_variable->get_regular_price();
953 // $rateS = $product_variable->get_sale_price();
954 if ( $product_variable->is_virtual( 'yes' ) ) {
955 $product_type = 'service';
956 $item_type = 'sales';
957 } else {
958 $product_type = 'goods';
959 $item_type = 'inventory';
960 }
961
962 $sku = $product_variable->get_sku();
963 $stock_quantity = $product_variable->get_stock_quantity();
964 $in_stock = ( $stock_quantity > 0 ) ? $stock_quantity : 0;
965 // Get Tax ID.
966 $tax_rates = WC_Tax::get_base_tax_rates( $product_variable->get_tax_class() );
967 $tax_id_key = '';
968 foreach ( $tax_rates as $tax_key => $tax_value ) {
969 $tax_id_key = $tax_key;
970 break;
971 }
972 $tax_option = get_option( 'cmbird_zoho_inventory_tax_rate_' . $tax_id_key );
973 $tax_id = explode( '##', $tax_option )[0];
974
975 $zi_status = ( 'publish' === get_post_status( $post_id ) ) ? 'active' : 'inactive';
976 // request data for adding/updating value to zoho.
977 $zidata = '';
978 if ( ! empty( $arrtibute_string ) ) {
979 $zidata .= $arrtibute_string;
980 }
981 $zidata .= '"name" : "' . $name . '",';
982 $zidata .= '"product_type" : "' . $product_type . '",';
983 $zidata .= '"sku" : "' . $sku . '",';
984 $zidata .= '"item_type" : "' . $item_type . '",';
985 // $zidata .= '"unit" : "pcs",';
986 $zidata .= '"status" : "' . $zi_status . '",';
987 if ( empty( $zoho_item_id ) && $in_stock > 0 ) {
988 $zidata .= '"initial_stock" : ' . $in_stock . ',';
989 $zidata .= '"initial_stock_rate" : ' . $in_stock . ',';
990 }
991 $zidata .= '"rate" : "' . $rate . '",';
992 $zidata .= '"tax_id" : "' . $tax_id . '",';
993 // Get cost_price from product meta using the WC product object.
994 $cost_price = $product_variable->get_meta( '_cogs_total_value', true );
995 if ( ! empty( $cost_price ) && is_numeric( $cost_price ) ) {
996 $zidata .= '"purchase_rate" : "' . $cost_price . '",';
997 }
998
999 $dimensions = (object) array();
1000 $dimensions->length = $product_variable->get_length();
1001 $dimensions->width = $product_variable->get_width();
1002 $dimensions->height = $product_variable->get_height();
1003 $dimensions->weight = $product_variable->get_weight();
1004 if ( ! empty( $dimensions ) ) {
1005 $zidata .= '"package_details" : ' . wp_json_encode( $dimensions );
1006 }
1007
1008 // $fd = fopen(__DIR__ . '/variations.txt', 'a+');
1009 // fwrite($fd,PHP_EOL.'Get data for $post_id '.$post_id);
1010 if ( ctype_digit( $zoho_item_id ) && ! empty( $zoho_item_id ) ) {
1011 // fwrite($fd, PHP_EOL . 'Update Item');
1012 $update_error_msg = $this->cmbird_zi_product_put( $post_id, $zoho_item_id, $zidata );
1013 $zidataa = '';
1014 // fwrite($fd, PHP_EOL . '{' . $zidata . '}');
1015 return $zidataa .= '{' . $zidata . '}';
1016 } else {
1017 // fwrite($fd, PHP_EOL . 'Create Item');
1018 // Check if the the given sku has product in zoho inventory.
1019 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
1020 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
1021 $sku_check = str_replace( ' ', '+', $sku );
1022 $url = $zoho_inventory_url . 'inventory/v1/items?search_text=' . $sku_check . '&filter_by=Status.Active&organization_id=' . $zoho_inventory_oid;
1023 $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho();
1024 $get_request = $execute_curl_call_handle->execute_curl_call_get( $url );
1025 $var_item_id = '';
1026 $groupitem_id = '';
1027
1028 // Guard against transport/errors and ensure Zoho returned a successful response with items.
1029 if ( ! is_wp_error( $get_request ) && property_exists( $get_request, 'code' ) && (int) $get_request->code === 0 && property_exists( $get_request, 'items' ) && is_array( $get_request->items ) ) {
1030 foreach ( $get_request->items as $zoho_item ) {
1031 // match SKU exactly (trimmed) to avoid false positives from Zoho's "contains" search
1032 if ( isset( $zoho_item->sku ) && trim( (string) $zoho_item->sku ) === trim( (string) $sku ) ) {
1033 // prefer active items only.
1034 if ( ! isset( $zoho_item->status ) || 'active' === $zoho_item->status ) {
1035 $var_item_id = $zoho_item->item_id ?? '';
1036 $groupitem_id = $zoho_item->group_id ?? '';
1037 break;
1038 }
1039 }
1040 }
1041 }
1042
1043 $zidataa = '';
1044 if ( $var_item_id ) {
1045 // Persist mapping on the variation using WC_Product API.
1046 $product_variable->update_meta_data( 'zi_item_id', $var_item_id );
1047 $product_variable->save();
1048 }
1049 if ( $groupitem_id ) {
1050 $parent_product_id = wp_get_post_parent_id( $post_id );
1051 if ( $parent_product_id ) {
1052 $parent_product = wc_get_product( $parent_product_id );
1053 if ( $parent_product ) {
1054 $parent_product->update_meta_data( 'zi_item_id', $groupitem_id );
1055 $parent_product->save();
1056 }
1057 }
1058 }
1059 // fwrite($fd, PHP_EOL . '$var_item_id : ' . $var_item_id);
1060 // fclose($fd);
1061 return $zidataa .= '{' . $zidata . '}';
1062 }
1063 }
1064
1065 /**
1066 * Check if category already exists and return updated one
1067 */
1068 protected function cmbird_zi_get_prod_updated_category( $product_id ) {
1069 // Check if product category already synced.
1070 $terms = get_the_terms( $product_id, 'product_cat' );
1071 if ( $terms ) {
1072 foreach ( $terms as $term ) {
1073 $product_cat_id = $term->term_id;
1074 $zoho_cat_id = get_option( "cmbird_zoho_id_for_term_id_{$product_cat_id}" );
1075 if ( $zoho_cat_id ) {
1076 break;
1077 }
1078 }
1079 }
1080 // Check if product has already mapped category.
1081 if ( empty( $zoho_cat_id ) ) {
1082 $zoho_cat_id = get_post_meta( $product_id, 'zi_category_id', true );
1083 }
1084
1085 if ( $zoho_cat_id ) {
1086 return $zoho_cat_id;
1087 } else {
1088 return false;
1089 }
1090 }
1091
1092 /**
1093 * Function for adding Simple product from Zoho to woocommerce.
1094 *
1095 * @param $prod - Product object for adding new product in woocommerce.
1096 * @param $user_id - Current Active user Id
1097 * @param string $type - product is composite item or not (composite)
1098 */
1099 public function cmbird_zi_product_to_woocommerce( $item, $item_stock = '', $type = '' ) {
1100 // $fd = fopen( __DIR__ . '/cmbird_zi_product_to_woocommerce.txt', 'a+' );
1101 try {
1102 if ( 'active' !== $item['status'] ) {
1103 return;
1104 }
1105 $product = new WC_Product();
1106
1107 $allow_backorders = get_option( 'woocommerce_allow_backorders' );
1108 $zi_disable_stock_sync = get_option( 'cmbird_zoho_disable_stock_sync_status' );
1109
1110 // Set the product data.
1111 $product->set_status( 'publish' );
1112 $product->set_name( $item['name'] );
1113 $product->set_regular_price( $item['rate'] );
1114 $product->set_short_description( $item['description'] );
1115 $product->set_sku( $item['sku'] );
1116
1117 // Set the stock management properties.
1118 if ( ! empty( $item_stock ) && ! $zi_disable_stock_sync ) {
1119 $product->set_manage_stock( true );
1120 $product->set_stock_quantity( $item_stock );
1121
1122 if ( $item_stock > 0 ) {
1123 $product->set_stock_status( 'instock' );
1124 } elseif ( $item_stock < 0 && 'yes' === $allow_backorders ) {
1125 $product->set_stock_status( 'onbackorder' );
1126 } else {
1127 $product->set_stock_status( 'outofstock' );
1128 }
1129 }
1130
1131 // Save the product.
1132 $product_id = $product->save();
1133
1134 // Map composite items metadata to convert product as a bundle product.
1135 if ( 'composite' === $type ) {
1136 update_post_meta( $product_id, '_wc_pb_layout_style', 'default' );
1137 update_post_meta( $product_id, '_wc_pb_add_to_cart_form_location', 'default' );
1138 wp_set_object_terms( $product_id, 'bundle', 'product_type' );
1139 }
1140
1141 return $product_id;
1142 } catch ( Exception $e ) {
1143 // Handle the exception, log it, or perform any necessary actions.
1144 return new WP_Error( 'Error creating WooCommerce product: ' . $e->getMessage() );
1145 }
1146 }
1147 }
1148 $cmbird_products_zi_export = new CMBIRD_Products_ZI_Export();
1149