Skip to content

Instantly share code, notes, and snippets.

@cored
Created August 7, 2017 17:50
Show Gist options
  • Save cored/17093d357a55093353a068c856fd2c66 to your computer and use it in GitHub Desktop.
Save cored/17093d357a55093353a068c856fd2c66 to your computer and use it in GitHub Desktop.
# == Schema Information
#
# Table name: warehouses
#
# id :integer not null, primary key
# name :string(255)
# address_id :integer
# supplier_id :integer
# manages_label_purchases :boolean
# scanner_api_token :string
# assignment_transit_day_handicap :integer default(0)
# assignment_tiebreak_priority :integer default(1)
# three_letter_code :string
# ups_account_id :string
# return_address_id :integer
# flexe_api_id :integer
# display_name :string default("Casper")
# flexe_v3_api_id :integer
# group :integer
# retired :boolean default(FALSE)
# created_at :datetime
# updated_at :datetime
#
class Warehouse < ActiveRecord::Base
DBS_WAREHOUSES = ['DB Schenker', 'DB Schenker France'].freeze
belongs_to :address
belongs_to :return_address, class_name: "Address"
has_many :shipments
has_many :bulk_shipments
has_many :warehouse_product_inventories
alias_attribute :product_inventories, :warehouse_product_inventories
has_many :warehouse_variation_inventories
alias_attribute :variation_inventories, :warehouse_variation_inventories
has_many :inventory_movements
has_many :shipment_batches
has_many :warehouse_carrier_service_options, dependent: :destroy
has_many :carrier_service_options, through: :warehouse_carrier_service_options
has_many :carriers, -> { distinct }, through: :carrier_service_options
has_many :warehouses_slots
has_many :courier_slots, through: :warehouses_slots
has_many :warehouse_products, dependent: :destroy
has_many :products, through: :warehouse_products
belongs_to :supplier
has_many :inventory_items, as: :location
has_many :shipment_batch_creation_options
has_many :warehouse_variations
has_many :stocked_warehouse_variations, -> { where(stocked_at_location: true) }, class_name: 'WarehouseVariation'
has_many :stocked_variations, through: :stocked_warehouse_variations, source: :variation
has_many :warehouse_trading_partner_variants
has_many :scheduled_shipment_batches, through: :shipment_batch_creation_options
has_many :recurring_shipment_batches, through: :shipment_batch_creation_options
has_many :cost_centers, dependent: :destroy
has_many :carrier_accounts, dependent: :destroy
has_many :pickups
after_initialize do
default_cost_center ||
cost_centers.new(code: CostCenter::DEFAULT_CODE, products: products)
end
def default_cost_center
cost_centers.detect{|cc| cc.code == CostCenter::DEFAULT_CODE}
end
# This is kind of a tag, but going to avoid a tag gem until we need
# more than one per warehouse.
enum group: {
flexe_warehouse: 0,
}
validates_uniqueness_of :name
validate :no_unshipped_shipments_at_retired_warehouses
validate :no_inventory_at_retired_warehouses
accepts_nested_attributes_for :warehouse_products
scope :can_auto_assign_shipment, -> (shipment) {
sql = <<-SQL
WITH set_1 AS (
#{can_auto_assign_based_on_products(shipment).to_sql}
), set_2 AS (
#{can_fulfill_requested_variations(shipment).to_sql}
) SELECT warehouses.id
FROM warehouses
INNER JOIN set_1
ON warehouses.id = set_1.id
INNER JOIN set_2
ON warehouses.id = set_2.id
SQL
ids = connection.execute(sql).values.flatten
active.where(id: ids)
}
scope :can_auto_assign_based_on_products, -> (shipment) {
merge_scope = WarehouseProduct
.where(product: shipment.products)
.auto_assign_trading_partner(shipment.trading_partner_identifier)
joins(:warehouse_products)
.merge(merge_scope)
.group("warehouses.id")
.having("COUNT(*) = ?", shipment.products.uniq.size)
}
scope :can_fulfill_requested_variations, -> (shipment) {
variations = shipment.shipment_items.map(&:requested_variation).uniq.compact
return if variations.empty?
joins(:stocked_warehouse_variations).merge(WarehouseVariation.where(variation: variations))
.group("warehouses.id")
.having("COUNT(*) = ?", variations.size)
}
scope :fosdick, -> { where("name ILIKE 'fosdick%'") }
scope :editable_by, -> (user) {
if user.role == 'supplier'
where(supplier: user.supplier)
elsif user.role == 'admin'
all
else
none
end
}
scope :active, -> { where(retired: false).order(:name) }
scope :retired, -> { where(retired: true).order(:name) }
NOT_ASSIGNED_ID = '-1'
# Arguments:
# either (x) where x is a Shipment
# or (shipment: x) where x is a Shipment
# or (items: x) where x is an array of hashes in the following format:
# [{sku: <string>, count: int}, {sku: <string>, count: int}, ...]
def has_inventory_to_ship?(s=nil, shipment: s, items: nil)
raise ArgumentError.new("implicit shipment parameter, or named shipment or named items parameter missing") unless shipment || items
raise ArgumentError.new("implicit parameter or named shipment parameter should be a Shipment") if shipment && !shipment.is_a?(Shipment)
if shipment
product_counts = shipment.products.group(:sku).count
else
product_counts = items.map{|item| [item[:sku], item[:count]] }
end
product_counts.all? do |product_sku, count|
product_inventory(Product.find_by_sku(product_sku)).sellable_count >= count
end
end
def display_warehouse_variations
variation_inventories
.joins(:variation)
.where(variations: { retired: false, sku: stocked_variations.pluck(:sku) })
.includes(:product)
.sort_by { |wv| wv.variation.sku }
end
def create_pickup_for(bulk_shipments)
pickups.create(bulk_shipments: bulk_shipments)
end
def variations_by_trading_partner
variation_hash_by_partner = {}
TradingPartnerIdentifier.all.each { |tpi| variation_hash_by_partner[tpi] = [] }
require 'pry'; binding.pry
stocked_variations.not_retired.find_each do |v|
v.trading_partner_identifiers.compact.each do |partner|
variation_hash_by_partner[partner] << v
end
end
variation_hash_by_partner
end
def inventory_for_shipment?(shipment)
shipment.products.group(:id).count.all? do |id, count|
inventory_items
.open_to_trading_partner(shipment.trading_partner_identifier)
.is_product(id)
.not_reserved
.count > count
end
end
def product_inventories
warehouse_product_inventories.order(:sku)
end
def product_inventory(product)
product_inventories.where(sku: product.sku).first
end
def variation_inventory(variation)
variation_inventories.where(variation: variation).first
end
def timezone
address.timezone
end
def no_unshipped_shipments_at_retired_warehouses
if changes.include?(:retired) && retired && shipments.ready_or_pending.any?
errors.add(:retired, "cannot be set to true when unshipped shipments exist at the warehouse")
end
end
def no_inventory_at_retired_warehouses
if changes.include?(:retired) && retired && inventory_items.any?
errors.add(:retired, "cannot be set to true until inventory is zeroed out")
end
end
def dbs?
DBS_WAREHOUSES.include?(name)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment