Class: Shipping::LtlShippingInsurance
- Inherits:
-
BaseService
- Object
- BaseService
- Shipping::LtlShippingInsurance
- Defined in:
- app/services/shipping/ltl_shipping_insurance.rb
Overview
Service object: ltl shipping insurance.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- LOADSURE_API_HASH =
Loadsurance integration is currently disabled. When re-enabled, fetch
credentials fromHeatwave::Configuration.fetch(:loadsurance_api)
rather than re-introducing top-level LOADSURE_* constants. {}.freeze
- INSURED_VALUE_MIN =
Insured value min.
5000.0- INSURANCE_FEE_MAX_PERCENT =
Insurance fee max percent.
0.05- INSURANCE_FEE_MAX_AMOUNT =
Insurance fee max amount.
250.0- INSURANCE_DED_MAX_PERCENT =
Insurance ded max percent.
0.2- INSURANCE_DED_MAX_AMOUNT =
Insurance ded max amount.
1000.0- INSURANCE_FEE_MIN_AMOUNT =
Insurance fee min amount.
5.0- LOADSURANCE_CARRIER_DATA_BY_CARRIER =
here we only populate parcel carriers that we know are covered by our Shipsurance contract
we do not cover freight carriers per Shipsurance contract
see: https://shipsurance-partner-api.readme.io/reference/extcarrierid-lookup { FedExFreight: { mode: 'ROAD', name: 'FedEx Freight', email: 'mark.lackinger@fedex.com', carrierId: { type: 'SCAC', value: 'FXFS' }, phone: '+12242422831', address: { address1: '5600 9TH ST', city: 'Zion', state: 'IL', postal: '60099', country: 'US' } }, Freightquote: { mode: 'ROAD', name: 'Freightquote.com Inc', email: 'brian.dougherty@freightquote.com', carrierId: { type: 'SCAC', value: 'FQCI' }, phone: '+18169496250', address: { address1: '901 West Carondelet Drive', city: 'Kansas City', state: 'MO', postal: '64114', country: 'US' } }, RlCarriers: { mode: 'ROAD', name: 'R&L Carriers', email: 'scott.brighton@rlcarriers.com', carrierId: { type: 'SCAC', value: 'RLCA' }, phone: '+18476953100', address: { address1: '375 Second Street', city: 'Elgin', state: 'IL', postal: '60123', country: 'US' } }, ShipengineRoadrunner: { mode: 'ROAD', name: 'Roadrunner Freight', email: 'robert.provost@rrts.com', carrierId: { type: 'SCAC', value: 'RDFS' }, phone: '+18478007768', address: { address1: '4801 68th Avenue', city: 'Kenosha', state: 'WI', postal: '53144', country: 'US' } }, ShipengineSaia: { mode: 'ROAD', name: 'Saia LTL Freight', email: 'ssanti@saia.com', carrierId: { type: 'SCAC', value: 'SAIA' }, phone: '+18474716588', address: { address1: '2260 Midlothian Road', city: 'Grayslake', state: 'IL', postal: '60030', country: 'US' } } }.then do |h| # Legacy carrier-string aliases for historical shipments.carrier rows # (Freightquote-brokered Saia/Roadrunner from before the direct # ShipEngine LTL integration). Kept so label regen / insurance lookups # by old `delivery.carrier` strings still resolve. Loadsurance # integration is currently disabled, so this is defensive only. h.merge(Saia: h[:ShipengineSaia], Roadrunner: h[:ShipengineRoadrunner]).freeze end
- COVERAGE_LIMIT =
Limit for coverage.
25_000.0- LTL_SELF_INSURED =
Per Christian Billen and Venu Adepu (1/30/2025): WarmlyYours self-insures all LTL freight
after 25+ years of zero claims or losses. When true this constant drives:- qualifies_for_rating? → false (hides Shipsurance/Loadsure UI section)
- do_not_ship_insure_via_carrier? → true (zeros carrier declared value)
- shipengine_ltl_base insured_value hardcoded to 0.0
Cross-border customs/commercial invoice values (calculate_declared_value,
unit_value_for_commercial_invoice) are NOT affected — those are separate concerns.
true
Instance Attribute Summary
Attributes inherited from BaseService
Instance Method Summary collapse
- #bill_shipping_to_customer_and_not_a_store_transfer?(delivery) ⇒ Boolean
- #book_shipping_insurance_for_delivery(delivery, _options) ⇒ Object
- #carrier_present?(carrier) ⇒ Boolean
- #carrier_qualifies_for_rating?(carrier) ⇒ Boolean
- #cross_border_fedex_freight?(delivery) ⇒ Boolean
- #delivery_carrier_present?(delivery) ⇒ Boolean
- #get_shipping_insurance_cost_for_delivery(delivery) ⇒ Object
- #get_shipping_insurance_cost_for_shipment(shipment) ⇒ Object
- #get_shipping_insurance_insured_value_for_delivery(delivery) ⇒ Object
- #get_shipping_insurance_insured_value_for_shipment(shipment) ⇒ Object
- #get_shipping_insurance_link_for_shipment(shipment) ⇒ Object
- #process(delivery, _options = {}) ⇒ Object
- #qualifies(_delivery) ⇒ Object
- #qualifies_for_rating?(delivery) ⇒ Boolean
- #void_shipping_insurance_for_delivery(delivery) ⇒ Object
Methods inherited from BaseService
#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #tagged_logger
Constructor Details
This class inherits a constructor from BaseService
Instance Method Details
#bill_shipping_to_customer_and_not_a_store_transfer?(delivery) ⇒ Boolean
163 164 165 166 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 163 def bill_shipping_to_customer_and_not_a_store_transfer?(delivery) # if billing to customer, let them pay for declared value via carrier delivery.bill_shipping_to_customer && !delivery.order&.is_store_transfer? # this is to be readable to me, a human end |
#book_shipping_insurance_for_delivery(delivery, _options) ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 211 def book_shipping_insurance_for_delivery(delivery, ) status = :ok = '' insurance_result_hash = {} quote_hash = HTTP.use(logging: { logger: Rails.logger }).timeout(60).auth("Bearer #{LOADSURE_API_HASH[:token]}").post("#{LOADSURE_API_HASH[:base_url]}/quote", json: quote_data(delivery)).parse.with_indifferent_access if quote_hash[:success] == false msgs = (quote_hash[:errors] || []).map { |e| e.dig(:message) } status = :error = msgs.join('. ') else quote_cost = quote_hash.dig(:insuranceProduct, :premium).to_f + quote_hash.dig(:insuranceProduct, :serviceFee).to_f quote_deductible = quote_hash.dig(:insuranceProduct, :deductible).to_f threshhold_exceeded_hash = insurance_quote_exceeds_thresholds?(quote_cost, apply_carrier_coverage_limit(delivery), quote_deductible) quote_token = quote_hash.dig(:quoteToken) if threshhold_exceeded_hash[:status] != :ok status = threshhold_exceeded_hash[:status] = threshhold_exceeded_hash[:status_message] elsif quote_token.present? insurance_result_hash = HTTP.use(logging: { logger: Rails.logger }).timeout(60).auth("Bearer #{LOADSURE_API_HASH[:token]}").post("#{LOADSURE_API_HASH[:base_url]}/purchaseQuote", json: { quoteToken: quote_token, additionalAuthorizedUsers: [LOADSURE_API_HASH[:additional_user_id]] }).parse.with_indifferent_access if insurance_result_hash[:error].present? status = :error = insurance_result_hash[:error] end end end delivery.shipments.update_all(shipping_insurance_data: { shipping_insurance_record_id: insurance_result_hash[:certificateNumber] }.merge(insurance_result_hash)) if status == :ok Result.new(status: status, status_message: ) end |
#carrier_present?(carrier) ⇒ Boolean
173 174 175 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 173 def carrier_present?(carrier) LOADSURANCE_CARRIER_DATA_BY_CARRIER[(carrier || 'none').to_sym].present? end |
#carrier_qualifies_for_rating?(carrier) ⇒ Boolean
181 182 183 184 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 181 def (carrier) # here we want to return a simple TRUE/FALSE boolean, this is to see if we can use this carrier_present?(carrier) end |
#cross_border_fedex_freight?(delivery) ⇒ Boolean
168 169 170 171 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 168 def cross_border_fedex_freight?(delivery) # we do not ship insure cross border FedEx Freight via Loadsurance because we need to pass declared value delivery.carrier == 'FedExFreight' && delivery.is_cross_border? # this is to be readable to me, a human end |
#delivery_carrier_present?(delivery) ⇒ Boolean
177 178 179 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 177 def delivery_carrier_present?(delivery) carrier_present?(delivery.carrier) end |
#get_shipping_insurance_cost_for_delivery(delivery) ⇒ Object
259 260 261 262 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 259 def get_shipping_insurance_cost_for_delivery(delivery) delivery.shipments.first&.shipping_insurance_data&.dig('premium').to_f # cost = delivery.shipments.where("carrier IS NOT NULL").sum{|s| get_shipping_insurance_cost_for_shipment(s)} end |
#get_shipping_insurance_cost_for_shipment(shipment) ⇒ Object
264 265 266 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 264 def get_shipping_insurance_cost_for_shipment(shipment) (get_shipping_insurance_cost_for_delivery(shipment.delivery) / [shipment.delivery.shipments.size, 1].max).round(2) end |
#get_shipping_insurance_insured_value_for_delivery(delivery) ⇒ Object
268 269 270 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 268 def get_shipping_insurance_insured_value_for_delivery(delivery) delivery.calculate_declared_value.to_f.abs end |
#get_shipping_insurance_insured_value_for_shipment(shipment) ⇒ Object
272 273 274 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 272 def get_shipping_insurance_insured_value_for_shipment(shipment) (shipment.delivery.calculate_declared_value.to_f.abs / [shipment.delivery.shipments.size, 1].max).round(2) end |
#get_shipping_insurance_link_for_shipment(shipment) ⇒ Object
276 277 278 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 276 def get_shipping_insurance_link_for_shipment(shipment) %(<a href='https://portal.loadsure.net/certificates/details/?certificateNumber=#{shipment.shipping_insurance_record_id}' target='_blank' rel='noopener'>#{shipment.shipping_insurance_record_id}</a> <small>(file claim <a href='https://portal.loadsure.net/claims/file?id=#{shipment.shipping_insurance_record_id}' target='_blank' rel='noopener' onclick='return confirm("Are you sure you want to file a claim?")'>here</a>)</small>).html_safe end |
#process(delivery, _options = {}) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 138 def process(delivery, = {}) status = :ok = 'WarmlyYours Self Insures all LTL Shipping' qualifies(delivery) # 03/18/24 Here we have decided per Venu and Christian to discontinue our Shipping Insurance with Loadsure, and self-insure after seeing no record of any claims or losses for LTL shipments in our history. # So we want to qualify all LTL shipments i.e. return TRUE, so we don't pass declared value to the LTL carriers, but then do nothing when it is ship insurance time # if qualifies_res.status == :ok # r = book_shipping_insurance_for_delivery(delivery, options) # unless r.status == :ok # status = :error # status_message = "Shipping insurance for delivery #{delivery.name} cannot be booked: #{r.status_message}" # end # else # status = qualifies_res.status # status_message = qualifies_res.status_message # end Result.new(status: status, status_message: ) end |
#qualifies(_delivery) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 186 def qualifies(_delivery) # status = :ok # status_message = '' # any_labeled_shipments = delivery.shipments.label_complete.any? # all_labeled_shipments_have_tracking_number = delivery.shipments.label_complete.all?{|s| s.tracking_number.present?} # unless qualifies_for_rating?(delivery) && any_labeled_shipments && all_labeled_shipments_have_tracking_number # status = :error # status_message_arr = [] # status_message_arr << "Order #{delivery.order&.reference_number} does not qualify for Loadsurance shipping insurance" # status_message_arr << "Carrier #{delivery.carrier} is not one of our covered Loadsurance carriers" if !delivery_carrier_present?(delivery) # status_message_arr << "When customer pays for shipping we do not ship insure via Loadsurance" if bill_shipping_to_customer_and_not_a_store_transfer?(delivery) # status_message_arr << "When delivery value is less than $#{INSURED_VALUE_MIN.round(2)} we do not ship insure via Loadsurance" if delivery.calculate_declared_value.to_f.abs < INSURED_VALUE_MIN # status_message_arr << "When delivery is FedExFreight cross-border, we do not ship insure via Loadsurance" if cross_border_fedex_freight?(delivery) # status_message_arr << "Delivery does not have any labeled shipments" if !any_labeled_shipments # status_message_arr << "Not all shipments have tracking numbers" if !all_labeled_shipments_have_tracking_number # status_message = "#{status_message_arr.join('. ')}." # end ############################################################################ # Note that we are disabling LTL shipping insurance for all deliveries based on price increases, lack of claims and an executive decision by Christian Billen and Venu Adepu on 1/30/2025 ############################################################################ status = :error = 'LTL shipping insurance is now disabled.' Result.new(status: status, status_message: ) end |
#qualifies_for_rating?(delivery) ⇒ Boolean
157 158 159 160 161 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 157 def (delivery) return false if LTL_SELF_INSURED delivery.supported_shipping_carrier? && delivery_carrier_present?(delivery) && (delivery.calculate_declared_value.to_f.abs > INSURED_VALUE_MIN) && !cross_border_fedex_freight?(delivery) && !bill_shipping_to_customer_and_not_a_store_transfer?(delivery) end |
#void_shipping_insurance_for_delivery(delivery) ⇒ Object
244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'app/services/shipping/ltl_shipping_insurance.rb', line 244 def void_shipping_insurance_for_delivery(delivery) status = :ok = '' void_result_hash = HTTP.use(logging: { logger: Rails.logger }).timeout(60).auth("Bearer #{LOADSURE_API_HASH[:token]}").post("#{LOADSURE_API_HASH[:base_url]}/cancelCertificate", json: void_data(delivery)).parse.with_indifferent_access if void_result_hash[:success] == false msgs = (void_result_hash[:errors] || []).map { |e| e.dig(:message) } status = :error = msgs.join('. ') end delivery.shipments.update_all(shipping_insurance_data: {}.merge(void_result_hash)) if status == :ok Result.new(status: status, status_message: ) end |