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