Class: Shipping::SpeedeeDelivery
- Defined in:
- app/services/shipping/speedee_delivery.rb
Constant Summary collapse
- COST_DISCREPANCY_THRESHOLD =
100.0- COST_DISCREPANCY_THRESHOLD_RATIO =
this is max ratio of discrepancy by total shipping cost
0.25- COST_DISCREPANCY_THRESHOLD_BY_TOTAL_VALUE_RATIO =
this is max ratio of discrepancy by total delivery value
0.02- OVERSIZE_SURCHARGE =
{'2': 60.00, '3': 65.00, '4': 70.00, '5': 75.00, '6': 80.00}
- DAS_SURCHARGE =
1.0- DAS_SURCHARGE_FOR_OVERSIZED =
1.0- HIGH_DIMENSIONAL_LENGTH_THRESHOLD =
130.0- DIMENSIONAL_LENGTH_MAX =
170.0- LENGTH_MAX =
120.0- HIGH_WEIGHT_THRESHOLD =
100.0- WEIGHT_MAX =
150.0- DECLARED_VALUE_MIN =
COD_SURCHARGE = 11.0 # we do NOT support COD for Speedee or any other carrier
100.0- DECLARED_VALUE_MAX =
5000.0- DECLARED_VALUE_SURCHARGE_PER_100 =
0.8- DECLARED_VALUE_SURCHARGE_MIN =
2.4- SIGNATURE_CONFIRMATION_SURCHARGE =
THis is the Direct Signature from SpeeDee
3.25
Instance Attribute Summary
Attributes inherited from Base
#address, #address2, #address3, #address_residential, #attention_name, #billing_account, #billing_country, #billing_zip, #ci_comments, #city, #close_report_only, #cod_amount, #cod_collection_type, #company, #country, #currency_code, #data, #debug, #declared_value, #delivery_instructions, #delivery_total_value, #description, #discount_price, #dropoff_type, #email, #eta, #export_reason, #freight_class, #freightquote_authorization_url, #freightquote_client_id, #freightquote_client_secret, #freightquote_customer_code, #freightquote_events_url, #freightquote_rating_url, #freightquote_shipping_url, #freightquote_voiding_url, #handling_instructions, #has_loading_dock, #image_type, #include_first_class_mail_options, #insured_value, #is_construction_site, #is_trade_show, #label_type, #limited_access, #line_items, #master_tracking_number, #measure_height, #measure_length, #measure_units, #measure_width, #media_mail, #multiple_piece_shipping, #negotiated_rates, #package, #package_count, #package_sequence_number, #package_total, #packages, #packaging_type, #pay_type, #phone, #pickup_datetime, #pickup_instructions, #plain_response, #price, #rate_data, #reference_number_1, #reference_number_2, #reference_number_3, #reference_number_code_1, #reference_number_code_2, #required, #requires_appointment, #requires_inside_delivery, #requires_liftgate, #response, #return_to_address, #return_to_address2, #return_to_address3, #return_to_address_residential, #return_to_attention_name, #return_to_city, #return_to_company, #return_to_country, #return_to_email, #return_to_has_loading_dock, #return_to_is_construction_site, #return_to_is_trade_show, #return_to_limited_access, #return_to_name, #return_to_phone, #return_to_requires_appointment, #return_to_requires_inside_delivery, #return_to_requires_liftgate, #return_to_state, #return_to_zip, #rl_carriers_api_key, #rl_carriers_shipping_url, #saturday_delivery, #sender_address, #sender_address2, #sender_address3, #sender_address_residential, #sender_attention_name, #sender_city, #sender_company, #sender_country, #sender_email, #sender_has_loading_dock, #sender_is_construction_site, #sender_is_trade_show, #sender_limited_access, #sender_name, #sender_phone, #sender_requires_appointment, #sender_requires_inside_delivery, #sender_requires_liftgate, #sender_state, #sender_tax_identification_number, #sender_zip, #service_code, #service_type, #services, #ship_date, #shipengine_api_key, #shipengine_canadapost_account_id, #shipengine_canadapost_parent_account_number, #shipengine_canpar_account_id, #shipengine_dhl_express_account_id, #shipengine_fed_ex_account_id, #shipengine_fed_ex_ca_account_id, #shipengine_purolator_account_id, #shipengine_ups_account_id, #shipengine_ups_ca_account_id, #shipengine_usps_account_id, #shipper_address, #shipper_address2, #shipper_address3, #shipper_address_residential, #shipper_attention_name, #shipper_city, #shipper_company, #shipper_country, #shipper_email, #shipper_has_loading_dock, #shipper_is_construction_site, #shipper_is_trade_show, #shipper_limited_access, #shipper_name, #shipper_phone, #shipper_requires_appointment, #shipper_requires_inside_delivery, #shipper_requires_liftgate, #shipper_state, #shipper_zip, #signature_confirmation, #skip_png_download, #skip_rate_test, #special_instructions, #state, #tax_identification_number, #time_in_transit, #total_shipment_weight, #transaction_type, #weight, #weight_units, #zip
Instance Method Summary collapse
- #find_rates(logger = nil) ⇒ Object
- #get_delivery_estimate ⇒ Object
- #get_fuel_surcharge_rate ⇒ Object
- #get_service_charges_hash(zone, package_arr) ⇒ Object
- #get_transportation_charges_hash(zone, package_arr, zip) ⇒ Object
- #label(return_label = false, logger = nil) ⇒ Object
- #void(tracking_number) ⇒ Object
Methods inherited from Base
#fedex, #initialize, #purolator, state_from_zip, #ups, #ups_freight
Constructor Details
This class inherits a constructor from Shipping::Base
Instance Method Details
#find_rates(logger = nil) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'app/services/shipping/speedee_delivery.rb', line 25 def find_rates(logger=nil) logger ||= Rails.logger @required = [:zip, :country, :sender_zip, :sender_country, :packages] @country ||= 'US' @sender_country ||= 'US' logger.debug "Shipping SpeedeeDelivery find_rates" rates = [] rate_estimates = [] successful, msg = nil res = get_delivery_estimate if res[:status] == :ok successful = true service_code = "SPEEDEE_DELIVERY" currency = "USD" estimate = Hash.new estimate[:carrier] = 'SpeedeeDelivery' estimate[:service_name] = 'Spee-Dee Delivery' estimate[:service_code] = service_code estimate[:price] = res[:price] estimate[:total_charges] = res[:price] estimate[:transportation_charges] = res[:transportation_charges] estimate[:service_options_charges] = res[:service_charges] rate_data = { base_price: res[:base_price], total_price: res[:price], carrier_name: service_code, days_in_transit: res[:days_in_transit], # we do NOT support COD for Speedee or any other carrier # cod_amount: res[:cod_amount], # cod_collection_type: res[:cod_collection_type], sort_code: res[:sort_code], signature_confirmation: res[:signature_confirmation], extra_charges_hash: res[:extra_charges_hash] } estimate[:rate_data] = rate_data estimate[:insured_value] = @insured_value.to_f.round(2) estimate[:currency] = currency # allows for things like estimate.service_code def estimate.method_missing(name, *args) has_key?(name) ? self[name] : super end #logger.info "!!!ups find_rates estimate: #{estimate.inspect}" rate_estimates << estimate else successful = false msg = res[:message] end response = Hash.new response[:success] = successful response[:message] = "SpeedeeDelivery: #{msg}" if msg.present? response[:request] = @data.to_s response[:xml] = @response.to_s response[:rates] = rate_estimates # allows for things like fedex.success? def response.method_missing(name, *args) has_key?(name) ? self[name] : super end return response end |
#get_delivery_estimate ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'app/services/shipping/speedee_delivery.rb', line 159 def get_delivery_estimate res = {} qualifies = true errs = [] zip = @zip[0..4].to_i unless @country == "US" qualifies = false errs << "Speedee Delivery does not ship outside the US" end speedee_service = SpeedeeService.by_zip(zip).first zone = SpeedeeZone.get_zone_from_zip(zip) unless speedee_service && zone qualifies = false errs << "Speedee Delivery does not ship to the destimation zip code: #{@zip}" end total_weight = 0.0 package_arr = [] @packages.each_with_index do |package, i| package_hash = {} package_hash[:length] = [package.inches(:length).to_f,0.1].max.round package_hash[:width] = [package.inches(:width).to_f,0.1].max.round package_hash[:height] =[package.inches(:height).to_f,0.1].max.round package_hash[:dimensional_length] = package_hash[:length] + 2.0*(package_hash[:width]+package_hash[:height]) package_hash[:weight] =[package.lbs.to_f,1.0].max.round package_hash[:declared_value] =package.value.to_f.round package_arr << package_hash total_weight += package_hash[:weight] end unless package_arr.all?{|ph| ph[:dimensional_length] <= DIMENSIONAL_LENGTH_MAX} qualifies = false errs << "Speedee Delivery package service does not ship packages with dimensional length (length plus girth) more than #{DIMENSIONAL_LENGTH_MAX} inches" end unless package_arr.all?{|ph| ph[:length] <= LENGTH_MAX} qualifies = false errs << "Speedee Delivery package service does not ship packages with length more than #{LENGTH_MAX} inches" end unless package_arr.all?{|ph| ph[:weight] <= WEIGHT_MAX} qualifies = false errs << "Speedee Delivery package service does not ship packages weighing more than #{WEIGHT_MAX} LBS" end if qualifies res[:status] = :ok transportation_charges_hash = get_transportation_charges_hash(zone, package_arr, zip) service_charges_hash = get_service_charges_hash(zone, package_arr) base_price = transportation_charges_hash[:total_charge] + service_charges_hash[:total_charge] total_price = (1.0 + get_fuel_surcharge_rate)*base_price.round(2) res[:base_price] = base_price res[:price] = total_price res[:service_charges] = service_charges_hash[:total_charge] res[:extra_charges_hash] = service_charges_hash.merge(transportation_charges_hash) res[:transportation_charges] = total_price - service_charges_hash[:total_charge] res[:days_in_transit] = speedee_service.days_in_transit_from_60047 # we do NOT support COD for Speedee or any other carrier # res[:cod_amount] = @cod_amount.to_f if @cod_amount.to_f > 0.0 # res[:cod_collection_type] = @cod_collection_type if @cod_amount.to_f > 0.0 res[:signature_confirmation] = @signature_confirmation res[:sort_code] = speedee_service.sort_code else res[:status] = :error res[:message] = errs.join(", ") end return res end |
#get_fuel_surcharge_rate ⇒ Object
267 268 269 270 |
# File 'app/services/shipping/speedee_delivery.rb', line 267 def get_fuel_surcharge_rate res = SpeedeeService.get_fuel_surcharge_rate res['rate'] end |
#get_service_charges_hash(zone, package_arr) ⇒ Object
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'app/services/shipping/speedee_delivery.rb', line 246 def get_service_charges_hash(zone, package_arr) total_charge = 0.0 declared_value_charge = 0.0 aod_charge = 0.0 # charge += COD_SURCHARGE if @cod_amount.to_f > 0.0 # we do NOT support COD for Speedee or any other carrier package_arr.each do |package_hash| declared_value_surcharge = 0.0 declared_value = [package_hash[:declared_value], DECLARED_VALUE_MAX].min # limit to the maximum allowed if declared_value > DECLARED_VALUE_MIN declared_value_surcharge = [((declared_value - DECLARED_VALUE_MIN)/100.0).ceil*DECLARED_VALUE_SURCHARGE_PER_100, DECLARED_VALUE_SURCHARGE_MIN].max declared_value_charge += declared_value_surcharge total_charge += declared_value_surcharge end if @signature_confirmation aod_charge += (SIGNATURE_CONFIRMATION_SURCHARGE) total_charge += (SIGNATURE_CONFIRMATION_SURCHARGE) end end {total_charge: total_charge, cod: 0.0, declared_value: declared_value_charge, aod: aod_charge} end |
#get_transportation_charges_hash(zone, package_arr, zip) ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'app/services/shipping/speedee_delivery.rb', line 223 def get_transportation_charges_hash(zone, package_arr, zip) total_charge = 0.0 oversized = 0.0 das_charged = 0.0 package_arr.each do |package_hash| das_charged += DAS_SURCHARGE if SpeedeeDasZip.where(zip: zip).first.present? # if delivering to zip in DAS zips, then add surchage if (package_hash[:dimensional_length] >= HIGH_DIMENSIONAL_LENGTH_THRESHOLD) # charge oversize fee if oversize via dimensional length oversized += OVERSIZE_SURCHARGE[zone.to_s.to_sym] || 0.0 das_charged += DAS_SURCHARGE_FOR_OVERSIZED if SpeedeeDasZip.where(zip: zip).first.present? # if delivering to zip in DAS zips, then add oversized surchage if oversized total_charge += oversized if (package_hash[:weight] >= HIGH_WEIGHT_THRESHOLD) # additionally, *also* charge from rate table if weight > threshold total_charge += SpeedeeRate.get_rate_from_weight_and_zone(package_hash[:weight], zone) end else # charge from rate table if not oversize total_charge += SpeedeeRate.get_rate_from_weight_and_zone(package_hash[:weight], zone) end end {total_charge: total_charge, oversized: oversized, das: das_charged} end |
#label(return_label = false, logger = nil) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'app/services/shipping/speedee_delivery.rb', line 93 def label(return_label=false, logger=nil) logger ||= Rails.logger @required = [:zip, :country, :sender_zip, :sender_country, :packages] @required += [:rate_data, :delivery_total_value] @country ||= 'US' @sender_country ||= 'US' check_required quoted_rate = ActiveSupport::HashWithIndifferentAccess.new(@rate_data || {}) # Here we will re-rate quote to ensure the shipping criteria and costs are not completely different than what was quoted. original_total_price = quoted_rate[:total_price].to_f estimate = nil new_total_price = nil # So let's re-find rates, but with the new criteria for the label estimate_res = find_rates if estimate_res[:success] # find the matching rate based on SCAC (if we have it) or Carrier Name estimate = estimate_res[:rates].first new_total_price = estimate&.dig(:rate_data, :total_price)&.to_f if new_total_price diff = (new_total_price - original_total_price).abs if @skip_rate_test || ((diff < COST_DISCREPANCY_THRESHOLD && diff/original_total_price < COST_DISCREPANCY_THRESHOLD_RATIO) || (@delivery_total_value.to_f > COST_DISCREPANCY_THRESHOLD * 10.0 && diff/@delivery_total_value.to_f < COST_DISCREPANCY_THRESHOLD_BY_TOTAL_VALUE_RATIO)) # we match and the price is proportionately close enough else ErrorReporting.warning("Threshold exceeded for SpeedeeDelivery label: original_total_price: #{original_total_price.round(2)}, new_total_price: #{new_total_price.round(2)}, difference: #{diff.round(2)}, original rate_data: #{@rate_data.inspect} new rate_data: #{estimate&.dig(:rate_data).inspect}") end end end logger.debug "Shipping SpeedeeDelivery @rate_data: #{@rate_data}" effective_rate_nested = estimate&.dig(:rate_data) || @rate_data rate_data = ActiveSupport::HashWithIndifferentAccess.new(effective_rate_nested || {}) response_array = [] @packages.each do |package| dim_len = ( [package.inches(:length).to_f,0.1].max.round + 2.0*([package.inches(:width).to_f,0.1].max.round+[package.inches(:height).to_f,0.1].max.round)) is_oversize = false is_oversize = true if dim_len >= HIGH_DIMENSIONAL_LENGTH_THRESHOLD begin # here we only get tracking numbers, will get labels from delivery and shipments response = Hash.new response[:tracking_number] = SpeedeeService.get_tracking_number(package.container_code, rate_data.merge({oversize: is_oversize})) rescue raise ShippingError, "could not generate SpeedeeDelivery labels" end # allows for things like ups.label.url def response.method_missing(name, *args) has_key?(name) ? self[name] : super end response_array << response end # don't allow people to edit the response response_array.freeze return {labels: response_array, shipment_identification_number: response_array.first.tracking_number, total_charges: new_total_price || original_total_price, rate_data: rate_data} end |
#void(tracking_number) ⇒ Object
154 155 156 157 |
# File 'app/services/shipping/speedee_delivery.rb', line 154 def void(tracking_number) # raise ShippingError, get_error if check if manifest sent, then too late return {:void_request_xml => "", :void_response_xml => ""} # if check if manifest not yet sent end |