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 / woo-functions.php
commercebird / includes Last commit date
classes 9 months ago sync 9 months ago index.php 1 year ago woo-functions.php 9 months ago
woo-functions.php
859 lines
1 <?php
2
3 /**
4 * All WooCommerce related functions.
5 *
6 * @category WooCommerce
7 * @package CommerceBird
8 * @author Fawad Tiemoerie <info@roadmapstudios.com>
9 * @license GNU General Public License v3.0
10 * @link https://commercebird.com
11 */
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Helper functions to ensure correct handling of Data being transferred via rest api
19 */
20 function cmbird_clear_product_cache( $object, $request, $is_creating ) {
21 if ( $is_creating ) {
22 $product_id = $object->get_id();
23 $zoho_inventory_access_token = get_option( 'cmbird_zoho_inventory_access_token' );
24 $cmbird_zi_product_sync = get_option( 'cmbird_zoho_disable_product_sync_status' );
25 if ( ! empty( $zoho_inventory_access_token ) && ! $cmbird_zi_product_sync ) {
26 // if request contain meta zi_item_id, then return.
27 if ( isset( $request['meta']['zi_item_id'] ) ) {
28 return;
29 }
30 $product_handler = new CMBIRD_Products_ZI_Export();
31 $product_handler->cmbird_zi_product_sync( $product_id );
32 }
33
34 // Sync to Zoho CRM as well.
35 $zoho_crm_access_token = get_option( 'cmbird_zoho_crm_access_token' );
36 if ( ! empty( $zoho_crm_access_token ) ) {
37 $cmbird_zcrm_product_sync = get_option( 'cmbird_zoho_crm_disable_product_sync_status' );
38 if ( ! $cmbird_zcrm_product_sync ) {
39 $product = wc_get_product( $product_id );
40 if ( $product ) {
41 $zcrm_product_handler = new CMBIRD_ZCRM_Product();
42 $zcrm_product_handler->cmbird_sync_product_to_zoho( $product );
43 }
44 }
45 }
46
47 wc_delete_product_transients( $product_id );
48 }
49 }
50 add_action( 'woocommerce_rest_insert_product_object', 'cmbird_clear_product_cache', 10, 3 );
51
52
53
54 /**
55 * Function to update the Contact in Zoho when customer updates address on frontend
56 *
57 * @param int $user_id - User ID.
58 */
59 function cmbird_update_contact_via_accountpage( $user_id ) {
60 $zoho_inventory_access_token = get_option( 'cmbird_zoho_inventory_access_token' );
61 if ( ! empty( $zoho_inventory_access_token ) ) {
62 $contact_class_handle = new CMBIRD_Contact_ZI();
63 $contact_class_handle->cmbird_contact_update_function( $user_id );
64
65 global $wpdb;
66 $meta_key_search = 'zi_contact_person_id%';
67 $query = $wpdb->get_results(
68 $wpdb->prepare(
69 "SELECT meta_key, meta_value
70 FROM {$wpdb->usermeta}
71 WHERE user_id = %d
72 AND meta_key LIKE %s",
73 $user_id,
74 $meta_key_search
75 )
76 );
77 if ( ! empty( $query ) ) {
78 // Usermeta keys containing 'zi_contact_person_id' exist.
79 foreach ( $query as $row ) {
80 $contact_class_handle->cmbird_update_contact_person( $user_id, $row->meta_value );
81 }
82 } else {
83 return;
84 }
85 } else {
86 return;
87 }
88 }
89 add_action( 'profile_update', 'cmbird_update_contact_via_accountpage' );
90
91 add_action( 'woocommerce_update_product', 'cmbird_zi_product_sync_class', 10, 1 );
92 add_action( 'woocommerce_new_product', 'cmbird_zi_product_sync_class', 10, 1 );
93 add_action( 'wp_ajax_zoho_admin_product_sync', 'cmbird_zi_product_sync_class' );
94 /**
95 * Sync a product to Zoho Inventory and Zoho CRM.
96 *
97 * If the product is a variable product, it will sync all variations to Zoho Inventory and Zoho CRM.
98 * If the product is a simple product or variation, it will sync the product to Zoho Inventory and Zoho CRM.
99 *
100 * @param int $product_id The ID of the product to sync.
101 *
102 * @return void
103 */
104 function cmbird_zi_product_sync_class( $product_id ) {
105 if ( ! is_admin() ) {
106 return;
107 }
108 if ( ! $product_id ) {
109 // Check nonce for security.
110 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'zoho_admin_product_sync' ) ) {
111 wp_send_json_error( 'Nonce verification failed' );
112 } else {
113 $product_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
114 if ( ! $product_id ) {
115 wp_send_json_error( 'Invalid Product ID' );
116 }
117 }
118 }
119 $zoho_inventory_access_token = get_option( 'cmbird_zoho_inventory_access_token' );
120 $cmbird_zi_product_sync = get_option( 'cmbird_zoho_disable_product_sync_status' );
121 if ( ! $cmbird_zi_product_sync && $zoho_inventory_access_token ) {
122 $product_handler = new CMBIRD_Products_ZI_Export();
123 $product_handler->cmbird_zi_product_sync( $product_id );
124 }
125
126 // Sync to Zoho CRM as well.
127 $zoho_crm_access_token = get_option( 'cmbird_zoho_crm_access_token' );
128 if ( ! empty( $zoho_crm_access_token ) ) {
129 $cmbird_zcrm_product_sync = get_option( 'cmbird_zoho_crm_disable_product_sync_status' );
130 if ( ! $cmbird_zcrm_product_sync ) {
131 $product = wc_get_product( $product_id );
132 if ( $product ) {
133 $zcrm_product_handler = new CMBIRD_ZCRM_Product();
134 $sync_result = $zcrm_product_handler->cmbird_sync_product_to_zoho( $product );
135
136 // Handle different response types based on product type.
137 if ( $product->is_type( 'variable' ) && is_array( $sync_result ) ) {
138 // Variable product returns array with details.
139 $success_count = $sync_result['success_count'];
140 $failed_count = $sync_result['failed_count'];
141 $parent_id = $sync_result['parent_product_id'];
142
143 // If parent_id is not in result, get it from post meta (for existing products).
144 if ( ! $parent_id ) {
145 $parent_id = get_post_meta( $product_id, 'zcrm_product_id', true );
146 }
147
148 if ( $success_count > 0 ) {
149 update_post_meta( $product_id, 'zcrm_product_sync_status', 'success' );
150 $message = sprintf(
151 'Variable product synced: %d successful, %d failed. Parent ID: %s',
152 $success_count,
153 $failed_count,
154 $parent_id ? $parent_id : 'N/A'
155 );
156 update_post_meta( $product_id, 'zcrm_product_sync_message', $message );
157 } else {
158 update_post_meta( $product_id, 'zcrm_product_sync_status', 'failed' );
159 update_post_meta( $product_id, 'zcrm_product_sync_message', 'Failed to sync variable product and variations to Zoho CRM' );
160 }
161 } elseif ( $sync_result ) {
162 // Simple product or variation returns single ID.
163 update_post_meta( $product_id, 'zcrm_product_sync_status', 'success' );
164 update_post_meta( $product_id, 'zcrm_product_sync_message', 'Product synced successfully to Zoho CRM with ID: ' . $sync_result );
165 } else {
166 update_post_meta( $product_id, 'zcrm_product_sync_status', 'failed' );
167 update_post_meta( $product_id, 'zcrm_product_sync_message', 'Failed to sync product to Zoho CRM' );
168 }
169 }
170 }
171 }
172
173 // if its variable product but without variations, then sync it.
174 $product = wc_get_product( $product_id );
175 if ( $product->is_type( 'variable' ) ) {
176 $variations = $product->get_available_variations();
177 if ( isset( $variations ) && count( $variations ) === 0 ) {
178 $zi_product_id = get_post_meta( $product_id, 'zi_item_id', true );
179 if ( ! empty( $zi_product_id ) ) {
180 $product_handler = new CMBIRD_Products_ZI();
181 $product_handler->import_variable_product_variations( $zi_product_id, $product_id );
182 }
183 }
184 }
185 }
186
187 /**
188 * Bulk-action to sync products from WooCommerce to Zoho
189 *
190 * @param: $bulk_array
191 * @return: $bulk_array
192 */
193 add_filter( 'bulk_actions-edit-product', 'cmbird_zi_sync_all_items_to_zoho' );
194 function cmbird_zi_sync_all_items_to_zoho( $bulk_array ) {
195 $bulk_array['sync_item_to_zoho'] = 'Sync to Zoho';
196 // add option to unmap items from zoho.
197 $bulk_array['unmap_item_to_zoho'] = 'Unmap from Zoho';
198 return $bulk_array;
199 }
200
201 add_filter( 'handle_bulk_actions-edit-product', 'cmbird_zi_sync_all_items_to_zoho_handler', 10, 3 );
202 function cmbird_zi_sync_all_items_to_zoho_handler( $redirect, $action, $object_ids ) {
203 // let's remove query args first.
204 $redirect = remove_query_arg( 'sync_item_to_zoho_done', $redirect );
205 $redirect = remove_query_arg( 'unmap_item_to_zoho_done', $redirect );
206
207 // do something for "Make Draft" bulk action.
208 if ( 'sync_item_to_zoho' === $action ) {
209
210 foreach ( $object_ids as $post_id ) {
211 // Sync to Zoho Inventory.
212 $product_handler = new CMBIRD_Products_ZI_Export();
213 $product_handler->cmbird_zi_product_sync( $post_id );
214
215 // Sync to Zoho CRM as well.
216 $zoho_crm_access_token = get_option( 'cmbird_zoho_crm_access_token' );
217 if ( ! empty( $zoho_crm_access_token ) ) {
218 $cmbird_zcrm_product_sync = get_option( 'cmbird_zoho_crm_disable_product_sync_status' );
219 if ( ! $cmbird_zcrm_product_sync ) {
220 $product = wc_get_product( $post_id );
221 if ( $product ) {
222 $zcrm_product_handler = new CMBIRD_ZCRM_Product();
223 $zcrm_product_handler->cmbird_sync_product_to_zoho( $product );
224 }
225 }
226 }
227 }
228
229 // do not forget to add query args to URL because we will show notices later.
230 $redirect = add_query_arg( 'sync_item_to_zoho_done', count( $object_ids ), $redirect );
231
232 } elseif ( 'unmap_item_to_zoho' === $action ) {
233 foreach ( $object_ids as $post_id ) {
234 $nonce = wp_create_nonce( 'zi_product_unmap_hook' );
235 cmbird_zi_product_unmap_hook( $post_id, $nonce );
236 }
237 // do not forget to add query args to URL because we will show notices later.
238 $redirect = add_query_arg( 'unmap_item_to_zoho_done', count( $object_ids ), $redirect );
239 }
240
241 return $redirect;
242 }
243
244 // output the message of bulk action.
245 add_action( 'admin_notices', 'cmbird_sync_item_to_zoho_notices' );
246 function cmbird_sync_item_to_zoho_notices() {
247 // verify nonce.
248 if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-edit-products' ) ) {
249 return;
250 }
251 if ( ! empty( $_REQUEST['sync_item_to_zoho_done'] ) ) {
252 echo '<div id="message" class="updated notice is-dismissible">
253 <p>Products Synced. If product is not synced, please click on Edit Product to see the API response.</p>
254 </div>';
255 }
256 if ( ! empty( $_REQUEST['unmap_item_to_zoho_done'] ) ) {
257 echo '<div id="message" class="updated notice is-dismissible">
258 <p>Products Unmapped from Zoho.</p>
259 </div>';
260 }
261 }
262
263 /**
264 * Function to be called by ajax hook when unmap button called.
265 * This function remove zoho mapped id.
266 */
267 add_action( 'wp_ajax_zi_product_unmap_hook', 'cmbird_zi_product_unmap_hook' );
268 function cmbird_zi_product_unmap_hook( $product_id, $nonce = '' ) {
269 // if nonce is not there in the request, then check if it is passed as parameter.
270 if ( ! $nonce ) {
271 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
272 }
273 // verify nonce.
274 if ( ! $nonce || ! wp_verify_nonce( $nonce, 'zi_product_unmap_hook' ) ) {
275 wp_send_json_error( 'Nonce verification failed' );
276 }
277 if ( ! $product_id && isset( $_POST['product_id'] ) ) {
278 $product_id = sanitize_text_field( wp_unslash( $_POST['product_id'] ) );
279 }
280
281 if ( $product_id ) {
282 $product = wc_get_product( $product_id );
283 // If this is variable items then unmap all of it's variations.
284 if ( $product->is_type( 'variable' ) ) {
285 $variations = $product->get_available_variations();
286 if ( isset( $variations ) && count( $variations ) > 0 ) {
287 foreach ( $variations as $child ) {
288 delete_post_meta( $child['variation_id'], 'zi_item_id' );
289 delete_post_meta( $child['variation_id'], 'zi_account_id' );
290 delete_post_meta( $child['variation_id'], 'zi_account_name' );
291 delete_post_meta( $child['variation_id'], 'zi_category_id' );
292 delete_post_meta( $child['variation_id'], 'zi_inventory_account_id' );
293 delete_post_meta( $child['variation_id'], 'zi_purchase_account_id' );
294 }
295 }
296 }
297 delete_post_meta( $product_id, 'zi_item_id' );
298 delete_post_meta( $product_id, 'zi_account_id' );
299 delete_post_meta( $product_id, 'zi_account_name' );
300 delete_post_meta( $product_id, 'zi_category_id' );
301 delete_post_meta( $product_id, 'zi_inventory_account_id' );
302 delete_post_meta( $product_id, 'zi_purchase_account_id' );
303
304 // Also unmap from Zoho CRM.
305 delete_post_meta( $product_id, 'zcrm_product_id' );
306 delete_post_meta( $product_id, 'zcrm_product_sync_status' );
307 delete_post_meta( $product_id, 'zcrm_product_sync_message' );
308
309 // update message.
310 update_post_meta( $product_id, 'zi_product_errmsg', 'Product is Unmapped from Zoho Inventory and CRM' );
311 }
312 }
313
314 /**
315 * Function to be called by ajax hook when unmap button called.
316 * This function remove zoho mapped id.
317 */
318 add_action( 'wp_ajax_zi_customer_unmap_hook', 'cmbird_zi_customer_unmap_hook' );
319 function cmbird_zi_customer_unmap_hook( $order_id ) {
320 // verify Nonce.
321 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'zi_customer_unmap_hook' ) ) {
322 wp_send_json_error( 'Nonce verification failed' );
323 }
324 if ( ! $order_id && isset( $_POST['order_id'] ) ) {
325 $order_id = sanitize_text_field( wp_unslash( $_POST['order_id'] ) );
326 }
327
328 $order = wc_get_order( $order_id );
329 $customer_id = $order->get_user_id();
330
331 if ( $customer_id ) {
332 delete_user_meta( $customer_id, 'zi_contact_id' );
333 delete_user_meta( $customer_id, 'zi_contact_persons_id' );
334 delete_user_meta( $customer_id, 'zi_contactperson_id_0' );
335 delete_user_meta( $customer_id, 'zi_contactperson_id_1' );
336 delete_user_meta( $customer_id, 'zi_currency_code' );
337 delete_user_meta( $customer_id, 'zi_currency_id' );
338 delete_user_meta( $customer_id, 'zi_created_time' );
339 delete_user_meta( $customer_id, 'zi_last_modified_time' );
340 delete_user_meta( $customer_id, 'zi_primary_contact_id' );
341 delete_user_meta( $customer_id, 'zi_billing_address_id' );
342 delete_user_meta( $customer_id, 'zi_shipping_address_id' );
343
344 $order->add_order_note( 'Zoho Sync: Customer is now unmapped. Please try syncing the order again' );
345 $order->save();
346 }
347 }
348
349 /**
350 * Add WordPress Meta box to show sync response
351 */
352 function cmbird_product_metabox() {
353 $zoho_inventory_access_token = get_option( 'cmbird_zoho_inventory_access_token' );
354 $zoho_crm_access_token = get_option( 'cmbird_zoho_crm_access_token' );
355 if ( ! $zoho_inventory_access_token && ! $zoho_crm_access_token ) {
356 return;
357 }
358 add_meta_box(
359 'zoho-product-sync',
360 __( 'Zoho Inventory', 'commercebird' ),
361 'cmbird_product_metabox_callback',
362 'product',
363 'side',
364 'high'
365 );
366 // normal metabox for zoho inventory body request.
367 add_meta_box(
368 'zoho-product-sync-normal',
369 __( 'Zoho Inventory - Full Details', 'commercebird' ),
370 'cmbird_product_metabox_body_request',
371 'product',
372 'normal',
373 'high'
374 );
375 }
376
377 /**
378 * Shows the full API request body when syncing the product to Zoho Inventory.
379 *
380 * @param WP_Post $post The current post object.
381 */
382 function cmbird_product_metabox_body_request( $post ) {
383 $body_request = get_post_meta( $post->ID, 'zi_product_body_request', true );
384
385 if ( empty( $body_request ) ) {
386 echo '<p>No API request body available. Try syncing the product first.</p>';
387 return;
388 }
389
390 // Format the JSON for better readability if it's valid JSON.
391 $formatted_json = $body_request;
392 $decoded = json_decode( $body_request, true );
393 if ( json_last_error() === JSON_ERROR_NONE ) {
394 $formatted_json = wp_json_encode( $decoded, JSON_PRETTY_PRINT );
395 }
396
397 echo '<h4>API Request Body (JSON):</h4>';
398 echo '<textarea readonly style="width:100%; height:400px; font-family:monospace; font-size:12px; border:1px solid # ccc; padding:10px;">' . esc_textarea( $formatted_json ) . '</textarea>';
399 echo '<p><small>This is the actual JSON data that was sent to Zoho Inventory API. You can copy this content.</small></p>';
400
401 // Add a copy button for convenience.
402 echo '<button type="button" onclick="copyBodyRequest()" style="margin-top:5px;" class="button">Copy to Clipboard</button>';
403 echo '<script>
404 function copyBodyRequest() {
405 const textarea = document.querySelector(\'textarea[readonly]\');
406 textarea.select();
407 document.execCommand(\'copy\');
408 alert(\'Request body copied to clipboard!\');
409 }
410 </script>';
411 }
412 /**
413 * Callback for product metabox to show sync response.
414 *
415 * @param WP_Post $post The current post object.
416 */
417 function cmbird_product_metabox_callback( $post ) {
418 $response = get_post_meta( $post->ID, 'zi_product_errmsg' );
419 echo 'API Response: ' . esc_html( implode( $response ) ) . '<br>';
420
421 // Show CRM sync status.
422 $zcrm_sync_status = get_post_meta( $post->ID, 'zcrm_product_sync_status', true );
423 $zcrm_sync_message = get_post_meta( $post->ID, 'zcrm_product_sync_message', true );
424 if ( $zcrm_sync_status ) {
425 $status_color = 'success' === $zcrm_sync_status ? 'green' : 'red';
426 echo '<p style="color:' . esc_attr( $status_color ) . ';"><strong>CRM Sync:</strong> ' . esc_html( $zcrm_sync_message ) . '</p>';
427 }
428
429 // Generate nonce.
430 $nonce = wp_create_nonce( 'zoho_admin_product_sync' );
431 $nonce_unmap = wp_create_nonce( 'zi_product_unmap_hook' );
432 $post_id = $post->ID;
433 echo '<br><a href="javascript:void(0)" style="width:100%; text-align: center;" class="button button-primary" onclick="zoho_admin_product_ajax(' . esc_attr( $post_id ) . ', \'' . esc_attr( $nonce ) . '\')">Sync Product</a>';
434 echo '<br><a href="javascript:void(0)" style="margin-top:10px; background:#b32d2e; border-color: # b32d2e; width:100%; text-align: center;" class="button button-primary" onclick="zoho_admin_unmap_product_ajax(' . esc_attr( $post_id ) . ', \'' . esc_attr( $nonce_unmap ) . '\')">Unmap this Product</a>';
435 $product = wc_get_product( $post->ID );
436 $product_type = $product->get_type();
437 if ( 'variable' === $product_type || 'variable-subscription' === $product_type ) {
438 echo '<p class="howto" style="color:#b32d2e;"><strong>Important : </strong> Please ensure all variations have price and SKU</p>';
439 }
440 // echo the zi_category_id.
441 $zi_category_id = get_post_meta( $post->ID, 'zi_category_id', true );
442 if ( $zi_category_id ) {
443 echo '<p class="howto"><strong>Zoho Category: </strong>' . esc_html( $zi_category_id ) . '</p>';
444 }
445 // make api call to get the zoho item details.
446 $zi_item_id = get_post_meta( $post->ID, 'zi_item_id', true );
447 if ( $zi_item_id && is_admin() ) {
448 $zoho_inventory_oid = get_option( 'cmbird_zoho_inventory_organization_id' );
449 $zoho_inventory_url = get_option( 'cmbird_zoho_inventory_url' );
450 $urlitem = "{$zoho_inventory_url}inventory/v1/items/{$zi_item_id}?organization_id=$zoho_inventory_oid";
451 // fwrite( $fd, PHP_EOL . 'URL : ' . $urlitem );
452
453 sleep( 1 ); // to avoid 429 error.
454 $execute_curl_call = new CMBIRD_API_Handler_Zoho();
455 $json = $execute_curl_call->execute_curl_call_get( $urlitem );
456 $code = (int) property_exists( $json, 'code' ) ? $json->code : '0';
457 if ( '0' === $code || 0 === $code ) {
458 // echo the item details here that are in array called "item".
459 $item = $json->item;
460 $actual_available_stock = (int) property_exists( $item, 'actual_available_stock' ) ? $item->actual_available_stock : '0';
461 $rate = (int) property_exists( $item, 'rate' ) ? $item->rate : '0';
462 echo '<p class="howto"><strong>Rate: </strong>' . esc_html( $rate ) . '</p>';
463 // echo the actual_available_stock.
464 echo '<p class="howto"><strong>Available Stock: </strong>' . esc_html( $actual_available_stock ) . '</p>';
465 // echo the zoho category name from the item.
466 $category_name = (string) property_exists( $item, 'category_name' ) ? $item->category_name : '';
467 if ( ! empty( $category_name ) || 'variable' === $product_type || 'variable-subscription' === $product_type ) {
468 echo '<p class="howto"><strong>Category Name: </strong>' . esc_html( $category_name ) . '</p>';
469 }
470 // echo all properties from item except description.
471 echo '<details><summary>More Details</summary>';
472 echo '<ul>';
473 foreach ( $item as $key => $value ) {
474 if ( 'description' === $key ) {
475 continue; // skip description.
476 }
477 // Skip empty arrays.
478 if ( is_array( $value ) && empty( $value ) ) {
479 continue;
480 }
481 echo '<li><strong>' . esc_html( $key ) . ': </strong>';
482 if ( is_array( $value ) || is_object( $value ) ) {
483 echo '<pre>' . esc_html( print_r( $value, true ) ) . '</pre>';
484 } else {
485 echo esc_html( $value );
486 }
487 echo '</li>';
488 }
489 echo '</ul>';
490 echo '</details>';
491 echo '<br>';
492 }
493 }
494 }
495 add_action( 'add_meta_boxes', 'cmbird_product_metabox' );
496
497
498 /**
499 * Add Zoho and Exact Item IDs to Product and Variations
500 *
501 * @return void
502 */
503 add_action( 'woocommerce_product_options_pricing', 'cmbird_item_id_field' );
504 add_action( 'woocommerce_variation_options_pricing', 'cmbird_item_id_variation_field', 10, 3 );
505 function cmbird_item_id_field() {
506 woocommerce_wp_text_input(
507 array(
508 'id' => '_cost_price',
509 'class' => 'readonly',
510 'wrapper_class' => 'form-row',
511 'label' => esc_html__( 'Cost Price', 'commercebird' ),
512 'data_type' => 'price',
513 'description' => esc_html__( 'You can edit this via the CommerceBird App', 'commercebird' ),
514 )
515 );
516 woocommerce_wp_text_input(
517 array(
518 'id' => 'eo_item_id',
519 'label' => esc_html__( 'Exact Item ID', 'commercebird' ),
520 'class' => 'readonly',
521 'desc_tip' => true,
522 'description' => esc_html__( 'This is the Exact Item ID of this product. You cannot change this', 'commercebird' ),
523 )
524 );
525 woocommerce_wp_text_input(
526 array(
527 'id' => 'zi_item_id',
528 'label' => esc_html__( 'Zoho Item ID', 'commercebird' ),
529 'class' => 'readonly',
530 'desc_tip' => true,
531 'description' => esc_html__( 'This is the Zoho Item ID of this product. You cannot change this', 'commercebird' ),
532 )
533 );
534 }
535 function cmbird_item_id_variation_field( $loop, $variation_data, $variation ) {
536 woocommerce_wp_text_input(
537 array(
538 'id' => 'cost_price[' . $loop . ']',
539 'class' => 'readonly',
540 'wrapper_class' => 'form-row',
541 'data_type' => 'price',
542 'label' => esc_html__( 'Cost Price', 'commercebird' ),
543 'value' => get_post_meta( $variation->ID, '_cost_price', true ),
544 'description' => esc_html__( 'You can edit this via the CommerceBird App', 'commercebird' ),
545 )
546 );
547 woocommerce_wp_text_input(
548 array(
549 'id' => 'eo_item_id[' . $loop . ']',
550 'class' => 'readonly',
551 'label' => esc_html__( 'Exact Item ID', 'commercebird' ),
552 'value' => get_post_meta( $variation->ID, 'eo_item_id', true ),
553 'desc_tip' => true,
554 'description' => esc_html__( 'This is the Exact Item ID of this product. You cannot change this', 'commercebird' ),
555 )
556 );
557 woocommerce_wp_text_input(
558 array(
559 'id' => 'zi_item_id[' . $loop . ']',
560 'class' => 'readonly',
561 'label' => esc_html__( 'Zoho Item ID', 'commercebird' ),
562 'value' => get_post_meta( $variation->ID, 'zi_item_id', true ),
563 'desc_tip' => true,
564 'description' => esc_html__( 'This is the Zoho Item ID of this product. You cannot change this', 'commercebird' ),
565 )
566 );
567 }
568
569 /**
570 * Adds 'Zoho Sync' column header to 'Orders' page immediately after 'Total' column.
571 *
572 * @param string[] $columns
573 * @return string[] $new_columns
574 */
575 function cmbird_zi_sync_column_orders_overview( $columns ) {
576 $zoho_inventory_access_token = get_option( 'cmbird_zoho_inventory_access_token' );
577 if ( empty( $zoho_inventory_access_token ) ) {
578 return $columns;
579 }
580 $new_columns = array();
581 foreach ( $columns as $column_name => $column_info ) {
582
583 $new_columns[ $column_name ] = $column_info;
584
585 if ( 'order_total' === $column_name ) {
586 $new_columns['zoho_sync'] = __( 'Zoho Sync', 'commercebird' );
587 }
588 }
589 return $new_columns;
590 }
591 add_filter( 'manage_woocommerce_page_wc-orders_columns', 'cmbird_zi_sync_column_orders_overview', 20 );
592
593 /**
594 * Adding Sync Status for Orders Column
595 *
596 * @param string $column Column name.
597 * @param int $order_id $order id.
598 * @return void
599 */
600 function cmbird_zi_add_zoho_orders_content( $column, $order_id ) {
601 $zi_url = get_option( 'cmbird_zoho_inventory_url' );
602 $zi_visit_url = str_replace( 'www.zohoapis', 'inventory.zoho', $zi_url );
603 switch ( $column ) {
604 case 'zoho_sync':
605 // Get custom order meta data.
606 $order = wc_get_order( $order_id );
607 $zi_order_id = $order->get_meta( 'zi_salesorder_id', true, 'edit' );
608 $url = $zi_visit_url . 'app#/salesorders/' . $zi_order_id;
609 if ( $zi_order_id ) {
610 echo '<span class="dashicons dashicons-yes-alt" style="color:green;"></span><a href="' . esc_url( $url ) . '" target="_blank"> <span class="dashicons dashicons-external" style="color:green;"></span> </a>';
611 } else {
612 echo '<span class="dashicons dashicons-dismiss" style="color:red;"></span>';
613 }
614 unset( $order );
615 break;
616 }
617 }
618 add_action( 'manage_woocommerce_page_wc-orders_custom_column', 'cmbird_zi_add_zoho_orders_content', 20, 2 );
619
620 /**
621 * Adds 'Zoho Sync' column content.
622 *
623 * @param string[] $column name of column being displayed
624 */
625 function cmbird_zi_add_zoho_column_content( $column ) {
626 global $post;
627 $post_type = get_post_type( $post );
628
629 if ( 'zoho_sync' === $column && 'product' === $post_type ) {
630 $product_id = $post->ID;
631 $zi_product_id = get_post_meta( $product_id, 'zi_item_id' );
632 if ( $zi_product_id ) {
633 echo '<span class="dashicons dashicons-yes-alt" style="color:green;"></span>';
634 } else {
635 echo '<span class="dashicons dashicons-dismiss" style="color:red;"></span>';
636 }
637 }
638 }
639 add_action( 'manage_product_posts_custom_column', 'cmbird_zi_add_zoho_column_content' );
640
641 /**
642 * Adds 'Zoho Sync' column header to 'Products' page.
643 *
644 * @param string[] $columns
645 * @return string[] $new_columns
646 */
647 function cmbird_zi_sync_column_products_overview( $columns ) {
648 $zoho_inventory_access_token = get_option( 'cmbird_zoho_inventory_access_token' );
649 if ( empty( $zoho_inventory_access_token ) ) {
650 return $columns;
651 }
652 $new_columns = array();
653
654 foreach ( $columns as $column_name => $column_info ) {
655
656 $new_columns[ $column_name ] = $column_info;
657
658 if ( 'product_cat' === $column_name ) {
659 $new_columns['zoho_sync'] = __( 'Zoho Sync', 'commercebird' );
660 }
661 }
662
663 return $new_columns;
664 }
665 add_filter( 'manage_edit-product_columns', 'cmbird_zi_sync_column_products_overview', 20 );
666
667 /**
668 * Make 'Zoho Sync' column filterable.
669 */
670 function cmbird_zi_sync_column_filterable() {
671 global $typenow;
672
673 if ( 'product' === $typenow ) {
674 // code here.
675 $value = isset( $_GET['zoho_sync_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['zoho_sync_filter'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended.
676
677 echo '<select name="zoho_sync_filter">';
678 echo '<option value="">Zoho Sync Filter</option>';
679
680 // Count synced products.
681 $synced_count = new WP_Query(
682 array(
683 'post_type' => 'product',
684 'meta_query' => array(
685 array(
686 'key' => 'zi_item_id',
687 'compare' => 'EXISTS',
688 ),
689 ),
690 'fields' => 'ids',
691 )
692 );
693 $synced_count = $synced_count->found_posts;
694 $synced_label = 'Synced';
695 if ( $synced_count > 0 ) {
696 $synced_label .= ' (' . $synced_count . ')';
697 }
698 echo '<option value="synced" ' . selected( $value, 'synced', false ) . '>' . esc_html( $synced_label ) . '</option>';
699
700 // Count not synced products.
701 $not_synced_count = new WP_Query(
702 array(
703 'post_type' => 'product',
704 'meta_query' => array(
705 array(
706 'key' => 'zi_item_id',
707 'compare' => 'NOT EXISTS',
708 ),
709 ),
710 'fields' => 'ids',
711 )
712 );
713 $not_synced_count = $not_synced_count->found_posts;
714 $not_synced_label = 'Not Synced';
715 if ( $not_synced_count > 0 ) {
716 $not_synced_label .= ' (' . $not_synced_count . ')';
717 }
718 echo '<option value="not_synced" ' . selected( $value, 'not_synced', false ) . '>' . esc_html( $not_synced_label ) . '</option>';
719
720 echo '</select>';
721 }
722 }
723 add_action( 'restrict_manage_posts', 'cmbird_zi_sync_column_filterable' );
724
725 /**
726 * Modify the product query based on the filter.
727 *
728 * @param WP_Query $query The query object.
729 */
730 function cmbird_zi_sync_column_filter_query( $query ) {
731 global $typenow, $pagenow;
732
733 if ( 'product' === $typenow && 'edit.php' === $pagenow && isset( $_GET['zoho_sync_filter'] ) && $_GET['zoho_sync_filter'] !== '' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended.
734 $value = sanitize_text_field( wp_unslash( $_GET['zoho_sync_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended.
735
736 $meta_query = array();
737
738 if ( 'synced' === $value ) {
739 $meta_query[] = array(
740 'key' => 'zi_item_id',
741 'compare' => 'EXISTS',
742 );
743 } elseif ( 'not_synced' === $value ) {
744 $meta_query[] = array(
745 'relation' => 'OR',
746 array(
747 'key' => 'zi_item_id',
748 'compare' => 'NOT EXISTS',
749 ),
750 array(
751 'key' => 'zi_item_id',
752 'value' => '',
753 'compare' => '=',
754 ),
755 );
756 }
757
758 $query->set( 'meta_query', $meta_query );
759 }
760 }
761 add_action( 'pre_get_posts', 'cmbird_zi_sync_column_filter_query' );
762
763 /**
764 * Change Action Scheduler default purge to 1 week
765 *
766 * @return int
767 */
768 function cmbird_action_scheduler_purge() {
769 return WEEK_IN_SECONDS;
770 }
771 add_filter( 'action_scheduler_retention_period', 'cmbird_action_scheduler_purge' );
772
773 add_filter(
774 'action_scheduler_default_cleaner_statuses',
775 function ( $statuses ) {
776 $statuses[] = ActionScheduler_Store::STATUS_FAILED;
777 return $statuses;
778 }
779 );
780
781 /**
782 * Register the custom order statuses "shipped" and "fulfilled"
783 *
784 * @return void
785 */
786 function cmbird_register_custom_order_statuses() {
787 register_post_status(
788 'wc-shipped',
789 array(
790 'label' => _x( 'Shipped', 'Order status', 'commercebird' ),
791 'public' => true,
792 'exclude_from_search' => false,
793 'show_in_admin_all_list' => true,
794 'show_in_admin_status_list' => true,
795 'label_count' => _n_noop( 'Shipped <span class="count">(%s)</span>', 'Shipped <span class="count">(%s)</span>', 'commercebird' ),
796 )
797 );
798 register_post_status(
799 'wc-fulfilled',
800 array(
801 'label' => _x( 'Fulfilled', 'Order status', 'commercebird' ),
802 'public' => true,
803 'exclude_from_search' => false,
804 'show_in_admin_all_list' => true,
805 'show_in_admin_status_list' => true,
806 'label_count' => _n_noop( 'Fulfilled <span class="count">(%s)</span>', 'Fulfilled <span class="count">(%s)</span>', 'commercebird' ),
807 )
808 );
809 }
810 add_action( 'init', 'cmbird_register_custom_order_statuses' );
811 /**
812 * Add custom order statuses to list of WC Order statuses
813 *
814 * @param array $order_statuses Existing order statuses.
815 * @return array Modified order statuses.
816 */
817 function cmbird_add_custom_order_statuses( $order_statuses ) {
818 $order_statuses['wc-shipped'] = _x( 'Shipped', 'Order status', 'commercebird' );
819 $order_statuses['wc-fulfilled'] = _x( 'Fulfilled', 'Order status', 'commercebird' );
820 return $order_statuses;
821 }
822 add_filter( 'wc_order_statuses', 'cmbird_add_custom_order_statuses' );
823
824 /**
825 * Modify the Products API to include brands.
826 *
827 * TODO: This is a temporary solution. We need to find a better way to include brands in the API response.
828 *
829 * @param $response The response object.
830 * @param $post The post object.
831 * @param $request The request object.
832 * @return mixed
833 */
834 // add_filter( 'woocommerce_rest_prepare_product_object', 'cmbird_rest_api_prepare_brands_tax', 10, 3 );
835 function cmbird_rest_api_prepare_brands_tax( $response, $object, $request ) {
836 $product_id = $object->get_id();
837 if ( empty( $response->data['product_brands'] ) ) {
838 $terms = array();
839 foreach ( wp_get_post_terms( $product_id, 'product_brands' ) as $term ) {
840 $terms[] = array(
841 'id' => $term->term_id,
842 'name' => $term->name,
843 'slug' => $term->slug,
844 );
845 }
846 $response->data['product_brands'] = $terms;
847 }
848 return $response;
849 }
850
851
852 function cmbird_prepare_insert_product( $product, $request ) {
853 if ( isset( $request['product_brands'] ) ) {
854 wp_set_post_terms( $request['id'], $request['product_brands'], 'product_brands', false );
855 }
856 return $product;
857 }
858 // add_filter( 'woocommerce_rest_pre_insert_product_object', 'cmbird_prepare_insert_product', 10, 2 );
859