class-categories.php
1 year ago
class-import-image.php
1 year ago
class-import-items.php
1 year ago
class-import-price-list.php
1 year ago
class-multi-currency.php
1 year ago
class-order-sync.php
1 year ago
class-product.php
1 year ago
class-users-contact.php
1 year ago
index.php
1 year ago
class-import-price-list.php
339 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Class to import Pricelists from Zoho to WooCommerce using B2B for WooCommerce |
| 4 | * |
| 5 | * @package WooZo Inventory |
| 6 | */ |
| 7 | if ( ! defined( 'ABSPATH' ) ) { |
| 8 | exit; |
| 9 | } |
| 10 | |
| 11 | class CMBIRD_Pricelist_ZI { |
| 12 | use CommerceBird\Admin\Traits\LogWriter; |
| 13 | |
| 14 | private array $config; |
| 15 | |
| 16 | public function __construct() { |
| 17 | $this->config = array( |
| 18 | 'ProductZI' => array( |
| 19 | 'OID' => get_option( 'cmbird_zoho_inventory_oid' ), |
| 20 | 'APIURL' => get_option( 'cmbird_zoho_inventory_url' ), |
| 21 | ), |
| 22 | ); |
| 23 | } |
| 24 | |
| 25 | /** |
| 26 | * Get all Zoho price list |
| 27 | * Example response: |
| 28 | * { |
| 29 | * "currency_code": Code based on currency, |
| 30 | * "currency_id": The currenct id of the currency, |
| 31 | * "decimal_place": Decimal place for pricebook., |
| 32 | * "description": Description about the pricebook, |
| 33 | * "is_default": To check the default pricebook.Allowed values: true,false, |
| 34 | * "is_increase": Mark up or Mark down to discounts.Allowed values: true,false, |
| 35 | * "name": Name of the pricebook, |
| 36 | * "percentage": About percentage of discounts, |
| 37 | * "pricebook_id": Unique ID generated by server for the price book, |
| 38 | * "pricebook_items": [ |
| 39 | * { |
| 40 | * "item_id": Unique ID generated by server for Item, |
| 41 | * "pricebook_rate": Rate of the price book for the Items, |
| 42 | * "pricebook_item_id": Unique ID generated by the server for each pricebook line item |
| 43 | * } |
| 44 | * ], |
| 45 | * "pricebook_type": Type of the pricebook.Allowed values: per_item,fixed_percentage, |
| 46 | * "rounding_type": Type of the rounding.Allowed values: no_rounding,round_to_dollor,round_to_dollar_minus_01,round_to_half_dollar,round_to_half_dollar_minus_01, |
| 47 | * "sales_or_purchase_type": Whether its sales or purchase type.Allowed values: sales,purchases, |
| 48 | * "status": Status of the price book |
| 49 | *} |
| 50 | * @return array of price list |
| 51 | */ |
| 52 | public function zi_get_all_pricelist(): array { |
| 53 | $in_cache = get_transient( 'zoho_pricelist' ); |
| 54 | if ( $in_cache ) { |
| 55 | return $in_cache; |
| 56 | } |
| 57 | |
| 58 | $url = $this->config['ProductZI']['APIURL'] . 'inventory/v1/pricebooks?organization_id=' . $this->config['ProductZI']['OID']; |
| 59 | $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho(); |
| 60 | $json = $execute_curl_call_handle->execute_curl_call_get( $url ); |
| 61 | |
| 62 | if ( isset( $json->pricebooks ) ) { |
| 63 | set_transient( 'zoho_pricelist', $json->pricebooks, MINUTE_IN_SECONDS ); |
| 64 | |
| 65 | return $json->pricebooks; |
| 66 | } |
| 67 | |
| 68 | return array(); |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Save pricelist function to update prices and discounts for user roles. |
| 73 | * |
| 74 | * @param array $post The post-data containing user role and price information. |
| 75 | * Example: |
| 76 | * { |
| 77 | * "zoho_inventory_pricelist": Zoho inventory pricelist id |
| 78 | * "wp_user_role": WordPress user role |
| 79 | * } |
| 80 | */ |
| 81 | public function save_price_list( array $post ): bool { |
| 82 | if ( class_exists( 'Addify_B2B_Plugin' ) ) { |
| 83 | $success = $this->addify_b2b( $post['zoho_inventory_pricelist'], $post['wp_user_role'] ); |
| 84 | } elseif ( class_exists( 'WooCommerceB2B' ) ) { |
| 85 | $success = $this->wc_b2b( $post['wcb2b'] ); |
| 86 | } |
| 87 | return $success; |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Get a Zoho price list based on a price book id |
| 92 | * |
| 93 | * @param int $pricebook_id |
| 94 | * |
| 95 | * @return array of price list |
| 96 | */ |
| 97 | private function get_zoho_pricebook( int $pricebook_id ): array { |
| 98 | $in_cache = get_transient( 'zoho_pricelist_' . $pricebook_id ); |
| 99 | if ( $in_cache ) { |
| 100 | return $in_cache; |
| 101 | } |
| 102 | $url = $this->config['ProductZI']['APIURL'] . 'inventory/v1/pricebooks/' . $pricebook_id . '?organization_id=' . $this->config['ProductZI']['OID']; |
| 103 | $execute_curl_call_handle = new CMBIRD_API_Handler_Zoho(); |
| 104 | $json = $execute_curl_call_handle->execute_curl_call_get( $url ); |
| 105 | if ( is_object( $json ) && property_exists( $json, 'pricebook' ) ) { |
| 106 | $json = json_decode( wp_json_encode( $json->pricebook ), true ); |
| 107 | set_transient( 'zoho_pricelist_' . $pricebook_id, $json, DAY_IN_SECONDS ); |
| 108 | update_option( 'cmbird_zoho_pricelist_id', $pricebook_id ); |
| 109 | |
| 110 | return $json; |
| 111 | } |
| 112 | |
| 113 | return array(); |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Get published product ids, based on zi_item_id meta-key |
| 118 | * |
| 119 | * @return array |
| 120 | */ |
| 121 | private function get_zoho_products(): array { |
| 122 | $in_cache = get_transient( 'zoho_products' ); |
| 123 | if ( $in_cache ) { |
| 124 | return $in_cache; |
| 125 | } |
| 126 | global $wpdb; |
| 127 | |
| 128 | $ids = $wpdb->get_results( |
| 129 | "SELECT p.ID AS id, pm.meta_value AS zi_item_id |
| 130 | FROM {$wpdb->postmeta} pm |
| 131 | INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID |
| 132 | WHERE pm.meta_key = 'zi_item_id' |
| 133 | AND p.post_type in ('product', 'product_variation') |
| 134 | AND p.post_status = 'publish' |
| 135 | AND pm.meta_value != ''", |
| 136 | ARRAY_A |
| 137 | ); |
| 138 | |
| 139 | $plist = wp_list_pluck( $ids, 'id', 'zi_item_id' ); |
| 140 | ksort( $plist ); |
| 141 | set_transient( 'zoho_products', $plist, DAY_IN_SECONDS ); |
| 142 | |
| 143 | return $plist; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Support For Addify B2B |
| 148 | * |
| 149 | * @param int $pricebook_id pricebook id |
| 150 | * @param string $role user role |
| 151 | */ |
| 152 | private function addify_b2b( int $pricebook_id, string $role ): bool { |
| 153 | $price_book = $this->get_zoho_pricebook( $pricebook_id ); |
| 154 | $zi_item_ids = $this->get_zoho_products(); |
| 155 | |
| 156 | if ( empty( $price_book ) ) { |
| 157 | return false; |
| 158 | } |
| 159 | |
| 160 | $meta_collection = array(); |
| 161 | if ( isset( $price_book['pricebook_type'] ) && $price_book['pricebook_type'] === 'fixed_percentage' ) { |
| 162 | $meta_collection['price'] = $price_book['percentage'] ?? 0; |
| 163 | $meta_collection['ids'] = array_fill_keys( array_values( $zi_item_ids ), $meta_collection['price'] ); |
| 164 | $meta_collection['orderby'] = $price_book['is_increase'] ? 'percentage_increase' : 'percentage_decrease'; |
| 165 | } else { |
| 166 | $meta_collection['orderby'] = 'fixed_price'; |
| 167 | foreach ( $price_book['pricebook_items'] as $itemlist ) { |
| 168 | if ( isset( $zi_item_ids[ $itemlist['item_id'] ] ) ) { |
| 169 | $meta_collection['ids'][ $zi_item_ids[ $itemlist['item_id'] ] ] = array(); |
| 170 | if ( is_array( $itemlist['price_brackets'] ) && ! empty( $itemlist['price_brackets'] ) ) { |
| 171 | $priceBracket = $itemlist['price_brackets'][0]; |
| 172 | $meta_collection['ids'][ $zi_item_ids[ $itemlist['item_id'] ] ] = array( |
| 173 | 'start_quantity' => $priceBracket['start_quantity'] ?? 0, |
| 174 | 'end_quantity' => $priceBracket['end_quantity'] ?? 0, |
| 175 | 'pricebook_rate' => $priceBracket['pricebook_rate'] ?? 0, |
| 176 | ); |
| 177 | } else { |
| 178 | $meta_collection['ids'][ $zi_item_ids[ $itemlist['item_id'] ] ] = $itemlist['pricebook_rate'] ?? 0; |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | if ( empty( $meta_collection['ids'] ) ) { |
| 185 | return false; |
| 186 | } |
| 187 | |
| 188 | foreach ( $meta_collection['ids'] as $product_id => $price ) { |
| 189 | $formatted_price = str_replace( '.', wc_get_price_decimal_separator(), $meta_collection['price'] ?? $price ); |
| 190 | $metavalue = array( |
| 191 | 'discount_type' => $meta_collection['orderby'], |
| 192 | 'user_role' => $role, |
| 193 | 'discount_value' => is_array( $formatted_price ) ? $formatted_price['pricebook_rate'] : $formatted_price, |
| 194 | 'min_qty' => is_array( $formatted_price ) ? $formatted_price['start_quantity'] : '', |
| 195 | 'max_qty' => is_array( $formatted_price ) ? $formatted_price['end_quantity'] : '', |
| 196 | ); |
| 197 | $postmeta_array = get_post_meta( $product_id, '_role_base_price', true ); |
| 198 | $updated = false; |
| 199 | |
| 200 | if ( is_array( $postmeta_array ) && ! empty( $postmeta_array ) ) { |
| 201 | foreach ( $postmeta_array as &$postmeta ) { |
| 202 | if ( $postmeta['user_role'] === $role ) { |
| 203 | $postmeta = $metavalue; |
| 204 | $updated = true; |
| 205 | break; |
| 206 | } |
| 207 | } |
| 208 | if ( ! $updated ) { |
| 209 | $postmeta_array[] = $metavalue; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | update_post_meta( $product_id, '_role_base_price', $postmeta_array ); |
| 214 | } |
| 215 | return true; |
| 216 | } |
| 217 | |
| 218 | |
| 219 | |
| 220 | private function wc_b2b( $group_settings ): bool { |
| 221 | $group_settings = json_decode( $group_settings, true ); |
| 222 | if ( empty( $group_settings ) ) { |
| 223 | return false; |
| 224 | } |
| 225 | foreach ( $group_settings as $group_setting ) { |
| 226 | $group_id = $group_setting['key']; |
| 227 | $pricebook_id = $group_setting['value']; |
| 228 | delete_transient( 'wcb2b_synced_groups' ); |
| 229 | $price_book = $this->get_zoho_pricebook( $pricebook_id ); |
| 230 | if ( empty( $price_book ) ) { |
| 231 | continue; |
| 232 | } |
| 233 | $zi_item_ids = $this->get_zoho_products(); |
| 234 | switch ( $price_book['pricebook_type'] ) { |
| 235 | case 'fixed_percentage': |
| 236 | update_post_meta( $group_id, 'wcb2b_group_discount', $price_book['percentage'] ); |
| 237 | update_post_meta( $group_id, 'pricebook_id', $pricebook_id ); |
| 238 | update_post_meta( $group_id, 'pricebook_name', $price_book['name'] ); |
| 239 | break; |
| 240 | case 'per_item': |
| 241 | foreach ( $price_book['pricebook_items'] as $pricebook_item ) { |
| 242 | if ( ! isset( $zi_item_ids[ $pricebook_item['item_id'] ] ) ) { |
| 243 | continue; |
| 244 | } |
| 245 | $product_id = $zi_item_ids[ $pricebook_item['item_id'] ]; |
| 246 | $product_group_prices = $product_group_min = $product_group_max = array(); |
| 247 | if ( is_array( $pricebook_item['price_brackets'] ) && count( $pricebook_item['price_brackets'] ) > 0 ) { |
| 248 | $price = $pricebook_item['price_brackets'][0]['pricebook_rate']; |
| 249 | $product_group_prices[ $group_id ]['regular_price'] = $price; |
| 250 | $product_group_min[ $group_id ] = $pricebook_item['price_brackets'][0]['start_quantity']; |
| 251 | $product_group_max[ $group_id ] = $pricebook_item['price_brackets'][0]['end_quantity']; |
| 252 | } else { |
| 253 | $product_group_prices[ $group_id ]['regular_price'] = $pricebook_item['pricebook_rate']; |
| 254 | } |
| 255 | update_post_meta( $group_id, 'pricebook_id', $pricebook_id ); |
| 256 | update_post_meta( $group_id, 'pricebook_name', $price_book['name'] ); |
| 257 | if ( ! empty( $product_group_prices ) ) { |
| 258 | update_post_meta( $product_id, 'wcb2b_product_group_prices', $product_group_prices ); |
| 259 | } |
| 260 | if ( ! empty( $product_group_min ) ) { |
| 261 | update_post_meta( $product_id, 'wcb2b_product_group_min', $product_group_min ); |
| 262 | } |
| 263 | if ( ! empty( $product_group_max ) ) { |
| 264 | update_post_meta( $product_id, 'wcb2b_product_group_max', $product_group_max ); |
| 265 | } |
| 266 | } |
| 267 | break; |
| 268 | default: |
| 269 | break; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | return true; |
| 274 | } |
| 275 | |
| 276 | public function wc_b2b_groups() { |
| 277 | $in_cache = get_transient( 'wc_b2b_groups' ); |
| 278 | if ( ! empty( $in_cache ) ) { |
| 279 | return $in_cache; |
| 280 | } |
| 281 | global $wpdb; |
| 282 | |
| 283 | $results = $wpdb->get_results( |
| 284 | "SELECT ID AS id, post_title AS label |
| 285 | FROM {$wpdb->posts} p |
| 286 | WHERE p.post_type = 'wcb2b_group' |
| 287 | AND p.post_status = 'publish'", |
| 288 | ARRAY_A // Return results as an associative array |
| 289 | ); |
| 290 | |
| 291 | if ( empty( $results ) ) { |
| 292 | return array(); |
| 293 | } |
| 294 | $groups = wp_list_pluck( $results, 'label', 'id' ); |
| 295 | set_transient( 'wc_b2b_groups', $groups, DAY_IN_SECONDS ); |
| 296 | |
| 297 | return $groups; |
| 298 | } |
| 299 | |
| 300 | /** |
| 301 | * Retrieves and caches the B2B groups from the database. |
| 302 | * |
| 303 | * @return array The B2B groups data. |
| 304 | */ |
| 305 | private function wc_b2b_groups_by_rule() { |
| 306 | $in_cache = get_transient( 'wc_b2b_groups' ); |
| 307 | if ( ! empty( $in_cache ) ) { |
| 308 | return $in_cache; |
| 309 | } |
| 310 | global $wpdb; |
| 311 | |
| 312 | $results = $wpdb->get_results( |
| 313 | "SELECT p . ID as id, pm . meta_value as rule |
| 314 | FROM {$wpdb->posts} p |
| 315 | INNER JOIN {$wpdb->postmeta} pm ON p . ID = pm . post_id |
| 316 | WHERE p . post_type = 'wcb2b_group' |
| 317 | and pm . meta_key = 'wcb2b_group_price_rule'", |
| 318 | ARRAY_A // Return results as an associative array |
| 319 | ); |
| 320 | |
| 321 | if ( empty( $results ) ) { |
| 322 | return array(); |
| 323 | } |
| 324 | $grouped_data = array_reduce( |
| 325 | $results, |
| 326 | function ($result, $item) { |
| 327 | $result[ $item['rule'] ][] = $item['id']; |
| 328 | |
| 329 | return $result; |
| 330 | }, |
| 331 | array() |
| 332 | ); |
| 333 | |
| 334 | set_transient( 'wc_b2b_groups', $grouped_data, DAY_IN_SECONDS ); |
| 335 | |
| 336 | return $grouped_data; |
| 337 | } |
| 338 | } |
| 339 |