| 1 |
<?php |
| 2 |
|
| 3 |
namespace SuperFrete_API\Controllers; |
| 4 |
|
| 5 |
use SuperFrete_API\Http\Request; |
| 6 |
use WC_Shipping_Zones; |
| 7 |
|
| 8 |
if (!defined('ABSPATH')) |
| 9 |
exit; // Segurança |
| 10 |
|
| 11 |
class ProductShipping { |
| 12 |
|
| 13 |
public function __construct() { |
| 14 |
|
| 15 |
add_action('woocommerce_after_add_to_cart_form', array(__CLASS__, 'calculator')); |
| 16 |
add_shortcode('pi_shipping_calculator', array($this, 'calculator_shortcode')); |
| 17 |
|
| 18 |
add_action('wc_ajax_pi_load_location_by_ajax', array(__CLASS__, 'loadLocation') ); |
| 19 |
add_action('woocommerce_after_add_to_cart_form', [$this, 'enqueue_scripts']); |
| 20 |
add_action('wp_ajax_superfrete_calculate', [$this, 'calculate_shipping']); |
| 21 |
add_action('wp_ajax_nopriv_superfrete_calculate', [$this, 'calculate_shipping']); // Para usuários não logados |
| 22 |
|
| 23 |
add_action('wp_ajax_superfrete_cal_shipping', array(__CLASS__, 'applyShipping')); |
| 24 |
add_action('wp_ajax_nopriv_superfrete_cal_shipping', array(__CLASS__, 'applyShipping')); |
| 25 |
add_action('wc_ajax_superfrete_cal_shipping', array(__CLASS__, 'applyShipping')); |
| 26 |
|
| 27 |
// Hook the hideCalculator method to the filter so virtual products are automatically hidden |
| 28 |
add_filter('superfrete_hide_calculator_on_single_product_page', array(__CLASS__, 'hideCalculator'), 10, 2); |
| 29 |
} |
| 30 |
|
| 31 |
static function resultHtml() { |
| 32 |
echo '<div id="superfrete-alert-container" class="superfrete-alert-container"></div>'; |
| 33 |
} |
| 34 |
|
| 35 |
function calculator_shortcode() { |
| 36 |
if ($this->position != 'shortcode') { |
| 37 |
return '<div class="error">' . __('Short code is disabled in setting', 'superfrete-product-page-shipping-calculator-woocommerce') . '</di>'; |
| 38 |
} |
| 39 |
|
| 40 |
if (function_exists('is_product') && !is_product()) { |
| 41 |
return '<div class="error">' . __('This shortcode will only work on product page', 'superfrete-product-page-shipping-calculator-woocommerce') . '</di>'; |
| 42 |
} |
| 43 |
|
| 44 |
global $product; |
| 45 |
|
| 46 |
if (!is_object($product) || $product->is_virtual() || !$product->is_in_stock()) |
| 47 |
return; |
| 48 |
|
| 49 |
ob_start(); |
| 50 |
self::calculator(); |
| 51 |
$html = ob_get_contents(); |
| 52 |
ob_end_clean(); |
| 53 |
return $html; |
| 54 |
} |
| 55 |
|
| 56 |
static function calculator() { |
| 57 |
global $product; |
| 58 |
|
| 59 |
// Hide calculator for virtual products (they don't need shipping) |
| 60 |
if (is_object($product) && $product->is_virtual()) { |
| 61 |
return; |
| 62 |
} |
| 63 |
|
| 64 |
if (apply_filters('superfrete_hide_calculator_on_single_product_page', false, $product)) { |
| 65 |
return; |
| 66 |
} |
| 67 |
|
| 68 |
if (is_object($product)) { |
| 69 |
$product_id = $product->get_id(); |
| 70 |
} else { |
| 71 |
$product = ""; |
| 72 |
} |
| 73 |
|
| 74 |
$enable_calculator = get_option('superfrete_enable_calculator'); |
| 75 |
|
| 76 |
if ($enable_calculator === 'no') |
| 77 |
return; |
| 78 |
|
| 79 |
$button_text = get_option('superfrete_open_drawer_button_text', 'Calcular Entrega'); |
| 80 |
$update_address_btn_text = get_option('superfrete_update_button_text', 'Calcular'); |
| 81 |
|
| 82 |
include plugin_dir_path(__FILE__) . '../../templates/woocommerce/shipping-calculator.php'; |
| 83 |
} |
| 84 |
|
| 85 |
static function hideCalculator($val, $product) { |
| 86 |
if (is_object($product) && $product->is_virtual()) |
| 87 |
return true; |
| 88 |
|
| 89 |
return $val; |
| 90 |
} |
| 91 |
|
| 92 |
static function applyShipping() { |
| 93 |
if (!isset($_POST['superfrete_nonce']) || !wp_verify_nonce($_POST['superfrete_nonce'], 'superfrete_nonce')) { |
| 94 |
wp_send_json_error(['message' => 'Requisição inválida.'], 403); |
| 95 |
} |
| 96 |
// Start timing the entire function execution |
| 97 |
$total_start_time = microtime(true); |
| 98 |
|
| 99 |
// Setup logging |
| 100 |
$log_data = [ |
| 101 |
'method' => 'applyShipping', |
| 102 |
'steps' => [], |
| 103 |
'total_time' => 0 |
| 104 |
]; |
| 105 |
|
| 106 |
if (!class_exists('WC_Shortcode_Cart')) { |
| 107 |
include_once WC_ABSPATH . 'includes/shortcodes/class-wc-shortcode-cart.php'; |
| 108 |
} |
| 109 |
|
| 110 |
|
| 111 |
if (self::doingCalculation()) { |
| 112 |
$step_start = microtime(true); |
| 113 |
|
| 114 |
if ((isset($_POST['action_auto_load']) && self::disableAutoLoadEstimate()) || empty($_POST['calc_shipping_postcode']) || !isset($_POST['calc_shipping_postcode'])) { |
| 115 |
$return['shipping_methods'] = sprintf('<div class="superfrete-alert">%s</div>', esc_html(get_option('superfrete_no_address_added_yet', 'Informe seu Endereço para calcular'))); |
| 116 |
wp_send_json($return); |
| 117 |
} |
| 118 |
|
| 119 |
$log_data['steps']['initial_validation'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; |
| 120 |
$step_start = microtime(true); |
| 121 |
|
| 122 |
$return = array(); |
| 123 |
\WC_Shortcode_Cart::calculate_shipping(); |
| 124 |
WC()->cart->calculate_totals(); |
| 125 |
|
| 126 |
$log_data['steps']['calculate_shipping'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; |
| 127 |
$step_start = microtime(true); |
| 128 |
|
| 129 |
// OPTIMIZATION: Use a lightweight shipping package instead of manipulating the cart |
| 130 |
// This avoids expensive cart operations that were causing performance issues |
| 131 |
|
| 132 |
// Get product ID and variation ID |
| 133 |
$product_id = filter_input(INPUT_POST, 'product_id', FILTER_VALIDATE_INT); |
| 134 |
$variation_id = filter_input(INPUT_POST, 'variation_id', FILTER_VALIDATE_INT); |
| 135 |
$quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT) ?: 1; |
| 136 |
|
| 137 |
// Get product data directly without cart manipulation |
| 138 |
if ($product_id) { |
| 139 |
$product = wc_get_product($variation_id ?: $product_id); |
| 140 |
|
| 141 |
if ($product) { |
| 142 |
// Skip calculation for virtual products |
| 143 |
if ($product->is_virtual()) { |
| 144 |
$return['shipping_methods'] = sprintf('<div class="superfrete-alert">%s</div>', esc_html__('Produtos virtuais não necessitam de frete.', 'superfrete')); |
| 145 |
wp_send_json($return); |
| 146 |
} |
| 147 |
// Get destination from form data (not customer data) |
| 148 |
$destination_postcode = sanitize_text_field($_POST['calc_shipping_postcode'] ?? ''); |
| 149 |
$destination_country = sanitize_text_field($_POST['calc_shipping_country'] ?? 'BR'); |
| 150 |
$destination_state = sanitize_text_field($_POST['calc_shipping_state'] ?? ''); |
| 151 |
$destination_city = sanitize_text_field($_POST['calc_shipping_city'] ?? ''); |
| 152 |
|
| 153 |
// Create a lightweight package for shipping calculation |
| 154 |
$package = [ |
| 155 |
'contents' => [ |
| 156 |
[ |
| 157 |
'data' => $product, |
| 158 |
'quantity' => $quantity |
| 159 |
] |
| 160 |
], |
| 161 |
'contents_cost' => $product->get_price() * $quantity, |
| 162 |
'applied_coupons' => [], |
| 163 |
'user' => [ |
| 164 |
'ID' => get_current_user_id(), |
| 165 |
], |
| 166 |
'destination' => [ |
| 167 |
'country' => $destination_country, |
| 168 |
'state' => $destination_state, |
| 169 |
'postcode' => $destination_postcode, |
| 170 |
'city' => $destination_city, |
| 171 |
], |
| 172 |
'cart_subtotal' => $product->get_price() * $quantity, |
| 173 |
]; |
| 174 |
|
| 175 |
$log_data['steps']['prepare_package'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; |
| 176 |
$step_start = microtime(true); |
| 177 |
|
| 178 |
// Debug: Check all registered shipping methods |
| 179 |
$wc_shipping = \WC_Shipping::instance(); |
| 180 |
$all_shipping_methods = $wc_shipping->get_shipping_methods(); |
| 181 |
$log_data['all_registered_methods'] = array_keys($all_shipping_methods); |
| 182 |
|
| 183 |
// Debug: Check all shipping zones |
| 184 |
$all_zones = \WC_Shipping_Zones::get_zones(); |
| 185 |
$log_data['all_zones'] = []; |
| 186 |
foreach ($all_zones as $zone_data) { |
| 187 |
$zone = new \WC_Shipping_Zone($zone_data['zone_id']); |
| 188 |
$zone_methods = []; |
| 189 |
foreach ($zone->get_shipping_methods() as $method) { |
| 190 |
$zone_methods[] = [ |
| 191 |
'id' => $method->id, |
| 192 |
'enabled' => $method->enabled, |
| 193 |
'title' => $method->get_title(), |
| 194 |
]; |
| 195 |
} |
| 196 |
$log_data['all_zones'][] = [ |
| 197 |
'id' => $zone->get_id(), |
| 198 |
'name' => $zone->get_zone_name(), |
| 199 |
'methods' => $zone_methods, |
| 200 |
]; |
| 201 |
} |
| 202 |
|
| 203 |
// Calculate shipping rates directly with our package |
| 204 |
$shipping_zone = \WC_Shipping_Zones::get_zone_matching_package($package); |
| 205 |
|
| 206 |
// Add detailed debugging for shipping zone |
| 207 |
$log_data['package_destination'] = $package['destination']; |
| 208 |
$log_data['shipping_zone'] = [ |
| 209 |
'id' => $shipping_zone->get_id(), |
| 210 |
'name' => $shipping_zone->get_zone_name(), |
| 211 |
'locations' => $shipping_zone->get_zone_locations(), |
| 212 |
]; |
| 213 |
|
| 214 |
// Detailed logging for shipping calculation |
| 215 |
$shipping_start = microtime(true); |
| 216 |
|
| 217 |
// Log shipping providers being used |
| 218 |
$active_shipping = []; |
| 219 |
$all_methods = $shipping_zone->get_shipping_methods(true); |
| 220 |
foreach ($all_methods as $method) { |
| 221 |
$active_shipping[] = [ |
| 222 |
'id' => $method->id, |
| 223 |
'enabled' => $method->is_enabled() ? 'yes' : 'no', |
| 224 |
'title' => $method->get_title(), |
| 225 |
'instance_id' => $method->get_instance_id(), |
| 226 |
]; |
| 227 |
} |
| 228 |
$log_data['active_shipping_methods'] = $active_shipping; |
| 229 |
$log_data['total_methods_found'] = count($all_methods); |
| 230 |
|
| 231 |
// Calculate shipping directly |
| 232 |
$rates = []; |
| 233 |
$method_times = []; |
| 234 |
|
| 235 |
foreach ($shipping_zone->get_shipping_methods(true) as $method) { |
| 236 |
$method_start = microtime(true); |
| 237 |
$method_id = $method->id; |
| 238 |
|
| 239 |
// Log method details |
| 240 |
$log_data['shipping_methods'][] = [ |
| 241 |
'id' => $method_id, |
| 242 |
'title' => $method->get_title(), |
| 243 |
'enabled' => $method->is_enabled() ? 'yes' : 'no', |
| 244 |
'class' => get_class($method), |
| 245 |
'instance_id' => $method->get_instance_id(), |
| 246 |
]; |
| 247 |
|
| 248 |
// Time each individual method |
| 249 |
try { |
| 250 |
// For SuperFrete shipping method, call calculate_shipping directly |
| 251 |
if ($method_id === 'superfrete_shipping') { |
| 252 |
$method->calculate_shipping($package); |
| 253 |
$method_rates = $method->rates; |
| 254 |
} else { |
| 255 |
$method_rates = $method->get_rates_for_package($package); |
| 256 |
} |
| 257 |
|
| 258 |
$log_data['method_results'][$method_id] = [ |
| 259 |
'rates_count' => is_array($method_rates) ? count($method_rates) : 0, |
| 260 |
'rates' => $method_rates ? array_keys($method_rates) : [], |
| 261 |
]; |
| 262 |
} catch (Exception $e) { |
| 263 |
$log_data['method_errors'][$method_id] = $e->getMessage(); |
| 264 |
$method_rates = []; |
| 265 |
} |
| 266 |
|
| 267 |
$method_time = round((microtime(true) - $method_start) * 1000, 2); |
| 268 |
$method_times[$method_id] = $method_time . ' ms'; |
| 269 |
|
| 270 |
// Log any methods taking over 500ms |
| 271 |
if ($method_time > 500) { |
| 272 |
$log_data['slow_methods'][$method_id] = $method_time . ' ms'; |
| 273 |
} |
| 274 |
|
| 275 |
if ($method_rates) { |
| 276 |
$rates = array_merge($rates, $method_rates); |
| 277 |
} |
| 278 |
} |
| 279 |
|
| 280 |
// Add method timing to logs |
| 281 |
$log_data['method_times'] = $method_times; |
| 282 |
|
| 283 |
// Calculate shipping time first |
| 284 |
$shipping_time = round((microtime(true) - $shipping_start) * 1000, 2); |
| 285 |
|
| 286 |
// Check for common API bottlenecks |
| 287 |
if ($shipping_time > 1000) { |
| 288 |
// Get the HTTP stats to see external API calls |
| 289 |
global $wp_version; |
| 290 |
$http_counts = [ |
| 291 |
'total_requests' => 0, |
| 292 |
'total_time' => 0, |
| 293 |
'average_time' => 0 |
| 294 |
]; |
| 295 |
|
| 296 |
// If using WordPress HTTP API, we can check the stats |
| 297 |
if (function_exists('_get_http_stats')) { |
| 298 |
$http_stats = _get_http_stats(); |
| 299 |
if (isset($http_stats['requests'])) { |
| 300 |
$http_counts['total_requests'] = count($http_stats['requests']); |
| 301 |
|
| 302 |
// Sum up all request times |
| 303 |
foreach ($http_stats['requests'] as $request) { |
| 304 |
if (isset($request['args']['timeout'])) { |
| 305 |
// Log timeout values |
| 306 |
$http_counts['timeouts'][] = $request['args']['timeout']; |
| 307 |
} |
| 308 |
if (isset($request['end_time']) && isset($request['start_time'])) { |
| 309 |
$request_time = $request['end_time'] - $request['start_time']; |
| 310 |
$http_counts['total_time'] += $request_time; |
| 311 |
|
| 312 |
// Log slow individual requests (over 500ms) |
| 313 |
if ($request_time > 0.5) { |
| 314 |
$http_counts['slow_requests'][] = [ |
| 315 |
'url' => isset($request['url']) ? preg_replace('/\?.*/', '', $request['url']) : 'unknown', |
| 316 |
'time' => round($request_time * 1000) . ' ms' |
| 317 |
]; |
| 318 |
} |
| 319 |
} |
| 320 |
} |
| 321 |
|
| 322 |
if ($http_counts['total_requests'] > 0) { |
| 323 |
$http_counts['average_time'] = round(($http_counts['total_time'] / $http_counts['total_requests']) * 1000, 2) . ' ms'; |
| 324 |
$http_counts['total_time'] = round($http_counts['total_time'] * 1000, 2) . ' ms'; |
| 325 |
} |
| 326 |
} |
| 327 |
} |
| 328 |
|
| 329 |
$log_data['http_api'] = $http_counts; |
| 330 |
} |
| 331 |
|
| 332 |
$log_data['steps']['calculate_shipping_only'] = $shipping_time . ' ms'; |
| 333 |
|
| 334 |
// Log potential slow API calls |
| 335 |
if ($shipping_time > 2000) { // If over 2 seconds |
| 336 |
$log_data['warning'] = 'Shipping calculation is slow - may indicate API rate limiting or network issues'; |
| 337 |
} |
| 338 |
|
| 339 |
$log_data['steps']['calculate_test_shipping'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; |
| 340 |
$step_start = microtime(true); |
| 341 |
|
| 342 |
// Sort rates by price (lowest to highest) before formatting |
| 343 |
uasort($rates, function($a, $b) { |
| 344 |
$cost_a = floatval($a->cost); |
| 345 |
$cost_b = floatval($b->cost); |
| 346 |
|
| 347 |
// Free shipping (cost = 0) should come first |
| 348 |
if ($cost_a == 0 && $cost_b > 0) return -1; |
| 349 |
if ($cost_b == 0 && $cost_a > 0) return 1; |
| 350 |
|
| 351 |
// Both free or both paid - sort by cost ascending (lowest first) |
| 352 |
if ($cost_a == $cost_b) { |
| 353 |
// If costs are equal, sort by delivery time (faster first) |
| 354 |
$time_a = isset($a->meta_data['delivery_time']) ? intval($a->meta_data['delivery_time']) : 999; |
| 355 |
$time_b = isset($b->meta_data['delivery_time']) ? intval($b->meta_data['delivery_time']) : 999; |
| 356 |
return $time_a <=> $time_b; |
| 357 |
} |
| 358 |
|
| 359 |
return $cost_a <=> $cost_b; |
| 360 |
}); |
| 361 |
|
| 362 |
// Format shipping methods (now sorted by price) |
| 363 |
$shipping_methods = []; |
| 364 |
foreach ($rates as $rate_id => $rate) { |
| 365 |
$title = wc_cart_totals_shipping_method_label($rate); |
| 366 |
$title = self::modifiedTitle($title, $rate); |
| 367 |
$shipping_methods[$rate_id] = apply_filters('superfrete_ppscw_shipping_method_name', $title, $rate, $product_id, $variation_id); |
| 368 |
} |
| 369 |
|
| 370 |
$log_data['steps']['get_shipping_methods'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; |
| 371 |
$step_start = microtime(true); |
| 372 |
|
| 373 |
$return['error'] = ''; // Empty the error to prevent notifications |
| 374 |
$return['shipping_methods'] = self::messageTemplate($shipping_methods); |
| 375 |
|
| 376 |
$log_data['steps']['format_methods_html'] = round((microtime(true) - $step_start) * 1000, 2) . ' ms'; |
| 377 |
|
| 378 |
// No need to restore cart or remove items - we never changed it |
| 379 |
$log_data['steps']['restore_cart'] = 0; |
| 380 |
} else { |
| 381 |
$return['error'] = __('Produto não encontrado.', 'superfrete'); |
| 382 |
$return['shipping_methods'] = ''; |
| 383 |
} |
| 384 |
} else { |
| 385 |
$return['error'] = __('ID do produto não fornecido.', 'superfrete'); |
| 386 |
$return['shipping_methods'] = ''; |
| 387 |
} |
| 388 |
|
| 389 |
// Calculate total execution time |
| 390 |
$log_data['total_time'] = round((microtime(true) - $total_start_time) * 1000, 2) . ' ms'; |
| 391 |
|
| 392 |
// Add log data to the return for debugging |
| 393 |
$return['performance_log'] = $log_data; |
| 394 |
|
| 395 |
// Log to the WordPress error log |
| 396 |
error_log('SuperFrete Performance Log: ' . wp_json_encode($log_data)); |
| 397 |
|
| 398 |
echo wp_json_encode($return); |
| 399 |
} |
| 400 |
wp_die(); |
| 401 |
} |
| 402 |
|
| 403 |
static function is_product_present_in_cart() { |
| 404 |
return false; |
| 405 |
} |
| 406 |
|
| 407 |
static function noShippingLocationInserted() { |
| 408 |
$country = WC()->customer->get_shipping_country(); |
| 409 |
if (empty($country) || $country == 'default') |
| 410 |
return true; |
| 411 |
|
| 412 |
return false; |
| 413 |
} |
| 414 |
|
| 415 |
static function onlyPassErrorNotice($notice_type) { |
| 416 |
if (self::doingCalculation()) { |
| 417 |
return array('error'); |
| 418 |
} |
| 419 |
return $notice_type; |
| 420 |
} |
| 421 |
|
| 422 |
static function doingCalculation() { |
| 423 |
|
| 424 |
|
| 425 |
if (wp_verify_nonce($_POST['superfrete_nonce'], 'superfrete_nonce') && !empty($_POST['calc_shipping']) ) { |
| 426 |
return true; |
| 427 |
} |
| 428 |
return false; |
| 429 |
} |
| 430 |
|
| 431 |
static function addTestProductForProperShippingCost() { |
| 432 |
$product_id = filter_input(INPUT_POST, 'product_id'); |
| 433 |
$quantity = filter_input(INPUT_POST, 'quantity'); |
| 434 |
if (empty($quantity)) |
| 435 |
$quantity = 1; |
| 436 |
|
| 437 |
if ($product_id) { |
| 438 |
$variation_id = filter_input(INPUT_POST, 'variation_id'); |
| 439 |
if (!$variation_id) { |
| 440 |
$variation_id = 0; |
| 441 |
} |
| 442 |
$item_key = self::addProductToCart($product_id, $variation_id, $quantity); |
| 443 |
} else { |
| 444 |
$item_key = ""; |
| 445 |
} |
| 446 |
return $item_key; |
| 447 |
} |
| 448 |
|
| 449 |
static function addProductToCart($product_id, $variation_id, $quantity = 1) { |
| 450 |
$consider_product_quantity = apply_filters('superfrete_ppscw_consider_quantity_in_shipping_calculation', get_option('superfrete_consider_quantity_field', 'dont-consider-quantity-field'), $product_id, $variation_id, $quantity); |
| 451 |
|
| 452 |
if ($consider_product_quantity == 'dont-consider-quantity-field') { |
| 453 |
if (self::productExistInCart($product_id, $variation_id)) |
| 454 |
return ""; |
| 455 |
$quantity = 1; |
| 456 |
} |
| 457 |
|
| 458 |
if (!empty($variation_id)) { |
| 459 |
$variation = self::getVariationAttributes($variation_id); |
| 460 |
} else { |
| 461 |
$variation = array(); |
| 462 |
} |
| 463 |
|
| 464 |
$item_key = WC()->cart->add_to_cart( |
| 465 |
$product_id, |
| 466 |
$quantity, |
| 467 |
$variation_id, |
| 468 |
$variation, |
| 469 |
array( |
| 470 |
'superfrete_test_product_for_calculation' => '1', |
| 471 |
) |
| 472 |
); |
| 473 |
return $item_key; |
| 474 |
} |
| 475 |
|
| 476 |
static function getVariationAttributes($product_id) { |
| 477 |
|
| 478 |
if (empty($product_id)) |
| 479 |
return array(); |
| 480 |
|
| 481 |
$product = wc_get_product($product_id); |
| 482 |
|
| 483 |
if (!is_object($product)) |
| 484 |
return array(); |
| 485 |
|
| 486 |
$variation = array(); |
| 487 |
$type = $product->get_type(); |
| 488 |
if ($type == 'variation') { |
| 489 |
$parent_id = $product->get_parent_id(); |
| 490 |
$parent_obj = wc_get_product($parent_id); |
| 491 |
$default_attributes = $parent_obj->get_default_attributes(); |
| 492 |
$variation_attributes = $product->get_variation_attributes(); |
| 493 |
// Get all parent attributes, needed to fetch attribute options. |
| 494 |
$parent_attributes = $parent_obj->get_attributes(); |
| 495 |
$variation = self::getAttributes($variation_attributes, $default_attributes, $parent_attributes); |
| 496 |
return $variation; |
| 497 |
} |
| 498 |
return $variation; |
| 499 |
} |
| 500 |
|
| 501 |
static function getAttributes($variation_attributes, $default_attributes, $parent_attributes) { |
| 502 |
$list = array(); |
| 503 |
foreach ($variation_attributes as $name => $value) { |
| 504 |
$att_name = str_replace('attribute_', "", $name); |
| 505 |
if (empty($value)) { |
| 506 |
$value = isset($default_attributes[$att_name]) ? $default_attributes[$att_name] : ""; |
| 507 |
|
| 508 |
if (empty($value) && isset($parent_attributes[$att_name])) { |
| 509 |
$attribute_obj = $parent_attributes[$att_name]; |
| 510 |
if ($attribute_obj->get_variation()) { |
| 511 |
$options = $attribute_obj->get_options(); |
| 512 |
if (!empty($options)) { |
| 513 |
// If taxonomy based, options are term IDs so convert the first one to slug. |
| 514 |
if ($attribute_obj->is_taxonomy()) { |
| 515 |
$term = get_term($options[0]); |
| 516 |
if (!is_wp_error($term) && $term) { |
| 517 |
$value = $term->slug; |
| 518 |
} else { |
| 519 |
$value = 'x'; |
| 520 |
} |
| 521 |
} else { |
| 522 |
// For custom attributes, simply use the first option. |
| 523 |
$value = $options[0]; |
| 524 |
} |
| 525 |
} else { |
| 526 |
$value = 'x'; // Fallback if no options found. |
| 527 |
} |
| 528 |
} else { |
| 529 |
$value = 'x'; // Fallback if attribute is not variation-enabled. |
| 530 |
} |
| 531 |
} |
| 532 |
} |
| 533 |
$list[$name] = $value; |
| 534 |
} |
| 535 |
return $list; |
| 536 |
} |
| 537 |
|
| 538 |
static function productExistInCart($product_id, $variation_id) { |
| 539 |
if (!WC()->cart->is_empty()) { |
| 540 |
foreach (WC()->cart->get_cart() as $cart_item) { |
| 541 |
if ($cart_item['product_id'] == $product_id && $cart_item['variation_id'] == $variation_id) { |
| 542 |
return true; |
| 543 |
} |
| 544 |
} |
| 545 |
} |
| 546 |
return false; |
| 547 |
} |
| 548 |
|
| 549 |
static function get_shipping_packages() { |
| 550 |
return array( |
| 551 |
array( |
| 552 |
'contents' => array(), |
| 553 |
'contents_cost' => 0, |
| 554 |
'applied_coupons' => '', |
| 555 |
'user' => array( |
| 556 |
'ID' => get_current_user_id(), |
| 557 |
), |
| 558 |
'destination' => array( |
| 559 |
'country' => self::get_customer()->get_shipping_country(), |
| 560 |
'state' => self::get_customer()->get_shipping_state(), |
| 561 |
'postcode' => self::get_customer()->get_shipping_postcode(), |
| 562 |
'city' => self::get_customer()->get_shipping_city(), |
| 563 |
), |
| 564 |
'cart_subtotal' => 0, |
| 565 |
), |
| 566 |
); |
| 567 |
} |
| 568 |
|
| 569 |
static function get_customer() { |
| 570 |
return WC()->customer; |
| 571 |
} |
| 572 |
|
| 573 |
static function getShippingMethods($packages) { |
| 574 |
$shipping_methods = array(); |
| 575 |
$product_id = filter_input(INPUT_POST, 'product_id'); |
| 576 |
$variation_id = filter_input(INPUT_POST, 'variation_id'); |
| 577 |
foreach ($packages as $package) { |
| 578 |
if (empty($package['rates']) || !is_array($package['rates'])) |
| 579 |
break; |
| 580 |
|
| 581 |
foreach ($package['rates'] as $id => $rate) { |
| 582 |
$title = wc_cart_totals_shipping_method_label($rate); |
| 583 |
$title = self::modifiedTitle($title, $rate); |
| 584 |
$shipping_methods[$id] = apply_filters('superfrete_ppscw_shipping_method_name', $title, $rate, $product_id, $variation_id); |
| 585 |
} |
| 586 |
} |
| 587 |
return $shipping_methods; |
| 588 |
} |
| 589 |
|
| 590 |
static function noMethodAvailableMsg() { |
| 591 |
|
| 592 |
if (self::noShippingLocationInserted()) { |
| 593 |
return wp_kses_post(get_option('superfrete_no_address_added_yet', 'Informe seu Endereço para calcular')); |
| 594 |
} else { |
| 595 |
return wp_kses_post(get_option('superfrete_no_shipping_methods_msg', 'Nenhum método de envio encontrado')); |
| 596 |
} |
| 597 |
} |
| 598 |
|
| 599 |
static function disableAutoLoadEstimate() { |
| 600 |
$auto_loading = get_option('superfrete_auto_calculation', 'disabled'); // Changed default to disabled |
| 601 |
|
| 602 |
if ($auto_loading == 'enabled') |
| 603 |
return false; |
| 604 |
|
| 605 |
return true; |
| 606 |
} |
| 607 |
|
| 608 |
static function messageTemplate($shipping_methods) { |
| 609 |
$html = ''; |
| 610 |
if (!empty($shipping_methods)) { |
| 611 |
$html .= '<div class="superfrete-shipping-methods">'; |
| 612 |
$html .= '<h3>' . __('Opções de Entrega', 'superfrete') . '</h3>'; |
| 613 |
|
| 614 |
foreach ($shipping_methods as $method_id => $method_name) { |
| 615 |
// Extract shipping method name and price |
| 616 |
$html .= '<div class="superfrete-shipping-method">'; |
| 617 |
|
| 618 |
// Get just the method name (everything before the colon) |
| 619 |
$method_parts = explode(':', $method_name, 2); |
| 620 |
$method_label = trim(strip_tags($method_parts[0])); |
| 621 |
|
| 622 |
// Add method name to the HTML |
| 623 |
$html .= '<span class="superfrete-shipping-method-name">' . esc_html($method_label) . '</span>'; |
| 624 |
|
| 625 |
// Handle price display - keeping the full HTML structure for WooCommerce price formatting |
| 626 |
if (count($method_parts) > 1 && !empty($method_parts[1])) { |
| 627 |
// Check if the price part contains HTML or not |
| 628 |
if (strpos($method_parts[1], '<span') !== false) { |
| 629 |
// Keep the HTML formatting intact for the price |
| 630 |
$html .= '<span class="superfrete-shipping-method-price">' . wp_kses_post($method_parts[1]) . '</span>'; |
| 631 |
} else { |
| 632 |
// Add simple text price |
| 633 |
$html .= '<span class="superfrete-shipping-method-price">' . esc_html(trim($method_parts[1])) . '</span>'; |
| 634 |
} |
| 635 |
} else { |
| 636 |
// No price found, show as Free |
| 637 |
$html .= '<span class="superfrete-shipping-method-price">Gratuito</span>'; |
| 638 |
} |
| 639 |
|
| 640 |
$html .= '</div>'; |
| 641 |
} |
| 642 |
|
| 643 |
$html .= '</div>'; |
| 644 |
} else { |
| 645 |
$message = get_option('superfrete_no_rates_message', 'Desculpe, não encontramos métodos de envio para [country]. Por favor, verifique seu endereço ou entre em contato conosco.'); |
| 646 |
|
| 647 |
$country = __('Brasil', 'superfrete'); |
| 648 |
|
| 649 |
if (isset(WC()->customer)) { |
| 650 |
$country_code = self::get_customer()->get_shipping_country(); |
| 651 |
if (!empty($country_code) && isset(WC()->countries) && $country_code !== 'default') { |
| 652 |
$country = WC()->countries->countries[$country_code]; |
| 653 |
} |
| 654 |
} |
| 655 |
|
| 656 |
$find_replace = [ |
| 657 |
'[country]' => $country |
| 658 |
]; |
| 659 |
|
| 660 |
$message = str_replace(array_keys($find_replace), array_values($find_replace), $message); |
| 661 |
|
| 662 |
$html .= '<div class="superfrete-no-shipping-methods">' . esc_html($message) . '</div>'; |
| 663 |
} |
| 664 |
|
| 665 |
return $html; |
| 666 |
} |
| 667 |
|
| 668 |
static function shortCode($message) { |
| 669 |
|
| 670 |
$country = __('Country', 'superfrete-product-page-shipping-calculator-woocommerce'); |
| 671 |
|
| 672 |
if (isset(WC()->customer)) { |
| 673 |
$country_code = self::get_customer()->get_shipping_country(); |
| 674 |
if (!empty($country_code) && isset(WC()->countries) && $country_code !== 'default') { |
| 675 |
$country = WC()->countries->countries[$country_code]; |
| 676 |
} |
| 677 |
} |
| 678 |
|
| 679 |
$find_replace = array( |
| 680 |
'[country]' => $country |
| 681 |
); |
| 682 |
|
| 683 |
$message = str_replace(array_keys($find_replace), array_values($find_replace), $message); |
| 684 |
|
| 685 |
return $message; |
| 686 |
} |
| 687 |
|
| 688 |
function enableShippingCalculationWithoutAddress($val) { |
| 689 |
if (wp_verify_nonce($_POST['superfrete_nonce'], 'superfrete_nonce') && ((isset($_POST['action']) && $_POST['action'] === 'superfrete_cal_shipping') || (isset($_POST['action']) && $_POST['action'] === 'superfrete_save_address_form'))) { |
| 690 |
return null; |
| 691 |
} |
| 692 |
return $val; |
| 693 |
} |
| 694 |
|
| 695 |
static function modifiedTitle($title, $rate) { |
| 696 |
|
| 697 |
if (isset($rate->cost) && $rate->cost == 0) { |
| 698 |
$free_display_type = get_option('superfrete_free_shipping_price', 'nothing'); |
| 699 |
|
| 700 |
if ($free_display_type == 'nothing') |
| 701 |
return $title; |
| 702 |
|
| 703 |
if ($free_display_type == 'zero') { |
| 704 |
$label = $rate->get_label(); |
| 705 |
$title = $label . ': ' . wc_price($rate->cost); |
| 706 |
} |
| 707 |
} |
| 708 |
|
| 709 |
return $title; |
| 710 |
} |
| 711 |
|
| 712 |
static function loadLocation() { |
| 713 |
$location = ['calc_shipping_country' => '', 'calc_shipping_state' => '', 'calc_shipping_city' => '', 'calc_shipping_postcode' => '']; |
| 714 |
|
| 715 |
if (function_exists('WC') && isset(WC()->customer) && is_object(WC()->customer)) { |
| 716 |
$location['calc_shipping_country'] = WC()->customer->get_shipping_country(); |
| 717 |
$location['calc_shipping_state'] = WC()->customer->get_shipping_state(); |
| 718 |
$location['calc_shipping_city'] = WC()->customer->get_shipping_city(); |
| 719 |
$location['calc_shipping_postcode'] = WC()->customer->get_shipping_postcode(); |
| 720 |
} |
| 721 |
|
| 722 |
wp_send_json($location); |
| 723 |
} |
| 724 |
|
| 725 |
/** |
| 726 |
* Exibe o formulário de cálculo de frete na página do produto. |
| 727 |
*/ |
| 728 |
public function display_calculator_form() { |
| 729 |
include plugin_dir_path(__FILE__) . '../../templates/woocommerce/shipping-calculator.php'; |
| 730 |
} |
| 731 |
/** |
| 732 |
* Adiciona os scripts necessários |
| 733 |
*/ |
| 734 |
public function enqueue_scripts() { |
| 735 |
$plugin_file = plugin_dir_path(__FILE__) . '../../superfrete.php'; |
| 736 |
|
| 737 |
// Inclui função get_plugin_data se ainda não estiver disponível |
| 738 |
if (!function_exists('get_plugin_data')) { |
| 739 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 740 |
} |
| 741 |
$plugin_data = get_plugin_data($plugin_file); |
| 742 |
$plugin_version = $plugin_data['Version']; |
| 743 |
|
| 744 |
// Enqueue JavaScript |
| 745 |
wp_enqueue_script( |
| 746 |
'superfrete-calculator', |
| 747 |
plugin_dir_url(__FILE__) . '../../assets/scripts/superfrete-calculator.js', |
| 748 |
['jquery'], |
| 749 |
$plugin_version, // Versão do script |
| 750 |
true |
| 751 |
); |
| 752 |
|
| 753 |
// Enqueue CSS |
| 754 |
wp_enqueue_style( |
| 755 |
'superfrete-calculator-style', |
| 756 |
plugin_dir_url(__FILE__) . '../../assets/styles/superfrete-calculator.css', |
| 757 |
[], |
| 758 |
$plugin_version |
| 759 |
); |
| 760 |
|
| 761 |
wp_localize_script('superfrete-calculator', 'superfrete_ajax', [ |
| 762 |
'ajax_url' => admin_url('admin-ajax.php'), |
| 763 |
]); |
| 764 |
|
| 765 |
// Localize script with additional settings |
| 766 |
wp_localize_script('superfrete-calculator', 'superfrete_setting', [ |
| 767 |
'wc_ajax_url' => add_query_arg(['wc-ajax' => '%%endpoint%%'], home_url('/')), |
| 768 |
'auto_load' => true, |
| 769 |
'country_code' => 'BR', |
| 770 |
'i18n' => [ |
| 771 |
'state_label' => __('Estado', 'superfrete'), |
| 772 |
'select_state_text' => __('Selecione um estado', 'superfrete') |
| 773 |
] |
| 774 |
]); |
| 775 |
} |
| 776 |
} |
| 777 |
|