PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.5.1
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.5.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-product.php
commercebird / includes / classes / zoho-inventory Last commit date
class-cmbird-categories-zi.php 9 months ago class-cmbird-image-zi.php 9 months ago class-import-items.php 9 months ago class-import-price-list.php 9 months ago class-multi-currency.php 9 months ago class-order-sync.php 9 months ago class-product.php 9 months ago class-users-contact.php 9 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 - 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 = str_replace( '"', '', $vname );
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, '_cost_price', 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( "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