PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / 2.5.0
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). v2.5.0
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 / apis / class-api-for-product-webhook.php
commercebird / includes / classes / apis Last commit date
class-api-for-cmbird.php 9 months ago class-api-for-exact-webhooks.php 9 months ago class-api-for-product-webhook.php 9 months ago class-api-for-shipping-status.php 9 months ago class-api-for-woo-order.php 9 months ago class-api-for-zoho-inventory.php 9 months ago class-commercebird-list-items-api-controller.php 10 months ago class-commercebird-media-api-controller.php 10 months ago class-commercebird-metadata-controller.php 9 months ago index.php 1 year ago trait-api-permission.php 9 months ago
class-api-for-product-webhook.php
680 lines
1 <?php
2
3 namespace CommerceBird\API;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 use CMBIRD_Image_ZI;
10 use CMBIRD_Products_ZI;
11 use CMBIRD_Products_ZI_Export;
12 use WC_Data_Exception;
13 use WC_Product_Variation;
14 use WC_Product_Variable;
15 use WP_REST_Response;
16 use wpdb;
17 use CMBIRD_Common_Functions;
18 use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
19
20 /**
21 * Handles product webhook operations for CommerceBird API.
22 */
23 class ProductWebhook {
24
25
26 use Api;
27
28 /**
29 * REST API endpoint for Zoho product webhook.
30 *
31 * @var string
32 */
33 private static string $endpoint = 'zoho-product';
34
35 /**
36 * Indicates if WooCommerce taxes are enabled.
37 *
38 * @var bool
39 */
40 private $is_tax_enabled;
41
42 /**
43 * ProductWebhook constructor.
44 * Registers REST route, checks tax settings, and sets up hooks for queue processing and cleanup.
45 */
46 public function __construct() {
47 register_rest_route(
48 self::$namespace,
49 self::$endpoint,
50 array(
51 'methods' => 'POST',
52 'callback' => array( $this, 'handle' ),
53 'permission_callback' => '__return_true',
54 )
55 );
56 // Check if WooCommerce taxes are enabled and store the result.
57 $this->is_tax_enabled = 'yes' === get_option( 'woocommerce_calc_taxes' );
58
59 // Register queue processing hook.
60 add_action( 'cmbird_process_webhook_queue', array( $this, 'process_queued_requests' ) );
61
62 // Add cleanup for old rate limit data.
63 add_action( 'wp_scheduled_delete', array( $this, 'cleanup_rate_limit_data' ) );
64 }
65
66 /**
67 * Retrieves whether WooCommerce taxes are enabled.
68 *
69 * @return bool
70 */
71 public function is_tax_enabled(): bool {
72 return $this->is_tax_enabled;
73 }
74
75
76 /**
77 * Processes incoming product webhook data.
78 *
79 * @throws WC_Data_Exception If there is an error with WooCommerce product data.
80 */
81 private function process( array $data ): WP_REST_Response {
82 $response = new WP_REST_Response();
83 $response->set_data( $this->empty_response );
84 $response->set_status( 404 );
85 if ( ! array_key_exists( 'item', $data ) && ! array_key_exists( 'inventory_adjustment', $data ) ) {
86 return $response;
87 }
88
89 // Accounting stock mode check.
90 $accounting_stock = get_option( 'cmbird_zoho_enable_accounting_stock_status' );
91 $zi_enable_locationstock = get_option( 'cmbird_zoho_enable_locationstock_status' );
92 $location_id = get_option( 'cmbird_zoho_location_id_status' );
93
94 // variable item sync.
95 if ( array_key_exists( 'item', $data ) ) {
96 return $this->process_product_data( $data['item'], $zi_enable_locationstock, $location_id, $accounting_stock );
97 }
98 // inventory_adjustment.
99 if ( array_key_exists( 'inventory_adjustment', $data ) ) {
100 return $this->inventory_adjustment( $data['inventory_adjustment'] );
101 }
102
103 return $response;
104 }
105
106 /**
107 * @param $item
108 * @param $zi_enable_locationstock
109 * @param $location_id
110 * @param $accounting_stock
111 *
112 * @return WP_REST_Response
113 * @throws WC_Data_Exception
114 */
115 public function process_product_data( $item, $zi_enable_locationstock, $location_id, $accounting_stock ): WP_REST_Response {
116 // $fd = fopen( __DIR__ . '/process_product_data.txt', 'a+' );
117
118 // clean orphaned data from the database.
119 $common_class = new CMBIRD_Common_Functions();
120 $common_class->clear_orphan_data();
121
122 global $wpdb;
123 $item_id = $item['item_id'];
124 $item_name = $item['name'];
125 $item_price = $item['rate'];
126 $item_sku = $item['sku'];
127 $item_description = $item['description'];
128 $item_status = $item['status'] === 'active' ? 'publish' : 'draft';
129 $item_brand = $item['brand'];
130 $category_id = $item['category_id'];
131 $custom_fields = $item['custom_fields'];
132 $item_image = $item['image_name'];
133 // Stock mode check.
134 $locations = $item['locations'];
135 if ( $zi_enable_locationstock ) {
136 foreach ( $locations as $location ) {
137 if ( $location['location_id'] === $location_id ) {
138 if ( $accounting_stock ) {
139 $item_stock = $location['location_available_for_sale_stock'];
140 } else {
141 $item_stock = $location['location_actual_available_for_sale_stock'];
142 }
143 }
144 }
145 } elseif ( $accounting_stock && ! $zi_enable_locationstock ) {
146 $item_stock = $item['available_for_sale_stock'];
147 } else {
148 $item_stock = $item['actual_available_for_sale_stock'];
149 }
150 if ( isset( $item['group_name'] ) ) {
151 $group_name = $item['group_name'];
152 } else {
153 $group_name = '';
154 }
155 $item_category = $item['category_name'];
156 if ( isset( $item['group_id'] ) ) {
157 $groupid = $item['group_id'];
158 } else {
159 $groupid = '';
160 }
161
162 // Item package details.
163 $details = $item['package_details'];
164 $weight = floatval( $details['weight'] );
165 $length = floatval( $details['length'] );
166 $width = floatval( $details['width'] );
167 $height = floatval( $details['height'] );
168
169 // fwrite($fd, PHP_EOL . '$groupid : ' . $groupid);
170 if ( ! empty( $groupid ) ) {
171 // fwrite($fd, PHP_EOL . 'Inside grouped items');
172 // find parent variable product.
173 $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key='zi_item_id' AND meta_value=%s", $groupid ) );
174 $group_id = $row->post_id;
175
176 if ( ! empty( $group_id ) ) {
177 $existing_parent_product = wc_get_product( $group_id );
178 $zi_disable_itemdescription_sync = get_option( 'cmbird_zoho_disable_description_sync_status' );
179 if ( ! empty( $item_description ) && ! $zi_disable_itemdescription_sync ) {
180 // fwrite($fd, PHP_EOL . 'Item description update : ' . $item_description);
181 $existing_parent_product->set_short_description( $item_description );
182 }
183 // Update the name of the variable product if allowed.
184 $zi_disable_itemname_sync = get_option( 'cmbird_zoho_disable_name_sync_status' );
185 if ( ! $zi_disable_itemname_sync ) {
186 $existing_parent_product->set_name( $item['group_name'] );
187 $slug = sanitize_title( $item['group_name'] );
188 $existing_parent_product->set_slug( $slug );
189 }
190 // Brand update if taxonomy product_brand exists.
191 if ( ! empty( $item_brand ) && taxonomy_exists( 'product_brand' ) ) {
192 wp_set_object_terms( $groupid, $item_brand, 'product_brand' );
193 } elseif ( ! empty( $item_brand ) && taxonomy_exists( 'product_brand' ) ) {
194 wp_set_object_terms( $groupid, $item_brand, 'product_brand' );
195 }
196 // Update the custom fields if the custom fields are not empty.
197 $cmbird_product_zi = new CMBIRD_Products_ZI();
198 if ( ! empty( $custom_fields ) ) {
199 $cmbird_product_zi->sync_item_custom_fields( $custom_fields, $groupid );
200 }
201
202 // Create or Update the Attributes.
203 // turn $item into array.
204 $gp_arr = (array) $item;
205 $attr_created = $cmbird_product_zi->sync_attributes_of_group( $gp_arr, $group_id );
206 if ( ! empty( $group_id ) && $attr_created ) {
207 // Create the variations.
208 $cmbird_product_zi->import_variable_product_variations( $item, $group_id );
209 }
210
211 // set the product status of the variable parent product.
212 // $existing_parent_product->set_status( $item_status );
213 $existing_parent_product->save();
214 } else {
215 // Add in scheduler to create the Variable Product.
216 $last_synced_page = get_option( 'cmbird_group_item_sync_page_cat_id_' . $category_id );
217 if ( ! intval( $last_synced_page ) ) {
218 $last_synced_page = 1;
219 }
220 $data = array(
221 'page' => $last_synced_page,
222 'category' => $category_id,
223 );
224 $existing_schedule = as_has_scheduled_action( 'import_group_items_cron', $data );
225 // Schedule the action if it doesn't exist.
226 if ( ! $existing_schedule ) {
227 as_schedule_single_action( time(), 'import_group_items_cron', $data );
228 }
229 }
230
231 $row_item = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key='zi_item_id' AND meta_value=%s", $item_id ) );
232 $variation_id = $row_item->post_id;
233 if ( $variation_id ) {
234 // updating existing variations.
235 $variation = new WC_Product_Variation( $variation_id );
236 // Prices.
237 if ( ! empty( $item_price ) ) {
238 $variation->set_price( $item_price );
239 }
240 $variation->set_regular_price( $item_price );
241 // Stock.
242 $zi_disable_stock_sync = get_option( 'cmbird_zoho_disable_stock_sync_status' );
243 if ( ! empty( $item_stock ) && ! $zi_disable_stock_sync ) {
244 // fwrite($fd, PHP_EOL . 'Stock is here:'. $item_stock );
245 $variation->set_stock_quantity( $item_stock );
246 $variation->set_manage_stock( true );
247 // $variation->set_stock_status('');
248 } else {
249 // fwrite($fd, PHP_EOL . 'Available Stock : false');
250 $variation->set_manage_stock( false );
251 }
252 // featured image.
253 $zi_disable_itemimage_sync = get_option( 'cmbird_zoho_disable_image_sync_status' );
254 if ( ! empty( $item_image ) && ! $zi_disable_itemimage_sync ) {
255 // fwrite($fd, PHP_EOL . 'Sync Image' );
256 $image_class = new CMBIRD_Image_ZI();
257 $image_class->cmbird_zi_get_image( $item_id, $item_name, $variation_id, $item_image );
258 }
259 // Disable or enable the variation based on the item_status.
260 $variation->set_status( $item_status );
261 // Update Purchase price.
262 $variation->update_meta_data( '_cost_price', $item['purchase_rate'] );
263
264 // Map taxes while syncing product from zoho.
265 if ( $item['tax_id'] && ! $this->is_tax_enabled() ) {
266 $zi_common_class = new CMBIRD_Common_Functions();
267 $woo_tax_class = $zi_common_class->get_tax_class_by_percentage( $item['tax_percentage'] );
268 $variation->set_tax_status( 'taxable' );
269 $variation->set_tax_class( $woo_tax_class );
270 }
271 // weight & dimensions.
272 $variation->set_weight( $weight );
273 $variation->set_length( $length );
274 $variation->set_width( $width );
275 $variation->set_height( $height );
276
277 $variation->save(); // Save the data.
278 } elseif ( 'publish' === $item_status ) {
279 $attribute_name11 = $item['attribute_option_name1'];
280 $attribute_name12 = $item['attribute_option_name2'];
281 $attribute_name13 = $item['attribute_option_name3'];
282 // Prepare the variation data.
283 $attribute_arr = array();
284 if ( ! empty( $attribute_name11 ) ) {
285 $sanitized_name1 = wc_sanitize_taxonomy_name( $item['attribute_name1'] );
286 $attribute_arr[ $sanitized_name1 ] = $attribute_name11;
287 }
288 if ( ! empty( $attribute_name12 ) ) {
289 $sanitized_name2 = wc_sanitize_taxonomy_name( $item['attribute_name2'] );
290 $attribute_arr[ $sanitized_name2 ] = $attribute_name12;
291 }
292 if ( ! empty( $attribute_name13 ) ) {
293 $sanitized_name3 = wc_sanitize_taxonomy_name( $item['attribute_name3'] );
294 $attribute_arr[ $sanitized_name3 ] = $attribute_name13;
295 }
296
297 // here actually create new variation because sku not found.
298 $zi_disable_stock_sync = get_option( 'cmbird_zoho_disable_stock_sync_status' );
299 $variation = new WC_Product_Variation();
300 $variation->set_parent_id( $group_id );
301 $variation->set_status( 'publish' );
302 $variation->set_regular_price( $item_price );
303 $variation->set_sku( $item_sku );
304 $variation->set_weight( $weight );
305 $variation->set_length( $length );
306 $variation->set_width( $width );
307 $variation->set_height( $height );
308 if ( ! $zi_disable_stock_sync ) {
309 $variation->set_stock_quantity( $item_stock );
310 $variation->set_manage_stock( true );
311 $variation->set_stock_status( '' );
312 } else {
313 $variation->set_manage_stock( false );
314 }
315 // Map taxes while syncing product from zoho.
316 if ( $item['tax_id'] && ! $this->is_tax_enabled() ) {
317 $zi_common_class = new CMBIRD_Common_Functions();
318 $woo_tax_class = $zi_common_class->get_tax_class_by_percentage( $item['tax_percentage'] );
319 $variation->set_tax_status( 'taxable' );
320 $variation->set_tax_class( $woo_tax_class );
321 }
322 $variation->add_meta_data( 'zi_item_id', $item->item_id );
323 $variation_id = $variation->save();
324
325 // Get the variation attributes with correct attribute values.
326 foreach ( $attribute_arr as $attribute => $term_name ) {
327 $taxonomy = $attribute;
328 // If taxonomy doesn't exists we create it.
329 if ( ! taxonomy_exists( $taxonomy ) ) {
330 register_taxonomy(
331 $taxonomy,
332 'product_variation',
333 array(
334 'hierarchical' => false,
335 'label' => ucfirst( $attribute ),
336 'query_var' => true,
337 'rewrite' => array( 'slug' => sanitize_title( $attribute ) ),
338 ),
339 );
340 }
341
342 // Check if the Term name exist and if not we create it.
343 if ( ! term_exists( $term_name, $taxonomy ) ) {
344 wp_insert_term( $term_name, $taxonomy );
345 }
346
347 $term_slug = get_term_by( 'name', $term_name, $taxonomy )->slug;
348 // Get the post Terms names from the parent variable product.
349 $post_term_names = wp_get_post_terms( $group_id, $taxonomy, array( 'fields' => 'names' ) );
350 // Check if the post term exist and if not we set it in the parent variable product.
351 if ( ! in_array( $term_name, $post_term_names, true ) ) {
352 wp_set_post_terms( $group_id, $term_name, $taxonomy, true );
353 }
354 // Set/save the attribute data in the product variation.
355 update_post_meta( $variation_id, 'attribute_' . $taxonomy, $term_slug );
356 }
357
358 // featured image.
359 $zi_disable_itemimage_sync = get_option( 'cmbird_zoho_disable_image_sync_status' );
360 if ( ! empty( $item_image ) && ! $zi_disable_itemimage_sync ) {
361 $image_class = new CMBIRD_Image_ZI();
362 $image_class->cmbird_zi_get_image( $item_id, $item_name, $variation_id, $item_image );
363 }
364
365 update_post_meta( $variation_id, 'zi_item_id', $item_id );
366 // Sync variation data with parent variable product.
367 WC_Product_Variable::sync( $group_id );
368 // Regenerate lookup table for attributes.
369 $lookup_data_store = new LookupDataStore();
370 $lookup_data_store->create_data_for_product( $group_id );
371 // End group item add process.
372 unset( $attribute_arr );
373 }
374 wc_delete_product_transients( $group_id ); // Clear/refresh cache.
375 // end of grouped item creation.
376 } else {
377 // fwrite($fd, PHP_EOL . 'Inside simple items');
378 // fwrite($fd, PHP_EOL . 'Item description Simple : ' . $item_description);
379 $row_item = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s", $item_id ) );
380 $mapped_product_id = $row_item->post_id;
381 // simple product.
382 // fwrite($fd, PHP_EOL . 'Before Match check');
383 $pdt_id = '';
384 if ( ! empty( $mapped_product_id ) && null !== $mapped_product_id ) {
385 $product_found = wc_get_product( $mapped_product_id );
386 if ( ! $product_found ) {
387 // remove all postmeta of that product id.
388 $wpdb->delete( $wpdb->postmeta, array( 'post_id' => $mapped_product_id ) );
389 } else {
390 $pdt_id = $mapped_product_id;
391 }
392 } elseif ( empty( $item['is_combo_product'] ) ) {
393 // fwrite($fd, PHP_EOL . 'Inside create product');
394
395 // Check if Category is selected before creating simple item.
396 if ( 'publish' === $item_status ) {
397 $opt_category = get_option( 'cmbird_zoho_item_category' );
398 $opt_category = maybe_unserialize( $opt_category );
399 $category_id = $item['category_id'];
400 if ( $opt_category ) {
401 if ( in_array( $category_id, $opt_category, true ) ) {
402 $product_class = new CMBIRD_Products_ZI_Export();
403 $pdt_id = $product_class->cmbird_zi_product_to_woocommerce( $item, $item_stock );
404 }
405 }
406 }
407 // fwrite($fd, PHP_EOL . 'After adding it : ' . $pdt_id);
408 }
409
410 // If there is product id then update metadata.
411 if ( ! empty( $pdt_id ) ) {
412 $simple_product = wc_get_product( $pdt_id );
413 // update the name if its allowed.
414 $zi_disable_itemname_sync = get_option( 'cmbird_zoho_disable_name_sync_status' );
415 if ( ! $zi_disable_itemname_sync ) {
416 $simple_product->set_name( $item_name );
417 $slug = sanitize_title( $item_name );
418 $simple_product->set_slug( $slug );
419 }
420 // update the zi_item_id using the product instance.
421 $simple_product->update_meta_data( 'zi_item_id', $item_id );
422 // update the status using set_status().
423 $simple_product->set_status( $item_status );
424 // Update the product SKU.
425 $simple_product->set_sku( $item_sku );
426 // price.
427 $sale_price = $simple_product->get_sale_price();
428 $simple_product->set_regular_price( $item_price );
429 if ( empty( $sale_price ) ) {
430 $simple_product->set_price( $item_price );
431 }
432 // Update Purchase price.
433 $simple_product->update_meta_data( '_cost_price', $item['purchase_rate'] );
434 // description.
435 $zi_disable_itemdescription_sync = get_option( 'cmbird_zoho_disable_description_sync_status' );
436 if ( ! empty( $item_description ) && ! $zi_disable_itemdescription_sync ) {
437 $simple_product->set_short_description( $item_description );
438 }
439 // Brand update if taxonomy product_brand(s) exists.
440 if ( ! empty( $item_brand ) && taxonomy_exists( 'product_brand' ) ) {
441 wp_set_object_terms( $pdt_id, $item_brand, 'product_brand' );
442 } elseif ( ! empty( $item_brand ) && taxonomy_exists( 'product_brand' ) ) {
443 wp_set_object_terms( $pdt_id, $item_brand, 'product_brand' );
444 }
445 // stock.
446 $zi_disable_stock_sync = get_option( 'cmbird_zoho_disable_stock_sync_status' );
447 if ( ! $zi_disable_stock_sync ) {
448 // fwrite( $fd, PHP_EOL . 'Inside1' );
449 if ( 'NULL' !== gettype( $item_stock ) ) {
450 // fwrite( $fd, PHP_EOL . 'Inside1.1' );
451 // Set manage stock to yes.
452 $simple_product->set_manage_stock( true );
453 // Update stock for simple product.
454 $simple_product->set_stock_quantity( number_format( $item_stock, 0, '.', '' ) );
455 if ( $item_stock > 0 ) {
456 // fwrite( $fd, PHP_EOL . 'Inside2' );
457 // Update stock status.
458 $simple_product->set_stock_status( 'instock' );
459 wp_set_post_terms( $pdt_id, 'instock', 'product_visibility', true );
460 } else {
461 // fwrite($fd, PHP_EOL . 'Inside3');
462 $stock_status = $simple_product->backorders_allowed() ? 'onbackorder' : 'outofstock';
463 $simple_product->set_stock_status( $stock_status );
464 wp_set_post_terms( $pdt_id, $stock_status, 'product_visibility', true );
465 }
466 }
467 }
468 // fwrite($fd, PHP_EOL . 'After stock');
469 // Update weight & dimensions of simple product.
470 $simple_product->set_weight( $weight );
471 $simple_product->set_length( $length );
472 $simple_product->set_width( $width );
473 $simple_product->set_height( $height );
474
475 // featured image.
476 $zi_disable_itemimage_sync = get_option( 'cmbird_zoho_disable_image_sync_status' );
477 if ( ! empty( $item_image ) && ! $zi_disable_itemimage_sync ) {
478 $image_class = new CMBIRD_Image_ZI();
479 $image_class->cmbird_zi_get_image( $item_id, $item_name, $pdt_id, $item_image );
480 }
481
482 // category.
483 if ( ! empty( $item_category ) && empty( $group_name ) ) {
484 $term = get_term_by( 'name', $item_category, 'product_cat' );
485 $term_id = $term->term_id;
486 if ( empty( $term_id ) ) {
487 $term = wp_insert_term(
488 $item_category,
489 'product_cat',
490 array(
491 'parent' => 0,
492 )
493 );
494 $term_id = $term->term_id;
495 }
496 // Remove "uncategorized" category if assigned.
497 $uncategorized_term = get_term_by( 'slug', 'uncategorized', 'product_cat' );
498 if ( $uncategorized_term && has_term( $uncategorized_term->term_id, 'product_cat', $pdt_id ) ) {
499 wp_remove_object_terms( $pdt_id, $uncategorized_term->term_id, 'product_cat' );
500 }
501 if ( ! is_wp_error( $term_id ) && isset( $term->term_id ) ) {
502 $existing_terms = wp_get_object_terms( $pdt_id, 'product_cat' );
503 if ( $existing_terms && count( $existing_terms ) > 0 ) {
504 $import_class = new CMBIRD_Products_ZI();
505 $is_term_exist = $import_class->zi_check_terms_exists( $existing_terms, $term_id );
506 if ( ! $is_term_exist ) {
507 $simple_product->update_meta_data( 'zi_category_id', $item['category_id'] );
508 wp_add_object_terms( $pdt_id, $term_id, 'product_cat' );
509 }
510 } else {
511 $simple_product->update_meta_data( 'zi_category_id', $item['category_id'] );
512 wp_set_object_terms( $pdt_id, $term_id, 'product_cat' );
513 }
514 }
515 }
516
517 // Update the custom fields if the custom fields are not empty.
518 if ( ! empty( $custom_fields ) ) {
519 $import_class = new CMBIRD_Products_ZI();
520 $import_class->sync_item_custom_fields( $custom_fields, $pdt_id );
521 }
522
523 // Map taxes while syncing product from zoho.
524 if ( $item['tax_id'] && ! $this->is_tax_enabled() ) {
525 $zi_common_class = new CMBIRD_Common_Functions();
526 $woo_tax_class = $zi_common_class->get_tax_class_by_percentage( $item['tax_percentage'] );
527 $simple_product->set_tax_status( 'taxable' );
528 $simple_product->set_tax_class( $woo_tax_class );
529 }
530 $simple_product->save();
531 wc_delete_product_transients( $pdt_id );
532 }
533 }
534 $response = new WP_REST_Response();
535 $response->set_data( 'success on variable product' );
536 $response->set_status( 200 );
537
538 // fclose( $fd ); // close logfile.
539
540 return $response;
541 }
542
543 /**
544 * Process queued webhook requests
545 * Called by WordPress cron system when queue processing is scheduled
546 */
547 public function process_queued_requests() {
548 $queue_key = 'cmbird_request_queue';
549 $current_queue = get_option( $queue_key, array() );
550
551 if ( empty( $current_queue ) || ! is_array( $current_queue ) ) {
552 return;
553 }
554
555 $current_time = time();
556 $processed_count = 0;
557 $max_process_per_batch = 10; // Process up to 10 requests per batch.
558
559 // Process queue items.
560 foreach ( $current_queue as $index => $queue_item ) {
561 if ( $processed_count >= $max_process_per_batch ) {
562 break; // Don't process too many at once.
563 }
564
565 // Check if request is too old (older than 5 minutes).
566 if ( ( $current_time - $queue_item['timestamp'] ) > 300 ) {
567 unset( $current_queue[ $index ] );
568 continue;
569 }
570
571 // Simulate the original request processing.
572 try {
573 if ( isset( $queue_item['data'] ) && ! empty( $queue_item['data'] ) ) {
574 $data = $queue_item['data'];
575
576 // Convert JSONString if present.
577 if ( array_key_exists( 'JSONString', $data ) ) {
578 $data = str_replace( '\\', '', $data['JSONString'] );
579 }
580
581 // Process the data.
582 $this->process( $data );
583 ++$processed_count;
584 }
585 } catch ( Exception $e ) {
586 // Log error but continue processing other items.
587 error_log( 'CommerceBird Webhook Queue Processing Error: ' . $e->getMessage() );
588 }
589
590 // Remove processed item from queue.
591 unset( $current_queue[ $index ] );
592 }
593
594 // Re-index array after unsetting elements.
595 $current_queue = array_values( $current_queue );
596
597 // Update the queue.
598 update_option( $queue_key, $current_queue );
599
600 // Schedule next processing if queue still has items.
601 if ( ! empty( $current_queue ) && ! wp_next_scheduled( 'cmbird_process_webhook_queue' ) ) {
602 wp_schedule_single_event( $current_time + 30, 'cmbird_process_webhook_queue' );
603 }
604 }
605
606 /**
607 * Cleanup old rate limiting data
608 * Called by WordPress scheduled delete action
609 */
610 public function cleanup_rate_limit_data() {
611 global $wpdb;
612
613 // Clean up old transients related to rate limiting.
614 $wpdb->query(
615 "DELETE FROM {$wpdb->options}
616 WHERE option_name LIKE '_transient_cmbird_rate_limit_%'
617 OR option_name LIKE '_transient_timeout_cmbird_rate_limit_%'"
618 );
619
620 // Clean up old processing locks (older than 1 hour).
621 $wpdb->query(
622 $wpdb->prepare(
623 "DELETE FROM {$wpdb->options}
624 WHERE option_name LIKE 'cmbird_processing_payload_%'
625 AND option_value < %d",
626 time() - 3600
627 )
628 );
629 }
630
631 /**
632 * @param $inventory_adjustment
633 * @param wpdb $wpdb
634 *
635 * @return WP_REST_Response
636 */
637 public function inventory_adjustment( $inventory_adjustment ): WP_REST_Response {
638 global $wpdb;
639 $item = $inventory_adjustment;
640 $line_items = $item['line_items'];
641 // get first item from line items array.
642 $item_id = $line_items[0]['item_id'];
643 $adjusted_stock = $line_items[0]['quantity_adjusted'];
644
645 $row_item = $wpdb->get_row(
646 $wpdb->prepare(
647 "SELECT * FROM {$wpdb->prefix}postmeta WHERE meta_key = 'zi_item_id' AND meta_value = %s",
648 $item_id
649 )
650 );
651 $mapped_product_id = $row_item->post_id;
652
653 if ( ! empty( $mapped_product_id ) ) {
654 // stock.
655 $zi_disable_stock_sync = get_option( 'cmbird_zoho_disable_stock_sync_status' );
656 $product = wc_get_product( $mapped_product_id );
657 // Check if the product is in stock.
658 if ( ! $zi_disable_stock_sync ) {
659 if ( $product->is_in_stock() ) {
660 // Get stock quantity.
661 $stock_quantity = $product->get_stock_quantity();
662 $new_stock = $stock_quantity + $adjusted_stock;
663 $product->set_stock_quantity( $new_stock );
664 } else {
665 $product->set_stock_quantity( $adjusted_stock );
666 $product->set_stock_status( 'instock' );
667 $product->set_manage_stock( true );
668 }
669 $product->save();
670 }
671 }
672
673 $response = new WP_REST_Response();
674 $response->set_data( 'Inventory Adjustment successful' );
675 $response->set_status( 200 );
676
677 return $response;
678 }
679 }
680