Module: Models::ShipQuotable
- Extended by:
- ActiveSupport::Concern
- Includes:
- ShipMeasurable
- Defined in:
- app/concerns/models/ship_quotable.rb
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
360 361 362 |
# File 'app/concerns/models/ship_quotable.rb', line 360 def carrier deliveries.first&.carrier end |
#chosen_shipping_method ⇒ Object
356 357 358 |
# File 'app/concerns/models/ship_quotable.rb', line 356 def chosen_shipping_method deliveries.first&.chosen_shipping_method end |
#days_commitment(use = :least) ⇒ Object
334 335 336 337 338 339 340 |
# File 'app/concerns/models/ship_quotable.rb', line 334 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>
10 |
# File 'app/concerns/models/ship_quotable.rb', line 10 has_many :deliveries, -> { order(:origin_address_id) }, inverse_of: name.tableize.singularize.to_sym, autosave: true, dependent: :destroy |
#determine_origin_address(line_item) ⇒ Object
113 114 115 116 117 118 |
# File 'app/concerns/models/ship_quotable.rb', line 113 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
342 343 344 |
# File 'app/concerns/models/ship_quotable.rb', line 342 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
313 314 315 316 317 318 319 |
# File 'app/concerns/models/ship_quotable.rb', line 313 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
128 129 130 |
# File 'app/concerns/models/ship_quotable.rb', line 128 def is_drop_ship? one_time_shipping_address.present? end |
#is_override? ⇒ Boolean
132 133 134 |
# File 'app/concerns/models/ship_quotable.rb', line 132 def is_override? deliveries.any?(&:override_shipping_method?) end |
#is_warehouse_pickup? ⇒ Boolean
405 406 407 |
# File 'app/concerns/models/ship_quotable.rb', line 405 def is_warehouse_pickup? deliveries.any?(&:is_warehouse_pickup?) end |
#line_items_grouped_by_deliveries_quoting(group_by_item_category = true) ⇒ Object
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'app/concerns/models/ship_quotable.rb', line 385 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
415 416 417 418 419 420 421 422 423 424 425 |
# File 'app/concerns/models/ship_quotable.rb', line 415 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
431 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 |
# File 'app/concerns/models/ship_quotable.rb', line 431 def need_to_pre_pack_reasons res = [] weight_key = :package weight_key = :ltl if deliveries.any? { |d| d.ships_ltl_freight? } # return [] if all shipments packed return res if deliveries.all? { |d| d.shipments.all? { |s| s.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]) # see if we can find a packing solution that is not from legacy_packaging algorithm but actually a packing from delivery history, item or manual ie a previous pre-pack # Also trust packing_calculator and item_group_packaging — both are computed from actual item shipping dimensions # and provide a reliable box plan. Treating them as legacy_packaging would incorrectly trigger pre_pack for # any quote whose item combo has no delivery history but whose items have known dimensions (introduced by PackingCalculator, Feb 2026). 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.any? do |li| li.dropship? end) && !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
325 326 327 328 |
# File 'app/concerns/models/ship_quotable.rb', line 325 def need_to_recalculate_shipping self.recalculate_shipping = true self.do_not_detect_shipping = false end |
#one_time_shipping_address ⇒ Object
124 125 126 |
# File 'app/concerns/models/ship_quotable.rb', line 124 def one_time_shipping_address shipping_address if shipping_address && shipping_address.one_time_only end |
#one_time_shipping_address=(val) ⇒ Object
120 121 122 |
# File 'app/concerns/models/ship_quotable.rb', line 120 def one_time_shipping_address=(val) self.shipping_address = val end |
#qualifies_for_cod? ⇒ Boolean
346 347 348 349 350 351 352 353 354 |
# File 'app/concerns/models/ship_quotable.rb', line 346 def qualifies_for_cod? # we limit COD orders to single delivery origin domestic orders from one of our warehouses res = false res = true if (begin (deliveries.quoting.count == 1) && 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
157 158 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 |
# File 'app/concerns/models/ship_quotable.rb', line 157 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.pluck(:id) remapped_shipment_ids += dq.shipments.packed.pluck(:id) 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.delete_all # We disassociated earlier the one that matters 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.detect 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 # little patch for new warehouse address matching 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.detect 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).each { |pp| pp.update_attribute(:delivery_id, delivery.id) } if remapped_pre_payment_ids.present? Shipment.where(id: remapped_shipment_ids).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).each { |shp| shp.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
105 106 107 108 109 110 111 |
# File 'app/concerns/models/ship_quotable.rb', line 105 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
91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'app/concerns/models/ship_quotable.rb', line 91 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
321 322 323 |
# File 'app/concerns/models/ship_quotable.rb', line 321 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
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 |
# File 'app/concerns/models/ship_quotable.rb', line 20 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
427 428 429 |
# File 'app/concerns/models/ship_quotable.rb', line 427 def ship_quoted deliveries.any?(&:quoting?) && prevent_recalculate_shipping? end |
#ship_weight ⇒ Object
330 331 332 |
# File 'app/concerns/models/ship_quotable.rb', line 330 def ship_weight @ship_weight ||= [0.1, line_items.includes(:item).non_shipping.without_children.map(&:total_shipping_weight).sum.round(1)].max end |
#shipments ⇒ ActiveRecord::Relation<Shipment>
11 |
# File 'app/concerns/models/ship_quotable.rb', line 11 has_many :shipments, through: :deliveries |
#shipping_address ⇒ Address
8 |
# File 'app/concerns/models/ship_quotable.rb', line 8 belongs_to :shipping_address, class_name: 'Address', validate: true, optional: true |
#shipping_non_db_default_but_db_customer? ⇒ Boolean
372 373 374 375 376 377 378 379 |
# File 'app/concerns/models/ship_quotable.rb', line 372 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
381 382 383 |
# File 'app/concerns/models/ship_quotable.rb', line 381 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
364 365 366 367 368 369 370 |
# File 'app/concerns/models/ship_quotable.rb', line 364 def shipping_via_wy_but_has_shipping_account? (begin chosen_shipping_method && 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
148 149 150 |
# File 'app/concerns/models/ship_quotable.rb', line 148 def ships_economy_package? deliveries.all?(&:ships_economy_package?) end |
#ships_freight? ⇒ Boolean
144 145 146 |
# File 'app/concerns/models/ship_quotable.rb', line 144 def ships_freight? deliveries.any?(&:ships_ltl_freight?) end |
#ships_freight_but_address_not_freight_ready? ⇒ Boolean
136 137 138 |
# File 'app/concerns/models/ship_quotable.rb', line 136 def ships_freight_but_address_not_freight_ready? ships_freight? && shipping_address && !shipping_address.is_valid_for_freight? end |
#should_ship_freight? ⇒ Boolean
152 153 154 |
# File 'app/concerns/models/ship_quotable.rb', line 152 def should_ship_freight? deliveries.any?(&:should_ship_ltl_freight?) end |
#should_ship_freight_but_address_not_freight_ready? ⇒ Boolean
140 141 142 |
# File 'app/concerns/models/ship_quotable.rb', line 140 def should_ship_freight_but_address_not_freight_ready? should_ship_freight? && shipping_address && !shipping_address.is_valid_for_freight? end |
#validate_deliveries ⇒ Object
409 410 411 412 413 |
# File 'app/concerns/models/ship_quotable.rb', line 409 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? end end |