order_optimizer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0845a565bc344866e5e3d4725d28b890c9f3e4d57de36ee2789ee331f000168
4
- data.tar.gz: b825661a3746a8b7bf574c3938efb2b412ac81163223a4836be013dda983100e
3
+ metadata.gz: d3dec3fd3cab38f6a56c1fa13e3ee398e4b196c1c992cdf6f4c7e87c2c19e2f1
4
+ data.tar.gz: 9cd52e4ae25d980de2b8b4ba1988bbe42dac0dd686d0d1c7f23584c7889cb3e8
5
5
  SHA512:
6
- metadata.gz: 3103e356f667e9256ff3115d0381f05fa27906083232300c51833fcd5b15118fd4b4154542f07e781882a25a4de8f70b4604c8748c529f92a91d41af315391c4
7
- data.tar.gz: 965406b44c6006b4ac863cc2dd3d8fc0872fd87a08bb4fadc45954cd81e6a724f7143975985c439a83a4a9a9d0630df54113a2e9e018cd16282ea420eff23315
6
+ metadata.gz: 958f1f99429e00465cdc9a562dff1717da0b145827430704766756626444559e25f1f0b615c7f19cb274bc1783e33fb2eb3ca34b8e4d114e08af5f01f75c05f2
7
+ data.tar.gz: 0cfb7dc00f8eacff31bff96cfedee003427e9e9e4677f8a382f7aa28e0148de5ca902ca576697aeba54c3b28a19a3302f451fb0944dc96464908742a9328287f
data/CHANGELOG.md CHANGED
@@ -1,4 +1,7 @@
1
1
  # OrderOptimizer Gem Changelog
2
2
 
3
+ ## 0.2.0 (2019-12-16)
4
+
5
+ - Add support for discounts when ordering a minimum quantity
3
6
 
4
7
  ## 0.1.0 First release
data/README.md CHANGED
@@ -11,7 +11,7 @@ Imagine a product can be ordered in different pack sizes and each pack size has
11
11
  | 10 | 6.00 |
12
12
  | 1 | 9.00 |
13
13
 
14
- In this example, it is quite obvious that it is cheaper buying one 10-pack instead of nine 1-packs when you need 9 units (because the 10-pack costs 60.00 but nine 1-packs would cost 81.00).
14
+ In this example, it is quite obvious that it is cheaper buying one 10-pack instead of nine 1-packs when you need 9 units (because the 10-pack costs 60.00 but nine 1-packs would cost 81.00).
15
15
 
16
16
  But what would be the cheapest combination when you need 946 units? Or 947?
17
17
 
@@ -56,6 +56,34 @@ cheapest_order_for_947_units.total # => 3_900.00
56
56
  cheapest_order_for_947_units.skus # => { '1000-pack' => 1 }
