PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.4.4
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.4.4
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-import-items.php
commercebird / includes / classes / zoho-inventory Last commit date
class-categories.php 1 year ago class-import-image.php 1 year ago class-import-items.php 1 year ago class-import-price-list.php 1 year ago class-multi-currency.php 1 year ago class-order-sync.php 1 year ago class-product.php 1 year ago class-users-contact.php 1 year ago index.php 1 year ago
class-import-items.php
1793 lines
1 <?php
2
3 /**
4 * Class to import Products from Zoho to WooCommerce
5 *
6 * @package zoho_inventory_api
7 */
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
13
14 class CMBIRD_Products_ZI {
15
16
17 private $config;
18 private $is_tax_enabled;
19
20 private $wc_decimal_separator;
21 private $wc_thousand_separator;
22
23 private $wc_price_decimal_separator;
24
25 public function __construct() {
26 $this->initialize_config();
27 add_action( 'init', array( $this, 'initialize_config' ) );
28 }
29
30 public function initialize_config() {
31 if ( ! function_exists( 'wc_get_price_decimal_separator' ) ) {
32 return; // Prevents error if WooCommerce is not active
33 }
34
35 $this->config = array(
36 'ProductZI' => array(
37 'OID' => get_option( 'cmbird_zoho_inventory_oid' ),
38 'APIURL' => get_option( 'cmbird_zoho_inventory_url' ),
39 ),
40 'Settings' => array(
41 'disable_description' => get_option( 'cmbird_zoho_disable_description_sync_status' ),
42 'disable_name' => get_option( 'cmbird_zoho_disable_name_sync_status' ),
43 'disable_price' => get_option( 'cmbird_zoho_disable_price_sync_status' ),
44 'disable_stock' => get_option( 'cmbird_zoho_disable_stock_sync_status' ),
45 'enable_accounting_stock' => get_option( 'cmbird_zoho_enable_accounting_stock_status' ),
46 'enable_location_stock' => get_option( 'cmbird_zoho_enable_locationstock_status' ),
47 'zoho_location_id' => get_option( 'cmbird_zoho_location_id_status' ),
48 'disable_image' => get_option( 'cmbird_zoho_disable_image_sync_status' ),
49 ),
50 // Get WooCommerce settings
51 'WooCommerce' => array(
52 'decimal_separator' => wc_get_price_decimal_separator(),
53 'thousand_separator' => wc_get_price_thousand_separator(),
54 'price_decimal_separator' => wc_get_price_decimals(),
55 'tax_enabled' => 'yes' === get_option( 'woocommerce_calc_taxes' ),
56 ),
57 );
58
59 // Check if WooCommerce taxes are enabled and store the result
60 $this->is_tax_enabled = $this->config['WooCommerce']['tax_enabled'];
61
62 // Check the WooCommerce settings for decimal and thousand separators
63 $this->wc_decimal_separator = $this->config['WooCommerce']['decimal_separator'];
64 $this->wc_thousand_separator = $this->config['WooCommerce']['thousand_separator'];
65 $this->wc_price_decimal_separator = $this->config['WooCommerce']['price_decimal_separator'];
66 }
67
68 /**
69 * Function to retrieve item details and sync items.
70 *
71 * @param string $url - URL to get details.
72 * @return mixed return true if data false if error.
73 */
74 public function zi_item_bulk_sync( $url ) {
75 // $fd = fopen( __DIR__ . '/zi_item_bulk_sync.txt', 'a+' );
76
77 global $wpdb;
78 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
79 $json = $execute_curl_call->execute_curl_call_get( $url );
80 $code = $json->code;
81
82 $is_tax_enabled = $this->is_tax_enabled;
83
84 // Memory management: Set memory limit if needed
85 if ( function_exists( 'ini_get' ) && ini_get( 'memory_limit' ) ) {
86 $current_limit = ini_get( 'memory_limit' );
87 if ( intval( $current_limit ) < 256 ) {
88 ini_set( 'memory_limit', '256M' );
89 }
90 }
91
92 // $message = $json->message;
93 // fwrite($fd, PHP_EOL . '$json->item : ' . print_r($json, true));
94
95 if ( 0 === $code || '0' === $code ) {
96 $processed_count = 0;
97
98 foreach ( $json->items as $arr ) {
99 // Memory cleanup every 50 items
100 if ( $processed_count > 0 && $processed_count % 50 === 0 ) {
101 wp_cache_flush();
102 if ( function_exists( 'gc_collect_cycles' ) ) {
103 gc_collect_cycles();
104 }
105 }
106
107 // fwrite( $fd, PHP_EOL . '$arr : ' . print_r( $arr, true ) );
108 if ( ( ! empty( $arr->item_id ) ) && ! ( $arr->is_combo_product ) ) {
109 // fwrite($fd, PHP_EOL . 'Item Id found : ' . $arr->item_id);
110
111 $product_res = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key='zi_item_id' AND meta_value=%s", $arr->item_id ) );
112
113 if ( $product_res && ! empty( $product_res->post_id ) ) {
114 $pdt_id = $product_res->post_id;
115 // Load the Product Object
116 $product = wc_get_product( $pdt_id );
117 if ( empty( $product ) || ! $product ) {
118 continue;
119 }
120
121 $zi_disable_item_description_sync = $this->config['Settings']['disable_description'];
122 if ( ! empty( $arr->description ) && ! $zi_disable_item_description_sync ) {
123 $product->set_short_description( $arr->description );
124 }
125
126 if ( ! empty( $arr->status ) ) {
127 $status = 'active' === $arr->status ? 'publish' : 'draft';
128 $product->set_status( $status );
129 }
130
131 $zi_disable_itemname_sync = $this->config['Settings']['disable_name'];
132 if ( ( ! $zi_disable_itemname_sync ) && ! empty( $arr->name ) ) {
133 $product->set_name( $arr->name );
134 // set slug but use sanitize_title to ensure it is safe
135 $product->set_slug( sanitize_title( $arr->name ) );
136 }
137
138 if ( ! empty( $arr->sku ) ) {
139 $product->set_sku( $arr->sku );
140 }
141
142 $zi_disable_itemprice_sync = $this->config['Settings']['disable_price'];
143 if ( ! empty( $arr->rate ) && ! $zi_disable_itemprice_sync ) {
144 $product->set_regular_price( $arr->rate );
145 $sale_price = $product->get_sale_price();
146 if ( empty( $sale_price ) ) {
147 $product->set_price( $arr->rate );
148 }
149 }
150
151 if ( isset( $arr->package_details ) ) {
152 $details = $arr->package_details;
153 if ( is_object( $details ) ) {
154 $product->set_weight( floatval( $details->weight ) );
155 $product->set_length( floatval( $details->length ) );
156 $product->set_width( floatval( $details->width ) );
157 $product->set_height( floatval( $details->height ) );
158 }
159 }
160
161 // Update Purchase Rate as Cost Price
162 $product->update_meta_data( '_cost_price', $arr->purchase_rate );
163
164 // To check status of stock sync option.
165 $zi_disable_stock_sync = $this->config['Settings']['disable_stock'];
166 if ( ! $zi_disable_stock_sync && isset( $arr->available_for_sale_stock ) ) {
167 $stock = '';
168 // Update stock
169 $accounting_stock = $this->config['Settings']['enable_accounting_stock'];
170 // Sync from specific location check
171 $zi_enable_locationstock = $this->config['Settings']['enable_location_stock'];
172 if ( $zi_enable_locationstock && isset( $arr->locations ) ) {
173 $locations = $arr->locations;
174 $location_id = $this->config['Settings']['zoho_location_id'];
175 foreach ( $locations as $location ) {
176 if ( $location->location_id === $location_id ) {
177 if ( $accounting_stock ) {
178 $stock = $location->location_available_for_sale_stock;
179 } else {
180 $stock = $location->location_actual_available_for_sale_stock;
181 }
182 }
183 }
184 } elseif ( $accounting_stock ) {
185 $stock = $arr->available_for_sale_stock;
186 } else {
187 $stock = $arr->actual_available_for_sale_stock;
188 }
189
190 if ( is_numeric( $stock ) ) {
191 $product->set_manage_stock( true );
192 $product->set_stock_quantity( $stock );
193 if ( $stock > 0 ) {
194 $product->set_stock_status( 'instock' );
195 } else {
196 $backorder_status = $product->backorders_allowed();
197 $status = ( $backorder_status === 'yes' ) ? 'onbackorder' : 'outofstock';
198 $product->set_stock_status( $status );
199 }
200 }
201 }
202
203 if ( ! empty( $arr->tax_id ) && ! $is_tax_enabled ) {
204 $zi_common_class = new CMBIRD_Common_Functions();
205 $woo_tax_class = $zi_common_class->get_tax_class_by_percentage( $arr->tax_percentage );
206 $product->set_tax_status( 'taxable' );
207 $product->set_tax_class( $woo_tax_class );
208 }
209 $product->save();
210 // Sync ACF Fields
211 $this->sync_item_custom_fields( $arr->custom_fields, $pdt_id );
212 // Clear/refresh cache
213 wc_delete_product_transients( $pdt_id );
214 }
215 }
216
217 $processed_count++;
218 }
219
220 // Final memory cleanup after processing all items
221 wp_cache_flush();
222 if ( function_exists( 'gc_collect_cycles' ) ) {
223 gc_collect_cycles();
224 }
225
226 // Clear any temporary variables
227 unset( $json, $execute_curl_call );
228 } else {
229 return false;
230 }
231 // fclose( $fd );
232 // Return if synced.
233 return true;
234 }
235
236 /**
237 * Function to add items recursively by cron job.
238 *
239 * @param [number] $page - Page number for getting item with pagination.
240 * @param [number] $category - Category id to get item of specific category.
241 * @return mixed
242 */
243 public function sync_item_recursively() {
244 // $fd = fopen( __DIR__ . '/simple-items-sync.txt', 'a+' );
245
246 $args = func_get_args();
247 // fwrite( $fd, PHP_EOL . 'Args ' . print_r( $args, true ) );
248 if ( is_array( $args ) ) {
249 if ( isset( $args['page'] ) && isset( $args['category'] ) ) {
250 $page = $args['page'];
251 $category = $args['category'];
252 } elseif ( isset( $args[0] ) && isset( $args[1] ) ) {
253 $page = $args[0];
254 $category = $args[1];
255 } elseif ( isset( $args[0] ) && ! isset( $args[1] ) ) {
256 $page = $args[0]['page'];
257 $category = $args[0]['category'];
258 } else {
259 return;
260 }
261 } else {
262 return;
263 }
264
265 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
266 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
267 $timeout = ini_get( 'max_execution_time' ) ? ini_get( 'max_execution_time' ) : 60;
268 // Dynamically set per_page based on timeout
269 if ( $timeout <= 30 ) {
270 $per_page = 20;
271 } elseif ( $timeout <= 60 ) {
272 $per_page = 50;
273 } else {
274 $per_page = 100;
275 }
276
277 $url = sprintf(
278 '%sinventory/v1/items?organization_id=%s&category_id=%s&page=%d&per_page=%d&sort_column=last_modified_time',
279 $zoho_inventory_url,
280 $zoho_inventory_oid,
281 $category,
282 $page,
283 $per_page
284 );
285 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
286 $json = $execute_curl_call->execute_curl_call_get( $url );
287 $code = (int) property_exists( $json, 'code' ) ? $json->code : '0';
288
289 global $wpdb;
290
291 /* Response for item sync with sync button. For cron sync blank array will return. */
292 $response_msg = array();
293 if ( empty( $code ) && property_exists( $json, 'items' ) ) {
294 $item_ids = array();
295 // log items.
296 // fwrite( $fd, PHP_EOL . 'Items : ' . print_r( $json, true ) );
297 // before your foreach, initialize once:
298 $logger = wc_get_logger();
299 $context = [ 'source' => 'cmbird_item_sync' ];
300
301 foreach ( $json->items as $arr ) {
302 $prod_id = wc_get_product_id_by_sku( $arr->sku );
303 $is_bundle = $arr->is_combo_product;
304 if ( isset( $arr->group_id ) ) {
305 $is_grouped = $arr->group_id;
306 }
307 // Flag to enable or disable sync.
308 $allow_to_import = false;
309 // Check if product exists with same sku.
310 if ( $prod_id ) {
311 $zi_item_id = get_post_meta( $prod_id, 'zi_item_id', true );
312 if ( empty( $zi_item_id ) ) {
313 // Map existing item with zoho id.
314 update_post_meta( $prod_id, 'zi_item_id', $arr->item_id );
315 $allow_to_import = true;
316 }
317 }
318 if ( '' == $is_bundle && empty( $is_grouped ) ) {
319 // If product not exists normal behavior of item sync.
320 $allow_to_import = true;
321 }
322 if ( ! empty( $is_grouped ) ) {
323 $this->sync_variation_of_group( $arr );
324 continue;
325 }
326
327 // Remove duplicated product based on the sku, remove also postmeta
328 // run query to check if product exists twice with same sku. Check based on SKU.
329 if ( $prod_id && ! empty( $arr->sku ) ) {
330 $duplicate_product = $wpdb->get_row( $wpdb->prepare( "SELECT post_id FROM {$wpdb->prefix}postmeta WHERE meta_key = '_sku' AND meta_value = %s AND post_id != %d LIMIT 1", $arr->sku, $prod_id ) );
331 if ( $duplicate_product ) {
332 // Remove the duplicate product.
333 wp_delete_post( $duplicate_product->post_id, true );
334 }
335 }
336
337 // Get the post id by doing a meta query on the postmeta table.
338 if ( empty( $prod_id ) ) {
339 $pdt = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s LIMIT 1", $arr->item_id ) );
340 $pdt_id = $pdt ? $pdt->post_id : '';
341 } else {
342 $pdt_id = $prod_id;
343 }
344
345 if ( empty( $pdt_id ) && $allow_to_import && 'active' === $arr->status ) {
346 // Create the product if it does not exist
347 try {
348 $product = new WC_Product();
349 $product->set_name( $arr->name );
350 $product->set_status( 'publish' );
351 if ( isset( $arr->sku ) && ! empty( $arr->sku ) ) {
352 $product->set_sku( $arr->sku );
353 }
354 $product->set_regular_price( $arr->rate );
355 $pdt_id = $product->save();
356 if ( $pdt_id ) {
357 update_post_meta( $pdt_id, 'zi_item_id', $arr->item_id );
358 }
359 } catch (Exception $e) {
360 // Log the error so you can inspect it in WooCommerce > Status > Logs
361 $message = sprintf(
362 'Error creating product for Zoho Item #%s (SKU: %s): %s',
363 $arr->item_id,
364 isset( $arr->sku ) ? $arr->sku : '(no sku)',
365 $e->getMessage()
366 );
367 $logger->error( $message, $context );
368 continue;
369 }
370 }
371
372 if ( $pdt_id ) {
373 if ( ! empty( $arr->category_name ) ) {
374 $term = get_term_by( 'name', $arr->category_name, 'product_cat' );
375 $term_id = $term->term_id;
376 if ( empty( $term_id ) ) {
377 $term = wp_insert_term(
378 $arr->category_name,
379 'product_cat',
380 array(
381 'parent' => 0,
382 )
383 );
384 $term_id = $term['term_id'];
385 }
386 if ( $term_id ) {
387 $existing_terms = wp_get_object_terms( $pdt_id, 'product_cat' );
388 if ( $existing_terms && count( $existing_terms ) > 0 ) {
389 $is_terms_exist = $this->zi_check_terms_exists( $existing_terms, $term_id );
390 if ( ! $is_terms_exist ) {
391 update_post_meta( $pdt_id, 'zi_category_id', $category );
392 wp_add_object_terms( $pdt_id, $term_id, 'product_cat' );
393 }
394 } else {
395 update_post_meta( $pdt_id, 'zi_category_id', $category );
396 wp_set_object_terms( $pdt_id, $term_id, 'product_cat' );
397 }
398 }
399 // Remove "uncategorized" category if assigned
400 $uncategorized_term = get_term_by( 'slug', 'uncategorized', 'product_cat' );
401 if ( $uncategorized_term && has_term( $uncategorized_term->term_id, 'product_cat', $pdt_id ) ) {
402 wp_remove_object_terms( $pdt_id, $uncategorized_term->term_id, 'product_cat' );
403 }
404 }
405
406 if ( ! empty( $arr->brand ) ) {
407 // check if the Brand taxonomy exists and then update the term
408 if ( taxonomy_exists( 'product_brand' ) ) {
409 wp_set_object_terms( $pdt_id, $arr->brand, 'product_brand' );
410 }
411 }
412 // Sync Featured Image if not disabled.
413 $zi_disable_image_sync = $this->config['Settings']['disable_image'];
414 if ( ! empty( $arr->image_document_id ) && ! $zi_disable_image_sync ) {
415 $image_class = new CMBIRD_Image_ZI();
416 $image_class->cmbird_zi_get_image( $arr->item_id, $arr->name, $pdt_id, $arr->image_name );
417 }
418
419 $item_ids[] = $arr->item_id;
420 } // end of wpdb post_id check
421 }
422 if ( ! empty( $item_ids ) ) {
423 $item_id_str = implode( ',', $item_ids );
424 // fwrite($fd, PHP_EOL . 'Before Bulk sync');
425 $item_details_url = "{$zoho_inventory_url}inventory/v1/itemdetails?item_ids={$item_id_str}&organization_id={$zoho_inventory_oid}";
426 $this->zi_item_bulk_sync( $item_details_url );
427
428 if ( isset( $json->page_context ) && $json->page_context->has_more_page ) {
429 $data = array(
430 'page' => $page + 1,
431 'category' => $category,
432 );
433 // fwrite( $fd, PHP_EOL . 'Data: ' . print_r( $data, true ) );
434 $existing_schedule = as_has_scheduled_action( 'import_simple_items_cron', array( $data ) );
435 if ( ! $existing_schedule ) {
436 as_schedule_single_action( time(), 'import_simple_items_cron', array( $data ) );
437 // fwrite( $fd, PHP_EOL . 'Scheduled' );
438 }
439 }
440 array_push( $response_msg, $this->zi_response_message( $code, $json->message ) );
441 }
442 }
443 // fclose( $fd );
444 return $response_msg;
445 }
446
447 /**
448 * Update or Create Custom Fields of Product
449 *
450 * @param array|object $custom_fields - item object coming in from simple item recursive
451 * @param int $pdt_id - product id
452 * @return void
453 */
454 public function sync_item_custom_fields( $custom_fields, $pdt_id ) {
455 if ( empty( $custom_fields ) || empty( $pdt_id ) ) {
456 return;
457 }
458
459 foreach ( $custom_fields as $custom_field ) {
460 // Extract data from custom field
461 $api_name = isset( $custom_field->api_name ) ? $custom_field->api_name : $custom_field['api_name'];
462 $value = isset( $custom_field->value ) ? $custom_field->value : $custom_field['value'];
463
464 // Check if both API name and value are present
465 if ( ! empty( $api_name ) && ! empty( $value ) ) {
466 // Check if ACF function exists
467 if ( function_exists( 'update_field' ) ) {
468 // Update ACF field
469 update_field( $api_name, $value, $pdt_id );
470 } else {
471 // Fall back to update post meta
472 update_post_meta( $pdt_id, $api_name, $value );
473 }
474 }
475 }
476 }
477
478
479 /**
480 * Function to add group items recursively by manual sync
481 *
482 * @param [number] $page - Page number for getting group item with pagination.
483 * @param [number] $category - Category id to get group item of specific category.
484 * @param [string] $source - Source from where function is calling : 'cron'/'sync'.
485 * @return mixed
486 */
487 public function sync_groupitem_recursively() {
488 // $fd = fopen( __DIR__ . '/sync_groupitem_recursively.txt', 'a+' );
489
490 $args = func_get_args();
491 if ( ! empty( $args ) ) {
492 if ( is_array( $args ) ) {
493 if ( isset( $args['page'] ) && isset( $args['category'] ) ) {
494 $page = $args['page'];
495 $category = $args['category'];
496 } elseif ( isset( $args[0] ) && isset( $args[1] ) ) {
497 $page = $args[0];
498 $category = $args[1];
499 } elseif ( isset( $args[0] ) && ! isset( $args[1] ) ) {
500 $page = $args[0]['page'];
501 $category = $args[0]['category'];
502 } else {
503 return;
504 }
505 } else {
506 return;
507 }
508
509 // Memory management: Set memory limit if needed
510 if ( function_exists( 'ini_get' ) && ini_get( 'memory_limit' ) ) {
511 $current_limit = ini_get( 'memory_limit' );
512 if ( intval( $current_limit ) < 256 ) {
513 ini_set( 'memory_limit', '256M' );
514 }
515 }
516
517 // fwrite($fd, PHP_EOL . 'Test name Update ' . print_r($data, true));
518 global $wpdb;
519 $zoho_inventory_oid = $this->config['ProductZI']['OID'];
520 $zoho_inventory_url = $this->config['ProductZI']['APIURL'];
521
522 $url = $zoho_inventory_url . 'inventory/v1/itemgroups/?organization_id=' . $zoho_inventory_oid . '&category_id=' . $category . '&page=' . $page . '&per_page=20&filter_by=Status.Active';
523 // fwrite($fd, PHP_EOL . '$url : ' . $url);
524
525 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
526 $json = $execute_curl_call->execute_curl_call_get( $url );
527
528 $code = $json->code;
529 // $message = $json->message;
530
531 $response_msg = array();
532
533 if ( '0' === $code || 0 === $code ) {
534 $zi_disable_description_sync = $this->config['Settings']['disable_description'];
535 $zi_disable_name_sync = $this->config['Settings']['disable_name'];
536 // fwrite( $fd, PHP_EOL . '$json->itemgroups : ' . print_r( $json->itemgroups, true ) );
537 $processed_count = 0;
538 foreach ( $json->itemgroups as $gp_arr ) {
539 // Memory cleanup every 10 group items
540 if ( $processed_count > 0 && $processed_count % 10 === 0 ) {
541 wp_cache_flush();
542 if ( function_exists( 'gc_collect_cycles' ) ) {
543 gc_collect_cycles();
544 }
545 }
546 $zi_group_id = $gp_arr->group_id;
547 $zi_group_name = $gp_arr->group_name;
548 // fwrite($fd, PHP_EOL . '$itemGroup : ' . print_r($gp_arr, true));
549 // skip if there is no first attribute
550 $zi_group_attribute1 = $gp_arr->attribute_id1;
551 if ( empty( $zi_group_attribute1 ) ) {
552 continue;
553 }
554
555 // Get Group ID
556 $group_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s LIMIT 1", $zi_group_id ) );
557 // array_push( $response_msg, $this->zi_response_message( 'SUCCESS', 'Zoho Group Item Synced: ' . $zi_group_name, $group_id ) );
558 /// end insert group product
559 // variable items
560 // fwrite($fd, PHP_EOL . '$group_id exists ' . $group_id);
561 if ( ! empty( $group_id ) ) {
562 $existing_parent_product = wc_get_product( $group_id );
563 // fwrite($fd, PHP_EOL . 'Existing group Id');
564 if ( ! empty( $gp_arr->description ) && ! $zi_disable_description_sync ) {
565 $existing_parent_product->set_short_description( $gp_arr->description );
566 }
567 if ( ! empty( $gp_arr->name ) && ! $zi_disable_name_sync ) {
568 $existing_parent_product->set_name( $gp_arr->name );
569 // santize the name for slug and save the slug
570 $slug = sanitize_title( $gp_arr->name );
571 $existing_parent_product->set_slug( $slug );
572 }
573 // add zi_category_id as meta
574 $existing_parent_product->update_meta_data( 'zi_category_id', $category );
575 // create attributes if not exists.
576 $attributes = $existing_parent_product->get_attributes();
577 if ( empty( $attributes ) ) {
578 // Create or Update the Attributes
579 $attr_created = $this->sync_attributes_of_group( $gp_arr, $group_id );
580 // fwrite($fd, PHP_EOL . '$attr_created ' . $attr_created);
581 }
582 $variations_check = $existing_parent_product->get_children();
583 if ( empty( $variations_check ) ) {
584 // fwrite( $fd, PHP_EOL . 'No Variations found' );
585 $this->import_variable_product_variations( $gp_arr, $group_id );
586 }
587 $existing_parent_product->save();
588 // ACF Fields
589 if ( ! empty( $gp_arr->custom_fields ) ) {
590 // fwrite($fd, PHP_EOL . 'Custom Fields : ' . print_r($gp_arr->custom_fields, true));
591 $this->sync_item_custom_fields( $gp_arr->custom_fields, $group_id );
592 }
593 // update Brand.
594 if ( ! empty( $gp_arr->brand ) ) {
595 // check if the Brand or Brands taxonomy exists and then update the term
596 if ( taxonomy_exists( 'product_brand' ) ) {
597 wp_set_object_terms( $group_id, $gp_arr->brand, 'product_brand' );
598 } elseif ( taxonomy_exists( 'product_brand' ) ) {
599 wp_set_object_terms( $group_id, $gp_arr->brand, 'product_brand' );
600 }
601 }
602 } else {
603 // Create the parent variable product
604 $parent_product = new WC_Product_Variable();
605 $parent_product->set_name( $zi_group_name );
606 $parent_product->set_status( 'publish' );
607 $parent_product->set_short_description( $gp_arr->description );
608 $parent_product->add_meta_data( 'zi_item_id', $zi_group_id );
609 $parent_product->add_meta_data( 'zi_category_id', $category );
610 $group_id = $parent_product->save();
611
612 // Sync category by finding it first
613 $category_handler = new CMBIRD_Categories_ZI();
614 $term_id = $category_handler->cmbird_subcategories_term_id( $category );
615 $term = get_term_by( 'id', $term_id, 'product_cat' );
616 if ( $term && ! is_wp_error( $term ) ) {
617 // Assign the category to the product
618 wp_set_object_terms( $group_id, $term->term_id, 'product_cat' );
619
620 // Remove the "uncategorized" category if it's assigned
621 $uncategorized_term = get_term_by( 'slug', 'uncategorized', 'product_cat' );
622 if ( $uncategorized_term && has_term( $uncategorized_term->term_id, 'product_cat', $group_id ) ) {
623 wp_remove_object_terms( $group_id, $uncategorized_term->term_id, 'product_cat' );
624 }
625 }
626
627 // update Brand.
628 if ( ! empty( $gp_arr->brand ) ) {
629 // check if the Brand or Brands taxonomy exists and then update the term
630 if ( taxonomy_exists( 'product_brand' ) ) {
631 wp_set_object_terms( $group_id, $gp_arr->brand, 'product_brand' );
632 } elseif ( taxonomy_exists( 'product_brand' ) ) {
633 wp_set_object_terms( $group_id, $gp_arr->brand, 'product_brand' );
634 }
635 }
636
637 // fwrite($fd, PHP_EOL . 'New $group_id ' . $group_id);
638 // ACF Fields
639 if ( ! empty( $gp_arr->custom_fields ) ) {
640 // fwrite($fd, PHP_EOL . 'Custom Fields : ' . print_r($gp_arr->custom_fields, true));
641 $this->sync_item_custom_fields( $gp_arr->custom_fields, $group_id );
642 }
643 // Create or Update the Attributes
644 $attr_created = $this->sync_attributes_of_group( $gp_arr, $group_id );
645
646 if ( ! empty( $group_id ) && $attr_created ) {
647 $this->import_variable_product_variations( $gp_arr, $group_id );
648 }
649 } // end of create variable product
650
651 ++$processed_count;
652 } // end foreach group items
653
654 // Final memory cleanup after processing all group items
655 wp_cache_flush();
656 if ( function_exists( 'gc_collect_cycles' ) ) {
657 gc_collect_cycles();
658 }
659
660 // Clear any temporary variables
661 unset( $json, $execute_curl_call );
662
663 if ( isset( $json->page_context ) && $json->page_context->has_more_page ) {
664 $data = array(
665 'page' => $page + 1,
666 'category' => $category,
667 );
668 $existing_schedule = as_has_scheduled_action( 'import_group_items_cron', $data );
669 // Check if the scheduled action exists
670 if ( ! $existing_schedule ) {
671 as_schedule_single_action( time(), 'import_group_items_cron', $data );
672 }
673 }
674 array_push( $response_msg, $this->zi_response_message( $code, $json->message ) );
675 }
676 // End of logging.
677 // fclose( $fd );
678 return $response_msg;
679 } else {
680 return;
681 }
682 }
683
684 /**
685 * Callback function for importing a variable product and its variations.
686 *
687 * @param object $gp_arr - Group item object.
688 * @param int $group_id - Group item id.
689 */
690 public function import_variable_product_variations( $gp_arr, $group_id ): void {
691 // $fd = fopen( __DIR__ . '/import_variable_product_variations.txt', 'a+' );
692
693 if ( empty( $gp_arr ) || empty( $group_id ) ) {
694 return;
695 }
696
697 global $wpdb;
698 $product = wc_get_product( $group_id );
699
700 // separators
701 $wc_decimal_separator = $this->wc_decimal_separator;
702 $wc_thousand_separator = $this->wc_thousand_separator;
703 $wc_price_decimal_separator = $this->wc_price_decimal_separator;
704
705 if ( ! is_wp_error( $product ) ) {
706
707 $item_group = $gp_arr;
708 // fwrite( $fd, PHP_EOL . 'Item Group : ' . print_r( $item_group, true ) );
709 $items = $item_group->items;
710 $attribute_name1 = $item_group->attribute_name1;
711 $attribute_name2 = $item_group->attribute_name2;
712 $attribute_name3 = $item_group->attribute_name3;
713
714 // fwrite( $fd, PHP_EOL . 'Items : ' . print_r( $items, true ) );
715 // get the options for stock sync
716 $zi_enable_locationstock = $this->config['Settings']['enable_location_stock'];
717 $location_id = $this->config['Settings']['zoho_location_id'];
718 $accounting_stock = $this->config['Settings']['enable_accounting_stock'];
719 $zi_disable_stock_sync = $this->config['Settings']['disable_stock'];
720
721 foreach ( $items as $item ) {
722 // reset this array
723 $attribute_arr = array();
724 $variation_id = '';
725 $status = $item->status === 'active' ? 'publish' : 'draft';
726
727 $zi_item_id = $item->item_id;
728 $variation_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s LIMIT 1", $zi_item_id ) );
729
730 if ( ! empty( $variation_id ) ) {
731 $v_product = wc_get_product( $variation_id );
732 // Check if the product object is valid
733 if ( $v_product && is_a( $v_product, 'WC_Product' ) ) {
734 if ( $v_product->is_type( 'simple' ) ) {
735 wp_delete_post( $variation_id, true );
736 }
737 }
738 }
739 // SKU check of the variation, if exits then remove it
740 if ( ! empty( $item->sku ) ) {
741 $sku_prod_id = wc_get_product_id_by_sku( $item->sku );
742 $v_product = wc_get_product( $sku_prod_id );
743 // Check if the product object is valid
744 if ( $v_product && is_a( $v_product, 'WC_Product' ) ) {
745 if ( $v_product->is_type( 'simple' ) ) {
746 wp_delete_post( $variation_id, true );
747 }
748 }
749 }
750 if ( ! empty( $variation_id ) ) {
751 continue;
752 }
753 // Stock mode check
754 $locations = $item->locations;
755
756 if ( $zi_enable_locationstock && $location_id ) {
757 foreach ( $locations as $location ) {
758 if ( $location->location_id === $location_id ) {
759 if ( $accounting_stock ) {
760 $stock = isset( $location->location_available_stock );
761 } else {
762 $stock = isset( $location->location_actual_available_stock );
763 }
764 }
765 }
766 } elseif ( $accounting_stock ) {
767 $stock = $item->available_stock;
768 } else {
769 $stock = $item->actual_available_stock;
770 }
771 // number format the rate
772 $rate = number_format( $item->rate, $wc_price_decimal_separator, $wc_decimal_separator, $wc_thousand_separator );
773
774 $attribute_name11 = $item->attribute_option_name1;
775 $attribute_name12 = $item->attribute_option_name2;
776 $attribute_name13 = $item->attribute_option_name3;
777 // Prepare the variation data
778 if ( ! empty( $attribute_name1 ) ) {
779 $sanitized_name1 = wc_sanitize_taxonomy_name( $attribute_name1 );
780 $attribute_arr[ $sanitized_name1 ] = $attribute_name11;
781 }
782 if ( ! empty( $attribute_name2 ) ) {
783 $sanitized_name2 = wc_sanitize_taxonomy_name( $attribute_name2 );
784 $attribute_arr[ $sanitized_name2 ] = $attribute_name12;
785 }
786 if ( ! empty( $attribute_name3 ) ) {
787 $sanitized_name3 = wc_sanitize_taxonomy_name( $attribute_name3 );
788 $attribute_arr[ $sanitized_name3 ] = $attribute_name13;
789 }
790 // fwrite( $fd, PHP_EOL . '$attribute_arr : ' . print_r( $attribute_arr, true ) );
791
792 // fwrite($fd, PHP_EOL . '$variation_attributes : ' . print_r($variation_attributes, true));
793 // Loop through the variations and create them
794 try {
795 $variation_post = array(
796 'post_title' => $product->get_name(),
797 'post_name' => 'product-' . $group_id . '-variation',
798 'post_status' => $status,
799 'post_parent' => $group_id,
800 'post_type' => 'product_variation',
801 'guid' => $product->get_permalink(),
802 );
803 // Creating the product variation
804 $variation_id = wp_insert_post( $variation_post );
805 if ( is_wp_error( $variation_id ) ) {
806 continue;
807 }
808 $variation = new WC_Product_Variation( $variation_id );
809 $variation->set_regular_price( $rate );
810 $variation->set_sku( $item->sku );
811 if ( ! $zi_disable_stock_sync && $stock > 0 ) {
812 $variation->set_stock_quantity( $stock );
813 $variation->set_manage_stock( true );
814 $variation->set_stock_status( '' );
815 } else {
816 $variation->set_manage_stock( false );
817 }
818 $variation->add_meta_data( 'zi_item_id', $item->item_id );
819 $variation_id = $variation->save();
820 } catch (Exception $e) {
821 // fwrite( $fd, PHP_EOL . 'Error : ' . $e->getMessage() );
822 continue;
823 }
824
825 // Get the variation attributes with correct attribute values
826 foreach ( $attribute_arr as $attribute => $term_name ) {
827 $taxonomy = 'pa_' . $attribute;
828 // If taxonomy doesn't exists we create it
829 if ( ! taxonomy_exists( $taxonomy ) ) {
830 register_taxonomy(
831 $taxonomy,
832 'product_variation',
833 array(
834 'hierarchical' => false,
835 'label' => ucfirst( $attribute ),
836 'query_var' => true,
837 'rewrite' => array( 'slug' => sanitize_title( $attribute ) ),
838 ),
839 );
840 }
841
842 // Check if the Term name exist and if not we create it.
843 if ( ! term_exists( $term_name, $taxonomy ) ) {
844 wp_insert_term( $term_name, $taxonomy );
845 }
846
847 $term_slug = get_term_by( 'name', $term_name, $taxonomy )->slug;
848 // Get the post Terms names from the parent variable product.
849 $post_term_names = wp_get_post_terms( $group_id, $taxonomy, array( 'fields' => 'names' ) );
850 // Check if the post term exist and if not we set it in the parent variable product.
851 if ( ! in_array( $term_name, $post_term_names, true ) ) {
852 wp_set_post_terms( $group_id, $term_name, $taxonomy, true );
853 }
854 // Set/save the attribute data in the product variation
855 update_post_meta( $variation_id, 'attribute_' . $taxonomy, $term_slug );
856 }
857
858 // update purchase price as meta data
859 if ( ! empty( $item->purchase_rate ) ) {
860 update_post_meta( $variation_id, '_cost_price', $item->purchase_rate );
861 }
862
863 // Featured Image of variation
864 if ( ! empty( $item->image_name ) ) {
865 $image_class = new CMBIRD_Image_ZI();
866 $variation_image_id = $image_class->cmbird_zi_get_image( $item->item_id, $item->name, $variation_id, $item->image_name );
867 if ( ! has_post_thumbnail( $group_id ) ) {
868 if ( $variation_image_id ) {
869 set_post_thumbnail( $group_id, $variation_image_id );
870 }
871 }
872 }
873 }
874 // End group item add process
875 // array_push($response_msg, $this->zi_response_message('SUCCESS', 'Zoho variable item created for zoho item id ' . $zi_item_id, $variation_id));
876
877 if ( $product && is_a( $product, 'WC_Product_Variable' ) ) {
878 // Sort the variations
879 $data_store = $product->get_data_store();
880 $data_store->sort_all_product_variations( $group_id );
881 }
882 // End of Logging
883 // fclose( $fd );
884 }
885 }
886
887 /**
888 * Update or Create the Product Attributes for the Variable Item Sync
889 *
890 * @param: $group_id - the parent product id in WooCommerce
891 * @return: bool - true if attributes were created successfully, false otherwise
892 */
893 public function sync_attributes_of_group( $gp_arr, $group_id ) {
894 // $fd = fopen(__DIR__ . '/sync_attributes_of_group.txt', 'a+');
895 // Check if the group item has attributes
896 if ( empty( $gp_arr->attribute_name1 ) ) {
897 return false;
898 }
899 // Create attributes
900 $success = true; // Track the success of attribute creation
901 $attributes_data = array();
902 $attribute_count = 0;
903 $attribute_options_map = array(); // Track unique attribute options
904
905 // Loop through the attribute names
906 for ( $i = 1; $i <= 3; $i++ ) {
907 $attribute_name_key = 'attribute_name' . $i;
908 $attribute_option_name_key = 'attribute_option_name' . $i;
909
910 // Get the attribute name
911 $attribute_name = $gp_arr->$attribute_name_key;
912
913 if ( ! empty( $attribute_name ) ) {
914 // Check if the attribute is already added to the attributes array
915 if ( ! isset( $attributes_data[ $attribute_name ] ) ) {
916 // Create the attribute and add it to the attributes array
917 $attribute = array(
918 'name' => $attribute_name,
919 'position' => $attribute_count,
920 'visible' => true,
921 'variation' => true,
922 'options' => array(),
923 );
924
925 // Loop through the items and retrieve attribute options
926 $attribute_options = array();
927 foreach ( $gp_arr->items as $item ) {
928 $attribute_option = $item->$attribute_option_name_key;
929 if ( ! empty( $attribute_option ) && ! in_array( $attribute_option, $attribute_options_map ) ) {
930 $attribute_options[] = $attribute_option;
931 $attribute_options_map[] = $attribute_option;
932 }
933 }
934
935 // Set the attribute options
936 $attribute['options'] = $attribute_options;
937
938 $attributes_data[] = $attribute;
939 ++$attribute_count;
940 }
941 }
942 }
943 // fwrite($fd, PHP_EOL . '$attributes : ' . print_r($attributes_data, true));
944
945 // Assign the attributes to the parent product
946 if ( count( $attributes_data ) > 0 ) {
947 $attributes = array(); // Initializing
948
949 // Loop through defined attribute data
950 foreach ( $attributes_data as $key => $attribute_array ) {
951 if ( isset( $attribute_array['name'] ) && isset( $attribute_array['options'] ) ) {
952 // Clean attribute name to get the taxonomy
953 $taxonomy = 'pa_' . wc_sanitize_taxonomy_name( $attribute_array['name'] );
954
955 $option_term_ids = array(); // Initializing
956
957 // Create the attribute if it doesn't exist
958 if ( ! taxonomy_exists( $taxonomy ) ) {
959 // Clean attribute label for better display
960 $attribute_label = ucfirst( $attribute_array['name'] );
961
962 // Register the new attribute taxonomy
963 $attribute_args = array(
964 'slug' => $taxonomy,
965 'name' => $attribute_label,
966 'type' => 'select',
967 'order_by' => 'menu_order',
968 'has_archives' => false,
969 );
970
971 $result = wc_create_attribute( $attribute_args );
972 register_taxonomy( $taxonomy, array( 'product' ), array() );
973
974 if ( ! is_wp_error( $result ) ) {
975 // fwrite($fd, PHP_EOL . 'result : ' . $result);
976 // Loop through defined attribute data options (terms values)
977 foreach ( $attribute_array['options'] as $option ) {
978 // Check if the term exists for the attribute taxonomy
979 $term = term_exists( $result, $taxonomy );
980 if ( empty( $term ) ) {
981 // Term doesn't exist, create a new one
982 $term_id = wp_insert_term( $option, $taxonomy );
983
984 if ( ! is_wp_error( $term_id ) ) {
985 $term_id = $term_id['term_id'];
986 } else {
987 $success = false;
988 $error_string = $term_id->get_error_message();
989 // fwrite($fd, PHP_EOL . 'error_string : ' . $error_string);
990 }
991 } else {
992 // Get the existing term ID
993 $term_id = $term['term_id'];
994 }
995 $option_term_ids[] = $term_id;
996 }
997
998 // Add the new attribute to the product attributes array
999 $attributes[ $taxonomy ] = array(
1000 'name' => $taxonomy,
1001 'value' => $option_term_ids, // Need to be term IDs
1002 'position' => $key + 1,
1003 'is_visible' => $attribute_array['visible'],
1004 'is_variation' => $attribute_array['variation'],
1005 'is_taxonomy' => '1',
1006 );
1007
1008 // Get the existing terms for the taxonomy
1009 $existing_terms = get_terms(
1010 array(
1011 'taxonomy' => $taxonomy,
1012 'hide_empty' => false,
1013 )
1014 );
1015
1016 // Loop through existing terms and assign them to the product
1017 foreach ( $existing_terms as $existing_term ) {
1018 $existing_term_ids[] = $existing_term->term_id;
1019 }
1020
1021 // Set the selected terms for the product
1022 wp_set_object_terms( $group_id, $existing_term_ids, $taxonomy );
1023 } else {
1024 $success = false;
1025 }
1026 } else {
1027 // Add existing attribute with its selected terms to the product attributes array
1028 $existing_terms = get_terms(
1029 array(
1030 'taxonomy' => $taxonomy,
1031 'hide_empty' => false,
1032 )
1033 );
1034
1035 if ( $existing_terms ) {
1036 $existing_term_ids = array();
1037 foreach ( $attribute_array['options'] as $option ) {
1038 $match_found = false;
1039 foreach ( $existing_terms as $existing_term ) {
1040 if ( $existing_term->name === $option ) {
1041 $existing_term_ids[] = $existing_term->term_id;
1042 $match_found = true;
1043 break;
1044 }
1045 }
1046 if ( ! $match_found ) {
1047 // Check if the term exists for the attribute taxonomy
1048 $term = term_exists( $option, $taxonomy );
1049
1050 if ( empty( $term ) ) {
1051 // Term doesn't exist, create a new one
1052 $term = wp_insert_term( $option, $taxonomy );
1053
1054 if ( ! is_wp_error( $term ) ) {
1055 // Get the term ID
1056 $term_id = $term['term_id'];
1057 $existing_term_ids[] = $term_id;
1058 } else {
1059 $success = false;
1060 }
1061 } else {
1062 // Get the existing term ID
1063 $term_id = $term['term_id'];
1064 $existing_term_ids[] = $term_id;
1065 }
1066 }
1067 }
1068
1069 if ( ! empty( $existing_term_ids ) ) {
1070 $attributes[ $taxonomy ] = array(
1071 'name' => $taxonomy,
1072 'value' => $existing_term_ids,
1073 'position' => $key + 1,
1074 'is_visible' => $attribute_array['visible'],
1075 'is_variation' => $attribute_array['variation'],
1076 'is_taxonomy' => '1',
1077 );
1078
1079 // Set the selected terms for the product
1080 wp_set_object_terms( $group_id, $existing_term_ids, $taxonomy, false );
1081 }
1082 } else {
1083 $option_term_ids = array(); // Initializing
1084
1085 // Loop through defined attribute data options (terms values)
1086 foreach ( $attribute_array['options'] as $option ) {
1087 // Check if the term exists for the attribute taxonomy
1088 $term = term_exists( $option, $taxonomy );
1089
1090 if ( empty( $term ) ) {
1091 // Term doesn't exist, create a new one
1092 $term = wp_insert_term( $option, $taxonomy );
1093
1094 if ( ! is_wp_error( $term ) ) {
1095 // Get the term ID
1096 $term_id = $term['term_id'];
1097 $option_term_ids[] = $term_id;
1098 } else {
1099 $success = false;
1100 }
1101 } else {
1102 // Get the existing term ID
1103 $term_id = $term['term_id'];
1104 $option_term_ids[] = $term_id;
1105 }
1106 }
1107
1108 // Set the selected terms for the product
1109 wp_set_object_terms( $group_id, $option_term_ids, $taxonomy, false );
1110
1111 // Add the new attribute to the product attributes array
1112 $attributes[ $taxonomy ] = array(
1113 'name' => $taxonomy,
1114 'value' => $option_term_ids,
1115 'position' => $key + 1,
1116 'is_visible' => $attribute_array['visible'],
1117 'is_variation' => $attribute_array['variation'],
1118 'is_taxonomy' => '1',
1119 );
1120 }
1121 }
1122 }
1123 }
1124
1125 // Save the meta entry for product attributes
1126 update_post_meta( $group_id, '_product_attributes', $attributes );
1127 $lookup_data_store = new LookupDataStore();
1128 $lookup_data_store->create_data_for_product( $group_id );
1129 }
1130 // fclose($fd);
1131 return $success;
1132 }
1133
1134 /**
1135 * Update or create variation in WooCommerce if Group-ID already exists in wpdB
1136 *
1137 * @param: $arr - item object coming in from simple item recursive function
1138 * @return: void
1139 */
1140 public function sync_variation_of_group( $item ) {
1141 // $fd = fopen( __DIR__ . '/sync_variation_of_group.txt', 'a+' );
1142
1143 // log the item
1144 // fwrite( $fd, PHP_EOL . 'Item : ' . print_r( $item, true ) );
1145
1146 global $wpdb;
1147 // Stock mode check
1148 $zi_disable_stock_sync = $this->config['Settings']['disable_stock'];
1149 $accounting_stock = $this->config['Settings']['enable_accounting_stock'];
1150 $is_tax_enabled = $this->is_tax_enabled;
1151
1152 if ( $accounting_stock ) {
1153 $stock = $item->available_stock;
1154 } else {
1155 $stock = $item->actual_available_stock;
1156 }
1157
1158 $item_id = $item->item_id;
1159 // $item_category = $item->category_name;
1160 $groupid = property_exists( $item, 'group_id' ) ? $item->group_id : 0;
1161 // find parent variable product
1162 $group_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s LIMIT 1", $groupid ) );
1163 // fwrite($fd, PHP_EOL . 'Row Data : ' . print_r($row, true));
1164 // fwrite($fd, PHP_EOL . 'Row $group_id : ' . $group_id);
1165 $stock_quantity = $stock < 0 ? 0 : $stock;
1166 // fwrite($fd, PHP_EOL . 'Before group item sync : ' . $group_id);
1167 if ( ! empty( $group_id ) ) {
1168 // fwrite( $fd, PHP_EOL . 'Inside item sync : ' . $item->name );
1169 // Brand
1170 if ( isset( $item->brand ) && ! empty( $group_id ) ) {
1171 if ( taxonomy_exists( 'product_brand' ) ) {
1172 wp_set_object_terms( $group_id, $item->brand, 'product_brand' );
1173 }
1174 }
1175
1176 $variation_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s LIMIT 1", $item_id ) );
1177 if ( ! empty( $item->sku ) && empty( $variation_id ) ) {
1178 $variation_id = wc_get_product_id_by_sku( $item->sku );
1179 }
1180 if ( $variation_id ) {
1181 // fwrite( $fd, PHP_EOL . 'Variation ID exists : ' . $variation_id );
1182 // If variation already exists then update it
1183 $variation = new WC_Product_Variation( $variation_id );
1184 // SKU - Imported
1185 if ( ! empty( $item->sku ) ) {
1186 $variation->set_sku( $item->sku );
1187 }
1188 // update purchase price as meta data
1189 if ( ! empty( $item->purchase_rate ) ) {
1190 update_post_meta( $variation_id, '_cost_price', $item->purchase_rate );
1191 }
1192 // Price - Imported
1193 $zi_disable_price_sync = $this->config['Settings']['disable_price'];
1194 $variation_sale_price = $variation->get_sale_price();
1195 if ( empty( $variation_sale_price ) && ! $zi_disable_price_sync ) {
1196 $variation->set_sale_price( $item->rate );
1197 }
1198 $variation->set_regular_price( $item->rate );
1199 // Set Tax Class
1200 if ( $item->tax_id && $is_tax_enabled ) {
1201 $zi_common_class = new CMBIRD_Common_Functions();
1202 $woo_tax_class = $zi_common_class->get_tax_class_by_percentage( $item->tax_percentage );
1203 $variation->set_tax_status( 'taxable' );
1204 $variation->set_tax_class( $woo_tax_class );
1205 }
1206 // Stock Imported code
1207 if ( ! $zi_disable_stock_sync && is_numeric( $stock_quantity ) ) {
1208 // fwrite( $fd, PHP_EOL . 'Stock Quantity : ' . $stock_quantity );
1209 $variation->set_manage_stock( true );
1210 if ( $stock_quantity > 0 ) {
1211 $variation->set_manage_stock( true );
1212 $variation->set_stock_quantity( $stock_quantity );
1213 $variation->set_stock_status( 'instock' );
1214 } elseif ( $stock_quantity <= 0 ) {
1215 $variation->set_manage_stock( true );
1216 $variation->set_stock_quantity( $stock_quantity );
1217 $stock_status = $variation->backorders_allowed() ? 'onbackorder' : 'outofstock';
1218 $variation->set_stock_status( $stock_status );
1219 }
1220 }
1221 // Featured Image of variation
1222 if ( ! empty( $item->image_document_id ) ) {
1223 $image_class = new CMBIRD_Image_ZI();
1224 $variation_image_id = $image_class->cmbird_zi_get_image( $item->item_id, $item->name, $variation_id, $item->image_name );
1225 if ( ! has_post_thumbnail( $group_id ) ) {
1226 if ( $variation_image_id ) {
1227 set_post_thumbnail( $group_id, $variation_image_id );
1228 }
1229 }
1230 }
1231 // enable or disable based on status from Zoho
1232 $status = ( 'active' === $item->status ) ? 'publish' : 'draft';
1233 $variation->set_status( $status );
1234 $variation->save();
1235 // clear cache
1236 wc_delete_product_transients( $variation_id );
1237 } else {
1238 // create new variation
1239 // if status is not active then return
1240 if ( 'active' !== $item->status ) {
1241 return;
1242 }
1243
1244 $attribute_name1 = $item->attribute_option_name1;
1245 $attribute_name2 = $item->attribute_option_name2;
1246 $attribute_name3 = $item->attribute_option_name3;
1247 // Prepare the variation data
1248 $attribute_arr = array();
1249 if ( ! empty( $attribute_name1 ) ) {
1250 $sanitized_name1 = wc_sanitize_taxonomy_name( $item->attribute_name1 );
1251 $attribute_arr[ $sanitized_name1 ] = $attribute_name1;
1252 }
1253 if ( ! empty( $attribute_name2 ) ) {
1254 $sanitized_name2 = wc_sanitize_taxonomy_name( $item->attribute_name2 );
1255 $attribute_arr[ $sanitized_name2 ] = $attribute_name2;
1256 }
1257 if ( ! empty( $attribute_name3 ) ) {
1258 $sanitized_name3 = wc_sanitize_taxonomy_name( $item->attribute_name3 );
1259 $attribute_arr[ $sanitized_name3 ] = $attribute_name3;
1260 }
1261 // fwrite( $fd, PHP_EOL . 'Attributes_arr: ' . print_r( $attribute_arr, true ) );
1262
1263 // here actually create new variation because sku not found
1264 $variation = new WC_Product_Variation();
1265 $variation->set_parent_id( $group_id );
1266 $variation->set_status( 'publish' );
1267 $variation->set_regular_price( $item->rate );
1268 $variation->set_sku( $item->sku );
1269 if ( ! $zi_disable_stock_sync ) {
1270 $variation->set_stock_quantity( $stock_quantity );
1271 $variation->set_manage_stock( true );
1272 $variation->set_stock_status( '' );
1273 } else {
1274 $variation->set_manage_stock( false );
1275 }
1276 $variation->add_meta_data( 'zi_item_id', $item->item_id );
1277 $variation_id = $variation->save();
1278
1279 // Get the variation attributes with correct attribute values
1280 foreach ( $attribute_arr as $attribute => $term_name ) {
1281 $taxonomy = $attribute;
1282 // If taxonomy doesn't exists we create it
1283 if ( ! taxonomy_exists( $taxonomy ) ) {
1284 register_taxonomy(
1285 $taxonomy,
1286 'product_variation',
1287 array(
1288 'hierarchical' => false,
1289 'label' => ucfirst( $attribute ),
1290 'query_var' => true,
1291 'rewrite' => array( 'slug' => sanitize_title( $attribute ) ),
1292 ),
1293 );
1294 }
1295
1296 // Check if the Term name exist and if not we create it.
1297 if ( ! term_exists( $term_name, $taxonomy ) ) {
1298 wp_insert_term( $term_name, $taxonomy );
1299 }
1300
1301 $term_slug = get_term_by( 'name', $term_name, $taxonomy )->slug;
1302 // Get the post Terms names from the parent variable product.
1303 $post_term_names = wp_get_post_terms( $group_id, $taxonomy, array( 'fields' => 'names' ) );
1304 // Check if the post term exist and if not we set it in the parent variable product.
1305 if ( ! in_array( $term_name, $post_term_names, true ) ) {
1306 wp_set_post_terms( $group_id, $term_name, $taxonomy, true );
1307 }
1308 // Set/save the attribute data in the product variation
1309 update_post_meta( $variation_id, 'attribute_' . $taxonomy, $term_slug );
1310 }
1311
1312 // update purchase price as meta data
1313 if ( ! empty( $item->purchase_rate ) ) {
1314 update_post_meta( $variation_id, '_cost_price', $item->purchase_rate );
1315 }
1316 // Stock
1317 if ( ! empty( $stock ) && ! $zi_disable_stock_sync ) {
1318 update_post_meta( $variation_id, 'manage_stock', true );
1319 if ( $stock > 0 ) {
1320 update_post_meta( $variation_id, '_stock', $stock );
1321 update_post_meta( $variation_id, '_stock_status', 'instock' );
1322 } else {
1323 $backorder_status = get_post_meta( $group_id, '_backorders', true );
1324 update_post_meta( $variation_id, '_stock', $stock );
1325 if ( $backorder_status === 'yes' ) {
1326 update_post_meta( $variation_id, '_stock_status', 'onbackorder' );
1327 } else {
1328 update_post_meta( $variation_id, '_stock_status', 'outofstock' );
1329 }
1330 }
1331 }
1332 // Featured Image of variation
1333 if ( ! empty( $item->image_document_id ) ) {
1334 $image_class = new CMBIRD_Image_ZI();
1335 $variation_image_id = $image_class->cmbird_zi_get_image( $item->item_id, $item->name, $variation_id, $item->image_name );
1336 if ( ! has_post_thumbnail( $group_id ) ) {
1337 if ( $variation_image_id ) {
1338 set_post_thumbnail( $group_id, $variation_image_id );
1339 }
1340 }
1341 }
1342 update_post_meta( $variation_id, 'zi_item_id', $item_id );
1343 WC_Product_Variable::sync( $group_id );
1344 // Regenerate lookup table for attributes
1345 $lookup_data_store = new LookupDataStore();
1346 $lookup_data_store->create_data_for_product( $group_id );
1347 // End group item add process
1348 unset( $attribute_arr );
1349 }
1350 // end of grouped item updating
1351 } else {
1352 // fwrite($fd, PHP_EOL . 'Group item empty');
1353 return;
1354 }
1355 // fwrite($fd, PHP_EOL . 'After group item sync');
1356 // fclose( $fd );
1357 }
1358
1359 /**
1360 * Helper Function to check if child of composite items already synced or not
1361 *
1362 * @param string $composite_zoho_id - zoho composite item id to check if it's child are already synced.
1363 * @param string $zi_url - zoho api url.
1364 * @param string $zi_key - zoho access token.
1365 * @param string $zi_org_id - zoho organization id.
1366 * @return array | bool of child id and metadata if child item already synced else will return false.
1367 */
1368 public function zi_check_if_child_synced_already( $composite_zoho_id, $zi_url, $zi_org_id, $prod_id ) {
1369 if ( $prod_id ) {
1370 $bundle_childs = WC_PB_DB::query_bundled_items(
1371 array(
1372 'return' => 'id=>product_id',
1373 'bundle_id' => array( $prod_id ),
1374 )
1375 );
1376 }
1377 global $wpdb;
1378
1379 $url = $zi_url . 'inventory/v1/compositeitems/' . $composite_zoho_id . '?organization_id=' . $zi_org_id;
1380
1381 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
1382 $json = $execute_curl_call->execute_curl_call_get( $url );
1383 $code = $json->code;
1384 // Flag to allow sync of parent composite item.
1385 $allow_sync = false;
1386 // Array of child object metadata.
1387 $product_array = array(); // [{prod_id:'',metadata:{key:'',value:''}},...].
1388 if ( '0' === $code || 0 === $code ) {
1389 foreach ( $json->composite_item->mapped_items as $child_item ) {
1390 $prod_meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s", $child_item->item_id ) );
1391 // If any child will not have zoho id in meta field then process will return false and syncing will be skipped for given item.
1392 if ( ! empty( $prod_meta->post_id ) ) {
1393
1394 $allow_sync = true;
1395 $product = wc_get_product( $prod_meta->post_id );
1396 $stock_status = 'out_of_stock';
1397 if ( $child_item->stock_on_hand > 0 ) {
1398 $stock_status = 'in_stock';
1399 } elseif ( $product && $product->backorders_allowed() ) {
1400 $stock_status = 'onbackorder';
1401 }
1402 $prod_obj = (object) array(
1403 'prod_id' => $prod_meta->post_id,
1404 'metadata' => (object) array(
1405 'quantity_min' => max( 1, $child_item->quantity ),
1406 'quantity_max' => max( 1, $child_item->quantity ),
1407 'stock_status' => $stock_status,
1408 'max_stock' => $child_item->stock_on_hand,
1409 ),
1410 );
1411 if ( is_array( $bundle_childs ) && ! empty( $bundle_childs ) ) {
1412 $index = array_search( $prod_meta->post_id, $bundle_childs );
1413 unset( $bundle_childs[ $index ] );
1414 }
1415 array_push( $product_array, $prod_obj );
1416 } else {
1417 continue;
1418 }
1419 }
1420 }
1421 if ( is_array( $bundle_childs ) && ! empty( $bundle_childs ) ) {
1422 foreach ( $bundle_childs as $item_id => $val ) {
1423 WC_PB_DB::delete_bundled_item( $item_id );
1424 }
1425 }
1426 if ( $allow_sync ) {
1427 return $product_array;
1428 }
1429 return false;
1430 }
1431 /**
1432 * Mapping of bundled product
1433 *
1434 * @param number $product_id - Product id of child item of bundle product.
1435 * @param number $bundle_id - Bundle id of product.
1436 * @param number $menu_order - Listing order of child product ($menu_order will useful at composite product details page).
1437 * @return void
1438 */
1439 public function add_bundle_product( $product_id, $bundle_id, $menu_order = 0 ) {
1440 $bundle_items = WC_PB_DB::query_bundled_items(
1441 array(
1442 'return' => 'id=>product_id',
1443 'bundle_id' => array( $bundle_id ),
1444 'product_id' => array( $product_id ),
1445 )
1446 );
1447 $data = array(
1448 'menu_order' => $menu_order,
1449 );
1450
1451 if ( count( $bundle_items ) > 0 ) {
1452 $result = WC_PB_DB::update_bundled_item( $bundle_id, $data );
1453 return $result;
1454 } else {
1455 // create data array of bundle item.
1456 $data = array(
1457 'product_id' => $product_id,
1458 'bundle_id' => $bundle_id,
1459 'menu_order' => $menu_order,
1460 );
1461 $bundle_id = WC_PB_DB::add_bundled_item( $data );
1462 return $bundle_id;
1463 }
1464 }
1465
1466 /**
1467 * Create or update bundle item metadata
1468 *
1469 * @param number $bundle_item_id bundle item id.
1470 * @param string $meta_key - metadata key.
1471 * @param string $meta_value - metadata value.
1472 * @return void
1473 */
1474
1475 public function zi_update_bundle_meta( $bundle_item_id, $meta_key, $meta_value ) {
1476 // first get metadata from db.
1477 $metadata = WC_PB_DB::get_bundled_item_meta( $bundle_item_id, $meta_key );
1478 if ( $metadata ) {
1479 $result = WC_PB_DB::update_bundled_item_meta( $bundle_item_id, $meta_key, $meta_value );
1480 } else {
1481 $result = WC_PB_DB::add_bundled_item_meta( $bundle_item_id, $meta_key, $meta_value );
1482 return $result;
1483 }
1484 }
1485
1486 /**
1487 * Function to sync composite item from zoho to woocommerce
1488 *
1489 * @param integer $page - Page number of composite item data.
1490 * @param string $category - Category id of composite data.
1491 * @return mixed - mostly array of response message.
1492 */
1493 public function recursively_sync_composite_item_from_zoho( $page, $category ) {
1494 // Start logging
1495 // $fd = fopen( __DIR__ . '/recursively_sync_composite_item_from_zoho.txt', 'a+' );
1496
1497 global $wpdb;
1498 $zi_org_id = $this->config['ProductZI']['OID'];
1499 $zi_url = $this->config['ProductZI']['APIURL'];
1500
1501 $current_user = wp_get_current_user();
1502 $admin_author_id = $current_user->ID;
1503 if ( ! $admin_author_id ) {
1504 $admin_author_id = 1;
1505 }
1506
1507 $url = $zi_url . 'inventory/v1/compositeitems/?organization_id=' . $zi_org_id . '&filter_by=Status.Active&category_id=' . $category . '&page=' . $page;
1508
1509 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
1510 $json = $execute_curl_call->execute_curl_call_get( $url );
1511 $code = $json->code;
1512 // $message = $json->message;
1513 // fwrite($fd, PHP_EOL . '$json : ' . print_r($json, true));
1514 // Response for item sync with sync button. For cron sync blank array will return.
1515 $response_msg = array();
1516 if ( $code === '0' || $code === 0 ) {
1517 if ( empty( $json->composite_items ) ) {
1518 array_push( $response_msg, $this->zi_response_message( 'ERROR', 'No composite item to sync for category : ' . $category ) );
1519 return $response_msg;
1520 }
1521 // Accounting stock mode check
1522 $accounting_stock = $this->config['Settings']['enable_accounting_stock'];
1523 foreach ( $json->composite_items as $comp_item ) {
1524 // fwrite( $fd, PHP_EOL . 'Composite Item : ' . print_r( $comp_item, true ) );
1525 // Sync stock from specific location check
1526 $zi_enable_locationstock = $this->config['Settings']['enable_locationstock'];
1527 $location_id = $this->config['Settings']['location_id'];
1528 $locations = $comp_item->locations;
1529
1530 if ( $zi_enable_locationstock === true ) {
1531 foreach ( $locations as $location ) {
1532 if ( $location->location_id === $location_id ) {
1533 if ( $accounting_stock ) {
1534 $stock = $location->location_available_for_sale_stock;
1535 } else {
1536 $stock = $location->location_actual_available_for_sale_stock;
1537 }
1538 }
1539 }
1540 } elseif ( $accounting_stock ) {
1541 $stock = $comp_item->available_for_sale_stock;
1542 } else {
1543 $stock = $comp_item->actual_available_for_sale_stock;
1544 }
1545
1546 // ----------------- Create composite item in woocommerce--------------.
1547 // Code to skip sync with item already exists with same sku.
1548 $prod_id = wc_get_product_id_by_sku( $comp_item->sku );
1549 // Flag to enable or disable sync.
1550 $allow_to_import = false;
1551 // Check if product exists with same sku.
1552 if ( $prod_id ) {
1553 $zi_item_id = get_post_meta( $prod_id, 'zi_item_id', true );
1554 if ( $zi_item_id === $comp_item->composite_item_id ) {
1555 // If product is with same sku and zi_item_id mapped.
1556 // Do not import ...
1557 $allow_to_import = false;
1558 } else {
1559 // Map existing item with zoho id.
1560 update_post_meta( $prod_id, 'zi_item_id', $comp_item->composite_item_id );
1561 $allow_to_import = false;
1562 }
1563 } else {
1564 // If product not exists normal bahaviour of item sync.
1565 $allow_to_import = true;
1566 }
1567 $zoho_comp_item_id = $comp_item->composite_item_id;
1568 if ( $comp_item->composite_item_id ) {
1569 $child_items = $this->zi_check_if_child_synced_already( $zoho_comp_item_id, $zi_url, $zi_org_id, $prod_id );
1570 // Check if child items already synced with zoho.
1571 if ( ! $child_items ) {
1572 array_push( $response_msg, $this->zi_response_message( 'ERROR', 'Child not synced for composite item : ' . $zoho_comp_item_id ) );
1573 continue;
1574 }
1575 $product_res = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->postmeta} WHERE meta_key = 'zi_item_id' AND meta_value = %s", $zoho_comp_item_id ) );
1576 if ( ! empty( $product_res->post_id ) ) {
1577 $com_prod_id = $product_res->post_id;
1578 }
1579 // Check if item is allowed to import or not.
1580 if ( $allow_to_import ) {
1581 $product_class = new CMBIRD_Products_ZI_Export();
1582 $item_array = json_decode( wp_json_encode( $comp_item ), true );
1583 $com_prod_id = $product_class->cmbird_zi_product_to_woocommerce( $item_array, $stock, 'composite' );
1584 update_post_meta( $com_prod_id, 'zi_item_id', $zoho_comp_item_id );
1585 }
1586 }
1587 // Map composite items to database.
1588 if ( ! empty( $com_prod_id ) ) {
1589 wp_set_object_terms( $com_prod_id, 'bundle', 'product_type' );
1590 foreach ( $child_items as $child_prod ) {
1591 // Adding product to bundle.
1592 $child_bundle_id = $this->add_bundle_product( $child_prod->prod_id, $com_prod_id );
1593 if ( $child_bundle_id ) {
1594 foreach ( $child_prod->metadata as $bundle_meta_key => $bundle_meta_val ) {
1595 $this->zi_update_bundle_meta( $child_bundle_id, $bundle_meta_key, $bundle_meta_val );
1596 }
1597 }
1598 }
1599 }
1600 // --------------------------------------------------------------------.
1601
1602 $is_synced_flag = false; // loggin purpose only .
1603
1604 $product = wc_get_product( $com_prod_id );
1605 foreach ( $comp_item as $key => $value ) {
1606 if ( $key === 'status' ) {
1607 if ( ! empty( $com_prod_id ) ) {
1608 $status = $value === 'active' ? 'publish' : 'draft';
1609 $product->set_status( $status );
1610 }
1611 }
1612 if ( $key === 'description' ) {
1613 if ( ! empty( $com_prod_id ) && ! empty( $value ) ) {
1614 $product->set_short_description( $value );
1615 }
1616 }
1617 if ( $key === 'name' ) {
1618 if ( ! empty( $com_prod_id ) ) {
1619 $product->set_name( $value );
1620 }
1621 }
1622 if ( $key === 'sku' ) {
1623 if ( ! empty( $com_prod_id ) ) {
1624 $product->set_sku( $value );
1625 }
1626 }
1627 // Check if stock sync allowed by plugin.
1628 if ( $key === 'available_stock' || $key === 'actual_available_stock' ) {
1629 $zi_disable_stock_sync = $this->config['Settings']['disable_stock'];
1630 if ( ! $zi_disable_stock_sync ) {
1631 if ( $stock ) {
1632 if ( ! empty( $com_prod_id ) ) {
1633 // If value is less than 0 default 1.
1634 $stock_quantity = $stock < 0 ? 0 : $stock;
1635 $product->set_manage_stock( true );
1636 $product->set_stock_quantity( $stock_quantity );
1637 if ( $stock_quantity > 0 ) {
1638 $status = 'instock';
1639 } else {
1640 $backorder_status = $product->backorders_allowed();
1641 $status = $backorder_status ? 'onbackorder' : 'outofstock';
1642 }
1643 $product->set_stock_status( $status );
1644 update_post_meta( $com_prod_id, '_wc_pb_bundled_items_stock_status', $status );
1645 }
1646 }
1647 }
1648 }
1649 if ( $key === 'rate' ) {
1650 if ( ! empty( $com_prod_id ) ) {
1651 $sale_price = $product->get_sale_price();
1652 if ( empty( $sale_price ) ) {
1653 $product->set_regular_price( $value );
1654 $product->set_price( $value );
1655 update_post_meta( $com_prod_id, '_wc_pb_base_price', $value );
1656 update_post_meta( $com_prod_id, '_wc_pb_base_regular_price', $value );
1657 update_post_meta( $com_prod_id, '_wc_sw_max_regular_price', $value );
1658 } else {
1659 $product->set_regular_price( $value );
1660 update_post_meta( $com_prod_id, '_wc_pb_base_price', $value );
1661 update_post_meta( $com_prod_id, '_wc_pb_base_regular_price', $value );
1662 update_post_meta( $com_prod_id, '_wc_sw_max_regular_price', $value );
1663 }
1664 }
1665 }
1666 $product->save();
1667
1668 if ( $key === 'image_document_id' ) {
1669 if ( ! empty( $com_prod_id ) && ! empty( $value ) ) {
1670 $image_class = new CMBIRD_Image_ZI();
1671 $image_class->cmbird_zi_get_image( $zoho_comp_item_id, $comp_item->name, $com_prod_id, $comp_item->image_name );
1672 }
1673 }
1674 if ( $key === 'category_name' ) {
1675 if ( ! empty( $com_prod_id ) && $comp_item->category_name != '' ) {
1676 $term = get_term_by( 'name', $comp_item->category_name, 'product_cat' );
1677 $term_id = $term->term_id;
1678 if ( empty( $term_id ) ) {
1679 $term = wp_insert_term(
1680 $comp_item->category_name,
1681 'product_cat',
1682 array(
1683 'parent' => 0,
1684 )
1685 );
1686 $term_id = $term['term_id'];
1687 }
1688 if ( $term_id ) {
1689 $existing_terms = wp_get_object_terms( $com_prod_id, 'product_cat' );
1690 if ( $existing_terms && count( $existing_terms ) > 0 ) {
1691 $is_terms_exist = $this->zi_check_terms_exists( $existing_terms, $term_id );
1692 if ( ! $is_terms_exist ) {
1693 update_post_meta( $com_prod_id, 'zi_category_id', $category );
1694 wp_add_object_terms( $com_prod_id, $term_id, 'product_cat' );
1695 }
1696 } else {
1697 update_post_meta( $com_prod_id, 'zi_category_id', $category );
1698 wp_set_object_terms( $com_prod_id, $term_id, 'product_cat' );
1699 }
1700 }
1701 // Remove "uncategorized" category if assigned
1702 $uncategorized_term = get_term_by( 'slug', 'uncategorized', 'product_cat' );
1703 if ( $uncategorized_term && has_term( $uncategorized_term->term_id, 'product_cat', $com_prod_id ) ) {
1704 wp_remove_object_terms( $com_prod_id, $uncategorized_term->term_id, 'product_cat' );
1705 }
1706 }
1707 }
1708 }
1709
1710 // sync dimensions and weight
1711 $item_url = "{$zi_url}inventory/v1/compositeitems/{$zoho_comp_item_id}?organization_id={$zi_org_id}";
1712 $this->zi_item_dimension_weight( $item_url, $com_prod_id, true );
1713
1714 // If item synced append to log : logging purpose only.
1715 if ( $is_synced_flag ) {
1716 array_push( $response_msg, $this->zi_response_message( 'SUCCESS', 'Composite item synced for id : ' . $comp_item->composite_item_id, $com_prod_id ) );
1717 }
1718 }
1719
1720 if ( $json->page_context->has_more_page ) {
1721 ++$page;
1722 $this->recursively_sync_composite_item_from_zoho( $page, $category );
1723 }
1724 } else {
1725 array_push( $response_msg, $this->zi_response_message( $code, $json->message ) );
1726 }
1727 // fclose( $fd ); // End of logging.
1728
1729 return $response_msg;
1730 }
1731
1732 /**
1733 * Function to retrieve item details, update weight and dimensions.
1734 *
1735 * @param string $url - URL to ge details.
1736 * @return mixed return true if data false if error.
1737 */
1738 public function zi_item_dimension_weight( $url, $product_id, $is_composite = false ) {
1739 // $fd = fopen(__DIR__ . '/zi_item_dimension_weight.txt', 'a+');
1740 // Check if item is for syncing purpose.
1741 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
1742 $json = $execute_curl_call->execute_curl_call_get( $url );
1743 $code = $json->code;
1744 $message = $json->message;
1745 if ( 0 === $code || '0' === $code ) {
1746 if ( $is_composite ) {
1747 // fwrite($fd, PHP_EOL . '$json : ' . print_r($json, true));
1748 $details = $json->composite_item->package_details;
1749 } else {
1750 $details = $json->item->package_details;
1751 }
1752 $product = wc_get_product( $product_id );
1753 $product->set_weight( floatval( $details->weight ) );
1754 $product->set_length( floatval( $details->length ) );
1755 $product->set_width( floatval( $details->width ) );
1756 $product->set_height( floatval( $details->height ) );
1757 $product->save();
1758 } else {
1759 false;
1760 }
1761 // fclose($fd);
1762 }
1763
1764 /**
1765 * Create response object based on data.
1766 *
1767 * @param mixed $index_col - Index value error message.
1768 * @param string $message - Response message.
1769 * @return object
1770 */
1771 public function zi_response_message( $index_col, $message, $woo_id = '' ) {
1772 return (object) array(
1773 'resp_id' => $index_col,
1774 'message' => $message,
1775 'woo_prod_id' => $woo_id,
1776 );
1777 }
1778
1779 /**
1780 * Helper Function to check if terms already exists.
1781 */
1782 public function zi_check_terms_exists( $existing_terms, $term_id ) {
1783 foreach ( $existing_terms as $woo_existing_term ) {
1784 if ( $woo_existing_term->term_id === $term_id ) {
1785 return true;
1786 } else {
1787 return false;
1788 }
1789 }
1790 }
1791 }
1792 $cmbird_products_zi = new CMBIRD_Products_ZI();
1793