class OrderOptimizer

Constants

VERSION

Public Class Methods

new(skus) click to toggle source
# File lib/order_optimizer.rb, line 8
def initialize(skus)
  @catalog = OrderOptimizer::Catalog.new(skus)
end

Public Instance Methods

cheapest_exact_order(required_qty:) click to toggle source
# File lib/order_optimizer.rb, line 17
def cheapest_exact_order(required_qty:)
  find_possible_orders(skus: @catalog.skus, required_qty: required_qty).select(&:exact?).min_by(&:total) ||
    OrderOptimizer::Order.new(required_qty: required_qty)
end
cheapest_order(required_qty:) click to toggle source
# File lib/order_optimizer.rb, line 12
def cheapest_order(required_qty:)
  find_possible_orders(skus: @catalog.skus, required_qty: required_qty).min_by(&:total) ||
    OrderOptimizer::Order.new(required_qty: required_qty)
end
possible_orders(required_qty:) click to toggle source
# File lib/order_optimizer.rb, line 22
def possible_orders(required_qty:)
  find_possible_orders(skus: @catalog.skus, required_qty: required_qty).sort_by(&:total)
end

Private Instance Methods

count_and_remainder_for_sku(quantity, sku) click to toggle source
# File lib/order_optimizer.rb, line 55
def count_and_remainder_for_sku(quantity, sku)
  count, remainder = quantity.divmod(sku.quantity)

  if sku.max_quantity && count * sku.quantity > sku.max_quantity
    new_count = (sku.max_quantity / sku.quantity).ceil
    remainder = (count - new_count) * sku.quantity

    [new_count, [remainder, 0].max, true]
  elsif sku.min_quantity && count * sku.quantity < sku.min_quantity
    new_count = (sku.min_quantity / sku.quantity).ceil
    remainder -= (new_count - count) * sku.quantity

    [new_count, [remainder, 0].max, false]
  else
    [count, remainder, false]
  end
end
find_possible_orders(required_qty:, skus:) click to toggle source
# File lib/order_optimizer.rb, line 28
def find_possible_orders(required_qty:, skus:)
  return [] if required_qty < 1 || skus.empty?

  orders = []

  skus.each do |sku|
    orders.reject(&:complete?).each do |order|
      count, remainder, skip_increase = count_and_remainder_for_sku(order.missing_qty, sku)

      orders << order.dup.add(sku, count: count) unless count.zero?
      orders << order.dup.add(sku, count: count + 1) unless remainder.zero? || skip_increase
    end

    count, remainder, skip_increase = count_and_remainder_for_sku(required_qty, sku)

    unless count.zero?
      orders << OrderOptimizer::Order.new(required_qty: required_qty).add(sku, count: count)
    end

    unless remainder.zero? || skip_increase
      orders << OrderOptimizer::Order.new(required_qty: required_qty).add(sku, count: count + 1)
    end
  end

  orders.select(&:complete?)
end