57
57
  ```
58
58
 
59
+ It is possible to define dicount prices a minimum quantity:
60
+
61
+ ```ruby
62
+ order_optimizer = OrderOptimizer.new(
63
+ '1-pack' => { quantity: 1, price_per_unit: 9 },
64
+ '10-discount' => { quantity: 1, min_quantity: 10, price_per_unit: 8 },
65
+ '20-pack' => { quantity: 20, price_per_unit: 7 }
66
+ )
67
+
68
+ order_optimizer.cheapest_order(required_qty: 8).skus
69
+ #=> { '1-pack' => 8 }
70
+
71
+ order_optimizer.cheapest_order(required_qty: 9).skus
72
+ #=> { '10-discount' => 10 }
73
+
74
+ order_optimizer.cheapest_order(required_qty: 17).skus
75
+ #=> { '10-discount' => 17 }
76
+
77
+ order_optimizer.cheapest_order(required_qty: 18).skus
78
+ #=> { '20-pack' => 1 }
79
+
80
+ order_optimizer.cheapest_order(required_qty: 21).skus
81
+ #=> { '20-pack' => 1, '1-pack' => 1 }
82
+
83
+ order_optimizer.cheapest_order(required_qty: 29).skus
84
+ #=> { '20-pack' => 1, '10-discount' => 10 }
85
+ ```
86
+
59
87
  ## Development
60
88
 
61
89
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -9,5 +9,13 @@ class OrderOptimizer
9
9
  def skus
10
10
  @skus.dup
11
11
  end
12
+
13
+ def skus_without_min_quantities
14
+ @skus.reject(&:min_quantity)
15
+ end
16
+
17
+ def skus_with_min_quantities
18
+ @skus.select(&:min_quantity)
19
+ end
12
20
  end
13
21
  end
@@ -1,12 +1,25 @@
1
1
  class OrderOptimizer
2
2
  class Sku
3
- attr_reader :id, :quantity, :price_per_unit, :price_per_sku
3
+ attr_reader :id, :quantity, :price_per_unit, :price_per_sku, :min_quantity
4
4
 
5
- def initialize(id, quantity:, price_per_unit:)
5
+ def initialize(id, quantity:, price_per_unit:, min_quantity: nil)
6
6
  @id = id
7
7
  @quantity = quantity
8
8
  @price_per_unit = BigDecimal(price_per_unit, 2)
9
9
  @price_per_sku = quantity * price_per_unit
10
+ @min_quantity = min_quantity
11
+ end
12
+
13
+ private
14
+
15
+ def set_min_quantity(min_qty)
16
+ return unless min_qty
17
+
18
+ if min_quantity.modulo(quantity).zero?
19
+ @min_quantity = min_qty
20
+ else
21
+ raise ':min_quantity must be a multiple of :quantity'
22
+ end
10
23
  end
11
24
  end
12
25
  end
@@ -1,3 +1,3 @@
1
1
  class OrderOptimizer
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -10,21 +10,39 @@ class OrderOptimizer
10
10
  end
11
11
 
12
12
  def cheapest_order(required_qty:)
13
- possible_orders(required_qty: required_qty)
14
- .min_by(&:total)
13
+ orders =
14
+ possible_orders(skus: @catalog.skus_without_min_quantities, required_qty: required_qty) +
15
+ possible_orders(skus: @catalog.skus_with_min_quantities, required_qty: required_qty) +
16
+ possible_orders(skus: @catalog.skus, required_qty: required_qty)
17
+
18
+ orders.min_by(&:total) || OrderOptimizer::Order.new
15
19
  end
16
20
 
17
21
  private
18
22
 
19
- def possible_orders(required_qty:)
23
+ def possible_orders(required_qty:, skus:)
20
24
  [].tap do |orders|
21
- skus = @catalog.skus
22
25
  order = OrderOptimizer::Order.new
23
- while sku = skus.shift
26
+
27
+ while required_qty.positive? && (sku = skus.shift)
24
28
  count, remainder = (required_qty - order.quantity).divmod(sku.quantity)
29
+ count, remainder = adjustment_for_skus_with_min_quantity(sku, count, remainder)
30
+
25
31
  order.add(sku, count: count) unless count.zero?
26
- orders << (remainder.zero? ? order.dup : order.dup.add(sku))
32
+
33
+ orders << (remainder.positive? ? order.dup.add(sku) : order.dup)
34
+
35
+ order = OrderOptimizer::Order.new if sku.min_quantity
27
36
  end
28
37
  end
29
38
  end
39
+
40
+ def adjustment_for_skus_with_min_quantity(sku, count, remainder)
41
+ units = count * sku.quantity
42
+
43
+ return count, remainder if sku.min_quantity.nil? || units >= sku.min_quantity
44
+
45
+ delta = ((sku.min_quantity - units).to_f / sku.quantity).ceil
46
+ [count + delta, remainder - (delta * sku.quantity)]
47
+ end
30
48
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: order_optimizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - crispymtn
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-12-13 00:00:00.000000000 Z
12
+ date: 2019-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler