Class: Shipping::ShipengineLtlBase
- Inherits:
-
ShipengineBase
- Object
- Base
- ShipengineBase
- Shipping::ShipengineLtlBase
- Defined in:
- app/services/shipping/shipengine_ltl_base.rb
Direct Known Subclasses
Constant Summary collapse
- COST_DISCREPANCY_THRESHOLD =
200.0- COST_DISCREPANCY_THRESHOLD_RATIO =
this is max ratio of discrepancy by total shipping cost
0.5- COST_DISCREPANCY_THRESHOLD_BY_TOTAL_VALUE_RATIO =
this is max ratio of discrepancy by total delivery value
0.05
Constants inherited from ShipengineBase
Shipping::ShipengineBase::MAX_LABEL_REFERENCE_LENGTH, Shipping::ShipengineBase::RATES_TIMEOUT_MS
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
- #label(_return_label = false, logger = nil) ⇒ Object
- #schedule_pickup(deliveries, datetime, logger = nil) ⇒ Object
Methods inherited from ShipengineBase
#find_rates, #get_separate_labels_per_package_from_label_pdf_url, #start_tracking, #track, #valid_address?, #void
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
#label(_return_label = false, logger = nil) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 92 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 |
# File 'app/services/shipping/shipengine_ltl_base.rb', line 9 def label(_return_label = false, logger = nil) logger ||= Rails.logger @required = [:shipengine_api_key] @required += %i[phone company address city state zip] @required += %i[sender_phone sender_email sender_company sender_address sender_city sender_state sender_zip] @required += %i[packages service_code] @required += [:billing_account] if %w[bill_third_party freight_collect].include?(@pay_type) @required += %i[billing_zip billing_country] if ['bill_third_party'].include?(@pay_type) @required += [:rate_data] @country ||= 'US' @sender_country ||= 'US' service_code_hash = service_code_map.detect { |h| h[:hw_service_code] == @service_code } || {} # deal with transitional back and forth to match service codes se_service_code = service_code_hash[:service_code] if se_service_code.nil? service_code_hash = service_code_map.detect { |h| h[:hw_service_code] == @service_code.split('SHIPENGINE_').last } || {} se_service_code = service_code_hash[:service_code] end if se_service_code.nil? service_code_hash = service_code_map.detect { |h| h[:hw_service_code] == "SHIPENGINE_#{@service_code}" } || {} se_service_code = service_code_hash[:service_code] end #################################################################################### quote_id = @rate_data['quote_id'] # quote id fallback will be original quote_id quote_expiration = @rate_data['quote_expiration'] # Here we may need to re-rate quote, because you need to buy a specific Shipengine LTL quote_id and all the relevant criteria like packages, weights, addresses and options have to match the quote id you are 'buying'. This pattern is different than our other carriers which give you an estimate and then you buy the label using the same (one hopes) criteria original_service_code = @rate_data['service_code'] || se_service_code if original_service_code.nil? # no match raise ShippingError, 'Cannot generate shipment no service_code found, please HOLD order and refresh shipping rates/methods' end original_total_price = @rate_data['total_price'].to_f # So let's re-find rates, but with the new criteria for the label estimate_res = find_rates # find the matching rate based on service code new_total_price = nil estimate = nil estimate = estimate_res[:rates].detect { |e| e.dig(:rate_data, :service_code) == original_service_code } if estimate.present? # we have a matching rate using the service code matching new_total_price = estimate.dig(:rate_data, :total_price).to_f if (new_total_price && original_total_price && ((diff = (new_total_price - original_total_price).abs) < COST_DISCREPANCY_THRESHOLD) && (diff / original_total_price < COST_DISCREPANCY_THRESHOLD_RATIO) ) || ((@delivery_total_value.to_f > 0.0) && (@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, so grab new quote id and carrier option id quote_id = estimate.dig(:rate_data, :quote_id) elsif @skip_rate_test # here we have an ST, and don't care about comparing rates, so take the matching rate quote_id = estimate.dig(:rate_data, :quote_id) end elsif quote_expiration && Date.current >= Date.parse(quote_expiration) raise ShippingError, 'Rate has expired, please HOLD order and refresh shipping rates/methods' end # no match or pricing threshold exceeded, test for quote expiration and let it go logger.debug "Shipping Freightquote @rate_data: #{@rate_data}" raise ShippingError, 'Missing data, please HOLD order and refresh shipping rates/methods' if quote_id.blank? #################################################################################### = logger.debug("Shipping #{carrier} label Request", carrier: carrier) res = get_label(quote_id, ) logger.debug("Shipping #{carrier} label Response", carrier: carrier) # { # "confirmation_number": "55667788", # "documents": [ # { # "format": "pdf", # "image": "JVBERi0xLjQKMSAwIG9iago8PAo...", # "type": "bill_of_lading" # } # ], # "message": null, # "pickup_id": "43f2a746-0f2c-4377-b30e-4d0a37c39444", # "pro_number": "1234578", # "pickup_id": "2292961b-c796-435f-9997-100725b1393a", # "quote_id": "4776018b-6b48-4d8f-8789-4e2d0610accc", # "warnings": [ # { # "external_code": "10000008", # "message": "Some of the accessorials submitted are not supported by the carrier and were not used." # } # ] # } msg = ([res.dig(:message)] + res.dig(:warnings).map { |w| "Code: #{w[:external_code]}, Message: #{w[:message]}" }).join(' ') raise ShippingError, msg unless res[:confirmation_number].present? && res[:pickup_id].present? && res[:documents].present? && res[:pro_number].present? total_price = new_total_price || original_total_price response = {} response[:tracking_number] = res[:pro_number] encoded_image = res.dig(:documents).detect { |doc| doc[:type] == 'bill_of_lading' }[:image] response[:bol_image] = Tempfile.new('bol') response[:bol_image].binmode response[:bol_image].write Base64.decode64(encoded_image) response[:bol_image].rewind response[:bol_image].flush response[:bol_image].fsync # allows for things like response.tracking_number? def response.method_missing(name, *args) has_key?(name) ? self[name] : super end { labels: [response], shipment_identification_number: res[:pro_number], carrier_bol: res[:confirmation_number], pickup_confirmation_number: res[:pickup_id], shipengine_label_id: res[:pickup_id], total_charges: total_price, ship_request_xml: .to_hash.inspect, ship_reply_xml: res.to_hash.inspect } end |
#schedule_pickup(deliveries, datetime, logger = nil) ⇒ Object
136 137 |
# File 'app/services/shipping/shipengine_ltl_base.rb', line 136 def schedule_pickup(deliveries, datetime, logger = nil) end |