Changeset 3371202
- Timestamp:
- 10/01/2025 03:23:44 PM (5 months ago)
- Location:
- philia-integration
- Files:
-
- 31 added
- 1 deleted
- 7 edited
-
tags/1.8.1/readme.txt (deleted)
-
tags/1.9 (added)
-
tags/1.9/assets (added)
-
tags/1.9/assets/philia-api-settings.js (added)
-
tags/1.9/assets/philia-api-sync.js (added)
-
tags/1.9/assets/philia-cashier-fields.js (added)
-
tags/1.9/includes (added)
-
tags/1.9/includes/cashier (added)
-
tags/1.9/includes/cashier/class-cashier-fields.php (added)
-
tags/1.9/includes/cashier/lib (added)
-
tags/1.9/includes/cashier/lib/SimpleXLSX.php (added)
-
tags/1.9/includes/cashier/lib/SimpleXLSXEx.php (added)
-
tags/1.9/includes/cashier/rest-routes (added)
-
tags/1.9/includes/cashier/rest-routes/class-cashier-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-coupon-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-customer-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-invoice-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-product-category-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-product-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-report-route.php (added)
-
tags/1.9/includes/cashier/rest-routes/class-reward-route.php (added)
-
tags/1.9/includes/class-api-log.php (added)
-
tags/1.9/includes/class-api-settings.php (added)
-
tags/1.9/includes/class-core-api.php (added)
-
tags/1.9/includes/class-login-redirect.php (added)
-
tags/1.9/includes/class-philia-wallet.php (added)
-
tags/1.9/includes/class-webhook-sender.php (added)
-
tags/1.9/includes/templates (added)
-
tags/1.9/includes/templates/wc-endpoint-wallet.php (added)
-
tags/1.9/includes/templates/woo-wallet-partial-payment.php (added)
-
tags/1.9/philia-integration.php (added)
-
tags/1.9/readme.txt (added)
-
trunk/includes/cashier/class-cashier-fields.php (modified) (5 diffs)
-
trunk/includes/cashier/rest-routes/class-cashier-route.php (modified) (7 diffs)
-
trunk/includes/cashier/rest-routes/class-invoice-route.php (modified) (10 diffs)
-
trunk/includes/cashier/rest-routes/class-product-route.php (modified) (24 diffs)
-
trunk/includes/cashier/rest-routes/class-reward-route.php (modified) (2 diffs)
-
trunk/philia-integration.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
philia-integration/trunk/includes/cashier/class-cashier-fields.php
r3368679 r3371202 60 60 // Enqueue necessary JavaScript for AJAX 61 61 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); 62 63 // hook for register new seller 64 add_action('user_register', array( $this, 'dokan_spmv_clone_all_products_for_new_seller' ), 10, 2); 62 65 } 63 66 … … 156 159 'philia_cashier_fields_section', 157 160 array( 'field_name' => 'login_image_url' ) 161 ); 162 163 add_settings_field( 164 'shop_type', 165 __( 'Shop Type', 'philia-integration' ), 166 array( $this, 'shop_type_callback' ), 167 'philia-cashier-fields', 168 'philia_cashier_fields_section' 158 169 ); 159 170 … … 291 302 292 303 /** 304 * Renders the Shop Type select field on the settings page. 305 * 306 * @return void Outputs the HTML for the shop type select field. 307 */ 308 public function shop_type_callback() 309 { 310 $cashier_fields = get_option( 'philia_cashier_fields' ); 311 $value = isset( $cashier_fields['shop_type'] ) ? $cashier_fields['shop_type'] : ''; 312 ?> 313 <select name="philia_cashier_fields[shop_type]" id="shop-type-field"> 314 <option value=""><?php esc_html_e( 'Select Shop Type', 'philia-integration' ); ?></option> 315 <option value="single-shop" <?php selected( $value, 'single-shop' ); ?>><?php esc_html_e( 'Single Shop', 'philia-integration' ); ?></option> 316 <option value="multi-vendor" <?php selected( $value, 'multi-vendor' ); ?>><?php esc_html_e( 'Multi Vendor', 'philia-integration' ); ?></option> 317 <option value="multi-inventory" <?php selected( $value, 'multi-inventory' ); ?>><?php esc_html_e( 'Multi Inventory', 'philia-integration' ); ?></option> 318 </select> 319 <?php 320 } 321 322 /** 293 323 * Enqueues necessary JavaScript files for AJAX actions on the settings page. 294 324 * … … 313 343 314 344 /** 345 * Automatically clones all base products for a new seller upon registration. 346 * 347 * This function hooks into the 'user_register' action. When a new user 348 * with the 'seller' role is created, it iterates through all published 349 * products. For each product group (identified by map_id), it creates a 350 * clone for the new seller, ensuring they only get one copy from each 351 * unique product line and not from other sellers' clones. 352 * 353 * @param int $user_id The ID of the newly registered user. 354 * @param array $userdata The user data array. 355 */ 356 public function dokan_spmv_clone_all_products_for_new_seller($user_id, $userdata) 357 { 358 // 1. Check if the new user is a seller. 359 if ($userdata['role'] != 'seller' ) { 360 return; 361 } 362 363 // 2. Ensure Dokan SPMV functionality is active. 364 if (!class_exists('Dokan_SPMV_Products') || !class_exists('WC_Admin_Duplicate_Product')) { 365 error_log('Dokan SPMV or WooCommerce is not active. Cloning process aborted for user ID: ' . $user_id); 366 return; 367 } 368 369 $cashier_settings = get_option('philia_cashier_fields'); 370 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 371 if( $shop_type != 'multi-inventory' ) { 372 // skip if shop type equal multi-inventory 373 return; 374 } 375 376 global $wpdb; 377 $spmv_products_instance = new \Dokan_SPMV_Products(); 378 $dokan_product_map_table = $wpdb->prefix . 'dokan_product_map'; 379 380 // Get all administrator user IDs 381 $admins = get_users( array( 382 'role' => 'administrator', 383 'fields' => 'ID', 384 ) ); 385 386 // 3. Get all original, published products. 387 $args = array( 388 'post_type' => 'product', 389 'posts_per_page' => -1, 390 'post_status' => 'publish', 391 'author__in' => $admins, 392 ); 393 394 $all_products_query = new \WP_Query($args); 395 396 if (!$all_products_query->have_posts()) { 397 return; 398 } 399 400 // 4. Loop through each product and clone it for the new seller. 401 while ($all_products_query->have_posts()) { 402 $all_products_query->the_post(); 403 $original_product_id = get_the_ID(); 404 $original_product = wc_get_product($original_product_id); 405 406 if (!$original_product) { 407 continue; 408 } 409 410 // Get the map_id to identify the product group. 411 $map_id = get_post_meta($original_product_id, '_has_multi_vendor', true); 412 413 // 5. CRITICAL CHECK: If a map_id exists, check if this seller already has a product in this group. 414 // This prevents cloning from other sellers' clones and ensures uniqueness. 415 if ($map_id) { 416 $existing_clone_count = $wpdb->get_var($wpdb->prepare( 417 "SELECT COUNT(*) FROM `{$dokan_product_map_table}` WHERE `map_id` = %d AND `seller_id` = %d", 418 $map_id, 419 $user_id 420 )); 421 422 if ($existing_clone_count > 0) { 423 // This seller already has a version of this product, so skip to the next one. 424 continue; 425 } 426 } 427 428 // A. Duplicate the product post. 429 $wc_duplicator = new \WC_Admin_Duplicate_Product(); 430 $cloned_product = $wc_duplicator->product_duplicate($original_product); 431 $cloned_product_id = $cloned_product->get_id(); 432 433 if (!$cloned_product_id) { 434 continue; 435 } 436 437 // B. Set the desired status for the new product. 438 $product_status = apply_filters('dokan_cloned_product_status', dokan_get_new_post_status()); 439 440 // C. Manage the mapping ID. 441 $update_product_ids = []; 442 if (!$map_id) { 443 // This is the first time this product is being cloned, create a new map_id. 444 $map_id = $spmv_products_instance->get_next_map_id(); 445 $update_product_ids[] = $original_product_id; // Add original product to the map. 446 } 447 $update_product_ids[] = $cloned_product_id; // Add the new clone to the map. 448 449 // D. Update the mapping table and product meta. 450 if ($spmv_products_instance->set_map_id($map_id, $update_product_ids)) { 451 update_post_meta($original_product_id, '_has_multi_vendor', $map_id); 452 update_post_meta($cloned_product_id, '_has_multi_vendor', $map_id); 453 } 454 455 // E. Update the cloned product's author and status. 456 wp_update_post(array( 457 'ID' => $cloned_product_id, 458 'post_author' => $user_id, 459 'post_status' => $product_status, 460 'post_title' => $original_product->get_title(), // Ensure title is consistent. 461 )); 462 463 // F. Update the status in the custom dokan map table as well. 464 $spmv_products_instance->update_product_status($cloned_product_id, $product_status); 465 466 // G. Fire the action hook for compatibility with other functions. 467 do_action('dokan_spmv_create_clone_product', $cloned_product_id, $original_product_id, $map_id); 468 } 469 470 wp_reset_postdata(); 471 } 472 473 /** 315 474 * Callback for the settings section description. 316 475 * … … 358 517 $sanitized_input['logo_url'] = isset( $input['logo_url'] ) ? esc_url_raw( $input['logo_url'] ) : ''; 359 518 $sanitized_input['login_image_url'] = isset( $input['login_image_url'] ) ? esc_url_raw( $input['login_image_url'] ) : ''; 519 $sanitized_input['shop_type'] = isset( $input['shop_type'] ) ? sanitize_text_field( $input['shop_type'] ) : ''; 360 520 $sanitized_input['barcode_field'] = isset( $input['barcode_field'] ) ? sanitize_text_field( $input['barcode_field'] ) : ''; 361 521 -
philia-integration/trunk/includes/cashier/rest-routes/class-cashier-route.php
r3368487 r3371202 44 44 45 45 if( empty( $username ) || empty( $password ) ) { 46 return new \WP_REST_Response(['error' => 'username and password is required.'], 401);46 return new \WP_REST_Response(['error' => esc_attr__('username and password is required.', 'philia-integration') ], 401); 47 47 } 48 48 … … 51 51 52 52 if (is_wp_error($user)) { 53 return new \WP_REST_Response(['error' => 'Invalid username or password.'], 401);53 return new \WP_REST_Response(['error' => esc_attr__('Invalid username or password.', 'philia-integration') ], 401); 54 54 } 55 55 56 56 // Check user role 57 if (!in_array(' philia_cashier', $user->roles) && !in_array('administrator', $user->roles) && !in_array('shop_manager', $user->roles)) {58 return new \WP_REST_Response(['error' => 'Unauthorized role.'], 403);57 if (!in_array('seller', $user->roles) && !in_array('philia_woo_cashier', $user->roles) && !in_array('administrator', $user->roles) && !in_array('shop_manager', $user->roles)) { 58 return new \WP_REST_Response(['error' => esc_attr__('Unauthorized role.', 'philia-integration') ], 403); 59 59 } 60 60 … … 66 66 set_transient('philia_access_token_' . $user->ID, $access_token, $expiration); 67 67 set_transient('philia_access_token_expires_' . $user->ID, $expiration, $expiration); 68 69 return new \WP_REST_Response(['access_token' => $access_token, 'expires_in' => time() + $expiration], 200); 68 69 // Get primary role (if multiple roles exist, take the first one) 70 $user_role = !empty($user->roles) ? $user->roles[0] : ''; 71 72 // Return token, expiry, and role 73 return new \WP_REST_Response([ 74 'access_token' => $access_token, 75 'expires_in' => time() + $expiration, 76 'role' => $user_role 77 ], 200); 70 78 } 71 79 … … 80 88 81 89 if (!is_array($cashier_settings)) { 82 return new \WP_REST_Response(['error' => 'No cashier fields found.'], 404);90 return new \WP_REST_Response(['error' => esc_attr__('No cashier fields found.', 'philia-integration') ], 404); 83 91 } 84 92 … … 100 108 $final_cashier_fields['logo_url'] = $cashier_settings['logo_url']; 101 109 $final_cashier_fields['login_image_url'] = $cashier_settings['login_image_url']; 110 $final_cashier_fields['shop_type'] = $cashier_settings['shop_type']; 102 111 103 112 return new \WP_REST_Response($final_cashier_fields, 200); … … 138 147 $user_query = new \WP_User_Query([ 139 148 'limit' => -1, 140 'role__in' => ['philia_woo_cashier', 'shop_manager', 'administrator' ]149 'role__in' => ['philia_woo_cashier', 'shop_manager', 'administrator', 'seller'] 141 150 ]); 142 151 … … 157 166 } 158 167 168 /** 169 * Retrieves the user info by the access token if valid and not expired. 170 * 171 * @param \WP_REST_Request $request The REST API request object. 172 * @return \WP_User|false User object if token is valid, false otherwise. 173 */ 174 public static function get_user_by_token(\WP_REST_Request $request) 175 { 176 $headers = $request->get_headers(); 177 $auth_header = isset($headers['authorization']) ? $headers['authorization'][0] : ''; 178 179 if (empty($auth_header) || strpos($auth_header, 'API-Key ') !== 0) { 180 return false; 181 } 182 183 $access_token = str_replace('API-Key ', '', $auth_header); 184 $user_id = self::get_user_id_by_token($access_token); 185 186 if (!$user_id) { 187 return false; 188 } 189 190 return get_userdata($user_id); 191 } 159 192 } 160 193 -
philia-integration/trunk/includes/cashier/rest-routes/class-invoice-route.php
r3368679 r3371202 100 100 $page = $request->get_param('page') ? absint($request->get_param('page')) : 1; 101 101 $page_size = $request->get_param('page_size') ? absint($request->get_param('page_size')) : 10; 102 103 $user_is_seller = 0; 104 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 105 if ($user && in_array('seller', (array)$user->roles)) { 106 $user_is_seller = 1; 107 } 102 108 103 109 // Build the query arguments. These arguments work for both HPOS and legacy storage. … … 130 136 if (!$order instanceof \WC_Order) { 131 137 continue; 138 } 139 140 // check seller 141 $seller_ids = dokan_get_seller_id_by_order( $order->get_id() ); 142 143 // Assuming one seller per order, get the first seller ID 144 $seller_id = ( is_array( $seller_ids ) ) ? reset( $seller_ids ) : (int) $seller_ids; 145 146 if ( $user_is_seller && $user->ID != $seller_id ) 147 { 148 continue; 149 } 150 151 // get seller info 152 $store_info = []; 153 if( function_exists('dokan_get_seller_id_by_order')) 154 { 155 // Get the seller ID from the order 156 $seller_ids = dokan_get_seller_id_by_order( $order->get_id() ); 157 158 // Assuming one seller per order, get the first seller ID 159 $seller_id = ( is_array( $seller_ids ) ) ? reset( $seller_ids ) : (int) $seller_ids; 160 161 if ( $seller_id ) 162 { 163 // Get the seller's user data 164 $seller_data = get_userdata( $seller_id ); 165 166 // Get the seller's store information 167 $store_info = dokan_get_store_info( $seller_id ); 168 169 // Retrieve the required information 170 $store_info = [ 171 'seller_first_name' => $seller_data->first_name, 172 'seller_last_name' => $seller_data->last_name, 173 'store_name' => isset( $store_info['store_name'] ) ? $store_info['store_name'] : '', 174 'store_phone' => isset( $store_info['phone'] ) ? $store_info['phone'] : '' 175 ]; 176 } 132 177 } 133 178 … … 160 205 'note' => $order->get_customer_note(), 161 206 'products' => $products, 207 'store_info' => ( !empty( $store_info ) ) ? $store_info : null 162 208 ]; 163 209 } … … 238 284 $order_subtotal = floatval($order->get_subtotal()); 239 285 $order_total_tax = floatval($order->get_total_tax()); 286 287 // get seller info 288 $store_info = []; 289 if( function_exists('dokan_get_seller_id_by_order')) 290 { 291 // Get the seller ID from the order 292 $seller_ids = dokan_get_seller_id_by_order( $order->get_id() ); 293 294 // Assuming one seller per order, get the first seller ID 295 $seller_id = ( is_array( $seller_ids ) ) ? reset( $seller_ids ) : (int) $seller_ids; 296 297 if ( $seller_id ) 298 { 299 // Get the seller's user data 300 $seller_data = get_userdata( $seller_id ); 301 302 // Get the seller's store information 303 $store_info = dokan_get_store_info( $seller_id ); 304 305 // Retrieve the required information 306 $store_info = [ 307 'seller_first_name' => $seller_data->first_name, 308 'seller_last_name' => $seller_data->last_name, 309 'store_name' => isset( $store_info['store_name'] ) ? $store_info['store_name'] : '', 310 'store_phone' => isset( $store_info['phone'] ) ? $store_info['phone'] : '' 311 ]; 312 } 313 } 240 314 241 315 $data = [ … … 256 330 'cashier_fields' => ($order->get_meta('_cashier_fields')) ? json_decode($order->get_meta('_cashier_fields'), true) : [], 257 331 'used_coupons' => $used_coupons, 332 'store_info' => ( !empty( $store_info ) ) ? $store_info : null 258 333 ]; 259 334 … … 391 466 $last_notice = end($notices); 392 467 if (isset($last_notice['notice'])) { 393 $error_message = wc_strip_all_tags($last_notice['notice']);468 $error_message = esc_attr($last_notice['notice']); 394 469 } 395 470 } … … 407 482 } 408 483 409 /**484 /** 410 485 * Calculate invoice total, discounts, and coupon applications. 411 486 * FINAL VERSION: Uses wc_attribute_label() and resolves all namespace issues. … … 418 493 $global_coupons_param_input = $request->get_param('coupon'); 419 494 $customer_id = $request->has_param('user_id') ? intval($request->get_param('user_id')) : 0; 495 $cashier_settings = get_option('philia_cashier_fields'); 496 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 420 497 421 498 if (empty($products_param) || !is_array($products_param)) { … … 439 516 $quantity = absint($product_data['quantity']); 440 517 $cart_item_data = ['philia_pos_index' => $index]; 518 519 // set vendor for un-selected products 520 if ( function_exists( 'dokan_get_vendor_by_product' ) ) 521 { 522 if( $shop_type === 'multi-inventory' || $shop_type === 'multi-vendor') 523 { 524 $seller_id = dokan_get_vendor_by_product( $product_id ); 525 526 if ( ! $seller_id ) { 527 $logged_user_info = Philia_Cashier_REST_Controller::get_user_by_token($request); 528 if ( $logged_user_info ) { 529 update_post_meta( $product_id, '_dokan_product_author', $logged_user_info->ID ); 530 } 531 } 532 } 533 } 441 534 442 535 $main_product = wc_get_product($product_id); … … 596 689 'price' => round($original_price, wc_get_price_decimals()), 597 690 'quantity' => $quantity, 691 'stock' => $product_obj->get_stock_quantity(), 598 692 'totalPrice' => round($line_total_excl_tax, wc_get_price_decimals()), 599 693 'totalDiscount' => round($line_discount_amount, wc_get_price_decimals()), -
philia-integration/trunk/includes/cashier/rest-routes/class-product-route.php
r3368679 r3371202 116 116 public function create_product(\WP_REST_Request $request) 117 117 { 118 // check permission for access 119 $cashier_settings = get_option('philia_cashier_fields'); 120 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 121 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 122 123 if (!$user) { 124 return new \WP_Error('invalid_token', 'Invalid access token or user not found.', ['status' => 401]); 125 } 126 127 $user_roles = (array) $user->roles; 128 $is_admin = in_array('administrator', $user_roles); 129 $is_seller = in_array('seller', $user_roles); 130 131 if (!$is_admin) { 132 if ($is_seller && $shop_type === 'multi-inventory') { 133 return new \WP_Error('permission_denied', esc_attr__('Sellers cannot create products in a multi-inventory setup.', 'philia-integration'), ['status' => 403]); 134 } 135 if ($is_seller && $shop_type !== 'multi-vendor') { 136 return new \WP_Error('permission_denied', esc_attr__('Sellers can only create products in a multi-vendor setup.', 'philia-integration'), ['status' => 403]); 137 } 138 if (!$is_seller) { 139 return new \WP_Error('permission_denied', esc_attr__('You do not have permission to create products.', 'philia-integration'), ['status' => 403]); 140 } 141 } 142 118 143 $name = sanitize_text_field($request->get_param('title')); 119 144 $status_param = intval($request->get_param('status')); … … 183 208 public function update_product(\WP_REST_Request $request) 184 209 { 210 $cashier_settings = get_option('philia_cashier_fields'); 211 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 212 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 213 214 if (!$user) { 215 return new \WP_Error('invalid_token', 'Invalid access token or user not found.', ['status' => 401]); 216 } 217 185 218 $product_id = intval($request->get_param('id')); 186 219 $product = wc_get_product($product_id); 187 220 188 221 if (!$product) { 189 return new \WP_Error('not_found', 'Product not found.', ['status' => 404]); 190 } 191 192 // Update common properties if they are provided in the request 193 $name = $request->get_param('title'); 194 if ($name !== null) { 195 $product->set_name(sanitize_text_field($name)); 196 } 197 198 $status_param = $request->get_param('status'); 199 if ($status_param !== null) { 200 $product->set_status(intval($status_param) === 0 ? 'draft' : 'publish'); 201 } 202 203 $category_ids = $request->get_param('product_cat'); 204 if ($category_ids !== null && is_array($category_ids)) { 205 $product->set_category_ids(array_map('intval', $category_ids)); 206 } 207 208 $sku = $request->get_param('sku'); 209 if ($sku !== null) { 210 $product->set_sku(sanitize_text_field($sku)); 211 } 212 213 // --- Product Type Specific Updates --- 214 if ($product->is_type('variable')) { 215 $variations_data = $request->get_param('variations'); 216 if ($variations_data !== null && is_array($variations_data)) { 217 foreach ($variations_data as $v_data) { 218 if (!is_array($v_data) || empty($v_data['id'])) continue; 219 220 $variation = wc_get_product(intval($v_data['id'])); 221 if ($variation && $variation->is_type('variation') && $variation->get_parent_id() === $product_id) { 222 223 if (isset($v_data['regular_price'])) { 224 $variation->set_regular_price(floatval($v_data['regular_price'])); 222 return new \WP_Error('not_found', esc_attr__('Product not found.', 'philia-integration'), ['status' => 404]); 223 } 224 225 $product_author_id = get_post_field('post_author', $product_id); 226 $user_roles = (array) $user->roles; 227 $is_admin = in_array('administrator', $user_roles); 228 $is_seller = in_array('seller', $user_roles); 229 230 // check product access only for owner 231 if (!$is_admin && $product_author_id != $user->ID) { 232 return new \WP_Error('permission_denied', esc_attr__('You do not have permission to edit this product.', 'philia-inegration'), ['status' => 403]); 233 } 234 235 // update product when user role is seller 236 if ($is_seller && $shop_type === 'multi-inventory') { 237 if ($product->is_type('variable')) { 238 $variations_data = $request->get_param('variations'); 239 if (is_array($variations_data)) { 240 foreach ($variations_data as $v_data) { 241 if (empty($v_data['id'])) continue; 242 $variation = wc_get_product(intval($v_data['id'])); 243 if ($variation && $variation->get_parent_id() === $product_id) { 244 if (isset($v_data['regular_price'])) $variation->set_regular_price(floatval($v_data['regular_price'])); 245 if (isset($v_data['sale_price'])) $variation->set_sale_price($v_data['sale_price'] !== null ? floatval($v_data['sale_price']) : ''); 246 if (isset($v_data['stock'])) { 247 $variation->set_manage_stock(true); 248 $variation->set_stock_quantity(intval($v_data['stock'])); 249 } 250 $variation->save(); 225 251 } 226 227 if (isset($v_data['sale_price'])) { 228 $sale_price = $v_data['sale_price'] !== null ? floatval($v_data['sale_price']) : ''; 229 $variation->set_sale_price($sale_price); 252 } 253 } 254 } else { // simple product 255 if ($request->get_param('regular_price') !== null) $product->set_regular_price(floatval($request->get_param('regular_price'))); 256 if ($request->get_param('sale_price') !== null) $product->set_sale_price($request->get_param('sale_price') !== '' ? floatval($request->get_param('sale_price')) : ''); 257 if ($request->get_param('stock') !== null) { 258 $product->set_manage_stock(true); 259 $product->set_stock_quantity(intval($request->get_param('stock'))); 260 } 261 } 262 $product->save(); 263 return new \WP_REST_Response(['id' => $product->get_id(), 'message' => 'Product inventory and price updated.'], 200); 264 } 265 266 if ($is_admin || ($is_seller && $shop_type === 'multi-vendor')) 267 { 268 // Update common properties if they are provided in the request 269 $name = $request->get_param('title'); 270 if ($name !== null) { 271 $product->set_name(sanitize_text_field($name)); 272 } 273 274 $status_param = $request->get_param('status'); 275 if ($status_param !== null) { 276 $product->set_status(intval($status_param) === 0 ? 'draft' : 'publish'); 277 } 278 279 $category_ids = $request->get_param('product_cat'); 280 if ($category_ids !== null && is_array($category_ids)) { 281 $product->set_category_ids(array_map('intval', $category_ids)); 282 } 283 284 $sku = $request->get_param('sku'); 285 if ($sku !== null) { 286 $product->set_sku(sanitize_text_field($sku)); 287 } 288 289 // --- Product Type Specific Updates --- 290 if ($product->is_type('variable')) { 291 $variations_data = $request->get_param('variations'); 292 if ($variations_data !== null && is_array($variations_data)) { 293 foreach ($variations_data as $v_data) { 294 if (!is_array($v_data) || empty($v_data['id'])) continue; 295 296 $variation = wc_get_product(intval($v_data['id'])); 297 if ($variation && $variation->is_type('variation') && $variation->get_parent_id() === $product_id) { 298 299 if (isset($v_data['regular_price'])) { 300 $variation->set_regular_price(floatval($v_data['regular_price'])); 301 } 302 303 if (isset($v_data['sale_price'])) { 304 $sale_price = $v_data['sale_price'] !== null ? floatval($v_data['sale_price']) : ''; 305 $variation->set_sale_price($sale_price); 306 } 307 308 if (isset($v_data['stock'])) { 309 $variation->set_manage_stock(true); 310 $variation->set_stock_quantity(intval($v_data['stock'])); 311 } 312 313 $variation->save(); 230 314 } 231 232 if (isset($v_data['stock'])) {233 $variation->set_manage_stock(true);234 $variation->set_stock_quantity(intval($v_data['stock']));235 }236 237 $variation->save();238 315 } 239 316 } 240 } 241 } else { // For simple products 242 $regular_price = $request->get_param('regular_price'); 243 if ($regular_price !== null) { 244 $product->set_regular_price(floatval($regular_price)); 245 } 246 247 $sale_price = $request->get_param('sale_price'); 248 if ($sale_price !== null) { 249 $product->set_sale_price($sale_price !== '' ? floatval($sale_price) : ''); 250 } 251 252 $stock = $request->get_param('stock'); 253 if ($stock !== null) { 254 $product->set_manage_stock(true); 255 $product->set_stock_quantity(intval($stock)); 256 } 257 } 258 259 $result = $product->save(); 260 if (is_wp_error($result)) { 261 return new \WP_Error('product_save_failed', 'Failed to save product: ' . $result->get_error_message(), ['status' => 500]); 262 } 263 264 // --- Optional Updates (Barcode & Image) --- 265 $barcode = $request->get_param('barcode'); 266 $barcode_field = get_option('philia_cashier_fields')['barcode_field'] ?? ''; 267 if (!empty($barcode_field) && $barcode !== null) { 268 update_post_meta($product->get_id(), $barcode_field, sanitize_text_field($barcode)); 269 } 270 271 $image = $request->get_file_params()['image'] ?? null; 272 if ($image && !empty($image['tmp_name'])) { 273 require_once ABSPATH . 'wp-admin/includes/image.php'; 274 require_once ABSPATH . 'wp-admin/includes/file.php'; 275 require_once ABSPATH . 'wp-admin/includes/media.php'; 276 277 $attachment_id = media_handle_upload('image', 0); 278 if (!is_wp_error($attachment_id)) { 279 set_post_thumbnail($product->get_id(), $attachment_id); 280 } 281 } 282 283 return new \WP_REST_Response(['id' => $product->get_id(), 'message' => 'Product updated.'], 200); 317 } else { // For simple products 318 $regular_price = $request->get_param('regular_price'); 319 if ($regular_price !== null) { 320 $product->set_regular_price(floatval($regular_price)); 321 } 322 323 $sale_price = $request->get_param('sale_price'); 324 if ($sale_price !== null) { 325 $product->set_sale_price($sale_price !== '' ? floatval($sale_price) : ''); 326 } 327 328 $stock = $request->get_param('stock'); 329 if ($stock !== null) { 330 $product->set_manage_stock(true); 331 $product->set_stock_quantity(intval($stock)); 332 } 333 } 334 335 $result = $product->save(); 336 if (is_wp_error($result)) { 337 return new \WP_Error('product_save_failed', 'Failed to save product: ' . $result->get_error_message(), ['status' => 500]); 338 } 339 340 // --- Optional Updates (Barcode & Image) --- 341 $barcode = $request->get_param('barcode'); 342 $barcode_field = get_option('philia_cashier_fields')['barcode_field'] ?? ''; 343 if (!empty($barcode_field) && $barcode !== null) { 344 update_post_meta($product->get_id(), $barcode_field, sanitize_text_field($barcode)); 345 } 346 347 $image = $request->get_file_params()['image'] ?? null; 348 if ($image && !empty($image['tmp_name'])) { 349 require_once ABSPATH . 'wp-admin/includes/image.php'; 350 require_once ABSPATH . 'wp-admin/includes/file.php'; 351 require_once ABSPATH . 'wp-admin/includes/media.php'; 352 353 $attachment_id = media_handle_upload('image', 0); 354 if (!is_wp_error($attachment_id)) { 355 set_post_thumbnail($product->get_id(), $attachment_id); 356 } 357 } 358 359 return new \WP_REST_Response(['id' => $product->get_id(), 'message' => esc_attr__('Product updated.', 'philia-integration')], 200); 360 } 361 362 return new \WP_Error('permission_denied', 'You do not have permission to update products in this configuration.', ['status' => 403]); 284 363 } 285 364 … … 290 369 * @return \WP_REST_Response|\WP_Error JSON response indicating result of deletion. 291 370 */ 292 public function delete_product(\WP_REST_Request $request) { 371 public function delete_product(\WP_REST_Request $request) 372 { 373 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 374 375 if (!$user) { 376 return new \WP_Error('invalid_token', 'Invalid access token or user not found.', ['status' => 401]); 377 } 378 293 379 $product_id = intval($request->get_param('id')); 294 380 $product = wc_get_product($product_id); … … 296 382 if (!$product) { 297 383 return new \WP_Error('not_found', 'Product not found.', ['status' => 404]); 384 } 385 386 $product_author_id = get_post_field('post_author', $product_id); 387 $is_admin = in_array('administrator', (array)$user->roles); 388 389 if (!$is_admin && $product_author_id != $user->ID) { 390 return new \WP_Error('permission_denied', 'You do not have permission to delete this product.', ['status' => 403]); 298 391 } 299 392 … … 316 409 $username = absint($request->get_param('username')) > 0 ? intval($request->get_param('username')) : null; 317 410 411 $cashier_settings = get_option('philia_cashier_fields'); 412 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 413 318 414 $args = [ 319 415 'status' => ['publish'], … … 321 417 ]; 322 418 419 // filter products by seller or owner id 420 // if ($shop_type === 'multi-vendor' || $shop_type === 'multi-inventory') { 421 // $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 422 // if ($user && !in_array('administrator', (array)$user->roles)) { 423 424 // } 425 // } 426 // get products by owner 427 $cashier_settings = get_option('philia_cashier_fields'); 428 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 429 if( $shop_type != 'single-shop' ) 430 { 431 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 432 $args['author'] = $user->ID; 433 } 434 323 435 $category_id = $request->get_param('category'); 324 436 if (!empty($category_id) && absint($category_id) > 0) { … … 455 567 'id' => $variation->get_id(), 456 568 'barcode' => $variation->get_sku(), 457 'price' => $variation->get_price(), 569 'regular_price' => (float) $variation->get_regular_price(), 570 'sale_price' => (float) $variation->get_sale_price(), 458 571 'discounted_price' => $variation->get_price(), 459 572 'visible' => $variation->is_visible(), … … 481 594 'categories' => $product->get_category_ids(), 482 595 'category_names' => $category_names, 483 'price' => $product->get_price(), 596 'regular_price' => (float) $product->get_regular_price(), 597 'sale_price' => (float) $product->get_sale_price(), 484 598 'description' => $product->get_description(), 485 599 'stock' => $product->get_stock_quantity(), … … 498 612 public function handle_product_import(\WP_REST_Request $request) 499 613 { 614 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 615 616 // check access for importing product 617 if (!$user || !in_array('administrator', (array)$user->roles)) { 618 return new \WP_Error('permission_denied', esc_attr__('You do not have permission to import products.', 'philia-integration'), ['status' => 403]); 619 } 620 500 621 $files = $request->get_file_params(); 501 622 … … 552 673 553 674 set_transient('import_status_' . $import_id, $status, DAY_IN_SECONDS); 554 wp_schedule_single_event(time(), 'philia_process_product_import', [$import_id, $ file_path]);675 wp_schedule_single_event(time(), 'philia_process_product_import', [$import_id, $user->ID, $file_path]); 555 676 556 677 return new \WP_REST_Response([ … … 597 718 // Add the new key to the status array 598 719 $status['progress_percentage'] = $percentage; 599 // --- END: ADDED CODE ---600 720 601 721 return new \WP_REST_Response($status, 200); … … 605 725 * Main import processor function 606 726 */ 607 public static function run_import_processor($import_id, $ file_path)727 public static function run_import_processor($import_id, $user_id, $file_path) 608 728 { 609 729 $status = get_transient('import_status_' . $import_id); … … 645 765 switch (strtolower(trim($row['type'] ?? 'simple'))) { 646 766 case 'variable': 647 $product_id = self::create_or_update_variable_product($row );767 $product_id = self::create_or_update_variable_product($row, $user_id); 648 768 if (!empty($row['barcode'])) { 649 769 $variable_products_map[$row['barcode']] = $product_id; … … 657 777 throw new \Exception("Parent product with barcode '{$parent_barcode}' not found."); 658 778 } 659 self::create_or_update_variation($row, $parent_id );779 self::create_or_update_variation($row, $parent_id, $user_id); 660 780 break; 661 781 662 782 case 'simple': 663 783 default: 664 self::create_or_update_simple_product($row );784 self::create_or_update_simple_product($row, $user_id); 665 785 break; 666 786 } … … 695 815 public function get_popular_products() 696 816 { 817 $cashier_settings = get_option('philia_cashier_fields'); 818 $shop_type = $cashier_settings['shop_type'] ?? 'single-shop'; 819 697 820 $args = [ 698 821 'status' => 'publish', 699 'limit' => 10, // Change this limit as needed822 'limit' => 10, 700 823 'orderby' => 'popularity', 701 824 ]; 702 825 826 // filter products by seller or owner id 827 if ($shop_type === 'multi-vendor' || $shop_type === 'multi-inventory') { 828 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 829 if ($user && !in_array('administrator', (array)$user->roles)) { 830 $args['author'] = $user->ID; 831 } 832 } 833 703 834 $products = wc_get_products($args); 835 704 836 $product_data = array_map(function($product) { 705 837 $image_url = wp_get_attachment_image_src( get_post_thumbnail_id( $product->get_id() ) , 'single-post-thumbnail' ); … … 804 936 * @return array 805 937 */ 806 protected function get_attributes( $product ) { 938 protected function get_attributes( $product ) 939 { 807 940 $attributes = array(); 808 941 … … 977 1110 * Create or update simple product with attributes 978 1111 */ 979 private static function create_or_update_simple_product(array $row )1112 private static function create_or_update_simple_product(array $row, $user_id) 980 1113 { 981 1114 $barcode = sanitize_text_field($row['barcode'] ?? ''); … … 1016 1149 } 1017 1150 } 1018 // --- END MODIFICATION ---1019 1151 1020 1152 // Stock management … … 1040 1172 } 1041 1173 1174 // Set product author if provided 1175 if ($new_id) { 1176 if ($user_id > 0) { 1177 wp_update_post([ 1178 'ID' => $new_id, 1179 'post_author' => $user_id 1180 ]); 1181 } 1182 } 1183 1042 1184 return $new_id; 1043 1185 } … … 1046 1188 * Create or update variable product 1047 1189 */ 1048 private static function create_or_update_variable_product(array $row )1190 private static function create_or_update_variable_product(array $row, $user_id) 1049 1191 { 1050 1192 $barcode = sanitize_text_field($row['barcode'] ?? ''); … … 1077 1219 self::set_product_barcode($new_id, $barcode); 1078 1220 } 1221 1222 // Set product author if provided 1223 if ($new_id) { 1224 if ($user_id > 0) { 1225 wp_update_post([ 1226 'ID' => $new_id, 1227 'post_author' => $user_id 1228 ]); 1229 } 1230 } 1079 1231 1080 1232 return $new_id; … … 1084 1236 * Create or update product variation 1085 1237 */ 1086 private static function create_or_update_variation(array $row, int $parent_id )1238 private static function create_or_update_variation(array $row, int $parent_id, int $user_id) 1087 1239 { 1088 1240 $barcode = sanitize_text_field($row['barcode'] ?? ''); … … 1155 1307 if ($new_id && !empty($barcode)) { 1156 1308 self::set_product_barcode($new_id, $barcode); 1309 } 1310 1311 // Set product author if provided 1312 if ($new_id) { 1313 if ($user_id > 0) { 1314 wp_update_post([ 1315 'ID' => $new_id, 1316 'post_author' => $user_id 1317 ]); 1318 } 1157 1319 } 1158 1320 … … 1415 1577 1416 1578 add_action( 'rest_api_init', 'Philia_API\register_philia_product_rest_controller' ); 1417 add_action('philia_process_product_import', ['Philia_API\Philia_Product_REST_Controller', 'run_import_processor'], 10, 2);1579 add_action('philia_process_product_import', ['Philia_API\Philia_Product_REST_Controller', 'run_import_processor'], 10, 3); -
philia-integration/trunk/includes/cashier/rest-routes/class-reward-route.php
r3368487 r3371202 145 145 $reward_id = absint( $request->get_param('reward_id') ); 146 146 $username = sanitize_text_field($request->get_param('username') ); 147 $user = Philia_Cashier_REST_Controller::get_user_by_token($request); 147 148 148 149 if( empty( $reward_id ) ) … … 195 196 'post_author' => get_current_user_id(), 196 197 'post_type' => 'shop_coupon', 198 'post_author' => ( $user->ID > 0 ) ? $user->ID : '' 197 199 ); 198 200 -
philia-integration/trunk/philia-integration.php
r3368679 r3371202 3 3 * Plugin Name: Philia Integration with WooCommerce 4 4 * Description: Sending Users, Products, Categories and invoices from WooCommerce to Philia and Receive Coupons and Cashback from Philia 5 * Version: 1. 8.15 * Version: 1.9 6 6 * Author: Philia 7 7 * Author URI: https://fa.philia.vip … … 17 17 18 18 // Define constants for plugin directory, URL, and table prefix. 19 define('PHILIA_PLUGIN_VERSION', '1. 8.1');19 define('PHILIA_PLUGIN_VERSION', '1.9'); 20 20 define('PHILIA_PLUGIN_DIR', plugin_dir_path(__FILE__)); 21 21 define('PHILIA_PLUGIN_URL', plugin_dir_url(__FILE__)); -
philia-integration/trunk/readme.txt
r3368843 r3371202 4 4 Requires at least: 6.0 5 5 Tested up to: 6.8 6 Stable tag: 1. 8.16 Stable tag: 1.9 7 7 License: GPL-2.0+ 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 46 46 == Changelog == 47 47 48 = 1.9 = 49 * Bugs fixes and improvements 50 * Supports Multi-Vendor & Multi-Inventory (Dokan) 51 48 52 = 1.8.1 = 49 53 * Bugs fixes and improvements
Note: See TracChangeset
for help on using the changeset viewer.