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