Changeset 3471597
- Timestamp:
- 02/28/2026 11:49:09 AM (5 weeks ago)
- Location:
- transdirect-shipping
- Files:
-
- 20 edited
- 1 copied
-
tags/8.0.2 (copied) (copied from transdirect-shipping/trunk)
-
tags/8.0.2/frontend/TransdirectShippingPublic.php (modified) (2 diffs)
-
tags/8.0.2/frontend/css/transdirect-shipping-public.css (modified) (1 diff)
-
tags/8.0.2/frontend/js/transdirect.js (modified) (4 diffs)
-
tags/8.0.2/functions.php (modified) (1 diff)
-
tags/8.0.2/includes/OrderSync.php (modified) (2 diffs)
-
tags/8.0.2/includes/TransdirectShipping.php (modified) (3 diffs)
-
tags/8.0.2/includes/TransdirectShippingTrait.php (modified) (2 diffs)
-
tags/8.0.2/includes/WCTransdirectShipping.php (modified) (3 diffs)
-
tags/8.0.2/readme.txt (modified) (1 diff)
-
tags/8.0.2/transdirect-shipping.php (modified) (7 diffs)
-
trunk/frontend/TransdirectShippingPublic.php (modified) (2 diffs)
-
trunk/frontend/css/transdirect-shipping-public.css (modified) (1 diff)
-
trunk/frontend/js/transdirect.js (modified) (4 diffs)
-
trunk/functions.php (modified) (1 diff)
-
trunk/includes/OrderSync.php (modified) (2 diffs)
-
trunk/includes/TransdirectShipping.php (modified) (3 diffs)
-
trunk/includes/TransdirectShippingTrait.php (modified) (2 diffs)
-
trunk/includes/WCTransdirectShipping.php (modified) (3 diffs)
-
trunk/readme.txt (modified) (1 diff)
-
trunk/transdirect-shipping.php (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
transdirect-shipping/tags/8.0.2/frontend/TransdirectShippingPublic.php
r3347335 r3471597 107 107 wp_enqueue_script('typeahead', 'https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js', array('jquery'), null, true); 108 108 109 wp_enqueue_script('transdirect', plugin_dir_url(__FILE__) . 'js/transdirect.js', ['wp-data', 'jquery', 'wp-element'],null, true); 109 wp_enqueue_script('transdirect', plugin_dir_url(__FILE__) . 'js/transdirect.js', ['wp-data', 'jquery', 'wp-element'], TRANSDIRECT_SHIPPING_VERSION, true); 110 111 $defaultShippingType = isset($apiDetails->street_type) ? $apiDetails->street_type : 'residential'; 112 if (WC()->session) { 113 $sessionType = WC()->session->get('td_shipping_type'); 114 if (!empty($sessionType)) { 115 $defaultShippingType = $sessionType; 116 } 117 } 110 118 111 119 wp_localize_script( … … 113 121 'transdirectData', 114 122 [ 115 'locationsUrl' => plugin_dir_url(__FILE__) . 'includes/locations.php', 116 'mode' => $apiDetails, 123 'locationsUrl' => plugin_dir_url(__FILE__) . 'includes/locations.php', 124 'mode' => $apiDetails, 125 'ajaxUrl' => admin_url('admin-ajax.php'), 126 'defaultShippingType' => $defaultShippingType, 117 127 ] 118 128 ); -
transdirect-shipping/tags/8.0.2/frontend/css/transdirect-shipping-public.css
r3347335 r3471597 24 24 } 25 25 26 #td-shipping-type-wrap { 27 padding: 0; 28 margin-bottom: 4px; 29 } 30 31 #td-shipping-type-radios { 32 display: flex; 33 align-items: baseline; 34 gap: 20px; 35 padding: 8px 0; 36 font-size: 16px; 37 line-height: 1.4; 38 } 39 40 #td-shipping-type-radios .td-shipping-type-label { 41 font-weight: 600; 42 margin-right: 4px; 43 } 44 45 #td-shipping-type-radios label { 46 display: inline-flex; 47 align-items: baseline; 48 gap: 6px; 49 cursor: pointer; 50 font-weight: 400; 51 } 52 53 #td-shipping-type-radios input[type="radio"] { 54 margin: 0; 55 cursor: pointer; 56 position: relative; 57 top: 1px; 58 } 59 60 .wc-block-components-shipping-rates-control.td-rates-loading { 61 opacity: 0.4; 62 pointer-events: none; 63 transition: opacity 0.2s ease; 64 } 65 66 .td-shipping-type-row td { 67 padding: 0.5em 0 !important; 68 } 69 70 .td-shipping-type-row #td-shipping-type-radios { 71 padding: 0; 72 margin: 0; 73 } 74 26 75 #autocomplete-div, #autocomplete-div-postcode { 27 background: #FFFFFF;76 background: #FFFFFF; 28 77 border: 1px solid #EDEDED; 29 border-radius: 3px 3px 3px 3px; 78 border-radius: 0 0 3px 3px; 79 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 30 80 display: none; 31 81 height: auto; 32 82 max-height: 150px; 33 margin: -5px 0 0 1px;34 83 overflow-y: auto; 35 84 padding: 5px; 36 85 position: absolute; 37 width: 189px; 38 z-index: 99; 39 width: 100%; 86 z-index: 999999; 87 box-sizing: border-box; 88 line-height: 1.4; 89 white-space: normal; 90 font-size: 14px; 40 91 } 41 92 -
transdirect-shipping/tags/8.0.2/frontend/js/transdirect.js
r3347335 r3471597 1 1 jQuery(document).ready(function () { 2 2 if (transdirectData.mode.mode === 'full_mode') { 3 function insertAutocompleteBox() { 4 const $cityInput = jQuery('#shipping-city'); 3 var latestRequestNumber = 0; 5 4 6 if ($cityInput.length && !jQuery('#autocomplete-div').length) { 7 const $autocompleteBox = jQuery('<div id="autocomplete-div"></div>'); 5 function buildRadioHTML() { 6 var current = transdirectData.defaultShippingType || 'residential'; 7 return '<div class="wc-block-components-shipping-rates-control" id="td-shipping-type-wrap">' + 8 '<div id="td-shipping-type-radios">' + 9 '<span class="td-shipping-type-label">Shipping Type</span>' + 10 '<label><input type="radio" name="td_shipping_type" value="residential"' + 11 (current === 'residential' ? ' checked' : '') + '> Residential</label>' + 12 '<label><input type="radio" name="td_shipping_type" value="business"' + 13 (current === 'business' ? ' checked' : '') + '> Commercial</label>' + 14 '</div></div>'; 15 } 8 16 9 jQuery($cityInput).parent().append($autocompleteBox); 10 11 const offset = $cityInput.offset(); 12 const height = $cityInput.outerHeight(); 13 14 $autocompleteBox.css({ 15 top: offset.top + height, 16 left: offset.left, 17 width: $cityInput.outerWidth() 18 }); 17 function insertElements() { 18 if (jQuery('#shipping-city').length && !jQuery('#autocomplete-div').length) { 19 jQuery('body').append('<div id="autocomplete-div"></div>'); 19 20 } 20 21 21 const $postcodeInput = jQuery('#shipping-postcode'); 22 if (jQuery('#shipping-postcode').length && !jQuery('#autocomplete-div-postcode').length) { 23 jQuery('body').append('<div id="autocomplete-div-postcode"></div>'); 24 } 22 25 23 if ($postcodeInput.length && !jQuery('#autocomplete-div-postcode').length) { 24 const $autocompleteBox = jQuery('<div id="autocomplete-div-postcode"></div>'); 26 var $ratesContainer = jQuery('.wc-block-components-shipping-rates-control').not('#td-shipping-type-wrap'); 27 if ($ratesContainer.length && !jQuery('#td-shipping-type-wrap').length) { 28 $ratesContainer.before(buildRadioHTML()); 29 } 30 } 25 31 26 jQuery($postcodeInput).parent().append($autocompleteBox);27 28 const offset = $postcodeInput.offset();29 const height = $postcodeInput.outerHeight();30 31 $auto completeBox.css({32 top: offset.top + height,32 function positionDropdown(inputSelector, autoSelector) { 33 var $input = jQuery(inputSelector); 34 var $auto = jQuery('#' + autoSelector); 35 if ($input.length && $auto.length) { 36 var offset = $input.offset(); 37 $auto.css({ 38 top: offset.top + $input.outerHeight(), 33 39 left: offset.left, 34 width: $ postcodeInput.outerWidth()40 width: $input.outerWidth() 35 41 }); 36 42 } 37 43 } 38 44 39 insertAutocompleteBox(); 40 var latestRequestNumber = 0; 45 insertElements(); 41 46 42 const observer = new MutationObserver(insertAutocompleteBox);47 var observer = new MutationObserver(insertElements); 43 48 observer.observe(document.body, { childList: true, subtree: true }); 44 49 … … 51 56 }); 52 57 58 var shippingTypeXhr = null; 59 var shippingTypeCartXhr = null; 60 61 jQuery('body').on('change', 'input[name="td_shipping_type"]', function () { 62 var newType = this.value; 63 var $rates = jQuery('.wc-block-components-shipping-rates-control').not('#td-shipping-type-wrap'); 64 65 if (shippingTypeXhr) { shippingTypeXhr.abort(); } 66 if (shippingTypeCartXhr) { shippingTypeCartXhr.abort(); } 67 68 $rates.addClass('td-rates-loading'); 69 70 shippingTypeXhr = jQuery.post(transdirectData.ajaxUrl, { 71 action: 'td_set_shipping_type', 72 shipping_type: newType 73 }, function () { 74 shippingTypeXhr = null; 75 76 var address = { 77 city: jQuery('#shipping-city').val() || '', 78 postcode: jQuery('#shipping-postcode').val() || '', 79 country: jQuery('#shipping-country').val() || 'AU', 80 state: jQuery('#shipping-state').val() || '' 81 }; 82 83 if (address.postcode && address.city) { 84 var apiBase = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) 85 ? wpApiSettings.root : '/wp-json/'; 86 var nonce = (typeof wcBlocksMiddlewareConfig !== 'undefined') 87 ? wcBlocksMiddlewareConfig.storeApiNonce : ''; 88 89 shippingTypeCartXhr = jQuery.ajax({ 90 url: apiBase + 'wc/store/v1/cart/update-customer', 91 method: 'POST', 92 contentType: 'application/json', 93 data: JSON.stringify({ shipping_address: address }), 94 beforeSend: function (xhr) { 95 if (nonce) { xhr.setRequestHeader('Nonce', nonce); } 96 if (typeof wpApiSettings !== 'undefined' && wpApiSettings.nonce) { 97 xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce); 98 } 99 }, 100 success: function (response) { 101 shippingTypeCartXhr = null; 102 if (typeof wp !== 'undefined' && wp.data && wp.data.dispatch) { 103 wp.data.dispatch('wc/store/cart').receiveCart(response); 104 } 105 $rates.removeClass('td-rates-loading'); 106 }, 107 error: function () { 108 shippingTypeCartXhr = null; 109 $rates.removeClass('td-rates-loading'); 110 } 111 }); 112 } else { 113 $rates.removeClass('td-rates-loading'); 114 } 115 jQuery(document.body).trigger('update_checkout'); 116 jQuery(document.body).trigger('update_cart'); 117 }); 118 }); 119 53 120 function td_autocomplete(td_selector, auto_selector) { 54 121 var key_val = jQuery(td_selector).val(); 55 var position = jQuery(td_selector).position();56 122 var html = ''; 57 123 … … 72 138 var main_content = '<ul id="auto_complete">' + html + '</ul>'; 73 139 jQuery("#loading-div").hide(); 74 jQuery("#" + auto_selector).show(); 75 jQuery("#" + auto_selector).html(main_content); 76 jQuery("#" + auto_selector).css('left', position.left); 77 jQuery("#" + auto_selector).css('top', parseInt(position.top) + 55); 140 positionDropdown(td_selector, auto_selector); 141 jQuery("#" + auto_selector).html(main_content).show(); 78 142 } else { 79 143 html = html + '<li>No Results Found</li>'; 80 144 var main_content = '<ul id="auto_complete">' + html + '</ul>'; 81 82 jQuery("#" + auto_selector).show(); 83 jQuery("#" + auto_selector).html(main_content); 84 jQuery("#" + auto_selector).css('left', position.left); 85 jQuery("#" + auto_selector).css('top', parseInt(position.top) + 55); 86 jQuery("#" + auto_selector).css('overflow-y', 'hidden'); 87 145 positionDropdown(td_selector, auto_selector); 146 jQuery("#" + auto_selector).html(main_content).css('overflow-y', 'hidden').show(); 88 147 } 89 148 }); … … 93 152 94 153 function get_value(postcode, locality, txt_selector, div_selector) { 95 const updateCity = (postcode, city) => { 96 if ( 97 wp.data && 98 wp.data.dispatch && 99 typeof wp.data.dispatch("wc/store/cart").receiveCart === "function" 100 ) { 101 if (typeof wp !== 'undefined' && wp.apiFetch) { 102 wp.apiFetch.use(wp.apiFetch.createNonceMiddleware(wpApiSettings.nonce)); 103 } 104 wp.data.dispatch("wc/store/cart").receiveCart({ 105 city: city, 154 if (wp.data && wp.data.dispatch) { 155 wp.data.dispatch('wc/store/cart').setShippingAddress({ 156 city: locality, 157 postcode: postcode, 158 country: 'AU', 159 }); 160 wp.data.dispatch('wc/store/cart').updateCustomerData({ 161 shipping_address: { 162 city: locality, 106 163 postcode: postcode, 107 164 country: 'AU', 108 }); 109 wp.data.dispatch('wc/store/cart').setShippingAddress({ 110 city: city, 111 postcode: postcode, 112 country: 'AU', 113 }); 114 wp.data.dispatch('wc/store/cart').updateCustomerData({ 115 shipping_address: { 116 city: city, 117 postcode: postcode, 118 country: 'AU', 119 } 120 }); 121 } 122 jQuery(document.body).trigger("update_checkout"); 123 jQuery(document.body).trigger("update_cart"); 124 }; 165 } 166 }); 167 } 168 jQuery(document.body).trigger("update_checkout"); 169 jQuery(document.body).trigger("update_cart"); 125 170 126 updateCity(postcode, locality);127 171 jQuery("#autocomplete-div").html(''); 128 172 jQuery("#autocomplete-div").hide(); -
transdirect-shipping/tags/8.0.2/functions.php
r3347335 r3471597 10 10 return strtolower(str_replace(' ', '_', $string)); 11 11 } 12 13 /** 14 * Detect whether WooCommerce High-Performance Order Storage (HPOS) is active. 15 */ 16 function td_is_hpos_enabled() 17 { 18 if ( 19 function_exists('wc_get_container') && 20 class_exists('\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController') 21 ) { 22 $controller = wc_get_container()->get( 23 \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class 24 ); 25 return $controller->custom_orders_table_usage_is_enabled(); 26 } 27 return false; 28 } -
transdirect-shipping/tags/8.0.2/includes/OrderSync.php
r3356427 r3471597 241 241 if ((isset($apiDetails->enable_sync) && $apiDetails->enable_sync == 'on') || (isset($apiDetails->sync_all_order) && $apiDetails->sync_all_order == 'on') || (isset($apiDetails->mode) && $apiDetails->mode == 'no_display_mode')) { 242 242 243 $filters = [ 244 'post_status' => ['draft', 'pending', 'publish'], 245 'post_type' => ['shop_order', 'shop_order_placehold'], 246 'posts_per_page' => 20, 247 'meta_query' => [ 248 [ 249 'key' => 'synced', 250 'value' => '0' 251 ] 252 ] 253 ]; 254 255 $post_query = new \WP_Query($filters); 256 257 if ($post_query->have_posts()) { 258 while ($post_query->have_posts()) : $post_query->the_post(); 259 $order = new \WC_Order($post_query->post->ID); 260 $order_id = $order->get_id(); 261 $booking_id = $order->get_meta('transdirect_booking_id'); 262 263 if ($booking_id && (isset($apiDetails->enable_sync) && $apiDetails->enable_sync == 'on')) { 264 $response = $this->td_sync_order($order, $booking_id, true); 265 if (isset($response->id) && $response->id != '' && get_post_meta($order_id, 'synced', true) == '0') { 266 update_post_meta($order_id, 'synced', '1'); 267 } 268 } else if (!$booking_id && (isset($apiDetails->sync_all_order) && $apiDetails->sync_all_order == 'on') || $apiDetails->mode == 'no_display_mode') { 269 $response = $this->td_sync_all_orders($order); 270 if (isset($response->id) && $response->id != '' && get_post_meta($order_id, 'synced', true) == '0') { 271 update_post_meta($order_id, 'synced', '1'); 272 } 273 } 274 endwhile; 275 wp_reset_query(); 243 if (td_is_hpos_enabled()) { 244 $orders = wc_get_orders([ 245 'limit' => 20, 246 'status' => array_keys(wc_get_order_statuses()), 247 'meta_query' => [ 248 [ 249 'key' => 'synced', 250 'value' => '0', 251 ] 252 ], 253 ]); 254 255 foreach ($orders as $order) { 256 $this->td_process_order_sync($order, $apiDetails); 257 } 258 } else { 259 $filters = [ 260 'post_status' => ['draft', 'pending', 'publish'], 261 'post_type' => ['shop_order', 'shop_order_placehold'], 262 'posts_per_page' => 20, 263 'meta_query' => [ 264 [ 265 'key' => 'synced', 266 'value' => '0', 267 ] 268 ], 269 ]; 270 271 $post_query = new \WP_Query($filters); 272 273 if ($post_query->have_posts()) { 274 while ($post_query->have_posts()) : $post_query->the_post(); 275 $order = new \WC_Order($post_query->post->ID); 276 $this->td_process_order_sync($order, $apiDetails); 277 endwhile; 278 wp_reset_query(); 279 } 280 } 281 } 282 } 283 284 /** 285 * Process sync for a single order (shared by HPOS and legacy paths). 286 */ 287 private function td_process_order_sync($order, $apiDetails) 288 { 289 $order_id = $order->get_id(); 290 $booking_id = $order->get_meta('transdirect_booking_id'); 291 292 if ($booking_id && (isset($apiDetails->enable_sync) && $apiDetails->enable_sync == 'on')) { 293 $response = $this->td_sync_order($order, $booking_id, true); 294 if (isset($response->id) && $response->id != '' && $order->get_meta('synced') === '0') { 295 $order->update_meta_data('synced', '1'); 296 $order->save(); 297 } 298 } else if (!$booking_id && ((isset($apiDetails->sync_all_order) && $apiDetails->sync_all_order == 'on') || $apiDetails->mode == 'no_display_mode')) { 299 $response = $this->td_sync_all_orders($order); 300 if (isset($response->id) && $response->id != '' && $order->get_meta('synced') === '0') { 301 $order->update_meta_data('synced', '1'); 302 $order->save(); 276 303 } 277 304 } … … 299 326 public function td_start_cron() 300 327 { 301 global $wpdb; 302 303 $filters = [ 304 'post_status' => ['draft', 'pending', 'publish'], 305 'post_type' => ['shop_order', 'shop_order_placehold'], 306 'posts_per_page' => -1, 307 'meta_query' => [ 308 [ 309 'key' => 'synced', 310 'value' => '0' 311 ] 312 ] 313 ]; 314 315 $post_query = new \WP_Query($filters); 316 317 if (!$post_query->have_posts()) { 328 if (td_is_hpos_enabled()) { 329 $unsynced = wc_get_orders([ 330 'limit' => 1, 331 'status' => array_keys(wc_get_order_statuses()), 332 'return' => 'ids', 333 'meta_query' => [ 334 [ 335 'key' => 'synced', 336 'value' => '0', 337 ] 338 ], 339 ]); 340 341 if (empty($unsynced)) { 342 $all_orders = wc_get_orders([ 343 'limit' => -1, 344 'status' => array_keys(wc_get_order_statuses()), 345 ]); 346 347 foreach ($all_orders as $order) { 348 if ($order->get_meta('synced') === '') { 349 $order->update_meta_data('synced', '0'); 350 $order->save(); 351 } 352 } 353 } 354 } else { 318 355 $filters = [ 319 'post_status' => ['draft', 'pending', 'publish'], 320 'post_type' => ['shop_order', 'shop_order_placehold'], 321 'posts_per_page' => -1 356 'post_status' => ['draft', 'pending', 'publish'], 357 'post_type' => ['shop_order', 'shop_order_placehold'], 358 'posts_per_page' => -1, 359 'meta_query' => [ 360 [ 361 'key' => 'synced', 362 'value' => '0', 363 ] 364 ], 322 365 ]; 323 366 324 $i = 0; 325 $loop = new \WP_Query($filters); 326 327 while ($loop->have_posts()) { 328 $loop->the_post(); 329 $order = new \WC_Order($loop->post->ID); 330 $orderId = get_post_meta($order->get_id(), 'synced', true); 331 332 if ($orderId == '') { 333 add_post_meta($order->get_id(), 'synced', '0'); 367 $post_query = new \WP_Query($filters); 368 369 if (!$post_query->have_posts()) { 370 $filters = [ 371 'post_status' => ['draft', 'pending', 'publish'], 372 'post_type' => ['shop_order', 'shop_order_placehold'], 373 'posts_per_page' => -1, 374 ]; 375 376 $loop = new \WP_Query($filters); 377 378 while ($loop->have_posts()) { 379 $loop->the_post(); 380 $order = new \WC_Order($loop->post->ID); 381 382 if (get_post_meta($order->get_id(), 'synced', true) === '') { 383 add_post_meta($order->get_id(), 'synced', '0'); 384 } 334 385 } 335 386 } -
transdirect-shipping/tags/8.0.2/includes/TransdirectShipping.php
r3356427 r3471597 142 142 $this->loader->add_action('enqueue_block_assets', $plugin_public, 'enqueue_scripts'); 143 143 $this->loader->add_action('woocommerce_store_api_checkout_order_processed', $this, 'before_create_order'); 144 $this->loader->add_action( 145 'woocommerce_checkout_create_order', 146 $this, 147 'before_create_order', 148 10, 149 2 150 ); 144 151 $this->loader->add_action('wp_footer', $this, 'override_shipping_options_text', 100); 145 $this->loader->add_action('woocommerce_init', $this, 'custom_address_type_field'); 152 $this->loader->add_filter('woocommerce_cart_shipping_packages', $this, 'add_shipping_type_to_package'); 153 $this->loader->add_action('woocommerce_review_order_before_shipping', $this, 'render_shipping_type_radios'); 154 $this->loader->add_action('wp_ajax_td_set_shipping_type', $this, 'handle_shipping_type_ajax'); 155 $this->loader->add_action('wp_ajax_nopriv_td_set_shipping_type', $this, 'handle_shipping_type_ajax'); 156 $this->loader->add_action('woocommerce_init', $this, 'init_shipping_type_session'); 146 157 } 147 158 … … 249 260 $is_td_order = false; 250 261 $api_array = []; 251 foreach (json_decode($cached_data)->quotes as $service => $quote) { 262 $decoded_data = $cached_data ? json_decode($cached_data) : null; 263 $quotes = isset($decoded_data->quotes) ? (array) $decoded_data->quotes : []; 264 foreach ($quotes as $service => $quote) { 252 265 if($selected_shipping === humanizeCamelString($service)) { 253 266 $is_td_order = true; … … 263 276 264 277 /** 265 * 266 * Add shipping type field to checkout shipping and billing address form. 267 * @access public 268 * 269 */ 270 public function custom_address_type_field() { 271 $api_details = $this->td_get_api_details(true); 272 if(isset($api_details->mode)) { 273 $displayMode = $api_details->mode; 274 if($displayMode === 'full_mode') { 275 woocommerce_register_additional_checkout_field( 276 array( 277 'id' => 'namespace/shipping-type', 278 'label' => 'Shipping Type', 279 'optionalLabel' => 'Shipping Type (optional)', 280 'location' => 'address', 281 'required' => true, 282 'type' => 'select', 283 'options' => array( 284 array( 285 'value' => 'residential', 286 'label' => 'Residential', 287 ), 288 array( 289 'value' => 'business', 290 'label' => 'Commercial', 291 ), 292 ), 293 'attributes' => array( 294 'aria-label' => 'Select Shipping Type', 295 ), 296 ) 297 ); 278 * Set the default shipping type in the WC session on first visit. 279 */ 280 public function init_shipping_type_session() { 281 if (!WC()->session) { 282 return; 283 } 284 285 $current = WC()->session->get('td_shipping_type'); 286 if (empty($current)) { 287 $api_details = $this->td_get_api_details(); 288 $default_type = isset($api_details->street_type) ? $api_details->street_type : 'residential'; 289 WC()->session->set('td_shipping_type', $default_type); 290 } 291 } 292 293 /** 294 * Render shipping type radio buttons for Classic Checkout 295 * (injected via woocommerce_review_order_before_shipping hook). 296 */ 297 public function render_shipping_type_radios() { 298 $api_details = $this->td_get_api_details(); 299 if (!isset($api_details->mode) || $api_details->mode !== 'full_mode') { 300 return; 301 } 302 303 $current = WC()->session ? WC()->session->get('td_shipping_type', 'residential') : 'residential'; 304 ?> 305 <tr class="td-shipping-type-row"> 306 <th>Shipping Type</th> 307 <td> 308 <div id="td-shipping-type-radios"> 309 <label> 310 <input type="radio" name="td_shipping_type" value="residential" <?php checked($current, 'residential'); ?>> 311 Residential 312 </label> 313 <label> 314 <input type="radio" name="td_shipping_type" value="business" <?php checked($current, 'business'); ?>> 315 Commercial 316 </label> 317 </div> 318 </td> 319 </tr> 320 <?php 321 } 322 323 /** 324 * AJAX handler to save the selected shipping type to WC session. 325 */ 326 public function handle_shipping_type_ajax() { 327 $type = isset($_POST['shipping_type']) ? sanitize_text_field($_POST['shipping_type']) : ''; 328 if (!empty($type) && WC()->session) { 329 WC()->session->set('td_shipping_type', $type); 330 331 $pkg_count = 1; 332 if (WC()->cart) { 333 $packages = WC()->cart->get_shipping_packages(); 334 if (!empty($packages)) { 335 $pkg_count = count($packages); 336 } 298 337 } 299 } 338 for ($i = 0; $i < $pkg_count; $i++) { 339 WC()->session->set('shipping_for_package_' . $i, false); 340 } 341 342 WC()->session->save_data(); 343 344 wp_send_json_success(); 345 } 346 wp_send_json_error(); 347 } 348 349 /** 350 * Include shipping type in the shipping package so it becomes part of 351 * WooCommerce's package hash. This forces rate recalculation when the 352 * customer changes shipping type without changing their address. 353 */ 354 public function add_shipping_type_to_package($packages) { 355 $shipping_type = WC()->session ? WC()->session->get('td_shipping_type', 'residential') : 'residential'; 356 357 foreach ($packages as &$package) { 358 $package['td_shipping_type'] = $shipping_type; 359 } 360 361 return $packages; 300 362 } 301 363 } -
transdirect-shipping/tags/8.0.2/includes/TransdirectShippingTrait.php
r3356427 r3471597 171 171 return $m->id === $this->shipping_method_id; 172 172 }); 173 return reset($td_method)->instance_settings ?? ''; 173 $first = reset($td_method); 174 return $first ? ($first->instance_settings ?? '') : ''; 174 175 }, $methods); 175 176 … … 177 178 return !empty($selected); 178 179 }); 179 return (object) reset($filtered); 180 $result = reset($filtered); 181 return (object) ($result ?: []); 180 182 } 181 183 } -
transdirect-shipping/tags/8.0.2/includes/WCTransdirectShipping.php
r3347335 r3471597 131 131 public function calculate_shipping($package = array()) 132 132 { 133 $batch_request_body = file_get_contents('php://input');134 $request_params = json_decode($batch_request_body, true);135 $shipping_type = "residential";136 133 $apiDetails = $this->td_get_api_details(true); 134 $default_type = isset($apiDetails->street_type) ? $apiDetails->street_type : 'residential'; 135 $shipping_type = WC()->session ? WC()->session->get('td_shipping_type', $default_type) : $default_type; 137 136 138 137 if(isset($apiDetails->mode) && $apiDetails->mode != 'no_display_mode') { 139 if (isset($request_params['requests']) && is_array($request_params['requests'])) {140 $shipping_data = $request_params['requests'][0]['data']['shipping_address'] ?? [];141 if (isset($shipping_data['namespace/shipping-type'])) {142 $shipping_type = sanitize_text_field($shipping_data['namespace/shipping-type']) ?? "residential";143 }144 } elseif (is_user_logged_in()) {145 $user_id = get_current_user_id();146 $shipping_type_meta = get_user_meta($user_id, '_wc_shipping/namespace/shipping-type', true);147 if (!empty($shipping_type_meta)) {148 $shipping_type = sanitize_text_field($shipping_type_meta);149 }150 }151 138 152 139 $data = $this->get_quote($shipping_type); … … 179 166 private function get_quote($shipping_type) 180 167 { 168 if (!function_exists('WC') || !WC()->cart) { 169 return null; 170 } 181 171 $get_shipping_packages = WC()->cart->get_shipping_packages(); 182 172 $shipping = reset($get_shipping_packages); … … 230 220 231 221 $response = $this->send('POST', 'bookings/v4', $this->api_key, $params, true); 222 $response = json_decode($response, true); 223 $quotes = $response['quotes'] ?? []; 224 225 $quotes_array = []; 226 foreach ($quotes as $carrier => $data) { 227 $quotes_array[$carrier] = $data; 228 } 229 230 uasort($quotes_array, function($a, $b) { 231 return $a['total'] <=> $b['total']; 232 }); 233 234 $response['quotes'] = $quotes_array; 235 $response = json_encode($response); 232 236 set_transient($hash_key, $request_hash, 5 * MINUTE_IN_SECONDS); 233 237 set_transient($response_key, $response, 5 * MINUTE_IN_SECONDS); -
transdirect-shipping/tags/8.0.2/readme.txt
r3347335 r3471597 4 4 Requires at least: 4.2 5 5 Tested up to: 6.8.2 6 Stable tag: 8.0.2 7 WC requires at least: 7.1 8 WC tested up to: 9.6 6 9 License: GPLv3 7 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html -
transdirect-shipping/tags/8.0.2/transdirect-shipping.php
r3356427 r3471597 9 9 * 10 10 * @link https://transdirect.com.au/ 11 * @since 8.0. 111 * @since 8.0.2 12 12 * @package Transdirect_Shipping 13 13 * … … 16 16 * Plugin URI: https://www.transdirect.com.au/e-commerce/woo-commerce/ 17 17 * Description: This plugin allows you to calculate shipping as per your delivery location. 18 * Version: 8.0. 118 * Version: 8.0.2 19 19 * Author: Transdirect 20 20 * Author URI: https://transdirect.com.au/ … … 23 23 * Text Domain: transdirect-shipping 24 24 * Domain Path: /languages 25 * WC requires at least: 7.1 26 * WC tested up to: 10.5 25 27 */ 26 28 … … 33 35 require_once 'functions.php'; 34 36 37 add_action('before_woocommerce_init', function () { 38 if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) { 39 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); 40 } 41 }); 42 35 43 /** 36 44 * Currently plugin version. 37 * Start at version 8.0. 1and use SemVer - https://semver.org45 * Start at version 8.0.2 and use SemVer - https://semver.org 38 46 * Rename this for your plugin and update it as you release new versions. 39 47 */ 40 define('TRANSDIRECT_SHIPPING_VERSION', '8.0. 1');48 define('TRANSDIRECT_SHIPPING_VERSION', '8.0.2'); 41 49 42 50 /** … … 59 67 * @return void 60 68 */ 61 function td_run_at_activation() 62 { 63 $filters = [ 64 'post_status' => 'any', 65 'post_type' => 'shop_order', 66 'posts_per_page' => -1 67 ]; 68 69 $i = 0; 70 $loop = new WP_Query($filters); 71 72 while ($loop->have_posts()) { 73 $loop->the_post(); 74 $order = new WC_Order($loop->post->ID); 75 $orderId = get_post_meta($order->id, 'synced', true); 76 if ($orderId == '') { 77 add_post_meta($order->id, 'synced', '0'); 69 register_activation_hook(__FILE__, function () { 70 update_option('td_run_sync_on_activation', true); 71 }); 72 73 add_action('plugins_loaded', function () { 74 if (!class_exists('WooCommerce')) { 75 return; 76 } 77 78 if (get_option('td_run_sync_on_activation')) { 79 td_run_at_activation_safe(); 80 delete_option('td_run_sync_on_activation'); 81 } 82 }); 83 84 function td_run_at_activation_safe() { 85 $is_hpos_enabled = td_is_hpos_enabled(); 86 $orders = []; 87 if ($is_hpos_enabled) { 88 $orders = wc_get_orders([ 89 'limit' => -1, 90 'return' => 'ids', 91 'status' => array_keys(wc_get_order_statuses()), 92 ]); 93 } else { 94 $loop = new WP_Query([ 95 'post_type' => 'shop_order', 96 'post_status' => 'any', 97 'posts_per_page' => -1, 98 'fields' => 'ids', 99 ]); 100 $orders = $loop->posts; 101 } 102 103 $total_orders = count($orders); 104 $batch_size = 100; 105 $count = 0; 106 107 for ($offset = 0; $offset < $total_orders; $offset += $batch_size) { 108 $batch = array_slice($orders, $offset, $batch_size); 109 foreach ($batch as $order_id) { 110 $synced = get_post_meta($order_id, 'synced', true); 111 if ($synced == '') { 112 add_post_meta($order_id, 'synced', '0'); 113 $count++; 114 } 78 115 } 79 116 } 80 117 } 81 register_activation_hook(__FILE__, 'td_run_at_activation');82 118 83 119 /** … … 137 173 } 138 174 139 register_deactivation_hook(__FILE__, 'td_cronstarter_deactivate');140 141 175 add_action('td_cron_order_sync', 'td_my_repeat_function'); 142 176 … … 175 209 } 176 210 177 178 /**179 *180 * Hook add action to make cron work in background when wordpress is load.181 *182 */183 add_action('admin_init', 'td_cronstarter_activation');184 add_action('wp', 'td_cronstarter_activation');185 186 /**187 *188 * This will start the and activate cron job every 5 minutes.189 * @access public190 *191 */192 function td_cronstarter_activation() {193 if (!get_transient( 'timeout_for_30_min' )) {194 set_transient( 'timeout_for_30_min', 'cron_activation_timeout', 0.5 * HOUR_IN_SECONDS );195 $startCron = new OrderSync();196 $startCron->td_start_cron();197 $startProductCron = new ProductSync();198 $startProductCron->td_start_product_cron();199 }200 }201 211 202 212 /** -
transdirect-shipping/trunk/frontend/TransdirectShippingPublic.php
r3347335 r3471597 107 107 wp_enqueue_script('typeahead', 'https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js', array('jquery'), null, true); 108 108 109 wp_enqueue_script('transdirect', plugin_dir_url(__FILE__) . 'js/transdirect.js', ['wp-data', 'jquery', 'wp-element'],null, true); 109 wp_enqueue_script('transdirect', plugin_dir_url(__FILE__) . 'js/transdirect.js', ['wp-data', 'jquery', 'wp-element'], TRANSDIRECT_SHIPPING_VERSION, true); 110 111 $defaultShippingType = isset($apiDetails->street_type) ? $apiDetails->street_type : 'residential'; 112 if (WC()->session) { 113 $sessionType = WC()->session->get('td_shipping_type'); 114 if (!empty($sessionType)) { 115 $defaultShippingType = $sessionType; 116 } 117 } 110 118 111 119 wp_localize_script( … … 113 121 'transdirectData', 114 122 [ 115 'locationsUrl' => plugin_dir_url(__FILE__) . 'includes/locations.php', 116 'mode' => $apiDetails, 123 'locationsUrl' => plugin_dir_url(__FILE__) . 'includes/locations.php', 124 'mode' => $apiDetails, 125 'ajaxUrl' => admin_url('admin-ajax.php'), 126 'defaultShippingType' => $defaultShippingType, 117 127 ] 118 128 ); -
transdirect-shipping/trunk/frontend/css/transdirect-shipping-public.css
r3347335 r3471597 24 24 } 25 25 26 #td-shipping-type-wrap { 27 padding: 0; 28 margin-bottom: 4px; 29 } 30 31 #td-shipping-type-radios { 32 display: flex; 33 align-items: baseline; 34 gap: 20px; 35 padding: 8px 0; 36 font-size: 16px; 37 line-height: 1.4; 38 } 39 40 #td-shipping-type-radios .td-shipping-type-label { 41 font-weight: 600; 42 margin-right: 4px; 43 } 44 45 #td-shipping-type-radios label { 46 display: inline-flex; 47 align-items: baseline; 48 gap: 6px; 49 cursor: pointer; 50 font-weight: 400; 51 } 52 53 #td-shipping-type-radios input[type="radio"] { 54 margin: 0; 55 cursor: pointer; 56 position: relative; 57 top: 1px; 58 } 59 60 .wc-block-components-shipping-rates-control.td-rates-loading { 61 opacity: 0.4; 62 pointer-events: none; 63 transition: opacity 0.2s ease; 64 } 65 66 .td-shipping-type-row td { 67 padding: 0.5em 0 !important; 68 } 69 70 .td-shipping-type-row #td-shipping-type-radios { 71 padding: 0; 72 margin: 0; 73 } 74 26 75 #autocomplete-div, #autocomplete-div-postcode { 27 background: #FFFFFF;76 background: #FFFFFF; 28 77 border: 1px solid #EDEDED; 29 border-radius: 3px 3px 3px 3px; 78 border-radius: 0 0 3px 3px; 79 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 30 80 display: none; 31 81 height: auto; 32 82 max-height: 150px; 33 margin: -5px 0 0 1px;34 83 overflow-y: auto; 35 84 padding: 5px; 36 85 position: absolute; 37 width: 189px; 38 z-index: 99; 39 width: 100%; 86 z-index: 999999; 87 box-sizing: border-box; 88 line-height: 1.4; 89 white-space: normal; 90 font-size: 14px; 40 91 } 41 92 -
transdirect-shipping/trunk/frontend/js/transdirect.js
r3347335 r3471597 1 1 jQuery(document).ready(function () { 2 2 if (transdirectData.mode.mode === 'full_mode') { 3 function insertAutocompleteBox() { 4 const $cityInput = jQuery('#shipping-city'); 3 var latestRequestNumber = 0; 5 4 6 if ($cityInput.length && !jQuery('#autocomplete-div').length) { 7 const $autocompleteBox = jQuery('<div id="autocomplete-div"></div>'); 5 function buildRadioHTML() { 6 var current = transdirectData.defaultShippingType || 'residential'; 7 return '<div class="wc-block-components-shipping-rates-control" id="td-shipping-type-wrap">' + 8 '<div id="td-shipping-type-radios">' + 9 '<span class="td-shipping-type-label">Shipping Type</span>' + 10 '<label><input type="radio" name="td_shipping_type" value="residential"' + 11 (current === 'residential' ? ' checked' : '') + '> Residential</label>' + 12 '<label><input type="radio" name="td_shipping_type" value="business"' + 13 (current === 'business' ? ' checked' : '') + '> Commercial</label>' + 14 '</div></div>'; 15 } 8 16 9 jQuery($cityInput).parent().append($autocompleteBox); 10 11 const offset = $cityInput.offset(); 12 const height = $cityInput.outerHeight(); 13 14 $autocompleteBox.css({ 15 top: offset.top + height, 16 left: offset.left, 17 width: $cityInput.outerWidth() 18 }); 17 function insertElements() { 18 if (jQuery('#shipping-city').length && !jQuery('#autocomplete-div').length) { 19 jQuery('body').append('<div id="autocomplete-div"></div>'); 19 20 } 20 21 21 const $postcodeInput = jQuery('#shipping-postcode'); 22 if (jQuery('#shipping-postcode').length && !jQuery('#autocomplete-div-postcode').length) { 23 jQuery('body').append('<div id="autocomplete-div-postcode"></div>'); 24 } 22 25 23 if ($postcodeInput.length && !jQuery('#autocomplete-div-postcode').length) { 24 const $autocompleteBox = jQuery('<div id="autocomplete-div-postcode"></div>'); 26 var $ratesContainer = jQuery('.wc-block-components-shipping-rates-control').not('#td-shipping-type-wrap'); 27 if ($ratesContainer.length && !jQuery('#td-shipping-type-wrap').length) { 28 $ratesContainer.before(buildRadioHTML()); 29 } 30 } 25 31 26 jQuery($postcodeInput).parent().append($autocompleteBox);27 28 const offset = $postcodeInput.offset();29 const height = $postcodeInput.outerHeight();30 31 $auto completeBox.css({32 top: offset.top + height,32 function positionDropdown(inputSelector, autoSelector) { 33 var $input = jQuery(inputSelector); 34 var $auto = jQuery('#' + autoSelector); 35 if ($input.length && $auto.length) { 36 var offset = $input.offset(); 37 $auto.css({ 38 top: offset.top + $input.outerHeight(), 33 39 left: offset.left, 34 width: $ postcodeInput.outerWidth()40 width: $input.outerWidth() 35 41 }); 36 42 } 37 43 } 38 44 39 insertAutocompleteBox(); 40 var latestRequestNumber = 0; 45 insertElements(); 41 46 42 const observer = new MutationObserver(insertAutocompleteBox);47 var observer = new MutationObserver(insertElements); 43 48 observer.observe(document.body, { childList: true, subtree: true }); 44 49 … … 51 56 }); 52 57 58 var shippingTypeXhr = null; 59 var shippingTypeCartXhr = null; 60 61 jQuery('body').on('change', 'input[name="td_shipping_type"]', function () { 62 var newType = this.value; 63 var $rates = jQuery('.wc-block-components-shipping-rates-control').not('#td-shipping-type-wrap'); 64 65 if (shippingTypeXhr) { shippingTypeXhr.abort(); } 66 if (shippingTypeCartXhr) { shippingTypeCartXhr.abort(); } 67 68 $rates.addClass('td-rates-loading'); 69 70 shippingTypeXhr = jQuery.post(transdirectData.ajaxUrl, { 71 action: 'td_set_shipping_type', 72 shipping_type: newType 73 }, function () { 74 shippingTypeXhr = null; 75 76 var address = { 77 city: jQuery('#shipping-city').val() || '', 78 postcode: jQuery('#shipping-postcode').val() || '', 79 country: jQuery('#shipping-country').val() || 'AU', 80 state: jQuery('#shipping-state').val() || '' 81 }; 82 83 if (address.postcode && address.city) { 84 var apiBase = (typeof wpApiSettings !== 'undefined' && wpApiSettings.root) 85 ? wpApiSettings.root : '/wp-json/'; 86 var nonce = (typeof wcBlocksMiddlewareConfig !== 'undefined') 87 ? wcBlocksMiddlewareConfig.storeApiNonce : ''; 88 89 shippingTypeCartXhr = jQuery.ajax({ 90 url: apiBase + 'wc/store/v1/cart/update-customer', 91 method: 'POST', 92 contentType: 'application/json', 93 data: JSON.stringify({ shipping_address: address }), 94 beforeSend: function (xhr) { 95 if (nonce) { xhr.setRequestHeader('Nonce', nonce); } 96 if (typeof wpApiSettings !== 'undefined' && wpApiSettings.nonce) { 97 xhr.setRequestHeader('X-WP-Nonce', wpApiSettings.nonce); 98 } 99 }, 100 success: function (response) { 101 shippingTypeCartXhr = null; 102 if (typeof wp !== 'undefined' && wp.data && wp.data.dispatch) { 103 wp.data.dispatch('wc/store/cart').receiveCart(response); 104 } 105 $rates.removeClass('td-rates-loading'); 106 }, 107 error: function () { 108 shippingTypeCartXhr = null; 109 $rates.removeClass('td-rates-loading'); 110 } 111 }); 112 } else { 113 $rates.removeClass('td-rates-loading'); 114 } 115 jQuery(document.body).trigger('update_checkout'); 116 jQuery(document.body).trigger('update_cart'); 117 }); 118 }); 119 53 120 function td_autocomplete(td_selector, auto_selector) { 54 121 var key_val = jQuery(td_selector).val(); 55 var position = jQuery(td_selector).position();56 122 var html = ''; 57 123 … … 72 138 var main_content = '<ul id="auto_complete">' + html + '</ul>'; 73 139 jQuery("#loading-div").hide(); 74 jQuery("#" + auto_selector).show(); 75 jQuery("#" + auto_selector).html(main_content); 76 jQuery("#" + auto_selector).css('left', position.left); 77 jQuery("#" + auto_selector).css('top', parseInt(position.top) + 55); 140 positionDropdown(td_selector, auto_selector); 141 jQuery("#" + auto_selector).html(main_content).show(); 78 142 } else { 79 143 html = html + '<li>No Results Found</li>'; 80 144 var main_content = '<ul id="auto_complete">' + html + '</ul>'; 81 82 jQuery("#" + auto_selector).show(); 83 jQuery("#" + auto_selector).html(main_content); 84 jQuery("#" + auto_selector).css('left', position.left); 85 jQuery("#" + auto_selector).css('top', parseInt(position.top) + 55); 86 jQuery("#" + auto_selector).css('overflow-y', 'hidden'); 87 145 positionDropdown(td_selector, auto_selector); 146 jQuery("#" + auto_selector).html(main_content).css('overflow-y', 'hidden').show(); 88 147 } 89 148 }); … … 93 152 94 153 function get_value(postcode, locality, txt_selector, div_selector) { 95 const updateCity = (postcode, city) => { 96 if ( 97 wp.data && 98 wp.data.dispatch && 99 typeof wp.data.dispatch("wc/store/cart").receiveCart === "function" 100 ) { 101 if (typeof wp !== 'undefined' && wp.apiFetch) { 102 wp.apiFetch.use(wp.apiFetch.createNonceMiddleware(wpApiSettings.nonce)); 103 } 104 wp.data.dispatch("wc/store/cart").receiveCart({ 105 city: city, 154 if (wp.data && wp.data.dispatch) { 155 wp.data.dispatch('wc/store/cart').setShippingAddress({ 156 city: locality, 157 postcode: postcode, 158 country: 'AU', 159 }); 160 wp.data.dispatch('wc/store/cart').updateCustomerData({ 161 shipping_address: { 162 city: locality, 106 163 postcode: postcode, 107 164 country: 'AU', 108 }); 109 wp.data.dispatch('wc/store/cart').setShippingAddress({ 110 city: city, 111 postcode: postcode, 112 country: 'AU', 113 }); 114 wp.data.dispatch('wc/store/cart').updateCustomerData({ 115 shipping_address: { 116 city: city, 117 postcode: postcode, 118 country: 'AU', 119 } 120 }); 121 } 122 jQuery(document.body).trigger("update_checkout"); 123 jQuery(document.body).trigger("update_cart"); 124 }; 165 } 166 }); 167 } 168 jQuery(document.body).trigger("update_checkout"); 169 jQuery(document.body).trigger("update_cart"); 125 170 126 updateCity(postcode, locality);127 171 jQuery("#autocomplete-div").html(''); 128 172 jQuery("#autocomplete-div").hide(); -
transdirect-shipping/trunk/functions.php
r3347335 r3471597 10 10 return strtolower(str_replace(' ', '_', $string)); 11 11 } 12 13 /** 14 * Detect whether WooCommerce High-Performance Order Storage (HPOS) is active. 15 */ 16 function td_is_hpos_enabled() 17 { 18 if ( 19 function_exists('wc_get_container') && 20 class_exists('\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController') 21 ) { 22 $controller = wc_get_container()->get( 23 \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class 24 ); 25 return $controller->custom_orders_table_usage_is_enabled(); 26 } 27 return false; 28 } -
transdirect-shipping/trunk/includes/OrderSync.php
r3356427 r3471597 241 241 if ((isset($apiDetails->enable_sync) && $apiDetails->enable_sync == 'on') || (isset($apiDetails->sync_all_order) && $apiDetails->sync_all_order == 'on') || (isset($apiDetails->mode) && $apiDetails->mode == 'no_display_mode')) { 242 242 243 $filters = [ 244 'post_status' => ['draft', 'pending', 'publish'], 245 'post_type' => ['shop_order', 'shop_order_placehold'], 246 'posts_per_page' => 20, 247 'meta_query' => [ 248 [ 249 'key' => 'synced', 250 'value' => '0' 251 ] 252 ] 253 ]; 254 255 $post_query = new \WP_Query($filters); 256 257 if ($post_query->have_posts()) { 258 while ($post_query->have_posts()) : $post_query->the_post(); 259 $order = new \WC_Order($post_query->post->ID); 260 $order_id = $order->get_id(); 261 $booking_id = $order->get_meta('transdirect_booking_id'); 262 263 if ($booking_id && (isset($apiDetails->enable_sync) && $apiDetails->enable_sync == 'on')) { 264 $response = $this->td_sync_order($order, $booking_id, true); 265 if (isset($response->id) && $response->id != '' && get_post_meta($order_id, 'synced', true) == '0') { 266 update_post_meta($order_id, 'synced', '1'); 267 } 268 } else if (!$booking_id && (isset($apiDetails->sync_all_order) && $apiDetails->sync_all_order == 'on') || $apiDetails->mode == 'no_display_mode') { 269 $response = $this->td_sync_all_orders($order); 270 if (isset($response->id) && $response->id != '' && get_post_meta($order_id, 'synced', true) == '0') { 271 update_post_meta($order_id, 'synced', '1'); 272 } 273 } 274 endwhile; 275 wp_reset_query(); 243 if (td_is_hpos_enabled()) { 244 $orders = wc_get_orders([ 245 'limit' => 20, 246 'status' => array_keys(wc_get_order_statuses()), 247 'meta_query' => [ 248 [ 249 'key' => 'synced', 250 'value' => '0', 251 ] 252 ], 253 ]); 254 255 foreach ($orders as $order) { 256 $this->td_process_order_sync($order, $apiDetails); 257 } 258 } else { 259 $filters = [ 260 'post_status' => ['draft', 'pending', 'publish'], 261 'post_type' => ['shop_order', 'shop_order_placehold'], 262 'posts_per_page' => 20, 263 'meta_query' => [ 264 [ 265 'key' => 'synced', 266 'value' => '0', 267 ] 268 ], 269 ]; 270 271 $post_query = new \WP_Query($filters); 272 273 if ($post_query->have_posts()) { 274 while ($post_query->have_posts()) : $post_query->the_post(); 275 $order = new \WC_Order($post_query->post->ID); 276 $this->td_process_order_sync($order, $apiDetails); 277 endwhile; 278 wp_reset_query(); 279 } 280 } 281 } 282 } 283 284 /** 285 * Process sync for a single order (shared by HPOS and legacy paths). 286 */ 287 private function td_process_order_sync($order, $apiDetails) 288 { 289 $order_id = $order->get_id(); 290 $booking_id = $order->get_meta('transdirect_booking_id'); 291 292 if ($booking_id && (isset($apiDetails->enable_sync) && $apiDetails->enable_sync == 'on')) { 293 $response = $this->td_sync_order($order, $booking_id, true); 294 if (isset($response->id) && $response->id != '' && $order->get_meta('synced') === '0') { 295 $order->update_meta_data('synced', '1'); 296 $order->save(); 297 } 298 } else if (!$booking_id && ((isset($apiDetails->sync_all_order) && $apiDetails->sync_all_order == 'on') || $apiDetails->mode == 'no_display_mode')) { 299 $response = $this->td_sync_all_orders($order); 300 if (isset($response->id) && $response->id != '' && $order->get_meta('synced') === '0') { 301 $order->update_meta_data('synced', '1'); 302 $order->save(); 276 303 } 277 304 } … … 299 326 public function td_start_cron() 300 327 { 301 global $wpdb; 302 303 $filters = [ 304 'post_status' => ['draft', 'pending', 'publish'], 305 'post_type' => ['shop_order', 'shop_order_placehold'], 306 'posts_per_page' => -1, 307 'meta_query' => [ 308 [ 309 'key' => 'synced', 310 'value' => '0' 311 ] 312 ] 313 ]; 314 315 $post_query = new \WP_Query($filters); 316 317 if (!$post_query->have_posts()) { 328 if (td_is_hpos_enabled()) { 329 $unsynced = wc_get_orders([ 330 'limit' => 1, 331 'status' => array_keys(wc_get_order_statuses()), 332 'return' => 'ids', 333 'meta_query' => [ 334 [ 335 'key' => 'synced', 336 'value' => '0', 337 ] 338 ], 339 ]); 340 341 if (empty($unsynced)) { 342 $all_orders = wc_get_orders([ 343 'limit' => -1, 344 'status' => array_keys(wc_get_order_statuses()), 345 ]); 346 347 foreach ($all_orders as $order) { 348 if ($order->get_meta('synced') === '') { 349 $order->update_meta_data('synced', '0'); 350 $order->save(); 351 } 352 } 353 } 354 } else { 318 355 $filters = [ 319 'post_status' => ['draft', 'pending', 'publish'], 320 'post_type' => ['shop_order', 'shop_order_placehold'], 321 'posts_per_page' => -1 356 'post_status' => ['draft', 'pending', 'publish'], 357 'post_type' => ['shop_order', 'shop_order_placehold'], 358 'posts_per_page' => -1, 359 'meta_query' => [ 360 [ 361 'key' => 'synced', 362 'value' => '0', 363 ] 364 ], 322 365 ]; 323 366 324 $i = 0; 325 $loop = new \WP_Query($filters); 326 327 while ($loop->have_posts()) { 328 $loop->the_post(); 329 $order = new \WC_Order($loop->post->ID); 330 $orderId = get_post_meta($order->get_id(), 'synced', true); 331 332 if ($orderId == '') { 333 add_post_meta($order->get_id(), 'synced', '0'); 367 $post_query = new \WP_Query($filters); 368 369 if (!$post_query->have_posts()) { 370 $filters = [ 371 'post_status' => ['draft', 'pending', 'publish'], 372 'post_type' => ['shop_order', 'shop_order_placehold'], 373 'posts_per_page' => -1, 374 ]; 375 376 $loop = new \WP_Query($filters); 377 378 while ($loop->have_posts()) { 379 $loop->the_post(); 380 $order = new \WC_Order($loop->post->ID); 381 382 if (get_post_meta($order->get_id(), 'synced', true) === '') { 383 add_post_meta($order->get_id(), 'synced', '0'); 384 } 334 385 } 335 386 } -
transdirect-shipping/trunk/includes/TransdirectShipping.php
r3356427 r3471597 142 142 $this->loader->add_action('enqueue_block_assets', $plugin_public, 'enqueue_scripts'); 143 143 $this->loader->add_action('woocommerce_store_api_checkout_order_processed', $this, 'before_create_order'); 144 $this->loader->add_action( 145 'woocommerce_checkout_create_order', 146 $this, 147 'before_create_order', 148 10, 149 2 150 ); 144 151 $this->loader->add_action('wp_footer', $this, 'override_shipping_options_text', 100); 145 $this->loader->add_action('woocommerce_init', $this, 'custom_address_type_field'); 152 $this->loader->add_filter('woocommerce_cart_shipping_packages', $this, 'add_shipping_type_to_package'); 153 $this->loader->add_action('woocommerce_review_order_before_shipping', $this, 'render_shipping_type_radios'); 154 $this->loader->add_action('wp_ajax_td_set_shipping_type', $this, 'handle_shipping_type_ajax'); 155 $this->loader->add_action('wp_ajax_nopriv_td_set_shipping_type', $this, 'handle_shipping_type_ajax'); 156 $this->loader->add_action('woocommerce_init', $this, 'init_shipping_type_session'); 146 157 } 147 158 … … 249 260 $is_td_order = false; 250 261 $api_array = []; 251 foreach (json_decode($cached_data)->quotes as $service => $quote) { 262 $decoded_data = $cached_data ? json_decode($cached_data) : null; 263 $quotes = isset($decoded_data->quotes) ? (array) $decoded_data->quotes : []; 264 foreach ($quotes as $service => $quote) { 252 265 if($selected_shipping === humanizeCamelString($service)) { 253 266 $is_td_order = true; … … 263 276 264 277 /** 265 * 266 * Add shipping type field to checkout shipping and billing address form. 267 * @access public 268 * 269 */ 270 public function custom_address_type_field() { 271 $api_details = $this->td_get_api_details(true); 272 if(isset($api_details->mode)) { 273 $displayMode = $api_details->mode; 274 if($displayMode === 'full_mode') { 275 woocommerce_register_additional_checkout_field( 276 array( 277 'id' => 'namespace/shipping-type', 278 'label' => 'Shipping Type', 279 'optionalLabel' => 'Shipping Type (optional)', 280 'location' => 'address', 281 'required' => true, 282 'type' => 'select', 283 'options' => array( 284 array( 285 'value' => 'residential', 286 'label' => 'Residential', 287 ), 288 array( 289 'value' => 'business', 290 'label' => 'Commercial', 291 ), 292 ), 293 'attributes' => array( 294 'aria-label' => 'Select Shipping Type', 295 ), 296 ) 297 ); 278 * Set the default shipping type in the WC session on first visit. 279 */ 280 public function init_shipping_type_session() { 281 if (!WC()->session) { 282 return; 283 } 284 285 $current = WC()->session->get('td_shipping_type'); 286 if (empty($current)) { 287 $api_details = $this->td_get_api_details(); 288 $default_type = isset($api_details->street_type) ? $api_details->street_type : 'residential'; 289 WC()->session->set('td_shipping_type', $default_type); 290 } 291 } 292 293 /** 294 * Render shipping type radio buttons for Classic Checkout 295 * (injected via woocommerce_review_order_before_shipping hook). 296 */ 297 public function render_shipping_type_radios() { 298 $api_details = $this->td_get_api_details(); 299 if (!isset($api_details->mode) || $api_details->mode !== 'full_mode') { 300 return; 301 } 302 303 $current = WC()->session ? WC()->session->get('td_shipping_type', 'residential') : 'residential'; 304 ?> 305 <tr class="td-shipping-type-row"> 306 <th>Shipping Type</th> 307 <td> 308 <div id="td-shipping-type-radios"> 309 <label> 310 <input type="radio" name="td_shipping_type" value="residential" <?php checked($current, 'residential'); ?>> 311 Residential 312 </label> 313 <label> 314 <input type="radio" name="td_shipping_type" value="business" <?php checked($current, 'business'); ?>> 315 Commercial 316 </label> 317 </div> 318 </td> 319 </tr> 320 <?php 321 } 322 323 /** 324 * AJAX handler to save the selected shipping type to WC session. 325 */ 326 public function handle_shipping_type_ajax() { 327 $type = isset($_POST['shipping_type']) ? sanitize_text_field($_POST['shipping_type']) : ''; 328 if (!empty($type) && WC()->session) { 329 WC()->session->set('td_shipping_type', $type); 330 331 $pkg_count = 1; 332 if (WC()->cart) { 333 $packages = WC()->cart->get_shipping_packages(); 334 if (!empty($packages)) { 335 $pkg_count = count($packages); 336 } 298 337 } 299 } 338 for ($i = 0; $i < $pkg_count; $i++) { 339 WC()->session->set('shipping_for_package_' . $i, false); 340 } 341 342 WC()->session->save_data(); 343 344 wp_send_json_success(); 345 } 346 wp_send_json_error(); 347 } 348 349 /** 350 * Include shipping type in the shipping package so it becomes part of 351 * WooCommerce's package hash. This forces rate recalculation when the 352 * customer changes shipping type without changing their address. 353 */ 354 public function add_shipping_type_to_package($packages) { 355 $shipping_type = WC()->session ? WC()->session->get('td_shipping_type', 'residential') : 'residential'; 356 357 foreach ($packages as &$package) { 358 $package['td_shipping_type'] = $shipping_type; 359 } 360 361 return $packages; 300 362 } 301 363 } -
transdirect-shipping/trunk/includes/TransdirectShippingTrait.php
r3356427 r3471597 171 171 return $m->id === $this->shipping_method_id; 172 172 }); 173 return reset($td_method)->instance_settings ?? ''; 173 $first = reset($td_method); 174 return $first ? ($first->instance_settings ?? '') : ''; 174 175 }, $methods); 175 176 … … 177 178 return !empty($selected); 178 179 }); 179 return (object) reset($filtered); 180 $result = reset($filtered); 181 return (object) ($result ?: []); 180 182 } 181 183 } -
transdirect-shipping/trunk/includes/WCTransdirectShipping.php
r3347335 r3471597 131 131 public function calculate_shipping($package = array()) 132 132 { 133 $batch_request_body = file_get_contents('php://input');134 $request_params = json_decode($batch_request_body, true);135 $shipping_type = "residential";136 133 $apiDetails = $this->td_get_api_details(true); 134 $default_type = isset($apiDetails->street_type) ? $apiDetails->street_type : 'residential'; 135 $shipping_type = WC()->session ? WC()->session->get('td_shipping_type', $default_type) : $default_type; 137 136 138 137 if(isset($apiDetails->mode) && $apiDetails->mode != 'no_display_mode') { 139 if (isset($request_params['requests']) && is_array($request_params['requests'])) {140 $shipping_data = $request_params['requests'][0]['data']['shipping_address'] ?? [];141 if (isset($shipping_data['namespace/shipping-type'])) {142 $shipping_type = sanitize_text_field($shipping_data['namespace/shipping-type']) ?? "residential";143 }144 } elseif (is_user_logged_in()) {145 $user_id = get_current_user_id();146 $shipping_type_meta = get_user_meta($user_id, '_wc_shipping/namespace/shipping-type', true);147 if (!empty($shipping_type_meta)) {148 $shipping_type = sanitize_text_field($shipping_type_meta);149 }150 }151 138 152 139 $data = $this->get_quote($shipping_type); … … 179 166 private function get_quote($shipping_type) 180 167 { 168 if (!function_exists('WC') || !WC()->cart) { 169 return null; 170 } 181 171 $get_shipping_packages = WC()->cart->get_shipping_packages(); 182 172 $shipping = reset($get_shipping_packages); … … 230 220 231 221 $response = $this->send('POST', 'bookings/v4', $this->api_key, $params, true); 222 $response = json_decode($response, true); 223 $quotes = $response['quotes'] ?? []; 224 225 $quotes_array = []; 226 foreach ($quotes as $carrier => $data) { 227 $quotes_array[$carrier] = $data; 228 } 229 230 uasort($quotes_array, function($a, $b) { 231 return $a['total'] <=> $b['total']; 232 }); 233 234 $response['quotes'] = $quotes_array; 235 $response = json_encode($response); 232 236 set_transient($hash_key, $request_hash, 5 * MINUTE_IN_SECONDS); 233 237 set_transient($response_key, $response, 5 * MINUTE_IN_SECONDS); -
transdirect-shipping/trunk/readme.txt
r3347335 r3471597 4 4 Requires at least: 4.2 5 5 Tested up to: 6.8.2 6 Stable tag: 8.0.2 7 WC requires at least: 7.1 8 WC tested up to: 9.6 6 9 License: GPLv3 7 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html -
transdirect-shipping/trunk/transdirect-shipping.php
r3356427 r3471597 9 9 * 10 10 * @link https://transdirect.com.au/ 11 * @since 8.0. 111 * @since 8.0.2 12 12 * @package Transdirect_Shipping 13 13 * … … 16 16 * Plugin URI: https://www.transdirect.com.au/e-commerce/woo-commerce/ 17 17 * Description: This plugin allows you to calculate shipping as per your delivery location. 18 * Version: 8.0. 118 * Version: 8.0.2 19 19 * Author: Transdirect 20 20 * Author URI: https://transdirect.com.au/ … … 23 23 * Text Domain: transdirect-shipping 24 24 * Domain Path: /languages 25 * WC requires at least: 7.1 26 * WC tested up to: 10.5 25 27 */ 26 28 … … 33 35 require_once 'functions.php'; 34 36 37 add_action('before_woocommerce_init', function () { 38 if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) { 39 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); 40 } 41 }); 42 35 43 /** 36 44 * Currently plugin version. 37 * Start at version 8.0. 1and use SemVer - https://semver.org45 * Start at version 8.0.2 and use SemVer - https://semver.org 38 46 * Rename this for your plugin and update it as you release new versions. 39 47 */ 40 define('TRANSDIRECT_SHIPPING_VERSION', '8.0. 1');48 define('TRANSDIRECT_SHIPPING_VERSION', '8.0.2'); 41 49 42 50 /** … … 59 67 * @return void 60 68 */ 61 function td_run_at_activation() 62 { 63 $filters = [ 64 'post_status' => 'any', 65 'post_type' => 'shop_order', 66 'posts_per_page' => -1 67 ]; 68 69 $i = 0; 70 $loop = new WP_Query($filters); 71 72 while ($loop->have_posts()) { 73 $loop->the_post(); 74 $order = new WC_Order($loop->post->ID); 75 $orderId = get_post_meta($order->id, 'synced', true); 76 if ($orderId == '') { 77 add_post_meta($order->id, 'synced', '0'); 69 register_activation_hook(__FILE__, function () { 70 update_option('td_run_sync_on_activation', true); 71 }); 72 73 add_action('plugins_loaded', function () { 74 if (!class_exists('WooCommerce')) { 75 return; 76 } 77 78 if (get_option('td_run_sync_on_activation')) { 79 td_run_at_activation_safe(); 80 delete_option('td_run_sync_on_activation'); 81 } 82 }); 83 84 function td_run_at_activation_safe() { 85 $is_hpos_enabled = td_is_hpos_enabled(); 86 $orders = []; 87 if ($is_hpos_enabled) { 88 $orders = wc_get_orders([ 89 'limit' => -1, 90 'return' => 'ids', 91 'status' => array_keys(wc_get_order_statuses()), 92 ]); 93 } else { 94 $loop = new WP_Query([ 95 'post_type' => 'shop_order', 96 'post_status' => 'any', 97 'posts_per_page' => -1, 98 'fields' => 'ids', 99 ]); 100 $orders = $loop->posts; 101 } 102 103 $total_orders = count($orders); 104 $batch_size = 100; 105 $count = 0; 106 107 for ($offset = 0; $offset < $total_orders; $offset += $batch_size) { 108 $batch = array_slice($orders, $offset, $batch_size); 109 foreach ($batch as $order_id) { 110 $synced = get_post_meta($order_id, 'synced', true); 111 if ($synced == '') { 112 add_post_meta($order_id, 'synced', '0'); 113 $count++; 114 } 78 115 } 79 116 } 80 117 } 81 register_activation_hook(__FILE__, 'td_run_at_activation');82 118 83 119 /** … … 137 173 } 138 174 139 register_deactivation_hook(__FILE__, 'td_cronstarter_deactivate');140 141 175 add_action('td_cron_order_sync', 'td_my_repeat_function'); 142 176 … … 175 209 } 176 210 177 178 /**179 *180 * Hook add action to make cron work in background when wordpress is load.181 *182 */183 add_action('admin_init', 'td_cronstarter_activation');184 add_action('wp', 'td_cronstarter_activation');185 186 /**187 *188 * This will start the and activate cron job every 5 minutes.189 * @access public190 *191 */192 function td_cronstarter_activation() {193 if (!get_transient( 'timeout_for_30_min' )) {194 set_transient( 'timeout_for_30_min', 'cron_activation_timeout', 0.5 * HOUR_IN_SECONDS );195 $startCron = new OrderSync();196 $startCron->td_start_cron();197 $startProductCron = new ProductSync();198 $startProductCron->td_start_product_cron();199 }200 }201 211 202 212 /**
Note: See TracChangeset
for help on using the changeset viewer.