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