ShippingGroup = Struct.new( :order_line_items, :vendor, :shipping_cost, :applied_shipping_allowance ) unless const_defined?(:ShippingGroup) ShippingDisplayOption = Struct.new( :value, :text ) unless const_defined?(:ShippingDisplayOption) CHOOSE_COUNTRY_FIRST = [ ShippingDisplayOption.new( "", "Please choose destination country first" ) ] unless const_defined?("CHOOSE_COUNTRY_FIRST") # PO Box or Domestic FCM_DOMESTIC_SHIPPING_OPTION = [ ShippingDisplayOption.new( "usps_fcm", "USPS First Class Mail") ] unless const_defined?("FCM_DOMESTIC_SHIPPING_OPTION") FREE_LIGHTWEIGHT_MAIL_OPTION = [ ShippingDisplayOption.new( "free_usps_fcm", "USPS First Class Mail - FREE!") ] unless const_defined?("DISCOUNT_LIGHTWEIGHT_MAIL_OPTION") # Domestic DOMESTIC_COMMON_SHIPPING_OPTIONS = [ ShippingDisplayOption.new( "next_business_day", "Next Business Day" ), ShippingDisplayOption.new( "two_day", "Second Business Day" ), ShippingDisplayOption.new( "three_day", "Third Business Day" ) ] unless const_defined?("DOMESTIC_COMMON_SHIPPING_OPTIONS") STANDARD_GROUND_OPTION = [ ShippingDisplayOption.new( "ground", "Standard Ground" ) ] unless const_defined?("STANDARD_GROUND_OPTION") FREE_GROUND_OPTION = [ ShippingDisplayOption.new( "free_ground", "Standard Ground - FREE!" ) ] unless const_defined?("FREE_GROUND_OPTION") STANDARD_MAIL_OPTION = [ ShippingDisplayOption.new( "usps_domestic", "USPS Priority Mail") ] unless const_defined?("STANDARD_MAIL_OPTION") DISCOUNT_MAIL_OPTION = [ ShippingDisplayOption.new( "discounted_usps_domestic", "USPS Priority Mail - DISCOUNTED!") ] unless const_defined?("DISCOUNT_MAIL_OPTION") # LOWER 48 FS_PROMO_DOMESTIC_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + FREE_GROUND_OPTION FS_PROMO_LIGHTWEIGHT_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + STANDARD_GROUND_OPTION + FREE_LIGHTWEIGHT_MAIL_OPTION STANDARD_DOMESTIC_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + STANDARD_GROUND_OPTION LIGHTWEIGHT_DOMESTIC_SHIPPING_OPTIONS = STANDARD_DOMESTIC_SHIPPING_OPTIONS + FCM_DOMESTIC_SHIPPING_OPTION PO_BOX_DOMESTIC_SHIPPING_OPTIONS = STANDARD_MAIL_OPTION LIGHTWEIGHT_PO_BOX_DOMESTIC_SHIPPING_OPTIONS = STANDARD_MAIL_OPTION + FCM_DOMESTIC_SHIPPING_OPTION # AK/HI FS_PROMO_AK_HI_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + STANDARD_GROUND_OPTION + DISCOUNT_MAIL_OPTION FS_PROMO_LIGHTWEIGHT_AK_HI_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + STANDARD_GROUND_OPTION + STANDARD_MAIL_OPTION + FREE_LIGHTWEIGHT_MAIL_OPTION STANDARD_AK_HI_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + STANDARD_GROUND_OPTION + STANDARD_MAIL_OPTION LIGHTWEIGHT_AK_HI_SHIPPING_OPTIONS = STANDARD_AK_HI_SHIPPING_OPTIONS + FCM_DOMESTIC_SHIPPING_OPTION # TERRITORIES FS_PROMO_TERRITORIES_SHIPPING_OPTIONS = DISCOUNT_MAIL_OPTION FS_PROMO_LIGHTWEIGHT_TERRITORIES_SHIPPING_OPTIONS = STANDARD_MAIL_OPTION + FREE_LIGHTWEIGHT_MAIL_OPTION STANDARD_TERRITORIES_SHIPPING_OPTIONS = STANDARD_MAIL_OPTION LIGHTWEIGHT_TERRITORIES_SHIPPING_OPTIONS = STANDARD_MAIL_OPTION + FCM_DOMESTIC_SHIPPING_OPTION # International COMMON_INTERNATIONAL_SHIPPING_OPTIONS = [ ShippingDisplayOption.new( "ups_express", "UPS Worldwide Express [1-3 days]" ), ShippingDisplayOption.new( "ups_expedited", "UPS Worldwide Expedited [2-5 days]" ), ShippingDisplayOption.new( "usps_international", "USPS Express Mail International") ] unless const_defined?("INTERNATIONAL_SHIPPING_OPTIONS") DISCOUNTED_INTERNATIONAL_PMI_OPTION = [ ShippingDisplayOption.new( "discounted_usps_int_priority", "USPS Priority Mail International - DISCOUNTED!") ] unless const_defined?("DISCOUNTED_INTERNATIONAL_PMI_OPTION") STANDARD_INTERNATIONAL_PMI_OPTION = [ ShippingDisplayOption.new( "usps_int_priority", "USPS Priority Mail International") ] unless const_defined?("STANDARD_INTERNATIONAL_PMI_OPTION") FS_PROMO_INTERNATIONAL_SHIPPING_OPTIONS = COMMON_INTERNATIONAL_SHIPPING_OPTIONS + DISCOUNTED_INTERNATIONAL_PMI_OPTION STANDARD_INTERNATIONAL_SHIPPING_OPTIONS = COMMON_INTERNATIONAL_SHIPPING_OPTIONS + STANDARD_INTERNATIONAL_PMI_OPTION ALL_SHIPPING_OPTIONS = DOMESTIC_COMMON_SHIPPING_OPTIONS + FREE_GROUND_OPTION + STANDARD_GROUND_OPTION + COMMON_INTERNATIONAL_SHIPPING_OPTIONS + DISCOUNTED_INTERNATIONAL_PMI_OPTION + STANDARD_INTERNATIONAL_PMI_OPTION + FCM_DOMESTIC_SHIPPING_OPTION + FREE_LIGHTWEIGHT_MAIL_OPTION + DISCOUNT_MAIL_OPTION + STANDARD_MAIL_OPTION unless const_defined?("ALL_SHIPPING_OPTIONS") INTERNATIONAL_SHIPPING_METHODS = [ ShippingMethod.new( :shipping_option => :usps_int_priority, :api => :usps, :name => "USPS Priority Mail International", :mail_class => "PriorityMailInternational", :multiplier => 1.45), ShippingMethod.new( :shipping_option => :usps_international, :api => :usps, :name => "USPS Express Mail International", :mail_class => "ExpressMailInternational", :multiplier => 1.45), ShippingMethod.new( :shipping_option => :ups_express, :api => :ups, :name => "UPS Worldwide Express", :service_code => "07", :multiplier => 0.7), ShippingMethod.new( :shipping_option => :ups_expedited, :api => :ups, :name => "UPS Worldwide Expedited", :service_code => "08", :multiplier => 0.7) ] unless const_defined?("INTERNATIONAL_SHIPPING_METHODS") DOMESTIC_FEDEX_SHIPPING_METHODS = [ ShippingMethod.new( :shipping_option => :ground, :api => :fedex, :name => "FedEx Ground", :transaction_type => "rate_ground", :service_type => "ground_service", :multiplier => 1.4 ), ShippingMethod.new( :shipping_option => :ground_res, :api => :fedex, :name => "FedEx Home Delivery", :transaction_type => "rate_ground", :service_type => "home_delivery", :multiplier => 1.4), ShippingMethod.new( :shipping_option => :three_day, :api => :fedex, :name => "FedEx Express Saver (3 Day)", :transaction_type => "rate_express", :service_type => "express_saver", :multiplier => 1.25 ), ShippingMethod.new( :shipping_option => :two_day, :api => :fedex, :name => "FedEx 2 Day", :transaction_type => "rate_express", :service_type => "2day", :multiplier => 1.25 ), ShippingMethod.new( :shipping_option => :next_business_day, :api => :fedex, :name => "FedEx Standard Overnight", :transaction_type => "rate_express", :service_type => "standard_overnight", :multiplier => 1.25 ) ] unless const_defined?("DOMESTIC_FEDEX_SHIPPING_METHODS") DOMESTIC_UPS_SHIPPING_METHODS = [ ShippingMethod.new( :shipping_option => :ground, :api => :ups, :name => "UPS Ground", :service_code => "03"), ShippingMethod.new( :shipping_option => :three_day, :api => :ups, :name => "UPS 3-Day Select", :service_code => "12", :multiplier => 1.1), ShippingMethod.new( :shipping_option => :two_day, :api => :ups, :name => "UPS 2nd Day Air", :service_code => "02", :multiplier => 1.1), ShippingMethod.new( :shipping_option => :next_business_day, :api => :ups, :name => "UPS Next Day Air", :service_code => "01", :multiplier => 1.1) ] unless const_defined?("DOMESTIC_UPS_SHIPPING_METHODS") DOMESTIC_USPS_SHIPPING_METHOD = [ ShippingMethod.new( :shipping_option => :upsmi_domestic, :api => :upsmi, :name => "UPS Mail Innovations", :service => "domestic"), ShippingMethod.new( :shipping_option => :usps_fcm, :api => :usps, :name => "USPS First Class Mail", :mail_class => "FIRST"), ShippingMethod.new( :shipping_option => :usps_domestic, :api => :usps, :name => "USPS Priority Mail", :mail_class => "PRIORITY") ] unless const_defined?("DOMESTIC_USPS_SHIPPING_METHOD") ALL_SHIPPING_METHODS = INTERNATIONAL_SHIPPING_METHODS + DOMESTIC_FEDEX_SHIPPING_METHODS + DOMESTIC_UPS_SHIPPING_METHODS + DOMESTIC_USPS_SHIPPING_METHOD # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Lookup the ShippingMethod object based on the shipping code passed into # the method here. # # Note that shipping_code will be one of the symbols based on the # :shipping_option => 'XXXXX' in the above declared ShippingMethod instances. # # Therefore shipping_code will be something like: # "ground", "three_day", "two_day", "next_business_day", "usps_domestic", etc. # # # shipping_code will be a STRING... NOT A SYMBOL! # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def lookup_shipping_method( shipping_code, api_override = false ) # Try domestic shipping first. For domestic we need to match based on the # shipping code ***AND*** the currently configured shipping API - which # will be either Fedex or UPS #RAILS_DEFAULT_LOGGER.debug "Looking up shipping method based on this code: #{shipping_code}." # Check to see what the currently configured shipping method is - Set by an administrator # in the admin interface. The only valid values are "ups" and "fedex" if api_override api = api_override else # Default shipping method is UPS api = "ups" end case api when "ups" shipping_method = get_shipping_method_from_array( shipping_code, DOMESTIC_UPS_SHIPPING_METHODS) when "fedex" shipping_method = get_shipping_method_from_array( shipping_code, DOMESTIC_FEDEX_SHIPPING_METHODS) end # ...if it turns out that we found an appropriate UPS or Fedex method from above - return it... return shipping_method unless shipping_method.nil? # ...otherwise lets see if it is a USPS domestic if ["usps_domestic", "upsmi_domestic", "usps_fcm"].include? shipping_code return get_shipping_method_from_array( shipping_code, DOMESTIC_USPS_SHIPPING_METHOD ) end # ... otherwise it must be]an international return get_shipping_method_from_array( shipping_code, INTERNATIONAL_SHIPPING_METHODS) end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get the generic shipping method currently selected # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def lookup_generic_shipping_method( shipping_code ) ALL_SHIPPING_OPTIONS.each do |opt| if opt.value == shipping_code return opt end end return nil end def lookup_generic_shipping_method_by_name( method_name ) ALL_SHIPPING_METHODS.each do |opt| if opt.sm_params[:name] == method_name return opt.sm_params[:shipping_option] end end return nil end def lookup_shipping_method_by_name( method_name ) ALL_SHIPPING_METHODS.each do |opt| if opt.sm_params[:name] == method_name return opt end end return nil end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get the total shipping cost for the order. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def get_shipping_cost_for_order( all_items, receiver_zip, receiver_country, in_stock_shipping_method_code, out_of_stock_shipping_method_code, consolidate = false ) raise Exception.new( "ShippingUtilities::get_shipping_cost_for_order is deprecated! Please use new ShippingUtilities::build_shipping_groups instead." ) end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Determine the shipping weight for some items # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def shipping_weight_for_items(order_line_items) weight = 0.0 order_line_items.each { |order_line_item| unless order_line_item.item.shipping_weight.nil? or order_line_item.item.shipping_weight == 0 weight += (order_line_item.quantity * order_line_item.item.shipping_weight) else weight += (order_line_item.quantity * 5) end } return weight end private # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get a shipping method from a narrowed down array of options # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def get_shipping_method_from_array( code, methods ) #RAILS_DEFAULT_LOGGER.debug "Code is: #{code}. Methods are: #{methods.inspect}" methods.each do |shipping_method| #RAILS_DEFAULT_LOGGER.debug "Shipping Method Compare: #{shipping_method.sm_params[:shipping_option]}" if shipping_method.sm_params[:shipping_option].to_s == code #RAILS_DEFAULT_LOGGER.debug "Returning Shipping Method: #{shipping_method.inspect}" return shipping_method end end return nil end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Technically the shipping utilities shouldn't have any awareness of what store # to bill stuff to - but given that we need some "special" logic to determine # what stores shipping credentials to use depending on what items are in the cart # this seems like the best option. # # If the cart has any NEML products - then use NEML # If the card is mixed up of NEMX and NEHP only - then use NEMX # Otherwise use whatever store that all products are from # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def determine_store_for_shipping( all_items ) return Store::NEML if all_items.length < 1 stores = Store.stores_for_items( all_items.collect{ |order_line_item| order_line_item.item || order_line_item.used_item } ) if stores.length == 1 return stores.values.first else if stores[Store::NEML] != nil return stores[Store::NEML] else return stores[Store::NEHP] end end end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Calculate the shipping costs for a group of products. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def calculate_shipping_costs_for_items( store, order_line_items, sender_zip, receiver_zip, receiver_country, shipping_method_code, consolidate = false ) # Before doing anything, check the validity of the zip code if we're shipping to the US. # Raise an exception if it's bunk. if receiver_country == "US" ShippingCalculatorBase.state_from_zip( receiver_zip ) end shipping_weight = shipping_weight_for_items( order_line_items ) ship_symbol = consolidate ? :order_in : order_line_items[0].item.shipping_status_symbol # Prevent exception if sender_zip is not defined (eg. zip not in Vendor table) if sender_zip.nil? sender_zip = 79363 end RAILS_DEFAULT_LOGGER.info "Shipping symbol - #{ship_symbol}" if ship_symbol == :drop_ship api_override = "ups" else api_override = false end RAILS_DEFAULT_LOGGER.info "Method: #{shipping_method_code}, Override: #{api_override}, Store: #{store.id}" shipping_method = lookup_shipping_method( shipping_method_code, api_override ) RAILS_DEFAULT_LOGGER.info "We found: #{shipping_method.inspect}" ship_api = shipping_method.sm_params[:api] # If we don't have an explicit multiplier, we want to use the price # returned from the API as is (so mult by 1.0) # DEPRECATED - NEW CODE BELOW! # shipping_method.sm_params[:multiplier] ||= 1.0 mult = Multiplier.find(:first, :conditions => ["store_id = ? and service = ?", store.id, shipping_method_code], :select => "ds, wh").send((ship_symbol == :drop_ship && receiver_country == "US") ? "ds" : "wh") min = store.send(ship_symbol == :drop_ship ? "ds_min" : "wh_min") ship_params = Hash.new ship_params[:shipping_weight] = shipping_weight ship_params[:sender_zip] = sender_zip ship_params[:receiver_zip] = receiver_zip ship_params[:receiver_country] = receiver_country case ship_api when :fedex ship_params[:fedex_account] = store.fedex_account ship_params[:fedex_url] = NewEnough::FEDEX_URL ship_params[:fedex_meter] = store.fedex_meter ship_params[:service_type] = shipping_method.sm_params[:service_type] ship_params[:transaction_type] = shipping_method.sm_params[:transaction_type] amt = (FedexCalculator.new.price( ship_params ) * mult).prec(2) when :ups ship_params[:ups_user] = NewEnough::UPS_USER ship_params[:ups_pass] = NewEnough::UPS_PASS ship_params[:ups_license] = NewEnough::UPS_LICENSE ship_params[:sender_country] = store.country ship_params[:sender_phone] = store.phone_number ship_params[:sender_name] = store.name ship_params[:receiver_country] = receiver_country ship_params[:shipping_method] = shipping_method.sm_params[:service_code] amt = (UpsCalculator.new.price( ship_params ) * mult).prec(2) when :usps ship_params[:mail_class] = shipping_method.sm_params[:mail_class] ship_params[:partner_id] = NewEnough::ENDICIA_PARTNER_ID ship_params[:account_id] = NewEnough::ENDICIA_ACCOUNT_ID ship_params[:pass_phrase] = NewEnough::ENDICIA_PASS_PHRASE amt = (UspsCalculator.new.price( ship_params ) * mult).prec(2) when :upsmi ship_params[:service] = shipping_method.sm_params[:service] amt = (UpsmiCalculator.new.price( ship_params ) * mult).prec(2) end return amt > min ? amt : min end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Determine the groups in which items will be shipped. This is necessary for # non-consolidated shipments when some items are out of stock or are being # drop-shipped from a vendors warehouse. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def build_shipments( order_line_items, consolidate=false ) shipments = [] unless consolidate # Build a hash of line items with associated data we can use without having to re-query the database for all the iterations. line_items = [] order_line_items.each do |order_line_item| item = order_line_item.item_id ? Item.find(order_line_item.item_id, :include => :product) : UsedItem.find(order_line_item.used_item_id) RAILS_DEFAULT_LOGGER.info("Build shipments found item: #{item.inspect} from store #{item.store_id}") ship_status_symbol = item.shipping_status_symbol_for_quantity(order_line_item.quantity) ship_status_symbol = :in_stock if [:closeout_instock,:from_stock_only_instock].include?(ship_status_symbol) line_items << OpenStruct.new( :ship_status_symbol => ship_status_symbol, :store_id => item.instance_of?(Item) ? item.product.store_id : 1, :vendor_id => item.instance_of?(Item) ? item.product.vendor_id : nil, :line_item => order_line_item, :drop_shippable => item.drop_shippable?, :consolidatable => false) end # MAGIC GOES HERE TO DO THE CONSOLIDATION line_items_by_sym = {} [:in_stock,:drop_ship,:order_in].each do |ship_sym| line_items_by_sym[ship_sym] = line_items.select{|li| li.ship_status_symbol == ship_sym} end if line_items_by_sym[:in_stock].length > 0 && line_items_by_sym[:drop_ship].length > 0 RAILS_DEFAULT_LOGGER.debug("Attempting to consolidate in_stocks and drop_ships.") # If we've got us some in_stocks and some drop_ships, let's see if # we can do some consolidating. It only makes sense to consolidate if we can completely # get rid of in_stocks. line_items_by_sym[:in_stock].each do |li| matching_ds = line_items_by_sym[:drop_ship].count{|dsli| dsli.vendor_id == li.vendor_id} > 0 li.consolidatable = (matching_ds && li.drop_shippable) end if line_items_by_sym[:in_stock].count{|li| !li.consolidatable} < 1 RAILS_DEFAULT_LOGGER.debug("Consolidating in_stocks and drop_ships.") #Woo-hoo! Let's consolidate line_items.each{|li| li.ship_status_symbol = :drop_ship if li.ship_status_symbol == :in_stock} end end line_items.each do |li| create_group_if_necessary_and_insert( shipments, li.ship_status_symbol, li.store_id, li.ship_status_symbol == :drop_ship ? li.vendor_id : li.store_id, li.line_item ) end else order_line_items.each do |order_line_item| item = order_line_item.item_id ? Item.find(order_line_item.item_id, :include => :product) : UsedItem.find(order_line_item.used_item_id) create_group_if_necessary_and_insert( shipments, :consolidated, item.store_id, item.store_id, order_line_item) end end set_consolidate_flags( shipments ) RAILS_DEFAULT_LOGGER.info "=====================================================" RAILS_DEFAULT_LOGGER.info "Shipments" RAILS_DEFAULT_LOGGER.info "=====================================================" RAILS_DEFAULT_LOGGER.info "#{shipments.inspect}" RAILS_DEFAULT_LOGGER.info "=====================================================" return shipments end def set_consolidate_flags( shipments ) #RAILS_DEFAULT_LOGGER.debug shipments.to_yaml # Now let's set appropriate consolidate flags on the shipments that are to be shipped together (NEML & NEHP) in_stock = [] order_in = [] consol = [] shipments.each do |s| if s.shipment_type == Shipment::IN_STOCK in_stock << s elsif s.shipment_type == Shipment::ORDER_IN order_in << s elsif s.shipment_type == Shipment::CONSOLIDATED consol << s end end # NEMX_TODO - Make this more robust if we need to combine with NEMX in the future. groups = [in_stock, order_in, consol] groups.each do |g| #RAILS_DEFAULT_LOGGER.debug "================================================================" #RAILS_DEFAULT_LOGGER.debug "Group #{g.inspect}" #RAILS_DEFAULT_LOGGER.debug "================================================================" if g.length == 2 # We can assume here that shipper_id == store_id always because a drop-ship from one store # will never be combined with a drop-ship from another store. If shipment was drop-shipped # shipper_id == vendor_id. if g[1].shipper_id == 1 g[0].combine_flag = "combine" g[1].combine_flag = "has_combined_shipment" g[1].combine_with_shipment = g[0] else g[1].combine_flag = "combine" g[0].combine_flag = "has_combined_shipment" g[0].combine_with_shipment = g[1] end end end end def create_group_if_necessary_and_insert( shipments, key, store_id, shipper_id, order_line_item) found = false shipments.each do |shipment| if shipment.shipment_type == shipment.shipment_type_from_symbol( key ) && shipment.shipper_id == shipper_id && shipment.store_id == store_id shipment.line_items << order_line_item found = true end end if found == false shipment = Shipment.new shipment.shipment_type = shipment.shipment_type_from_symbol( key ) shipment.shipper_id = shipper_id shipment.store_id = store_id shipment.line_items << order_line_item shipments << shipment end end # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # If shipments will be combined, consolidate them for the public view # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def build_shipping_groups( shipments, ship_method, customer_zip, customer_country, consolidate = false ) RAILS_DEFAULT_LOGGER.debug "In build_shipping_groups, ship_method: #{ship_method}" shipping_groups = {} shipments.each do |shipment| unless shipment.combine_flag == "combine" @group_line_items = [] shipment.line_items.each {|li| @group_line_items << li } if !shipment.combine_with_shipment.nil? shipment.combine_with_shipment.line_items.each {|cli| @group_line_items << cli } end ship_symbol = consolidate ? :order_in : @group_line_items[0].item.shipping_status_symbol shipping_weight = shipping_weight_for_items( @group_line_items ) if ship_method == 'free_ground' && !shipment.has_oversized_item? if ship_symbol == :drop_ship shipment.shipping_method = 'ground' RAILS_DEFAULT_LOGGER.debug("Drop Ship") elsif shipping_weight <= 5.0 # This is where the UPS Basic selection will kick in once we're approved. # TODO: UPS Basic shipment.shipping_method = 'ground' RAILS_DEFAULT_LOGGER.debug("< 5#") else shipment.shipping_method = 'ground' RAILS_DEFAULT_LOGGER.debug("Normal") end elsif ship_method == 'free_ground' && shipment.has_oversized_item? shipment.shipping_method = 'ground' elsif ship_method == 'discounted_usps_domestic' shipment.shipping_method = 'usps_domestic' elsif ship_method == 'discounted_usps_int_priority' shipment.shipping_method = 'usps_int_priority' elsif ship_method == 'free_usps_fcm' shipment.shipping_method = 'usps_fcm' else shipment.shipping_method = ship_method end shipment.shipping_cost = calculate_shipping_costs_for_items( determine_store_for_shipping(@group_line_items), @group_line_items, shipment.ships_from.zip_code, customer_zip, customer_country, shipment.shipping_method, consolidate) if ship_method == 'free_ground' shipment.applied_shipping_allowance = shipment.has_oversized_item? ? 0.0 : shipment.shipping_cost elsif ship_method == 'free_usps_fcm' shipment.applied_shipping_allowance = shipment.shipping_cost elsif ['discounted_usps_int_priority', 'discounted_usps_domestic'].include?(ship_method) shipment.applied_shipping_allowance = equivalent_ship_discount_for_weight( shipping_weight ) shipment.applied_shipping_allowance = shipment.shipping_cost if shipment.applied_shipping_allowance > shipment.shipping_cost else shipment.applied_shipping_allowance = 0.0 end if shipment.shipment_type == Shipment::DROP_SHIP vendor = shipment.ships_from key = "#{vendor.id}-#{shipment.store_id}" else vendor = nil key = shipment.shipment_type_symbol end add_key_if_necessary_and_insert( shipping_groups, key, shipment, @group_line_items, vendor) end # unless end RAILS_DEFAULT_LOGGER.info "=====================================================" RAILS_DEFAULT_LOGGER.info "Shipping Groups" RAILS_DEFAULT_LOGGER.info "=====================================================" RAILS_DEFAULT_LOGGER.info "#{shipping_groups.inspect}" RAILS_DEFAULT_LOGGER.info "=====================================================" return shipping_groups end def add_key_if_necessary_and_insert( shipping_groups, key, shipment, order_line_items, vendor ) if shipping_groups[ key ].nil? shipping_groups[key] = ShippingGroup.new( [], vendor, shipment.shipping_cost, shipment.applied_shipping_allowance ) end order_line_items.each do |item| shipping_groups[key].order_line_items << item end end def equivalent_ship_discount_for_weight( weight_in_pounds ) # Base of $6.53 covers shipments up to 1 lb. # We need to figure out rounded-up weight over 1 lb. additional_poundage = weight_in_pounds - 1 ship_discount = 6.53 + (additional_poundage * 0.29) # Now we add in the ground multiplier so the rate is realistic # if someone plays looky-loo with our domestic rates mult = Multiplier.find(:first, :conditions => {:store_id => 1, :service => 'ground'}).wh return (ship_discount * mult).prec(2) end end