Changeset 3361859
- Timestamp:
- 09/15/2025 02:22:21 PM (6 months ago)
- Location:
- live-rates-for-shipstation
- Files:
-
- 20 edited
- 1 copied
-
tags/1.0.4 (copied) (copied from live-rates-for-shipstation/trunk)
-
tags/1.0.4/changelog.txt (modified) (1 diff)
-
tags/1.0.4/core/assets/admin.css (modified) (1 diff)
-
tags/1.0.4/core/assets/modules/settings.js (modified) (9 diffs)
-
tags/1.0.4/core/assets/modules/shipping-zone.js (modified) (4 diffs)
-
tags/1.0.4/core/settings-shipstation.php (modified) (9 diffs)
-
tags/1.0.4/core/shipping-method-shipstation.php (modified) (21 diffs)
-
tags/1.0.4/core/shipstation-api.php (modified) (10 diffs)
-
tags/1.0.4/core/views/services-table.php (modified) (8 diffs)
-
tags/1.0.4/live-rates-for-shipstation.php (modified) (4 diffs)
-
tags/1.0.4/readme.txt (modified) (3 diffs)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/core/assets/admin.css (modified) (1 diff)
-
trunk/core/assets/modules/settings.js (modified) (9 diffs)
-
trunk/core/assets/modules/shipping-zone.js (modified) (4 diffs)
-
trunk/core/settings-shipstation.php (modified) (9 diffs)
-
trunk/core/shipping-method-shipstation.php (modified) (21 diffs)
-
trunk/core/shipstation-api.php (modified) (10 diffs)
-
trunk/core/views/services-table.php (modified) (8 diffs)
-
trunk/live-rates-for-shipstation.php (modified) (4 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
live-rates-for-shipstation/tags/1.0.4/changelog.txt
r3339837 r3361859 2 2 3 3 This is a brief text document keeping track of changes to the plugin. For a full history, see the Github Repository. 4 5 = 1.0.4 = 6 7 Relase Date: September 15, 2025 8 9 * Overview 10 * Patches an issue where the exports would not take the Stores set product units. 11 * Adds functionality for Flat Rate Adjustments on a global scale. 12 * Adds functionality for Flat Rate Adjustments on a per service scale. 13 * Shoutouts to both @centuryperf and @jkmail120 for reporting these issues! 14 15 * Code Updates 16 * \IQLRSS\Core\Settings_Shipstation::export_shipstation_shipping_method() 17 * Hopefully better ShipStation integration when orders export. 18 * \IQLRSS\Core\Shipping_Method_Shipstation::get_individual_requests() 19 * Updates to garb the store dimensions. 20 * \IQLRSS\Core\Shipping_Method_Shipstation::calculate_shipping() 21 * Updates for flatrate calculations. 4 22 5 23 = 1.0.3 = -
live-rates-for-shipstation/tags/1.0.4/core/assets/admin.css
r3339099 r3361859 24 24 #customBoxes input[type=text] {-moz-appearance: textfield;} 25 25 26 .iqrlssimple-flex-2 {display: flex;} 27 .iqrlssimple-flex-2 > * {flex: 1 1 calc( 50% - 8px );} 28 .iqrlssimple-flex-2 > :first-child {flex-basis: fit-content; padding-right: 4px;} 29 .iqrlssimple-flex-2 > :last-child {padding-left: 4px;} 30 26 31 .iqlrss-api-row fieldset {position: relative; display: block; width: fit-content;} 27 32 .iqlrss-api-row #iqlrssVerifyButton {position: absolute; top: 0px; right: 0; margin-top: -1px; margin-right: -85px;} -
live-rates-for-shipstation/tags/1.0.4/core/assets/modules/settings.js
r3339099 r3361859 4 4 * Not really meant to be used as an object but more for 5 5 * encapsulation and organization. 6 * 7 * @todo Populate (or recreate) Carriers Select2 whenever API is verified. 6 8 * 7 9 * @global {Object} iqlrss - Localized object of saved values. … … 34 36 35 37 this.apiClearCache(); 36 this. priceAdjustmentNumbersOnly();37 this.s ingleLowestSetup();38 this.setupPriceAdjustments(); 39 this.setupSingleLowest(); 38 40 39 41 } … … 158 160 const $row = $elm.closest( 'tr' ); 159 161 if( ! $row || 'none' != $row.style.display ) return; 162 163 /* Skip the Return Lowest Label if related isn't checked */ 164 if( -1 != $elm.name.indexOf( 'global_adjustment' ) && '' == document.querySelectorAll( '[name*=global_adjustment_type]' ).value ) { 165 return; 166 } 160 167 161 168 /* Skip the Return Lowest Label if related isn't checked */ … … 293 300 * Only allow numbers for the Price Adjustment input. 294 301 */ 295 priceAdjustmentNumbersOnly() { 296 297 const $adjustmentInput = document.querySelector( '[type=text][name*=global_adjustment' ); 302 setupPriceAdjustments() { 303 304 const $adjustmentSelect = document.querySelector( 'select[name*=global_adjustment_type]' ); 305 const $adjustmentInput = document.querySelector( '[type=text][name*=global_adjustment' ); 306 307 /* Select Change - Show Input Row */ 308 $adjustmentSelect.addEventListener( 'change', ( e ) => { 309 $adjustmentInput.value = ''; 310 this.rowMakeVisible( $adjustmentInput.closest( 'tr' ), ( e.target.value ) ) 311 } ); 312 313 /* Input Update - Only FloatString */ 298 314 $adjustmentInput.addEventListener( 'input', ( e ) => { 299 e.target.value = e.target.value.replace( / [^0-9.]/g, '' );315 e.target.value = e.target.value.replace( /(\..*?)\./g, '$1' ).replace( /[^0-9.]/g, '' ); 300 316 } ); 301 317 … … 306 322 * Show / Hide the Single Lowest label 307 323 */ 308 s ingleLowestSetup() {324 setupSingleLowest() { 309 325 310 326 const $lowestcb = document.querySelector( '[type=checkbox][name*=return_lowest' ); … … 320 336 321 337 /* Eh, just trigger it */ 322 if( 'none' != $lowestcb.closest( 'tr' ).style.display ) {338 if( $lowestcb.checked && 'none' == $lowestLabel.closest( 'tr' ).style.display ) { 323 339 $lowestcb.dispatchEvent( new Event( 'change' ) ); 324 340 } … … 337 353 if( visible ) { 338 354 339 $row.setAttribute( 'style', 'opacity:0' ); 355 if( null !== $row.offsetParent ) return; 356 357 $row.style = 'opacity:0'; 340 358 $row.animate( { 341 359 opacity: [ 1 ] … … 350 368 }, { 351 369 duration: 300 352 } ).onfinish = () => $row.s etAttribute( 'style', 'display:none;' );370 } ).onfinish = () => $row.style = 'display:none;'; 353 371 354 372 } … … 374 392 $err.remove(); 375 393 376 $err.s etAttribute( 'style', 'height:0px;opacity:0;overflow:hidden;' );394 $err.style = 'height:0px;opacity:0;overflow:hidden;'; 377 395 $row.querySelector( 'fieldset' ).appendChild( $err ); 378 396 -
live-rates-for-shipstation/tags/1.0.4/core/assets/modules/shipping-zone.js
r3339099 r3361859 19 19 this.customBoxesRemove(); 20 20 21 this.setupPriceAdjustments() 21 22 this.inputsNumbersOnly(); 22 23 this.wooAccommodations(); … … 42 43 43 44 document.querySelectorAll( '#customBoxes [name]' ).forEach( ( $elm ) => { 44 if( 'text' == $elm. getAttribute( 'type' )) $elm.removeAttribute( 'required' );45 if( 'text' == $elm.type ) $elm.removeAttribute( 'required' ); 45 46 } ); 46 47 document.getElementById( 'customBoxes' ).style.display = 'none'; … … 80 81 $clone.classList.remove( 'mimic' ); 81 82 $clone.querySelectorAll( '[name]' ).forEach( ( $elm ) => { 82 $elm. setAttribute( 'name', $elm.getAttribute( 'name' ).replace( 'mimic', count ));83 if( 'text' == $elm. getAttribute( 'type' ) && -1 == $elm.getAttribute( 'name' ).indexOf( '[wm]' ) ) $elm.setAttribute( 'required', true );83 $elm.name = $elm.name.replace( 'mimic', count ); 84 if( 'text' == $elm.type && -1 == $elm.name.indexOf( '[wm]' ) ) $elm.required = true; 84 85 } ); 85 86 … … 116 117 117 118 /** 119 * Price Adjustments 120 * Manage the show/hide functionality. 121 */ 122 setupPriceAdjustments() { 123 124 /** 125 * Adjustment Type Change 126 * Show / Hide Price Input 127 */ 128 document.addEventListener( 'change', ( e ) => { 129 130 if( 'SELECT' != e.target.tagName ) return; 131 if( -1 == e.target.name.indexOf( 'adjustment_type' ) ) return; 132 133 const $adjustmentSelect = e.target; 134 const $adjustmentInput = $adjustmentSelect.closest( 'td' ).querySelector( 'input' ); 135 136 if( '' == $adjustmentSelect.value ) { 137 138 $adjustmentInput.animate( { 139 opacity: 0 140 }, { 141 duration: 300, 142 fill: 'forwards', 143 } ).onfinish = () => { 144 $adjustmentInput.value = ''; 145 $adjustmentInput.classList.add( 'iqlrss-hide' ); 146 }; 147 148 } else if( null === $adjustmentInput.offsetParent ) { 149 150 $adjustmentInput.classList.remove( 'iqlrss-hide' ); 151 $adjustmentInput.animate( { 152 opacity: [0, 1] 153 }, { 154 duration: 300, 155 fill: 'forwards', 156 } ).onfinish = () => { 157 $adjustmentInput.value = ''; 158 }; 159 160 } else { 161 $adjustmentInput.value = ( $adjustmentSelect.value != iqlrss.global_adjustment_type ) ? '0' : ''; 162 } 163 164 } ); 165 166 } 167 168 169 /** 118 170 * Only allow numbers in inputs. 119 171 */ 120 172 inputsNumbersOnly() { 121 173 174 /** 175 * All Custom Packing Box inputs. 176 * Any numbers-only classes 177 */ 122 178 document.addEventListener( 'input', ( e ) => { 123 179 if( 'INPUT' !== e.target.tagName ) return; 124 if( -1 != e.target. getAttribute( 'name' ).indexOf( 'custombox' ) || e.target.classList.contains( 'iqlrss-numbers-only' ) ) {125 e.target.value = e.target.value.replace( / [^0-9.]/g, '' );180 if( -1 != e.target.name.indexOf( 'custombox' ) || e.target.classList.contains( 'iqlrss-numbers-only' ) ) { 181 e.target.value = e.target.value.replace( /(\..*?)\./g, '$1' ).replace( /[^0-9.]/g, '' ); 126 182 } 127 183 } ); -
live-rates-for-shipstation/tags/1.0.4/core/settings-shipstation.php
r3339099 r3361859 88 88 89 89 $data = array( 90 'api_verified' => \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false, true ), 90 'api_verified' => \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false ), 91 'global_adjustment_type' => \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type', '' ), 91 92 'rest' => array( 92 93 'nonce' => wp_create_nonce( 'wp_rest' ), … … 100 101 'confirm_box_removal' => esc_html__( 'Please confirm you would like to completely remove (x) custom boxes.', 'live-rates-for-shipstation' ), 101 102 'error_rest_generic' => esc_html__( 'Something went wrong with the REST Request. Please resave permalinks and try again.', 'live-rates-for-shipstation' ), 102 'error_verification_required' => esc_html__( 'Please click the Verify API button to ensure a connection exists.', 'live-rates-for-shipstation' ), 103 'error_verification_required' => esc_html__( 'Please click the Verify API button to ensure a connection exists.', 'live-rates-for-shipstation' ), 104 'desc_global_adjustment_percentage' => esc_html__( 'Example: IF UPS Ground is $7.25 and you input 15% ($1.08), the final shipping rate the customer sees is: $8.33', 'live-rates-for-shipstation' ), 105 'desc_global_adjustment_flatrate' => esc_html__( 'Example: IF UPS Ground is $5.50 and you input $2.37, the final shipping rate the customer sees is: $7.87', 'live-rates-for-shipstation' ), 103 106 ), 104 107 ); … … 106 109 ?><script type="text/javascript"> 107 110 111 /* JS Localization */ 108 112 const iqlrss = JSON.parse( '<?php echo wp_json_encode( $data ); ?>' ); 109 113 110 <?php 111 /** 112 * Modules load too late to effectively immediately hide elements. 113 * This runs on the ShipStation settings page to hide additional 114 * settings whenever the API is unauthenticated. 115 */ 116 if( ! $data['api_verified'] ) : 117 ?> 118 119 if( document.getElementById( 'woocommerce_shipstation_iqlrss_api_key' ) ) { ( () => { 114 /* Early setting field JS */ 115 if( document.getElementById( 'woocommerce_shipstation_iqlrss_api_key' ) ) { ( function() { 116 117 /* Hide an element, ezpz */ 118 const fnHide = ( $el ) => $el.closest( 'tr' ).style.display = 'none'; 119 120 <?php 121 /** 122 * Modules load too late to effectively immediately hide elements. 123 * This runs on the ShipStation settings page to hide additional 124 * settings whenever the API is unauthenticated. 125 */ 126 if( ! $data['api_verified'] ) : 127 ?> 128 120 129 document.querySelectorAll( '[name*=iqlrss]' ).forEach( ( $elm ) => { 121 if( $elm. getAttribute( 'name' ).includes( 'api_key' ) ) return;122 if( $elm. getAttribute( 'name' ).includes( 'cart_weight' ) ) return;123 $elm.closest( 'tr' ).style.display = 'none';130 if( $elm.name.includes( 'api_key' ) ) return; 131 if( $elm.name.includes( 'cart_weight' ) ) return; 132 fnHide( $elm ); 124 133 } ); 125 } )(); } 126 127 <?php endif; ?> 134 135 <?php else : ?> 136 137 document.querySelectorAll( '[name*=iqlrss]' ).forEach( ( $elm ) => { 138 139 if( 'checkbox' == $elm.type && $elm.name.includes( 'return_lowest' ) && ! $elm.checked ) { 140 fnHide( document.querySelector( '[name*=return_lowest_label]' ) ); 141 } 142 143 if( $elm.name.includes( 'global_adjustment_type' ) && '' == $elm.value ) { 144 fnHide( document.querySelector( '[type=text][name*=global_adjustment]' ) ); 145 } 146 } ); 147 148 <?php endif; ?> 149 150 } )(); } 151 128 152 </script><?php 129 153 … … 155 179 public function display_cart_weight() { 156 180 157 $show_weight = \IQLRSS\Driver::get_ss_opt( 'cart_weight', 'no' , true);181 $show_weight = \IQLRSS\Driver::get_ss_opt( 'cart_weight', 'no' ); 158 182 if( 'no' == $show_weight ) return; 159 183 … … 289 313 290 314 // Set transient to clear any WC_Session caches if they are found. 291 $ time= absint( apply_filters( 'wc_session_expiration', DAY_IN_SECONDS * 2 ) );292 set_transient( \IQLRSS\Driver::plugin_prefix( 'wcs_timeout' ), time(), $ time);315 $expires = absint( apply_filters( 'wc_session_expiration', DAY_IN_SECONDS * 2 ) ); 316 set_transient( \IQLRSS\Driver::plugin_prefix( 'wcs_timeout' ), time(), $expires ); 293 317 294 318 } … … 326 350 add_filter( 'woocommerce_shipping_methods', array ($this, 'append_shipstation_method' ) ); 327 351 add_filter( 'woocommerce_settings_api_form_fields_shipstation', array( $this, 'append_shipstation_integration_settings' ) ); 352 add_filter( 'woocommerce_settings_api_sanitized_fields_shipstation',array( $this, 'save_shipstation_integration_settings' ) ); 353 add_filter( 'woocommerce_shipstation_export_get_order', array( $this, 'export_shipstation_shipping_method' ) ); 328 354 329 355 } … … 366 392 } 367 393 } 394 395 // Backwards compatibility for v1.0.3 when only percentage was supported by default. 396 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '0' ); 397 $adjustment_type_default = ( empty( $global_adjustment_type ) && ! empty( $global_adjustment ) ) ? 'percentage' : ''; 368 398 369 399 foreach( $fields as $key => $field ) { … … 391 421 ); 392 422 423 $appended_fields[ \IQLRSS\Driver::plugin_prefix( 'global_adjustment_type' ) ] = array( 424 'title' => esc_html__( 'Shipping Price Adjustment', 'live-rates-for-shipstation' ), 425 'type' => 'select', 426 'options' => \IQLRSS\Core\Shipping_Method_Shipstation::get_adjustment_types( true ), 427 'description' => esc_html__( 'This adjustment is added on top of the returned shipping rates to help you cover shipping costs. Can be overridden per zone, per service.', 'live-rates-for-shipstation' ), 428 'default' => $adjustment_type_default, 429 ); 430 393 431 $appended_fields[ \IQLRSS\Driver::plugin_prefix( 'global_adjustment' ) ] = array( 394 'title' => esc_html__( ' Shipping Price Adjustment (%)', 'live-rates-for-shipstation' ),432 'title' => esc_html__( 'Global Price Adjustment', 'live-rates-for-shipstation' ), 395 433 'type' => 'text', 396 'placeholder' => '0%', 397 'description' => esc_html__( 'This percent is added on top of the returned shipping rates to help you cover shipping costs. Can be overridden per zone, per service.', 'live-rates-for-shipstation' ), 398 'desc_tip' => esc_html__( 'Example: IF UPS Ground is $7.25 - 15% would be $1.08 making the final rate: $8.33', 'live-rates-for-shipstation' ), 434 'placeholder' => '0', 435 'description' => esc_html__( 'Optional global ShipStation rate adjustment.', 'live-rates-for-shipstation' ), 399 436 'default' => '', 400 437 ); … … 431 468 432 469 470 /** 471 * Modify the saved settings after WooCommerce has sanitized them. 472 * Not much we need to do here, WooCommerce does most the heavy lifting. 473 * 474 * @param Array $settings 475 * 476 * @return Array $settings 477 */ 478 public function save_shipstation_integration_settings( $settings ) { 479 480 // No API Key? Invalid! 481 $api_key_key = \IQLRSS\Driver::plugin_prefix( 'api_key' ); 482 if( ! isset( $settings[ $api_key_key ] ) || empty( $settings[ $api_key_key ] ) ) { 483 484 $settings[ \IQLRSS\Driver::plugin_prefix( 'api_key_valid' ) ] = false; 485 if( isset( $settings[ \IQLRSS\Driver::plugin_prefix( 'api_key_vt' ) ] ) ) { 486 unset( $settings[ \IQLRSS\Driver::plugin_prefix( 'api_key_vt' ) ] ); 487 } 488 } 489 490 return $settings; 491 492 } 493 494 495 /** 496 * Update the WC_Order Shipping Method to match the ShipStation Carrier. 497 * Ex. USPSPriorityMail 498 * 499 * @link https://help.shipstation.com/hc/en-us/articles/360025856192-Custom-Store-Development-Guide 500 * 501 * @param WC_Order $order 502 * 503 * @return WC_Order $order 504 */ 505 public function export_shipstation_shipping_method( $order ) { 506 507 if( ! is_a( $order, 'WC_Order' ) ) { 508 return $order; 509 } 510 511 $methods = $order->get_shipping_methods(); 512 $plugin_method_id = \IQLRSS\Driver::plugin_prefix( 'shipstation' ); 513 514 foreach( $methods as $method ) { 515 516 // Not our shipping method. 517 if( $method->get_method_id() != $plugin_method_id ) continue; 518 519 $service_name = $method->get_meta( 'service_name', true ); 520 $carrier_name = $method->get_meta( 'carrier_name', true ); 521 522 // Missing metadata. 523 if( empty( $service_name ) || empty( $carrier_name ) ) continue; 524 525 $method->set_props( array( 526 'name' => preg_replace( '/([^a-zA-Z0-9])/', '', sprintf( '%s %s', $carrier_name, $service_name ) ), 527 ) ); 528 $method->apply_changes(); // Temporarily apply changes. This does not update the database. 529 530 } 531 532 return $order; 533 534 } 535 536 433 537 434 538 /**------------------------------------------------------------------------------------------------ **/ -
live-rates-for-shipstation/tags/1.0.4/core/shipping-method-shipstation.php
r3339837 r3361859 38 38 39 39 /** 40 * Array of store specific settings. 41 * 42 * @var Array 43 */ 44 protected $store_data = array( 45 'weight_unit' => '', 46 'dim_unit' => '', // Dimension 47 ); 48 49 50 /** 40 51 * Array of global carriers 41 52 * There are the carriers saved in Integration settings. … … 79 90 $this->supports = array( 'instance-settings' ); 80 91 81 $this->carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() , true);82 $saved_key = \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false , true);92 $this->carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() ); 93 $saved_key = \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false ); 83 94 84 95 // Only show in Shipping Zones if API Key is invalid. … … 86 97 $this->supports[] = 'shipping-zones'; 87 98 } 99 100 // Set the store unit term and associate it with ShipStations term. 101 $this->store_data = array( 102 'weight_unit' => get_option( 'woocommerce_weight_unit', $this->store_data['weight_unit'] ), 103 'dim_unit' => get_option( 'woocommerce_dimension_unit', $this->store_data['dim_unit'] ), 104 ); 88 105 89 106 $this->init_instance_form_fields(); … … 180 197 $settings = get_option( 'woocommerce_shipstation_settings' ); 181 198 $saved_services = $this->get_option( 'services', array() ); 182 $saved_carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() , true);199 $saved_carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() ); 183 200 $shipStationAPI = $this->shipStationApi; 184 201 … … 186 203 187 204 $sorted_services = array(); 205 206 // See $this->validate_services_field() 188 207 foreach( $saved_services as $k => $s ) { 189 208 … … 233 252 * Validate service field. 234 253 * 235 * @ param mixed $key - Field key.254 * @return Array $services 236 255 */ 237 256 public function validate_services_field() { … … 253 272 $posted_services = wp_unslash( $_POST[ $prefix ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 254 273 274 // Global adjustment 275 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '' ); 276 $global_adjustment_type = \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type', '' ); 277 255 278 // Group by Carriers then Services 256 279 $services = array(); … … 258 281 foreach( $carrier_services as $service_code => $service_arr ) { 259 282 260 // Skip non-enabled and non-renamed services.261 if( ! isset( $service_arr['enabled'] ) && empty( $service_arr['nickname'] ) ) continue;262 263 283 $carrier_code = sanitize_text_field( $carrier_code ); 264 284 $service_code = sanitize_text_field( $service_code ); 265 $ services[ $carrier_code ][ $service_code ]= array_filter( array(285 $data = array_filter( array( 266 286 267 287 // User Input … … 276 296 ) ); 277 297 278 // Allow 0 value user input. 279 if( $service_arr['adjustment'] >= 0 ) { 280 $services[ $carrier_code ][ $service_code ]['adjustment'] = floatval( $service_arr['adjustment'] ); 298 // The above removes empty values. 299 // Price Adjustments 300 $data['adjustment'] = ( $service_arr['adjustment'] ) ? floatval( $service_arr['adjustment'] ) : ''; 301 $data['adjustment_type']= $service_arr['adjustment_type']; 302 303 // Maybe unset if we don't need the data. 304 if( $data['adjustment_type'] == $global_adjustment_type ) { 305 306 // equal or equal empty -> 0 == '' 307 if( $data['adjustment'] == $global_adjustment || '' == $data['adjustment'] ) { 308 unset( $data['adjustment'] ); 309 unset( $data['adjustment_type'] ); 310 } 281 311 } 282 312 313 /** 314 * We don't want to array_filter() since 315 * Global Adjust could be populated, and 316 * Service is set to '' (No Adjustment). 317 */ 318 $services[ $carrier_code ][ $service_code ] = $data; 319 283 320 } 284 321 } … … 292 329 * Validate customboxes field. 293 330 * 294 * @ param mixed $key - Field key.331 * @return Array $boxes 295 332 */ 296 333 public function validate_customboxes_field() { … … 370 407 } 371 408 372 $global_upcharge = floatval( \IQLRSS\Driver::get_ss_opt( 'global_adjustment', 0, true ) ); 373 $packing_type = $this->get_option( 'packing', 'individual' ); 409 $global_adjustment = floatval( \IQLRSS\Driver::get_ss_opt( 'global_adjustment', 0 ) ); 410 $global_adjustment_type = \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type','' ); 411 $global_adjustment_type = ( empty( $global_adjustment_type ) && ! empty( $global_adjustment ) ) ? 'percentage' : $global_adjustment_type; 412 413 $packing_type = $this->get_option( 'packing', 'individual' ); 374 414 $request = array( 375 415 'from_country_code' => WC()->countries->get_base_country(), … … 444 484 445 485 // Apply service upcharge 446 if( isset( $service_arr['adjustment'] ) && $service_arr['adjustment'] > 0 ) { 447 448 $adjustment = floatval( $saved_services[ $shiprate['carrier_code'] ]['adjustment'] ); 449 $cost += ( $adjustment > 0 ) ? ( $cost * ( $adjustment / 100 ) ) : 0; 450 451 } else if( ! empty( $global_upcharge ) ) { 452 $cost += ( $cost * ( $global_upcharge / 100 ) ); 486 if( isset( $service_arr['adjustment'] ) ) { 487 488 /** 489 * Adjustment type could be '' to skip global adjustment. 490 * Defaults to percentage for v1.03 backwards compatibility. 491 */ 492 $adjustment = floatval( $service_arr['adjustment'] ); 493 $adjustment_type = ( isset( $service_arr['adjustment_type'] ) ) ? $service_arr['adjustment_type'] : 'percentage'; 494 495 if( ! empty( $adjustment_type ) && $adjustment > 0 ) { 496 $cost += ( 'flatrate' == $adjustment_type ) ? $adjustment : ( $cost * ( $adjustment / 100 ) ); 497 } 498 499 } else if( ! empty( $global_adjustment_type ) && $global_adjustment > 0 ) { 500 $cost += ( 'flatrate' == $global_adjustment_type ) ? floatval( $global_adjustment ) : ( $cost * ( floatval( $global_adjustment ) / 100 ) ); 453 501 } 454 502 … … 464 512 'dimensions' => $req['dimensions'], 465 513 'weight' => $req['weight'], 514 'service_name' => $shiprate['name'], 466 515 'carrier_code' => $shiprate['carrier_code'], 516 'carrier_name' => $shiprate['carrier_name'], 467 517 ), 468 518 ); … … 480 530 } 481 531 482 $single_lowest = \IQLRSS\Driver::get_ss_opt( 'return_lowest', 'no' , true);483 $single_lowest_label = \IQLRSS\Driver::get_ss_opt( 'return_lowest_label', '' , true);532 $single_lowest = \IQLRSS\Driver::get_ss_opt( 'return_lowest', 'no' ); 533 $single_lowest_label = \IQLRSS\Driver::get_ss_opt( 'return_lowest_label', '' ); 484 534 485 535 // Add all shipping rates, let the user decide. … … 547 597 esc_html__( 'Product ID #%1$d missing (%2$s) dimensions. Shipping calculations terminated.', 'live-rates-for-shipstation' ), 548 598 $item['product_id'], 549 implode( ', ', array_diff_key( $this->dimension_keys, $physicals ) ) 599 implode( ', ', array_diff_key( array( 600 'width' => 'width', 601 'height' => 'height', 602 'length' => 'length', 603 'weight' => 'weight', 604 ), $physicals ) ) 550 605 ) ); 551 606 return array(); … … 553 608 554 609 $request['weight'] = array( 555 'value' => (float) max( 0.5, round( wc_get_weight( $physicals['weight'], 'lbs' ), 2 )),556 'unit' => 'pound',610 'value' => (float)round( wc_get_weight( $physicals['weight'], $this->store_data['weight_unit'] ), 2 ), 611 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['weight_unit'] ), 557 612 ); 558 613 … … 562 617 563 618 $request['dimensions'] = array( 564 ' unit' => 'inch',565 ' length' => max( 1, round( wc_get_dimension( $physicals[2], 'in' ), 2 )),566 ' width' => max( 1, round( wc_get_dimension( $physicals[1], 'in' ), 2 )),567 ' height' => max( 1, round( wc_get_dimension( $physicals[0], 'in' ), 2 )),619 'length' => round( wc_get_dimension( $physicals[2], $this->store_data['dim_unit'] ), 2 ), 620 'width' => round( wc_get_dimension( $physicals[1], $this->store_data['dim_unit'] ), 2 ), 621 'height' => round( wc_get_dimension( $physicals[0], $this->store_data['dim_unit'] ), 2 ), 622 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['dim_unit'] ), 568 623 ); 569 624 … … 631 686 esc_html__( 'Product ID #%1$d missing (%2$s) dimensions. Shipping calculations terminated.', 'live-rates-for-shipstation' ), 632 687 $item['product_id'], 633 implode( ', ', array_diff_key( $this->dimension_keys, $physicals ) ) 688 implode( ', ', array_diff_key( array( 689 'width' => 'width', 690 'height' => 'height', 691 'length' => 'length', 692 'weight' => 'weight', 693 ), $physicals ) ) 634 694 ) ); 635 695 return array(); 636 696 } 637 697 638 $data['weight'] = (float) max( 0.5, round( wc_get_weight( $physicals['weight'], 'lbs' ), 2 ));639 640 // Unset weight and sort dimensions698 $data['weight'] = (float)round( wc_get_weight( $physicals['weight'], $this->store_data['weight_unit'] ), 2 ); 699 700 // Unset weight to exclude it from sort 641 701 unset( $physicals['weight'] ); 642 702 sort( $physicals ); 643 703 644 704 $data = array( 645 'length' => max( 1, round( wc_get_dimension( $physicals[2], 'in' ), 2 )),646 'width' => max( 1, round( wc_get_dimension( $physicals[1], 'in' ), 2 )),647 'height' => max( 1, round( wc_get_dimension( $physicals[0], 'in' ), 2 )),705 'length' => round( wc_get_dimension( $physicals[2], $this->store_data['dim_unit'] ), 2 ), 706 'width' => round( wc_get_dimension( $physicals[1], $this->store_data['dim_unit'] ), 2 ), 707 'height' => round( wc_get_dimension( $physicals[0], $this->store_data['dim_unit'] ), 2 ), 648 708 ) + $data; 649 709 … … 670 730 'weight' => array( 671 731 'value' => $package->weight, 672 'unit' => 'pound',732 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['weight_unit'] ), 673 733 ), 674 734 'dimensions' => array( 675 'unit' => 'inch',676 735 'length' => $package->length, 677 736 'width' => $package->width, 678 737 'height' => $package->height, 738 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['dim_unit'] ), 679 739 ), 680 740 ); … … 796 856 /**------------------------------------------------------------------------------------------------ **/ 797 857 /** 858 * Return an array of Price Adjustment Type options. 859 * 860 * @return Array 861 */ 862 public static function get_adjustment_types( $include_empty = false ) { 863 864 $types = array( 865 'flatrate' => esc_html__( 'Flat Rate', 'live-rates-for-shipstation' ), 866 'percentage' => esc_html__( 'Percentage', 'live-rates-for-shipstation' ), 867 ); 868 869 return ( false == $include_empty ) ? $types : array_merge( array( 870 '' => esc_html__( 'No Adjustments', 'live-rates-for-shipstation' ), 871 ), $types ); 872 873 } 874 875 876 /** 798 877 * Log error in WooCommerce 799 878 * Passthru method - log what's given and give it back. … … 808 887 protected function log( $error, $level = 'debug', $context = array() ) { 809 888 810 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', false ) ) {889 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', 0, true ) ) { 811 890 return $error; 812 891 } -
live-rates-for-shipstation/tags/1.0.4/core/shipstation-api.php
r3339099 r3361859 15 15 16 16 /** 17 * API Endpoint17 * Key prefix 18 18 * 19 19 * @var String 20 20 */ 21 protected $apiurl = 'https://api.shipstation.com'; // No trailing slash. 21 protected $prefix; 22 23 24 /** 25 * Seconds to hold cache. 26 * Defaults to 1 week. 27 * 28 * @var Integer 29 */ 30 protected $cache_time; 31 32 33 /** 34 * Skip cache check 35 * 36 * @var Boolean 37 */ 38 protected $skip_cache = false; 22 39 23 40 … … 31 48 32 49 /** 33 * Key prefix50 * The API Key 34 51 * 35 52 * @var String 36 53 */ 37 protected $prefix; 38 39 40 /** 41 * Skip cache check 42 * 43 * @var Boolean 44 */ 45 protected $skip_cache = false; 46 47 48 /** 49 * The API Key 50 * 51 * @var String 52 */ 53 private $key = ''; 54 private $key; 54 55 55 56 … … 62 63 63 64 $this->prefix = \IQLRSS\Driver::get( 'slug' ); 64 $this->key = \IQLRSS\Driver::get_ss_opt( 'api_key', '' , true);65 $this->key = \IQLRSS\Driver::get_ss_opt( 'api_key', '' ); 65 66 $this->skip_cache = (boolean)$skip_cache; 67 $this->cache_time = defined( 'WEEK_IN_SECONDS' ) ? WEEK_IN_SECONDS : 604800; 66 68 67 69 } … … 118 120 * Return an array of carriers. 119 121 * While getting carriers, set services and packages. 122 * 123 * @link https://docs.shipstation.com/openapi/carriers 120 124 * 121 125 * @param String $carrier_code … … 191 195 // Cache Carriers 192 196 if( ! empty( $data['carriers'] ) ) { 193 set_transient( $trans_key, $data['carriers'], MONTH_IN_SECONDS);197 set_transient( $trans_key, $data['carriers'], $this->cache_time ); 194 198 } 195 199 … … 198 202 foreach( $data['services'] as $carrier_id => $service_arr ) { 199 203 $service_key = sprintf( '%s_%s_services', $trans_key, $carrier_id ); 200 set_transient( $service_key, $service_arr, MONTH_IN_SECONDS);204 set_transient( $service_key, $service_arr, $this->cache_time ); 201 205 } 202 206 } … … 206 210 foreach( $data['packages'] as $carrier_id => $package_arr ) { 207 211 $package_key = sprintf( '%s_%s_packages', $trans_key, $carrier_id ); 208 set_transient( $package_key, $package_arr, MONTH_IN_SECONDS);212 set_transient( $package_key, $package_arr, $this->cache_time ); 209 213 } 210 214 } … … 269 273 /**------------------------------------------------------------------------------------------------ **/ 270 274 /** 275 * Convert a WooCommerce unit term to a 276 * ShipStation unit term. 277 * 278 * @param String $unit 279 * 280 * @return String $term 281 */ 282 public function convert_unit_term( $unit ) { 283 284 $known = array( 285 'kg' => 'kilogram', 286 'g' => 'gram', 287 'lbs' => 'pound', 288 'oz' => 'ounce', 289 'cm' => 'centimeter', 290 'in' => 'inch', 291 ); 292 293 return ( isset( $known[ $unit ] ) ) ? $known[ $unit ] : $unit; 294 295 } 296 297 298 /** 271 299 * Make an API Request 272 300 * … … 345 373 */ 346 374 protected function get_endpoint_url( $endpoint ) { 347 return sprintf( '%s/v2/%s/', $this->apiurl, $endpoint ); 375 376 return sprintf( '%s/v2/%s/', 377 'https://api.shipstation.com', 378 $endpoint 379 ); 380 348 381 } 349 382 … … 380 413 protected function log( $error, $level = 'debug', $context = array() ) { 381 414 382 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', false ) ) {415 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', 0, true ) ) { 383 416 return $error; 384 417 } -
live-rates-for-shipstation/tags/1.0.4/core/views/services-table.php
r3339099 r3361859 18 18 } 19 19 20 $api_key = \IQLRSS\Driver::get_ss_opt( 'api_key', '', true ); 21 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '0', true ); 20 $api_key = \IQLRSS\Driver::get_ss_opt( 'api_key', '' ); 21 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '0' ); 22 $global_adjustment_type = \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type', '' ); 23 $global_adjustment_type = ( empty( $global_adjustment_type ) && ! empty( $global_adjustment ) ) ? 'percentage' : $global_adjustment_type; 22 24 23 25 ?> … … 33 35 <th style="width: 50px;"><?php esc_html_e( 'Enabled', 'live-rates-for-shipstation' ); ?></th> 34 36 <th><?php esc_html_e( 'Name', 'live-rates-for-shipstation' ); ?></th> 35 <th><?php esc_html_e( 'Price Adjustment (%)', 'live-rates-for-shipstation' ); ?></th>37 <th><?php esc_html_e( 'Price Adjustment', 'live-rates-for-shipstation' ); ?></th> 36 38 <th><?php esc_html_e( 'Carrier', 'live-rates-for-shipstation' ); ?></th> 37 39 </tr> … … 39 41 <tbody><?php 40 42 41 if( empty( $api_key ) || ! \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false , true) ) {43 if( empty( $api_key ) || ! \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false ) ) { 42 44 print( '<tr><th colspan="4">' ); 43 45 echo wp_kses( sprintf( … … 62 64 63 65 $saved_atts = array( 64 'enabled' => ( isset( $service_arr['enabled'] ) ) ? $service_arr['enabled'] : false, 65 'nickname' => ( isset( $service_arr['nickname'] ) ) ? $service_arr['nickname'] : '', 66 'adjustment' => ( isset( $service_arr['adjustment'] ) ) ? $service_arr['adjustment'] : '', 66 'enabled' => ( isset( $service_arr['enabled'] ) ) ? $service_arr['enabled'] : false, 67 'nickname' => ( isset( $service_arr['nickname'] ) ) ? $service_arr['nickname'] : '', 68 'adjustment_type' => ( isset( $service_arr['adjustment_type'] ) ) ? $service_arr['adjustment_type'] : $global_adjustment_type, 69 'adjustment' => ( isset( $service_arr['adjustment'] ) ) ? $service_arr['adjustment'] : '', 67 70 ); 68 71 … … 99 102 100 103 // Service Price Adjustment 101 printf( '<td><input type="text" name="%s" value="%s" placeholder="%s" style="max-width:60px;"></td>', 102 esc_attr( $attr_name . '[adjustment]' ), 103 esc_attr( $saved_atts['adjustment'] ), 104 esc_attr( $global_adjustment . '%' ), 105 ); 104 print( '<td><div class="iqrlssimple-flex-2">' ); 105 106 printf( '<div><select name="%s" style="width:100%%;">', esc_attr( $attr_name . '[adjustment_type]' ) ); 107 foreach( static::get_adjustment_types( true ) as $slug => $label ) { 108 printf( '<option value="%s"%s>%s</option>', 109 esc_attr( $slug ), 110 selected( $saved_atts['adjustment_type'], $slug, false ), 111 $label 112 ); 113 } 114 print( '</select></div>' ); 115 116 printf( '<div><input type="text" name="%s" value="%s" placeholder="%s" style="max-width:60px;" class="%s"></div>', 117 esc_attr( $attr_name . '[adjustment]' ), 118 esc_attr( $saved_atts['adjustment'] ), 119 esc_attr( $global_adjustment ), 120 esc_attr( 'iqlrss-numbers-only' . ( ( '' == $saved_atts['adjustment_type'] ) ? ' iqlrss-hide' : '' ) ), 121 ); 122 print( '</div></td>' ); 106 123 107 124 // Carrier Name … … 109 126 110 127 print( '</tr>' ); 128 129 // Set a processed flag for the next array which is not reorganized. 130 $saved_services[ $carrier_code ][ $service_code ]['processed'] = true; 111 131 112 132 } … … 128 148 129 149 $service_arr = ( ! is_array( $service_arr ) ) ? (array)$service_arr : $service_arr; 130 if( isset( $saved_services[ $carrier_code ][ $service_arr['service_code'] ] ) ) continue;150 if( isset( $saved_services[ $carrier_code ][ $service_arr['service_code'] ]['processed'] ) ) continue; 131 151 132 152 print( '<tr>' ); … … 159 179 ); 160 180 161 // Service Name 162 printf( '<td><input type="text" name="%s" value="" placeholder="%s" class="iqlrss-numbers-only" style="max-width:60px;"></td>', 163 esc_attr( $attr_name . '[adjustment]' ), 164 esc_attr( $global_adjustment . '%' ), 165 ); 181 // Service Price Adjustment 182 print( '<td><div class="iqrlssimple-flex-2">' ); 183 184 printf( '<div><select name="%s" style="width:100%%;">', esc_attr( $attr_name . '[adjustment_type]' ) ); 185 foreach( static::get_adjustment_types( true ) as $slug => $label ) { 186 printf( '<option value="%s"%s>%s</option>', 187 esc_attr( $slug ), 188 selected( $global_adjustment_type, $slug, false ), 189 $label 190 ); 191 } 192 print( '</select></div>' ); 193 194 printf( '<div><input type="text" name="%s" value="" placeholder="%s" style="max-width:60px;" class="%s"></div>', 195 esc_attr( $attr_name . '[adjustment]' ), 196 esc_attr( $global_adjustment ), 197 esc_attr( 'iqlrss-numbers-only' . ( ( '' == $global_adjustment_type ) ? ' iqlrss-hide' : '' ) ) 198 ); 199 print( '</div></td>' ); 166 200 167 201 // Carrier Name -
live-rates-for-shipstation/tags/1.0.4/live-rates-for-shipstation.php
r3339837 r3361859 4 4 * Plugin URI: https://iqcomputing.com/contact/ 5 5 * Description: ShipStation shipping method with live rates. 6 * Version: 1.0. 36 * Version: 1.0.4 7 7 * Requries at least: 5.9 8 8 * Author: IQComputing … … 26 26 * @var String 27 27 */ 28 protected static $version = '1.0. 3';28 protected static $version = '1.0.4'; 29 29 30 30 … … 55 55 * @param String $key 56 56 * @param Mixed $default 57 * @param Boolean $ prefix - Prefix Key with plugin slug.57 * @param Boolean $skip_prefix - Skip Plugin Prefix and return a core ShipStation setting value. 58 58 * 59 59 * @return Mixed 60 60 */ 61 public static function get_ss_opt( $key, $default = '', $ prefix = false ) {61 public static function get_ss_opt( $key, $default = '', $skip_prefix = false ) { 62 62 63 if( $prefix ) $key = static::plugin_prefix( $key );63 if( ! $skip_prefix ) $key = static::plugin_prefix( $key ); 64 64 $settings = get_option( 'woocommerce_shipstation_settings' ); 65 65 return ( isset( $settings[ $key ] ) && '' !== $settings[ $key ] ) ? $settings[ $key ] : $default; … … 141 141 142 142 } ); 143 add_action( 'plugins_loaded', array( '\IQLRSS\Driver', 'drive' ), 15);143 add_action( 'plugins_loaded', array( '\IQLRSS\Driver', 'drive' ), 8 ); -
live-rates-for-shipstation/tags/1.0.4/readme.txt
r3339837 r3361859 4 4 Requires at least: 5.9 5 5 Tested up to: 6.8 6 Stable tag: 1.0. 36 Stable tag: 1.0.4 7 7 License: GPLv3 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 49 49 == Changelog == 50 50 51 = 1.0.4 (2025-09-15) = 52 * Patches issues with shipping units not match store set units. 53 * Adds new Flat Rate Adjustments to global and shipping services. 54 * Adds additional metadata to WC Order Items in regards to shipping. 55 * Shoutouts to both @centuryperf and @jkmail120 for reporting these issues! 56 51 57 = 1.0.3 (2025-08-05) = 52 58 * Patches an issue with Shipping Method availability (Thanks to @sportswreathshop for reporting!) … … 54 60 = 1.0.2 (2025-08-04) = 55 61 * Refines API caching that clears on settings save (thanks again to @dpkonofa for test/reporting!). 56 57 = 1.0.1 (2025-08-01) =58 * Patches an issue with Individual Shipping Requests (thanks @dpkonofa !).59 * Attempt to discern ShipStation Carriers from Manually Connected Carriers.60 61 = 1.0.0 (2025-07-28) =62 * Initial release -
live-rates-for-shipstation/trunk/changelog.txt
r3339837 r3361859 2 2 3 3 This is a brief text document keeping track of changes to the plugin. For a full history, see the Github Repository. 4 5 = 1.0.4 = 6 7 Relase Date: September 15, 2025 8 9 * Overview 10 * Patches an issue where the exports would not take the Stores set product units. 11 * Adds functionality for Flat Rate Adjustments on a global scale. 12 * Adds functionality for Flat Rate Adjustments on a per service scale. 13 * Shoutouts to both @centuryperf and @jkmail120 for reporting these issues! 14 15 * Code Updates 16 * \IQLRSS\Core\Settings_Shipstation::export_shipstation_shipping_method() 17 * Hopefully better ShipStation integration when orders export. 18 * \IQLRSS\Core\Shipping_Method_Shipstation::get_individual_requests() 19 * Updates to garb the store dimensions. 20 * \IQLRSS\Core\Shipping_Method_Shipstation::calculate_shipping() 21 * Updates for flatrate calculations. 4 22 5 23 = 1.0.3 = -
live-rates-for-shipstation/trunk/core/assets/admin.css
r3339099 r3361859 24 24 #customBoxes input[type=text] {-moz-appearance: textfield;} 25 25 26 .iqrlssimple-flex-2 {display: flex;} 27 .iqrlssimple-flex-2 > * {flex: 1 1 calc( 50% - 8px );} 28 .iqrlssimple-flex-2 > :first-child {flex-basis: fit-content; padding-right: 4px;} 29 .iqrlssimple-flex-2 > :last-child {padding-left: 4px;} 30 26 31 .iqlrss-api-row fieldset {position: relative; display: block; width: fit-content;} 27 32 .iqlrss-api-row #iqlrssVerifyButton {position: absolute; top: 0px; right: 0; margin-top: -1px; margin-right: -85px;} -
live-rates-for-shipstation/trunk/core/assets/modules/settings.js
r3339099 r3361859 4 4 * Not really meant to be used as an object but more for 5 5 * encapsulation and organization. 6 * 7 * @todo Populate (or recreate) Carriers Select2 whenever API is verified. 6 8 * 7 9 * @global {Object} iqlrss - Localized object of saved values. … … 34 36 35 37 this.apiClearCache(); 36 this. priceAdjustmentNumbersOnly();37 this.s ingleLowestSetup();38 this.setupPriceAdjustments(); 39 this.setupSingleLowest(); 38 40 39 41 } … … 158 160 const $row = $elm.closest( 'tr' ); 159 161 if( ! $row || 'none' != $row.style.display ) return; 162 163 /* Skip the Return Lowest Label if related isn't checked */ 164 if( -1 != $elm.name.indexOf( 'global_adjustment' ) && '' == document.querySelectorAll( '[name*=global_adjustment_type]' ).value ) { 165 return; 166 } 160 167 161 168 /* Skip the Return Lowest Label if related isn't checked */ … … 293 300 * Only allow numbers for the Price Adjustment input. 294 301 */ 295 priceAdjustmentNumbersOnly() { 296 297 const $adjustmentInput = document.querySelector( '[type=text][name*=global_adjustment' ); 302 setupPriceAdjustments() { 303 304 const $adjustmentSelect = document.querySelector( 'select[name*=global_adjustment_type]' ); 305 const $adjustmentInput = document.querySelector( '[type=text][name*=global_adjustment' ); 306 307 /* Select Change - Show Input Row */ 308 $adjustmentSelect.addEventListener( 'change', ( e ) => { 309 $adjustmentInput.value = ''; 310 this.rowMakeVisible( $adjustmentInput.closest( 'tr' ), ( e.target.value ) ) 311 } ); 312 313 /* Input Update - Only FloatString */ 298 314 $adjustmentInput.addEventListener( 'input', ( e ) => { 299 e.target.value = e.target.value.replace( / [^0-9.]/g, '' );315 e.target.value = e.target.value.replace( /(\..*?)\./g, '$1' ).replace( /[^0-9.]/g, '' ); 300 316 } ); 301 317 … … 306 322 * Show / Hide the Single Lowest label 307 323 */ 308 s ingleLowestSetup() {324 setupSingleLowest() { 309 325 310 326 const $lowestcb = document.querySelector( '[type=checkbox][name*=return_lowest' ); … … 320 336 321 337 /* Eh, just trigger it */ 322 if( 'none' != $lowestcb.closest( 'tr' ).style.display ) {338 if( $lowestcb.checked && 'none' == $lowestLabel.closest( 'tr' ).style.display ) { 323 339 $lowestcb.dispatchEvent( new Event( 'change' ) ); 324 340 } … … 337 353 if( visible ) { 338 354 339 $row.setAttribute( 'style', 'opacity:0' ); 355 if( null !== $row.offsetParent ) return; 356 357 $row.style = 'opacity:0'; 340 358 $row.animate( { 341 359 opacity: [ 1 ] … … 350 368 }, { 351 369 duration: 300 352 } ).onfinish = () => $row.s etAttribute( 'style', 'display:none;' );370 } ).onfinish = () => $row.style = 'display:none;'; 353 371 354 372 } … … 374 392 $err.remove(); 375 393 376 $err.s etAttribute( 'style', 'height:0px;opacity:0;overflow:hidden;' );394 $err.style = 'height:0px;opacity:0;overflow:hidden;'; 377 395 $row.querySelector( 'fieldset' ).appendChild( $err ); 378 396 -
live-rates-for-shipstation/trunk/core/assets/modules/shipping-zone.js
r3339099 r3361859 19 19 this.customBoxesRemove(); 20 20 21 this.setupPriceAdjustments() 21 22 this.inputsNumbersOnly(); 22 23 this.wooAccommodations(); … … 42 43 43 44 document.querySelectorAll( '#customBoxes [name]' ).forEach( ( $elm ) => { 44 if( 'text' == $elm. getAttribute( 'type' )) $elm.removeAttribute( 'required' );45 if( 'text' == $elm.type ) $elm.removeAttribute( 'required' ); 45 46 } ); 46 47 document.getElementById( 'customBoxes' ).style.display = 'none'; … … 80 81 $clone.classList.remove( 'mimic' ); 81 82 $clone.querySelectorAll( '[name]' ).forEach( ( $elm ) => { 82 $elm. setAttribute( 'name', $elm.getAttribute( 'name' ).replace( 'mimic', count ));83 if( 'text' == $elm. getAttribute( 'type' ) && -1 == $elm.getAttribute( 'name' ).indexOf( '[wm]' ) ) $elm.setAttribute( 'required', true );83 $elm.name = $elm.name.replace( 'mimic', count ); 84 if( 'text' == $elm.type && -1 == $elm.name.indexOf( '[wm]' ) ) $elm.required = true; 84 85 } ); 85 86 … … 116 117 117 118 /** 119 * Price Adjustments 120 * Manage the show/hide functionality. 121 */ 122 setupPriceAdjustments() { 123 124 /** 125 * Adjustment Type Change 126 * Show / Hide Price Input 127 */ 128 document.addEventListener( 'change', ( e ) => { 129 130 if( 'SELECT' != e.target.tagName ) return; 131 if( -1 == e.target.name.indexOf( 'adjustment_type' ) ) return; 132 133 const $adjustmentSelect = e.target; 134 const $adjustmentInput = $adjustmentSelect.closest( 'td' ).querySelector( 'input' ); 135 136 if( '' == $adjustmentSelect.value ) { 137 138 $adjustmentInput.animate( { 139 opacity: 0 140 }, { 141 duration: 300, 142 fill: 'forwards', 143 } ).onfinish = () => { 144 $adjustmentInput.value = ''; 145 $adjustmentInput.classList.add( 'iqlrss-hide' ); 146 }; 147 148 } else if( null === $adjustmentInput.offsetParent ) { 149 150 $adjustmentInput.classList.remove( 'iqlrss-hide' ); 151 $adjustmentInput.animate( { 152 opacity: [0, 1] 153 }, { 154 duration: 300, 155 fill: 'forwards', 156 } ).onfinish = () => { 157 $adjustmentInput.value = ''; 158 }; 159 160 } else { 161 $adjustmentInput.value = ( $adjustmentSelect.value != iqlrss.global_adjustment_type ) ? '0' : ''; 162 } 163 164 } ); 165 166 } 167 168 169 /** 118 170 * Only allow numbers in inputs. 119 171 */ 120 172 inputsNumbersOnly() { 121 173 174 /** 175 * All Custom Packing Box inputs. 176 * Any numbers-only classes 177 */ 122 178 document.addEventListener( 'input', ( e ) => { 123 179 if( 'INPUT' !== e.target.tagName ) return; 124 if( -1 != e.target. getAttribute( 'name' ).indexOf( 'custombox' ) || e.target.classList.contains( 'iqlrss-numbers-only' ) ) {125 e.target.value = e.target.value.replace( / [^0-9.]/g, '' );180 if( -1 != e.target.name.indexOf( 'custombox' ) || e.target.classList.contains( 'iqlrss-numbers-only' ) ) { 181 e.target.value = e.target.value.replace( /(\..*?)\./g, '$1' ).replace( /[^0-9.]/g, '' ); 126 182 } 127 183 } ); -
live-rates-for-shipstation/trunk/core/settings-shipstation.php
r3339099 r3361859 88 88 89 89 $data = array( 90 'api_verified' => \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false, true ), 90 'api_verified' => \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false ), 91 'global_adjustment_type' => \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type', '' ), 91 92 'rest' => array( 92 93 'nonce' => wp_create_nonce( 'wp_rest' ), … … 100 101 'confirm_box_removal' => esc_html__( 'Please confirm you would like to completely remove (x) custom boxes.', 'live-rates-for-shipstation' ), 101 102 'error_rest_generic' => esc_html__( 'Something went wrong with the REST Request. Please resave permalinks and try again.', 'live-rates-for-shipstation' ), 102 'error_verification_required' => esc_html__( 'Please click the Verify API button to ensure a connection exists.', 'live-rates-for-shipstation' ), 103 'error_verification_required' => esc_html__( 'Please click the Verify API button to ensure a connection exists.', 'live-rates-for-shipstation' ), 104 'desc_global_adjustment_percentage' => esc_html__( 'Example: IF UPS Ground is $7.25 and you input 15% ($1.08), the final shipping rate the customer sees is: $8.33', 'live-rates-for-shipstation' ), 105 'desc_global_adjustment_flatrate' => esc_html__( 'Example: IF UPS Ground is $5.50 and you input $2.37, the final shipping rate the customer sees is: $7.87', 'live-rates-for-shipstation' ), 103 106 ), 104 107 ); … … 106 109 ?><script type="text/javascript"> 107 110 111 /* JS Localization */ 108 112 const iqlrss = JSON.parse( '<?php echo wp_json_encode( $data ); ?>' ); 109 113 110 <?php 111 /** 112 * Modules load too late to effectively immediately hide elements. 113 * This runs on the ShipStation settings page to hide additional 114 * settings whenever the API is unauthenticated. 115 */ 116 if( ! $data['api_verified'] ) : 117 ?> 118 119 if( document.getElementById( 'woocommerce_shipstation_iqlrss_api_key' ) ) { ( () => { 114 /* Early setting field JS */ 115 if( document.getElementById( 'woocommerce_shipstation_iqlrss_api_key' ) ) { ( function() { 116 117 /* Hide an element, ezpz */ 118 const fnHide = ( $el ) => $el.closest( 'tr' ).style.display = 'none'; 119 120 <?php 121 /** 122 * Modules load too late to effectively immediately hide elements. 123 * This runs on the ShipStation settings page to hide additional 124 * settings whenever the API is unauthenticated. 125 */ 126 if( ! $data['api_verified'] ) : 127 ?> 128 120 129 document.querySelectorAll( '[name*=iqlrss]' ).forEach( ( $elm ) => { 121 if( $elm. getAttribute( 'name' ).includes( 'api_key' ) ) return;122 if( $elm. getAttribute( 'name' ).includes( 'cart_weight' ) ) return;123 $elm.closest( 'tr' ).style.display = 'none';130 if( $elm.name.includes( 'api_key' ) ) return; 131 if( $elm.name.includes( 'cart_weight' ) ) return; 132 fnHide( $elm ); 124 133 } ); 125 } )(); } 126 127 <?php endif; ?> 134 135 <?php else : ?> 136 137 document.querySelectorAll( '[name*=iqlrss]' ).forEach( ( $elm ) => { 138 139 if( 'checkbox' == $elm.type && $elm.name.includes( 'return_lowest' ) && ! $elm.checked ) { 140 fnHide( document.querySelector( '[name*=return_lowest_label]' ) ); 141 } 142 143 if( $elm.name.includes( 'global_adjustment_type' ) && '' == $elm.value ) { 144 fnHide( document.querySelector( '[type=text][name*=global_adjustment]' ) ); 145 } 146 } ); 147 148 <?php endif; ?> 149 150 } )(); } 151 128 152 </script><?php 129 153 … … 155 179 public function display_cart_weight() { 156 180 157 $show_weight = \IQLRSS\Driver::get_ss_opt( 'cart_weight', 'no' , true);181 $show_weight = \IQLRSS\Driver::get_ss_opt( 'cart_weight', 'no' ); 158 182 if( 'no' == $show_weight ) return; 159 183 … … 289 313 290 314 // Set transient to clear any WC_Session caches if they are found. 291 $ time= absint( apply_filters( 'wc_session_expiration', DAY_IN_SECONDS * 2 ) );292 set_transient( \IQLRSS\Driver::plugin_prefix( 'wcs_timeout' ), time(), $ time);315 $expires = absint( apply_filters( 'wc_session_expiration', DAY_IN_SECONDS * 2 ) ); 316 set_transient( \IQLRSS\Driver::plugin_prefix( 'wcs_timeout' ), time(), $expires ); 293 317 294 318 } … … 326 350 add_filter( 'woocommerce_shipping_methods', array ($this, 'append_shipstation_method' ) ); 327 351 add_filter( 'woocommerce_settings_api_form_fields_shipstation', array( $this, 'append_shipstation_integration_settings' ) ); 352 add_filter( 'woocommerce_settings_api_sanitized_fields_shipstation',array( $this, 'save_shipstation_integration_settings' ) ); 353 add_filter( 'woocommerce_shipstation_export_get_order', array( $this, 'export_shipstation_shipping_method' ) ); 328 354 329 355 } … … 366 392 } 367 393 } 394 395 // Backwards compatibility for v1.0.3 when only percentage was supported by default. 396 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '0' ); 397 $adjustment_type_default = ( empty( $global_adjustment_type ) && ! empty( $global_adjustment ) ) ? 'percentage' : ''; 368 398 369 399 foreach( $fields as $key => $field ) { … … 391 421 ); 392 422 423 $appended_fields[ \IQLRSS\Driver::plugin_prefix( 'global_adjustment_type' ) ] = array( 424 'title' => esc_html__( 'Shipping Price Adjustment', 'live-rates-for-shipstation' ), 425 'type' => 'select', 426 'options' => \IQLRSS\Core\Shipping_Method_Shipstation::get_adjustment_types( true ), 427 'description' => esc_html__( 'This adjustment is added on top of the returned shipping rates to help you cover shipping costs. Can be overridden per zone, per service.', 'live-rates-for-shipstation' ), 428 'default' => $adjustment_type_default, 429 ); 430 393 431 $appended_fields[ \IQLRSS\Driver::plugin_prefix( 'global_adjustment' ) ] = array( 394 'title' => esc_html__( ' Shipping Price Adjustment (%)', 'live-rates-for-shipstation' ),432 'title' => esc_html__( 'Global Price Adjustment', 'live-rates-for-shipstation' ), 395 433 'type' => 'text', 396 'placeholder' => '0%', 397 'description' => esc_html__( 'This percent is added on top of the returned shipping rates to help you cover shipping costs. Can be overridden per zone, per service.', 'live-rates-for-shipstation' ), 398 'desc_tip' => esc_html__( 'Example: IF UPS Ground is $7.25 - 15% would be $1.08 making the final rate: $8.33', 'live-rates-for-shipstation' ), 434 'placeholder' => '0', 435 'description' => esc_html__( 'Optional global ShipStation rate adjustment.', 'live-rates-for-shipstation' ), 399 436 'default' => '', 400 437 ); … … 431 468 432 469 470 /** 471 * Modify the saved settings after WooCommerce has sanitized them. 472 * Not much we need to do here, WooCommerce does most the heavy lifting. 473 * 474 * @param Array $settings 475 * 476 * @return Array $settings 477 */ 478 public function save_shipstation_integration_settings( $settings ) { 479 480 // No API Key? Invalid! 481 $api_key_key = \IQLRSS\Driver::plugin_prefix( 'api_key' ); 482 if( ! isset( $settings[ $api_key_key ] ) || empty( $settings[ $api_key_key ] ) ) { 483 484 $settings[ \IQLRSS\Driver::plugin_prefix( 'api_key_valid' ) ] = false; 485 if( isset( $settings[ \IQLRSS\Driver::plugin_prefix( 'api_key_vt' ) ] ) ) { 486 unset( $settings[ \IQLRSS\Driver::plugin_prefix( 'api_key_vt' ) ] ); 487 } 488 } 489 490 return $settings; 491 492 } 493 494 495 /** 496 * Update the WC_Order Shipping Method to match the ShipStation Carrier. 497 * Ex. USPSPriorityMail 498 * 499 * @link https://help.shipstation.com/hc/en-us/articles/360025856192-Custom-Store-Development-Guide 500 * 501 * @param WC_Order $order 502 * 503 * @return WC_Order $order 504 */ 505 public function export_shipstation_shipping_method( $order ) { 506 507 if( ! is_a( $order, 'WC_Order' ) ) { 508 return $order; 509 } 510 511 $methods = $order->get_shipping_methods(); 512 $plugin_method_id = \IQLRSS\Driver::plugin_prefix( 'shipstation' ); 513 514 foreach( $methods as $method ) { 515 516 // Not our shipping method. 517 if( $method->get_method_id() != $plugin_method_id ) continue; 518 519 $service_name = $method->get_meta( 'service_name', true ); 520 $carrier_name = $method->get_meta( 'carrier_name', true ); 521 522 // Missing metadata. 523 if( empty( $service_name ) || empty( $carrier_name ) ) continue; 524 525 $method->set_props( array( 526 'name' => preg_replace( '/([^a-zA-Z0-9])/', '', sprintf( '%s %s', $carrier_name, $service_name ) ), 527 ) ); 528 $method->apply_changes(); // Temporarily apply changes. This does not update the database. 529 530 } 531 532 return $order; 533 534 } 535 536 433 537 434 538 /**------------------------------------------------------------------------------------------------ **/ -
live-rates-for-shipstation/trunk/core/shipping-method-shipstation.php
r3339837 r3361859 38 38 39 39 /** 40 * Array of store specific settings. 41 * 42 * @var Array 43 */ 44 protected $store_data = array( 45 'weight_unit' => '', 46 'dim_unit' => '', // Dimension 47 ); 48 49 50 /** 40 51 * Array of global carriers 41 52 * There are the carriers saved in Integration settings. … … 79 90 $this->supports = array( 'instance-settings' ); 80 91 81 $this->carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() , true);82 $saved_key = \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false , true);92 $this->carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() ); 93 $saved_key = \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false ); 83 94 84 95 // Only show in Shipping Zones if API Key is invalid. … … 86 97 $this->supports[] = 'shipping-zones'; 87 98 } 99 100 // Set the store unit term and associate it with ShipStations term. 101 $this->store_data = array( 102 'weight_unit' => get_option( 'woocommerce_weight_unit', $this->store_data['weight_unit'] ), 103 'dim_unit' => get_option( 'woocommerce_dimension_unit', $this->store_data['dim_unit'] ), 104 ); 88 105 89 106 $this->init_instance_form_fields(); … … 180 197 $settings = get_option( 'woocommerce_shipstation_settings' ); 181 198 $saved_services = $this->get_option( 'services', array() ); 182 $saved_carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() , true);199 $saved_carriers = \IQLRSS\Driver::get_ss_opt( 'carriers', array() ); 183 200 $shipStationAPI = $this->shipStationApi; 184 201 … … 186 203 187 204 $sorted_services = array(); 205 206 // See $this->validate_services_field() 188 207 foreach( $saved_services as $k => $s ) { 189 208 … … 233 252 * Validate service field. 234 253 * 235 * @ param mixed $key - Field key.254 * @return Array $services 236 255 */ 237 256 public function validate_services_field() { … … 253 272 $posted_services = wp_unslash( $_POST[ $prefix ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 254 273 274 // Global adjustment 275 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '' ); 276 $global_adjustment_type = \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type', '' ); 277 255 278 // Group by Carriers then Services 256 279 $services = array(); … … 258 281 foreach( $carrier_services as $service_code => $service_arr ) { 259 282 260 // Skip non-enabled and non-renamed services.261 if( ! isset( $service_arr['enabled'] ) && empty( $service_arr['nickname'] ) ) continue;262 263 283 $carrier_code = sanitize_text_field( $carrier_code ); 264 284 $service_code = sanitize_text_field( $service_code ); 265 $ services[ $carrier_code ][ $service_code ]= array_filter( array(285 $data = array_filter( array( 266 286 267 287 // User Input … … 276 296 ) ); 277 297 278 // Allow 0 value user input. 279 if( $service_arr['adjustment'] >= 0 ) { 280 $services[ $carrier_code ][ $service_code ]['adjustment'] = floatval( $service_arr['adjustment'] ); 298 // The above removes empty values. 299 // Price Adjustments 300 $data['adjustment'] = ( $service_arr['adjustment'] ) ? floatval( $service_arr['adjustment'] ) : ''; 301 $data['adjustment_type']= $service_arr['adjustment_type']; 302 303 // Maybe unset if we don't need the data. 304 if( $data['adjustment_type'] == $global_adjustment_type ) { 305 306 // equal or equal empty -> 0 == '' 307 if( $data['adjustment'] == $global_adjustment || '' == $data['adjustment'] ) { 308 unset( $data['adjustment'] ); 309 unset( $data['adjustment_type'] ); 310 } 281 311 } 282 312 313 /** 314 * We don't want to array_filter() since 315 * Global Adjust could be populated, and 316 * Service is set to '' (No Adjustment). 317 */ 318 $services[ $carrier_code ][ $service_code ] = $data; 319 283 320 } 284 321 } … … 292 329 * Validate customboxes field. 293 330 * 294 * @ param mixed $key - Field key.331 * @return Array $boxes 295 332 */ 296 333 public function validate_customboxes_field() { … … 370 407 } 371 408 372 $global_upcharge = floatval( \IQLRSS\Driver::get_ss_opt( 'global_adjustment', 0, true ) ); 373 $packing_type = $this->get_option( 'packing', 'individual' ); 409 $global_adjustment = floatval( \IQLRSS\Driver::get_ss_opt( 'global_adjustment', 0 ) ); 410 $global_adjustment_type = \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type','' ); 411 $global_adjustment_type = ( empty( $global_adjustment_type ) && ! empty( $global_adjustment ) ) ? 'percentage' : $global_adjustment_type; 412 413 $packing_type = $this->get_option( 'packing', 'individual' ); 374 414 $request = array( 375 415 'from_country_code' => WC()->countries->get_base_country(), … … 444 484 445 485 // Apply service upcharge 446 if( isset( $service_arr['adjustment'] ) && $service_arr['adjustment'] > 0 ) { 447 448 $adjustment = floatval( $saved_services[ $shiprate['carrier_code'] ]['adjustment'] ); 449 $cost += ( $adjustment > 0 ) ? ( $cost * ( $adjustment / 100 ) ) : 0; 450 451 } else if( ! empty( $global_upcharge ) ) { 452 $cost += ( $cost * ( $global_upcharge / 100 ) ); 486 if( isset( $service_arr['adjustment'] ) ) { 487 488 /** 489 * Adjustment type could be '' to skip global adjustment. 490 * Defaults to percentage for v1.03 backwards compatibility. 491 */ 492 $adjustment = floatval( $service_arr['adjustment'] ); 493 $adjustment_type = ( isset( $service_arr['adjustment_type'] ) ) ? $service_arr['adjustment_type'] : 'percentage'; 494 495 if( ! empty( $adjustment_type ) && $adjustment > 0 ) { 496 $cost += ( 'flatrate' == $adjustment_type ) ? $adjustment : ( $cost * ( $adjustment / 100 ) ); 497 } 498 499 } else if( ! empty( $global_adjustment_type ) && $global_adjustment > 0 ) { 500 $cost += ( 'flatrate' == $global_adjustment_type ) ? floatval( $global_adjustment ) : ( $cost * ( floatval( $global_adjustment ) / 100 ) ); 453 501 } 454 502 … … 464 512 'dimensions' => $req['dimensions'], 465 513 'weight' => $req['weight'], 514 'service_name' => $shiprate['name'], 466 515 'carrier_code' => $shiprate['carrier_code'], 516 'carrier_name' => $shiprate['carrier_name'], 467 517 ), 468 518 ); … … 480 530 } 481 531 482 $single_lowest = \IQLRSS\Driver::get_ss_opt( 'return_lowest', 'no' , true);483 $single_lowest_label = \IQLRSS\Driver::get_ss_opt( 'return_lowest_label', '' , true);532 $single_lowest = \IQLRSS\Driver::get_ss_opt( 'return_lowest', 'no' ); 533 $single_lowest_label = \IQLRSS\Driver::get_ss_opt( 'return_lowest_label', '' ); 484 534 485 535 // Add all shipping rates, let the user decide. … … 547 597 esc_html__( 'Product ID #%1$d missing (%2$s) dimensions. Shipping calculations terminated.', 'live-rates-for-shipstation' ), 548 598 $item['product_id'], 549 implode( ', ', array_diff_key( $this->dimension_keys, $physicals ) ) 599 implode( ', ', array_diff_key( array( 600 'width' => 'width', 601 'height' => 'height', 602 'length' => 'length', 603 'weight' => 'weight', 604 ), $physicals ) ) 550 605 ) ); 551 606 return array(); … … 553 608 554 609 $request['weight'] = array( 555 'value' => (float) max( 0.5, round( wc_get_weight( $physicals['weight'], 'lbs' ), 2 )),556 'unit' => 'pound',610 'value' => (float)round( wc_get_weight( $physicals['weight'], $this->store_data['weight_unit'] ), 2 ), 611 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['weight_unit'] ), 557 612 ); 558 613 … … 562 617 563 618 $request['dimensions'] = array( 564 ' unit' => 'inch',565 ' length' => max( 1, round( wc_get_dimension( $physicals[2], 'in' ), 2 )),566 ' width' => max( 1, round( wc_get_dimension( $physicals[1], 'in' ), 2 )),567 ' height' => max( 1, round( wc_get_dimension( $physicals[0], 'in' ), 2 )),619 'length' => round( wc_get_dimension( $physicals[2], $this->store_data['dim_unit'] ), 2 ), 620 'width' => round( wc_get_dimension( $physicals[1], $this->store_data['dim_unit'] ), 2 ), 621 'height' => round( wc_get_dimension( $physicals[0], $this->store_data['dim_unit'] ), 2 ), 622 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['dim_unit'] ), 568 623 ); 569 624 … … 631 686 esc_html__( 'Product ID #%1$d missing (%2$s) dimensions. Shipping calculations terminated.', 'live-rates-for-shipstation' ), 632 687 $item['product_id'], 633 implode( ', ', array_diff_key( $this->dimension_keys, $physicals ) ) 688 implode( ', ', array_diff_key( array( 689 'width' => 'width', 690 'height' => 'height', 691 'length' => 'length', 692 'weight' => 'weight', 693 ), $physicals ) ) 634 694 ) ); 635 695 return array(); 636 696 } 637 697 638 $data['weight'] = (float) max( 0.5, round( wc_get_weight( $physicals['weight'], 'lbs' ), 2 ));639 640 // Unset weight and sort dimensions698 $data['weight'] = (float)round( wc_get_weight( $physicals['weight'], $this->store_data['weight_unit'] ), 2 ); 699 700 // Unset weight to exclude it from sort 641 701 unset( $physicals['weight'] ); 642 702 sort( $physicals ); 643 703 644 704 $data = array( 645 'length' => max( 1, round( wc_get_dimension( $physicals[2], 'in' ), 2 )),646 'width' => max( 1, round( wc_get_dimension( $physicals[1], 'in' ), 2 )),647 'height' => max( 1, round( wc_get_dimension( $physicals[0], 'in' ), 2 )),705 'length' => round( wc_get_dimension( $physicals[2], $this->store_data['dim_unit'] ), 2 ), 706 'width' => round( wc_get_dimension( $physicals[1], $this->store_data['dim_unit'] ), 2 ), 707 'height' => round( wc_get_dimension( $physicals[0], $this->store_data['dim_unit'] ), 2 ), 648 708 ) + $data; 649 709 … … 670 730 'weight' => array( 671 731 'value' => $package->weight, 672 'unit' => 'pound',732 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['weight_unit'] ), 673 733 ), 674 734 'dimensions' => array( 675 'unit' => 'inch',676 735 'length' => $package->length, 677 736 'width' => $package->width, 678 737 'height' => $package->height, 738 'unit' => $this->shipStationApi->convert_unit_term( $this->store_data['dim_unit'] ), 679 739 ), 680 740 ); … … 796 856 /**------------------------------------------------------------------------------------------------ **/ 797 857 /** 858 * Return an array of Price Adjustment Type options. 859 * 860 * @return Array 861 */ 862 public static function get_adjustment_types( $include_empty = false ) { 863 864 $types = array( 865 'flatrate' => esc_html__( 'Flat Rate', 'live-rates-for-shipstation' ), 866 'percentage' => esc_html__( 'Percentage', 'live-rates-for-shipstation' ), 867 ); 868 869 return ( false == $include_empty ) ? $types : array_merge( array( 870 '' => esc_html__( 'No Adjustments', 'live-rates-for-shipstation' ), 871 ), $types ); 872 873 } 874 875 876 /** 798 877 * Log error in WooCommerce 799 878 * Passthru method - log what's given and give it back. … … 808 887 protected function log( $error, $level = 'debug', $context = array() ) { 809 888 810 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', false ) ) {889 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', 0, true ) ) { 811 890 return $error; 812 891 } -
live-rates-for-shipstation/trunk/core/shipstation-api.php
r3339099 r3361859 15 15 16 16 /** 17 * API Endpoint17 * Key prefix 18 18 * 19 19 * @var String 20 20 */ 21 protected $apiurl = 'https://api.shipstation.com'; // No trailing slash. 21 protected $prefix; 22 23 24 /** 25 * Seconds to hold cache. 26 * Defaults to 1 week. 27 * 28 * @var Integer 29 */ 30 protected $cache_time; 31 32 33 /** 34 * Skip cache check 35 * 36 * @var Boolean 37 */ 38 protected $skip_cache = false; 22 39 23 40 … … 31 48 32 49 /** 33 * Key prefix50 * The API Key 34 51 * 35 52 * @var String 36 53 */ 37 protected $prefix; 38 39 40 /** 41 * Skip cache check 42 * 43 * @var Boolean 44 */ 45 protected $skip_cache = false; 46 47 48 /** 49 * The API Key 50 * 51 * @var String 52 */ 53 private $key = ''; 54 private $key; 54 55 55 56 … … 62 63 63 64 $this->prefix = \IQLRSS\Driver::get( 'slug' ); 64 $this->key = \IQLRSS\Driver::get_ss_opt( 'api_key', '' , true);65 $this->key = \IQLRSS\Driver::get_ss_opt( 'api_key', '' ); 65 66 $this->skip_cache = (boolean)$skip_cache; 67 $this->cache_time = defined( 'WEEK_IN_SECONDS' ) ? WEEK_IN_SECONDS : 604800; 66 68 67 69 } … … 118 120 * Return an array of carriers. 119 121 * While getting carriers, set services and packages. 122 * 123 * @link https://docs.shipstation.com/openapi/carriers 120 124 * 121 125 * @param String $carrier_code … … 191 195 // Cache Carriers 192 196 if( ! empty( $data['carriers'] ) ) { 193 set_transient( $trans_key, $data['carriers'], MONTH_IN_SECONDS);197 set_transient( $trans_key, $data['carriers'], $this->cache_time ); 194 198 } 195 199 … … 198 202 foreach( $data['services'] as $carrier_id => $service_arr ) { 199 203 $service_key = sprintf( '%s_%s_services', $trans_key, $carrier_id ); 200 set_transient( $service_key, $service_arr, MONTH_IN_SECONDS);204 set_transient( $service_key, $service_arr, $this->cache_time ); 201 205 } 202 206 } … … 206 210 foreach( $data['packages'] as $carrier_id => $package_arr ) { 207 211 $package_key = sprintf( '%s_%s_packages', $trans_key, $carrier_id ); 208 set_transient( $package_key, $package_arr, MONTH_IN_SECONDS);212 set_transient( $package_key, $package_arr, $this->cache_time ); 209 213 } 210 214 } … … 269 273 /**------------------------------------------------------------------------------------------------ **/ 270 274 /** 275 * Convert a WooCommerce unit term to a 276 * ShipStation unit term. 277 * 278 * @param String $unit 279 * 280 * @return String $term 281 */ 282 public function convert_unit_term( $unit ) { 283 284 $known = array( 285 'kg' => 'kilogram', 286 'g' => 'gram', 287 'lbs' => 'pound', 288 'oz' => 'ounce', 289 'cm' => 'centimeter', 290 'in' => 'inch', 291 ); 292 293 return ( isset( $known[ $unit ] ) ) ? $known[ $unit ] : $unit; 294 295 } 296 297 298 /** 271 299 * Make an API Request 272 300 * … … 345 373 */ 346 374 protected function get_endpoint_url( $endpoint ) { 347 return sprintf( '%s/v2/%s/', $this->apiurl, $endpoint ); 375 376 return sprintf( '%s/v2/%s/', 377 'https://api.shipstation.com', 378 $endpoint 379 ); 380 348 381 } 349 382 … … 380 413 protected function log( $error, $level = 'debug', $context = array() ) { 381 414 382 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', false ) ) {415 if( ! \IQLRSS\Driver::get_ss_opt( 'logging_enabled', 0, true ) ) { 383 416 return $error; 384 417 } -
live-rates-for-shipstation/trunk/core/views/services-table.php
r3339099 r3361859 18 18 } 19 19 20 $api_key = \IQLRSS\Driver::get_ss_opt( 'api_key', '', true ); 21 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '0', true ); 20 $api_key = \IQLRSS\Driver::get_ss_opt( 'api_key', '' ); 21 $global_adjustment = \IQLRSS\Driver::get_ss_opt( 'global_adjustment', '0' ); 22 $global_adjustment_type = \IQLRSS\Driver::get_ss_opt( 'global_adjustment_type', '' ); 23 $global_adjustment_type = ( empty( $global_adjustment_type ) && ! empty( $global_adjustment ) ) ? 'percentage' : $global_adjustment_type; 22 24 23 25 ?> … … 33 35 <th style="width: 50px;"><?php esc_html_e( 'Enabled', 'live-rates-for-shipstation' ); ?></th> 34 36 <th><?php esc_html_e( 'Name', 'live-rates-for-shipstation' ); ?></th> 35 <th><?php esc_html_e( 'Price Adjustment (%)', 'live-rates-for-shipstation' ); ?></th>37 <th><?php esc_html_e( 'Price Adjustment', 'live-rates-for-shipstation' ); ?></th> 36 38 <th><?php esc_html_e( 'Carrier', 'live-rates-for-shipstation' ); ?></th> 37 39 </tr> … … 39 41 <tbody><?php 40 42 41 if( empty( $api_key ) || ! \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false , true) ) {43 if( empty( $api_key ) || ! \IQLRSS\Driver::get_ss_opt( 'api_key_valid', false ) ) { 42 44 print( '<tr><th colspan="4">' ); 43 45 echo wp_kses( sprintf( … … 62 64 63 65 $saved_atts = array( 64 'enabled' => ( isset( $service_arr['enabled'] ) ) ? $service_arr['enabled'] : false, 65 'nickname' => ( isset( $service_arr['nickname'] ) ) ? $service_arr['nickname'] : '', 66 'adjustment' => ( isset( $service_arr['adjustment'] ) ) ? $service_arr['adjustment'] : '', 66 'enabled' => ( isset( $service_arr['enabled'] ) ) ? $service_arr['enabled'] : false, 67 'nickname' => ( isset( $service_arr['nickname'] ) ) ? $service_arr['nickname'] : '', 68 'adjustment_type' => ( isset( $service_arr['adjustment_type'] ) ) ? $service_arr['adjustment_type'] : $global_adjustment_type, 69 'adjustment' => ( isset( $service_arr['adjustment'] ) ) ? $service_arr['adjustment'] : '', 67 70 ); 68 71 … … 99 102 100 103 // Service Price Adjustment 101 printf( '<td><input type="text" name="%s" value="%s" placeholder="%s" style="max-width:60px;"></td>', 102 esc_attr( $attr_name . '[adjustment]' ), 103 esc_attr( $saved_atts['adjustment'] ), 104 esc_attr( $global_adjustment . '%' ), 105 ); 104 print( '<td><div class="iqrlssimple-flex-2">' ); 105 106 printf( '<div><select name="%s" style="width:100%%;">', esc_attr( $attr_name . '[adjustment_type]' ) ); 107 foreach( static::get_adjustment_types( true ) as $slug => $label ) { 108 printf( '<option value="%s"%s>%s</option>', 109 esc_attr( $slug ), 110 selected( $saved_atts['adjustment_type'], $slug, false ), 111 $label 112 ); 113 } 114 print( '</select></div>' ); 115 116 printf( '<div><input type="text" name="%s" value="%s" placeholder="%s" style="max-width:60px;" class="%s"></div>', 117 esc_attr( $attr_name . '[adjustment]' ), 118 esc_attr( $saved_atts['adjustment'] ), 119 esc_attr( $global_adjustment ), 120 esc_attr( 'iqlrss-numbers-only' . ( ( '' == $saved_atts['adjustment_type'] ) ? ' iqlrss-hide' : '' ) ), 121 ); 122 print( '</div></td>' ); 106 123 107 124 // Carrier Name … … 109 126 110 127 print( '</tr>' ); 128 129 // Set a processed flag for the next array which is not reorganized. 130 $saved_services[ $carrier_code ][ $service_code ]['processed'] = true; 111 131 112 132 } … … 128 148 129 149 $service_arr = ( ! is_array( $service_arr ) ) ? (array)$service_arr : $service_arr; 130 if( isset( $saved_services[ $carrier_code ][ $service_arr['service_code'] ] ) ) continue;150 if( isset( $saved_services[ $carrier_code ][ $service_arr['service_code'] ]['processed'] ) ) continue; 131 151 132 152 print( '<tr>' ); … … 159 179 ); 160 180 161 // Service Name 162 printf( '<td><input type="text" name="%s" value="" placeholder="%s" class="iqlrss-numbers-only" style="max-width:60px;"></td>', 163 esc_attr( $attr_name . '[adjustment]' ), 164 esc_attr( $global_adjustment . '%' ), 165 ); 181 // Service Price Adjustment 182 print( '<td><div class="iqrlssimple-flex-2">' ); 183 184 printf( '<div><select name="%s" style="width:100%%;">', esc_attr( $attr_name . '[adjustment_type]' ) ); 185 foreach( static::get_adjustment_types( true ) as $slug => $label ) { 186 printf( '<option value="%s"%s>%s</option>', 187 esc_attr( $slug ), 188 selected( $global_adjustment_type, $slug, false ), 189 $label 190 ); 191 } 192 print( '</select></div>' ); 193 194 printf( '<div><input type="text" name="%s" value="" placeholder="%s" style="max-width:60px;" class="%s"></div>', 195 esc_attr( $attr_name . '[adjustment]' ), 196 esc_attr( $global_adjustment ), 197 esc_attr( 'iqlrss-numbers-only' . ( ( '' == $global_adjustment_type ) ? ' iqlrss-hide' : '' ) ) 198 ); 199 print( '</div></td>' ); 166 200 167 201 // Carrier Name -
live-rates-for-shipstation/trunk/live-rates-for-shipstation.php
r3339837 r3361859 4 4 * Plugin URI: https://iqcomputing.com/contact/ 5 5 * Description: ShipStation shipping method with live rates. 6 * Version: 1.0. 36 * Version: 1.0.4 7 7 * Requries at least: 5.9 8 8 * Author: IQComputing … … 26 26 * @var String 27 27 */ 28 protected static $version = '1.0. 3';28 protected static $version = '1.0.4'; 29 29 30 30 … … 55 55 * @param String $key 56 56 * @param Mixed $default 57 * @param Boolean $ prefix - Prefix Key with plugin slug.57 * @param Boolean $skip_prefix - Skip Plugin Prefix and return a core ShipStation setting value. 58 58 * 59 59 * @return Mixed 60 60 */ 61 public static function get_ss_opt( $key, $default = '', $ prefix = false ) {61 public static function get_ss_opt( $key, $default = '', $skip_prefix = false ) { 62 62 63 if( $prefix ) $key = static::plugin_prefix( $key );63 if( ! $skip_prefix ) $key = static::plugin_prefix( $key ); 64 64 $settings = get_option( 'woocommerce_shipstation_settings' ); 65 65 return ( isset( $settings[ $key ] ) && '' !== $settings[ $key ] ) ? $settings[ $key ] : $default; … … 141 141 142 142 } ); 143 add_action( 'plugins_loaded', array( '\IQLRSS\Driver', 'drive' ), 15);143 add_action( 'plugins_loaded', array( '\IQLRSS\Driver', 'drive' ), 8 ); -
live-rates-for-shipstation/trunk/readme.txt
r3339837 r3361859 4 4 Requires at least: 5.9 5 5 Tested up to: 6.8 6 Stable tag: 1.0. 36 Stable tag: 1.0.4 7 7 License: GPLv3 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 49 49 == Changelog == 50 50 51 = 1.0.4 (2025-09-15) = 52 * Patches issues with shipping units not match store set units. 53 * Adds new Flat Rate Adjustments to global and shipping services. 54 * Adds additional metadata to WC Order Items in regards to shipping. 55 * Shoutouts to both @centuryperf and @jkmail120 for reporting these issues! 56 51 57 = 1.0.3 (2025-08-05) = 52 58 * Patches an issue with Shipping Method availability (Thanks to @sportswreathshop for reporting!) … … 54 60 = 1.0.2 (2025-08-04) = 55 61 * Refines API caching that clears on settings save (thanks again to @dpkonofa for test/reporting!). 56 57 = 1.0.1 (2025-08-01) =58 * Patches an issue with Individual Shipping Requests (thanks @dpkonofa !).59 * Attempt to discern ShipStation Carriers from Manually Connected Carriers.60 61 = 1.0.0 (2025-07-28) =62 * Initial release
Note: See TracChangeset
for help on using the changeset viewer.