Module: Models::ShipQuotable
- Extended by:
- ActiveSupport::Concern
- Includes:
- ShipMeasurable
- Defined in:
- app/concerns/models/ship_quotable.rb
Overview
ActiveSupport::Concern mixin: ship quotable.
Belongs to collapse
Has many collapse
Instance Method Summary collapse
- #carrier ⇒ Object
- #chosen_shipping_method ⇒ Object
- #days_commitment(use = :least) ⇒ Object
- #determine_origin_address(line_item) ⇒ Object
- #everything_in_stock? ⇒ Boolean
- #friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) ⇒ Object
- #is_drop_ship? ⇒ Boolean
- #is_override? ⇒ Boolean
- #is_warehouse_pickup? ⇒ Boolean
- #line_items_grouped_by_deliveries_quoting(group_by_item_category = true) ⇒ Object
- #line_items_match_deliveries_if_any ⇒ Object
- #need_to_pre_pack_reasons ⇒ Object
- #need_to_recalculate_shipping ⇒ Object
- #one_time_shipping_address ⇒ Object
- #one_time_shipping_address=(val) ⇒ Object
- #qualifies_for_cod? ⇒ Boolean
-
#refresh_deliveries_quoting(include_shipping_lines = false) ⇒ Object
include shipping line used mostly to convert for quote => order conversion so we preserve.
- #reset_deliveries_shipping_option ⇒ Object
- #reset_shipping(message = nil, autosave = true) ⇒ Object
- #retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) ⇒ Object
-
#retrieve_shipping_costs(rate_ship_date: nil) ⇒ Object
Retrieve shipping costs for all deliveries.
- #ship_quoted ⇒ Object
- #ship_weight ⇒ Object
- #shipping_non_db_default_but_db_customer? ⇒ Boolean
- #shipping_signature_confirmation_non_db_default_but_db_customer? ⇒ Boolean
- #shipping_via_wy_but_has_shipping_account? ⇒ Boolean
- #ships_economy_package? ⇒ Boolean
- #ships_freight? ⇒ Boolean
- #ships_freight_but_address_not_freight_ready? ⇒ Boolean
- #should_ship_freight? ⇒ Boolean
- #should_ship_freight_but_address_not_freight_ready? ⇒ Boolean
- #validate_deliveries ⇒ Object
Methods included from ShipMeasurable
#cartons_total, #crates_total, #pallets_total, #ship_freight_class_from_shipments, #ship_volume_from_shipments, #ship_volume_from_shipments_in_cubic_feet, #ship_weight_from_shipments, #shipment_set, #shipments_for_measure
Instance Method Details
#carrier ⇒ Object
361 362 363 |
# File 'app/concerns/models/ship_quotable.rb', line 361 def carrier deliveries.first&.carrier end |
#chosen_shipping_method ⇒ Object
357 358 359 |
# File 'app/concerns/models/ship_quotable.rb', line 357 def chosen_shipping_method deliveries.first&.chosen_shipping_method end |
#days_commitment(use = :least) ⇒ Object
336 337 338 339 340 341 342 |
# File 'app/concerns/models/ship_quotable.rb', line 336 def days_commitment(use = :least) if use == :least deliveries.map { |dq| dq.selected_shipping_cost&.days_commitment || 3 }.min else deliveries.map { |dq| dq.selected_shipping_cost&.days_commitment || 3 }.max end end |
#deliveries ⇒ ActiveRecord::Relation<Delivery>
12 |
# File 'app/concerns/models/ship_quotable.rb', line 12 has_many :deliveries, -> { order(:origin_address_id) }, inverse_of: name.tableize.singularize.to_sym, autosave: true, dependent: :destroy |
#determine_origin_address(line_item) ⇒ Object
115 116 117 118 119 120 |
# File 'app/concerns/models/ship_quotable.rb', line 115 def determine_origin_address(line_item) # credit orders should always use the shipping address of the warehouse, in case one of the items was previously shipped from a different location, and STs should always use the shipping address of the store warehouse pending a better solution to multiple warehouse stores (like Amazon) return store.warehouse_address if is_a?(Order) && ((order_type == 'CO') || (order_type == 'ST')) shipping_address.try(:is_warehouse) || single_origin ? store.warehouse_address : line_item.fulfillment_origin_address end |
#everything_in_stock? ⇒ Boolean
344 345 346 |
# File 'app/concerns/models/ship_quotable.rb', line 344 def everything_in_stock? LineItem.inventory_check(self) == :ok end |
#friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) ⇒ Object
315 316 317 318 319 320 321 |
# File 'app/concerns/models/ship_quotable.rb', line 315 def friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) if show_customer_pays_info @friendly_shipping_method_with_pay_info ||= retrieve_friendly_shipping_method(show_customer_pays_info, for_www, with_delivery_commitment) else @friendly_shipping_method ||= retrieve_friendly_shipping_method(show_customer_pays_info, for_www, with_delivery_commitment) end end |
#is_drop_ship? ⇒ Boolean
130 131 132 |
# File 'app/concerns/models/ship_quotable.rb', line 130 def is_drop_ship? one_time_shipping_address.present? end |
#is_override? ⇒ Boolean
134 135 136 |
# File 'app/concerns/models/ship_quotable.rb', line 134 def is_override? deliveries.any?(&:override_shipping_method?) end |
#is_warehouse_pickup? ⇒ Boolean
406 407 408 |
# File 'app/concerns/models/ship_quotable.rb', line 406 def is_warehouse_pickup? deliveries.any?(&:is_warehouse_pickup?) end |
#line_items_grouped_by_deliveries_quoting(group_by_item_category = true) ⇒ Object
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'app/concerns/models/ship_quotable.rb', line 386 def line_items_grouped_by_deliveries_quoting(group_by_item_category = true) items = {} # group by origin address (warehouse pickups all have the same origin/destination address) deliveries.quoting.to_a.sort_by(&:origin_address_id).each do |dq| items[dq] ||= [] # Required to set order end line_items.parents_only.non_shipping.includes([{ catalog_item: { store_item: :item } }, :item, :delivery, { children: :item }]).to_a.each do |li| dq = li.delivery items[dq] ||= [] items[dq] << li if dq.nil? && Rails.env.production? logger.error "ship_quotable#line_items_grouped_by_deliveries_quoting had non shipping line item(s) with delivery nil #{self.class} ID: #{id}, line item ID: #{li.id}, Line Item:\n#{li.inspect}" # Mailer.admin_notification("ship_quotable#line_items_grouped_by_deliveries_quoting had non shipping line item(s) with delivery nil #{self.class} ID: #{self.id}, line item ID: #{li.id}","Line Item:\n#{li.inspect}").deliver_now end end items.each { |key, lines| items[key] = LineItem.group_by_category(lines) } if group_by_item_category items end |
#line_items_match_deliveries_if_any ⇒ Object
416 417 418 419 420 421 422 423 424 425 426 |
# File 'app/concerns/models/ship_quotable.rb', line 416 def line_items_match_deliveries_if_any res = true quoting_deliveries = deliveries.select?(&:quoting?) quoting_deliveries_line_items = quoting_deliveries.map { |dq| dq.line_items.active_goods_lines }.flatten if quoting_deliveries.present? && (md5_hash_items != md5_hash_items(quoting_deliveries_line_items)) res = false errors.add(:base, "line items don't match all deliveries.select{|d| d.state == 'quoting'} line items") end res end |
#need_to_pre_pack_reasons ⇒ Object
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
# File 'app/concerns/models/ship_quotable.rb', line 432 def need_to_pre_pack_reasons res = [] weight_key = :package weight_key = :ltl if deliveries.any?(&:ships_ltl_freight?) # return [] if all shipments packed return res if deliveries.all? { |d| d.shipments.all?(&:packed?) } # return [] if we are dealing with a single pallet delivery of less than 400lbs, even a legacy suggested pallet is a pretty decent guess, and skips a lot of back and forth on quote revisions or tweaks of some accessories etc, no need to bother warehouse return res if deliveries.size == 1 && deliveries.first.shipments.size == 1 && deliveries.first.shipments.first.pallet? && (ship_weight <= PRE_PACK_SHIPPING_WEIGHT_THRESHOLD[weight_key]) # Skip pre-pack only when EVERY delivery's packing comes from a trusted source — # actual history (from_delivery / from_item / from_manual_entry) or a modern # parcel calculator (packing_calculator / item_group_packaging) computed from # real item shipping dimensions. The freight fallback (`:freight_calculator`, # formerly `:legacy_packaging`) stays OUT of the trusted list because its # density-weighted approximation is less reliable for billing — those orders # still get the pre_pack reason check below. packing_solutions = deliveries.map { |d| Shipping::DeterminePackaging.new.process(delivery: d, is_freight: d.ships_ltl_freight?) } return res if packing_solutions.all? { |packing_solution| packing_solution.present? && %w[from_delivery from_item from_manual_entry packing_calculator item_group_packaging].include?(packing_solution.source_type.to_s) } # eliminate any custom or drop-ship items (e.g. custom LED mirrors or custom mats) since our warehouse won't know how to estimate packaging, better to use specialized suggested shipping if line_items.none?(&:dropship?) && !is_warehouse_pickup? && ((calculate_shipping_cost > PRE_PACK_SHIPPING_COST_THRESHOLD) || (ship_weight > PRE_PACK_SHIPPING_WEIGHT_THRESHOLD[weight_key]) || (days_commitment < PRE_PACK_DAYS_COMMITMENT_THRESHOLD) || # tweak to pair too many packages criteria with a high enough shipping cost deliveries.any? do |d| d.shipments.count > PRE_PACK_NUM_PACKAGES_THRESHOLD && calculate_shipping_cost > 0.5 * PRE_PACK_SHIPPING_COST_THRESHOLD end) res << "Shipping cost of $#{calculate_shipping_cost.round(2)} is too high: exceeds current threshold of $#{PRE_PACK_SHIPPING_COST_THRESHOLD.round(2)} for suggested packaging" if calculate_shipping_cost > PRE_PACK_SHIPPING_COST_THRESHOLD res << "Shipping weight of #{ship_weight.round} lbs is too high: exceeds current threshold of #{PRE_PACK_SHIPPING_WEIGHT_THRESHOLD[weight_key].round} lbs for suggested packaging" if ship_weight > PRE_PACK_SHIPPING_WEIGHT_THRESHOLD[weight_key] res << 'Expedited/Express shipping speed needs accurate non-suggested packaging' if days_commitment < PRE_PACK_DAYS_COMMITMENT_THRESHOLD && 2 * calculate_shipping_cost > PRE_PACK_SHIPPING_COST_THRESHOLD res << "Too many suggested packages: exceeds current threshold of #{PRE_PACK_NUM_PACKAGES_THRESHOLD}" if deliveries.any? do |d| d.shipments.count > PRE_PACK_NUM_PACKAGES_THRESHOLD && calculate_shipping_cost > 0.5 * PRE_PACK_SHIPPING_COST_THRESHOLD end end res end |
#need_to_recalculate_shipping ⇒ Object
327 328 329 330 |
# File 'app/concerns/models/ship_quotable.rb', line 327 def need_to_recalculate_shipping self.recalculate_shipping = true self.do_not_detect_shipping = false end |
#one_time_shipping_address ⇒ Object
126 127 128 |
# File 'app/concerns/models/ship_quotable.rb', line 126 def one_time_shipping_address shipping_address if shipping_address&.one_time_only end |
#one_time_shipping_address=(val) ⇒ Object
122 123 124 |
# File 'app/concerns/models/ship_quotable.rb', line 122 def one_time_shipping_address=(val) self.shipping_address = val end |
#qualifies_for_cod? ⇒ Boolean
348 349 350 351 352 353 354 355 |
# File 'app/concerns/models/ship_quotable.rb', line 348 def qualifies_for_cod? # we limit COD orders to single delivery origin domestic orders from one of our warehouses true if (begin deliveries.quoting.one? && deliveries.quoting.first.origin_address.is_warehouse? && (deliveries.quoting.first.origin_address.country_iso3 == deliveries.quoting.first.destination_address.country_iso3) rescue StandardError false end) && line_items.goods.any? end |
#refresh_deliveries_quoting(include_shipping_lines = false) ⇒ Object
include shipping line used mostly to convert for quote => order conversion so we preserve
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 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'app/concerns/models/ship_quotable.rb', line 159 def refresh_deliveries_quoting(include_shipping_lines = false) logger.tagged "#{self.class.name}/#{id}: ShipQuotable#refresh_deliveries_quoting" do logger.debug('refresh_deliveries_quoting', include_shipping_lines: include_shipping_lines, shipping_address_id: shipping_address_id, quoting_delivery_count: deliveries.count { |d| d.state == 'quoting' }) return [] unless shipping_address.present? || installation_postal_code.present? # just skip if no shipping address or installation postal code cod_collection_type_to_use = nil if respond_to?(:cod_collection_type) self.cod_collection_type = nil unless qualifies_for_cod? cod_collection_type_to_use = cod_collection_type end # Reload deliveries from DB so we see any deliveries created by a concurrent process. # Without this, a stale in-memory association can miss deliveries, leaving orphan # duplicates that each carry their own shipping charge (see quote 601367). deliveries.reload unless new_record? # Keep a reference to the old delivery quotes now we will delete them later # Keep track of the old delivery quotes attributes, they will be used to merge into the new one. # IMPORTANT: ensure we skip the "id" attribute or it will run afoul of the unique delivery_id_idx constraint! quoting_deliveries = deliveries.select { |d| (d.quoting? || d.pre_pack?) && !d.marked_for_destruction? && d.persisted? } # Remapped object arrays remapped_pre_payment_ids = [] remapped_shipment_ids = [] old_override_attributes = [] old_deliveries_quoting_attributes = quoting_deliveries.map do |dq| remapped_pre_payment_ids += dq.payments.ids remapped_shipment_ids += dq.shipments.packed.ids unless dq.is_smart_service? if dq.selected_shipping_cost.present? && dq.selected_shipping_cost.is_override? && !dq.is_warehouse_pickup? # Here we want to preserve a manually selected override, but not warehouse pickups old_override_attributes << { origin_address_id: dq.origin_address_id, cost: dq.selected_shipping_cost.cost, description_override: dq.selected_shipping_cost.description_override } end # Dupe removes the id which is what you want if dq.order&.shipment_reference_number.blank? # Here we want to copy the shipment reference (or FBA ID) to carrier_bol when present dq.dup.attributes.merge(carrier_bol: dq.order&.shipment_reference_number) else dq.dup.attributes end end if remapped_pre_payment_ids.empty? && respond_to?(:payment_ids) && payment_ids.present? # Let's just assume each payment gets mapped payment_ids.each { |pid| remapped_pre_payment_ids << pid } end remapped_pre_payment_ids.uniq! remapped_shipment_ids.uniq! # Unmap those pre payments and shipments from their original deliveries from a db point of view Payment.where(id: remapped_pre_payment_ids).update_all(delivery_id: nil) if remapped_pre_payment_ids.present? Shipment.where(id: remapped_shipment_ids).update_all(delivery_id: nil) if remapped_shipment_ids.present? # Kill the quoting deliveries! cascading foreign key might not do things in the right order, so manually # do it quoting_deliveries.each do |qd| logger.debug('Removing quoting delivery', delivery_id: qd.id) next unless qd.id && (qd = Delivery.where(id: qd.id).first).present? # Fresh associations if found qd.shipments.destroy_all # We disassociated earlier the one that matters; destroy_all runs Shipment#destroy so shipment_events nullify (FK is RESTRICT) qd.shipping_costs.each do |sc| sc.line_items.delete_all sc.delete end qd.delete end logger.debug('Processing deliveries', old_delivery_count: old_deliveries_quoting_attributes.size) if shipping_address || installation_postal_code if include_shipping_lines line_items_to_use = line_items.active_lines else line_items_to_use = line_items.active_non_shipping_lines # IMPORTANT, if you use scope you will lose any line items that have not yet been saved line_items.active_shipping_lines.each(&:destroy) end # IMPORTANT: Only process line items that are NOT already assigned to a non-quoting delivery. # This prevents orphaning deliveries that have already transitioned to awaiting_po_fulfillment # or other non-quoting states (which may have POs or other commitments). non_quoting_delivery_ids = deliveries.reject { |d| d.quoting? || d.pre_pack? || d.new_record? }.map(&:id) if non_quoting_delivery_ids.any? line_items_to_use = line_items_to_use.reject { |li| non_quoting_delivery_ids.include?(li.delivery_id) } logger.debug('Filtered out line items already in non-quoting deliveries', non_quoting_delivery_ids: non_quoting_delivery_ids, remaining_count: line_items_to_use.size) end logger.debug('Line items to process', count: line_items_to_use.size) # Group our line items by delivery origin address line_items_to_use.to_a.group_by { |li| determine_origin_address(li) }.each do |fulfillment_origin_address, grouped_line_items| logger.debug('Processing origin address group', origin_address_id: fulfillment_origin_address.id, line_item_count: grouped_line_items.size) # here we try to preserve the same attributes if the delivery quotes are from the same origin matched_old_delivery_quoting_attributes = old_deliveries_quoting_attributes.find do |dqa| (dqa['origin_address_id'].to_i == fulfillment_origin_address.id) || ((dqa['origin_address_id'].to_i == 1) && fulfillment_origin_address.is_warehouse?) end matched_old_delivery_quoting_attributes ||= {} logger.debug('Matched old delivery attributes', matched: matched_old_delivery_quoting_attributes.present?) dq_attributes = matched_old_delivery_quoting_attributes.merge( state: 'quoting', # here we force state quoting, overrides pre_pack supplier_id: fulfillment_origin_address.party_id, origin_address_id: fulfillment_origin_address.id, bill_shipping_to_customer: customer.bill_shipping_to_customer, do_not_recalculate: false, cod_collection_type: cod_collection_type_to_use ) # handle signature_confirmation and saturday_delivery options, assume nil means default ie unselected manually as false or true dq_attributes[:signature_confirmation] = signature_confirmation if dq_attributes[:signature_confirmation].nil? dq_attributes[:saturday_delivery] = saturday_delivery if dq_attributes[:saturday_delivery].nil? delivery = deliveries.build(dq_attributes) unless shipping_address.nil? || (installation_postal_code && try(:is_www_ship_by_zip)) if shipping_address.new_record? delivery.destination_address = shipping_address # A new one so we have to assign this way and not rely on shipping_address_id else delivery.destination_address_id = shipping_address.id # Not a new one so we assign using shipping_address_id end end grouped_line_items.each { |li| li.delivery = delivery } delivery.line_items = grouped_line_items delivery.save! # here we try to preserve the same override shipping cost attributes if the delivery quotes are from the same origin matched_override_attributes = old_override_attributes.find do |oa| (oa[:origin_address_id] == fulfillment_origin_address.id) || ((oa[:origin_address_id] == 1) && fulfillment_origin_address.is_warehouse?) end if matched_override_attributes # we have something, so create a shipping cost with these attributes so = ShippingOption.where(name: 'override', country: delivery.resource.store.country.iso).first shpcst = ShippingCost.new(shipping_option: so, cost: matched_override_attributes[:cost], description_override: matched_override_attributes[:description_override]) delivery.shipping_costs << shpcst # Update both selected_shipping_cost_id AND shipping_option_id to preserve the override # This ensures the override is fully restored after the delivery is recreated delivery.update_columns(selected_shipping_cost_id: shpcst.id, shipping_option_id: so.id) end # Remap our unmapped payments and shipments! doing it this way so we get an audit trail Payment.where(id: remapped_pre_payment_ids).find_each { |pp| pp.update_attribute!(:delivery_id, delivery.id) } if remapped_pre_payment_ids.present? Shipment.where(id: remapped_shipment_ids).find_each { |shp| shp.update_attribute!(:delivery_id, delivery.id) } if remapped_shipment_ids.present? && !delivery.is_smart_service? if remapped_shipment_ids.present? && delivery.quantities_remaining_to_allocate > 0 && delivery.delta_weight_factor_remaining_to_allocate > DELTA_WEIGHT_FACTOR_THRESHOLD_TO_REPACK_LTL # && delivery.delta_volume_factor_remaining_to_allocate > DELTA_VOLUME_FACTOR_THRESHOLD_TO_REPACK_LTL) # We want to make these unpacked since delivery items have changed and enough are unallocated # Here we do not unpack LTL freight if the delta_weight_remaining_to_allocate is less than DELTA_WEIGHT_FACTOR_THRESHOLD_TO_REPACK, because pallets do not change even if we change a lot items, and all weights are measured before ship labeling. Shipment.where(id: remapped_shipment_ids).find_each(&:unpack) end logger.debug('Created delivery', delivery_id: delivery.id, line_item_count: delivery.line_items.size) end end end deliveries.quoting end |
#reset_deliveries_shipping_option ⇒ Object
107 108 109 110 111 112 113 |
# File 'app/concerns/models/ship_quotable.rb', line 107 def reset_deliveries_shipping_option # this could be called on a before_save callback so don't assume arive_record associations or save, just reset all deliveries shipping_option field to nil. This is so that on say a change of shipping address, we don't inherit the previous delivery's shipping option when recalculating shipping, like we might on a change of items, but fall back to customer's default. Rails.logger.debug { "reset_deliveries_shipping_option called for shipquotable: #{self.class} #{id}, #{reference_number}" } deliveries.each do |d| d.shipping_option = nil end end |
#reset_shipping(message = nil, autosave = true) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'app/concerns/models/ship_quotable.rb', line 93 def reset_shipping( = nil, autosave = true) ||= 'Shipping was reset' self.shipping_cost = 0.0 self.recalculate_shipping = false self.do_not_detect_shipping = true self.last_shipping_rate_request_result = [{ code: :shipping_reset, message: }] line_items.shipping_only.destroy_all deliveries.select { |d| %w[quoting pre_pack].include?(d.state) }.each(&:destroy!) return unless autosave save reload end |
#retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) ⇒ Object
323 324 325 |
# File 'app/concerns/models/ship_quotable.rb', line 323 def retrieve_friendly_shipping_method(show_customer_pays_info = false, for_www = false, with_delivery_commitment = false) deliveries.map { |dq| dq.friendly_shipping_method(show_customer_pays_info, for_www, with_delivery_commitment) }.join(', ') end |
#retrieve_shipping_costs(rate_ship_date: nil) ⇒ Object
Retrieve shipping costs for all deliveries
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 |
# File 'app/concerns/models/ship_quotable.rb', line 22 def retrieve_shipping_costs(rate_ship_date: nil) # Guard against recursion - this can happen when delivery.save! triggers # callbacks (like reset_ships_economy_if_unselected) that save the parent order again return [{ code: :already_processing, message: 'Shipping costs are already being retrieved' }] if @retrieve_shipping_costs_guard @retrieve_shipping_costs_guard = true lock_key = "#{self.class.name.downcase}|#{id || object_id}|retrieve_shipping_costs".freeze result = self.class.with_advisory_lock_result(lock_key, timeout_seconds: 0) do logger.tagged lock_key do if new_record? self.retrieving_shipping_costs = true else update_column(:retrieving_shipping_costs, true) end logger.debug('retrieve_shipping_costs starting', shipping_address_id: shipping_address_id, rate_ship_date: rate_ship_date) retrieve = try(:editing_locked?) || shipping_address.present? || (installation_postal_code.present? && try(:draft?)) if retrieve unless begin editing_locked? rescue StandardError false end if shipping_address_id_changed? && shipping_address_id_was && WAREHOUSE_ADDRESS_IDS.include?(shipping_address_id_was) # we reset selected shipping options because this is like a new shipping calculation and we want sensible defaults, not override deliveries.each { |d| d.update_columns(selected_shipping_cost_id: nil, shipping_option_id: nil) } end deliveries_to_process = refresh_deliveries_quoting.reject(&:do_not_recalculate) deliveries_to_process.each do |delivery| delivery.retrieve_shipping_costs(rate_ship_date: rate_ship_date) # Trigger set_proper_shipping_cost callback now that shipping costs are available delivery.reload # Ensure shipping_costs association is fresh delivery.updated_at = Time.current # Mark as changed to trigger before_save delivery.save! # Trigger before_save callback which calls set_proper_shipping_cost end end # Reload the association so deleted deliveries (removed by refresh_deliveries_quoting) # are not included — stale in-memory records would carry ghost error messages. self.last_shipping_rate_request_result = deliveries.reload.select { |d| d.state == 'quoting' }.map(&:last_shipping_rate_request_result) # Persist the newly built messaging_log so the next request (after redirect) can read it. messaging_logs.select(&:new_record?).each(&:save!) unless new_record? else reset_shipping 'No shipping address provided - shipping will not be calculated', false end self.recalculate_shipping = false self.shipping_issue_alerted = false logger.debug('retrieve_shipping_costs complete') if new_record? self.retrieving_shipping_costs = false else update_columns(recalculate_shipping: false, retrieving_shipping_costs: false) end end last_shipping_rate_request_result end # If lock was not acquired, return already processing message return [{ code: :already_processing, message: 'Shipping costs are already being retrieved' }] unless result.lock_was_acquired? # Return the result from the block (last_shipping_rate_request_result) result.result ensure @retrieve_shipping_costs_guard = false end |
#ship_quoted ⇒ Object
428 429 430 |
# File 'app/concerns/models/ship_quotable.rb', line 428 def ship_quoted deliveries.any?(&:quoting?) && prevent_recalculate_shipping? end |
#ship_weight ⇒ Object
332 333 334 |
# File 'app/concerns/models/ship_quotable.rb', line 332 def ship_weight @ship_weight ||= [0.1, line_items.includes(:item).non_shipping.without_children.sum(&:total_shipping_weight).round(1)].max end |
#shipments ⇒ ActiveRecord::Relation<Shipment>
13 |
# File 'app/concerns/models/ship_quotable.rb', line 13 has_many :shipments, through: :deliveries |
#shipping_address ⇒ Address
10 |
# File 'app/concerns/models/ship_quotable.rb', line 10 belongs_to :shipping_address, class_name: 'Address', validate: true, optional: true |
#shipping_non_db_default_but_db_customer? ⇒ Boolean
373 374 375 376 377 378 379 380 |
# File 'app/concerns/models/ship_quotable.rb', line 373 def shipping_non_db_default_but_db_customer? (begin customer.is_direct_buy? && chosen_shipping_method && (Customer.find(Customer::DB_PARENT_ID).shipping_account_number(carrier, billing_address, shipping_address).nil? || (chosen_shipping_method.shipping_account_number != Customer.find(Customer::DB_PARENT_ID).shipping_account_number(carrier, billing_address, shipping_address))) rescue StandardError false end) end |
#shipping_signature_confirmation_non_db_default_but_db_customer? ⇒ Boolean
382 383 384 |
# File 'app/concerns/models/ship_quotable.rb', line 382 def shipping_signature_confirmation_non_db_default_but_db_customer? customer&.is_direct_buy? && chosen_shipping_method && !signature_confirmation end |
#shipping_via_wy_but_has_shipping_account? ⇒ Boolean
365 366 367 368 369 370 371 |
# File 'app/concerns/models/ship_quotable.rb', line 365 def shipping_via_wy_but_has_shipping_account? (begin chosen_shipping_method&.shipping_account_number && customer.shipping_account_numbers(carrier, billing_address, shipping_address).index(chosen_shipping_method.shipping_account_number).nil? rescue StandardError false end) end |
#ships_economy_package? ⇒ Boolean
150 151 152 |
# File 'app/concerns/models/ship_quotable.rb', line 150 def ships_economy_package? deliveries.all?(&:ships_economy_package?) end |
#ships_freight? ⇒ Boolean
146 147 148 |
# File 'app/concerns/models/ship_quotable.rb', line 146 def ships_freight? deliveries.any?(&:ships_ltl_freight?) end |
#ships_freight_but_address_not_freight_ready? ⇒ Boolean
138 139 140 |
# File 'app/concerns/models/ship_quotable.rb', line 138 def ships_freight_but_address_not_freight_ready? ships_freight? && shipping_address && !shipping_address.is_valid_for_freight? end |
#should_ship_freight? ⇒ Boolean
154 155 156 |
# File 'app/concerns/models/ship_quotable.rb', line 154 def should_ship_freight? deliveries.any?(&:should_ship_ltl_freight?) end |
#should_ship_freight_but_address_not_freight_ready? ⇒ Boolean
142 143 144 |
# File 'app/concerns/models/ship_quotable.rb', line 142 def should_ship_freight_but_address_not_freight_ready? should_ship_freight? && shipping_address && !shipping_address.is_valid_for_freight? end |
#validate_deliveries ⇒ Object
410 411 412 413 414 |
# File 'app/concerns/models/ship_quotable.rb', line 410 def validate_deliveries deliveries.quoting.each do |dq| errors.add(:base, {}.merge(eval(dq.last_shipping_rate_request_result.to_s))[:message]) if dq.shipping_costs.empty? # rubocop:disable Security/Eval end end |