order_optimizer 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +29 -1
- data/lib/order_optimizer/catalog.rb +8 -0
- data/lib/order_optimizer/sku.rb +15 -2
- data/lib/order_optimizer/version.rb +1 -1
- data/lib/order_optimizer.rb +24 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3dec3fd3cab38f6a56c1fa13e3ee398e4b196c1c992cdf6f4c7e87c2c19e2f1
|
4
|
+
data.tar.gz: 9cd52e4ae25d980de2b8b4ba1988bbe42dac0dd686d0d1c7f23584c7889cb3e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 958f1f99429e00465cdc9a562dff1717da0b145827430704766756626444559e25f1f0b615c7f19cb274bc1783e33fb2eb3ca34b8e4d114e08af5f01f75c05f2
|
7
|
+
data.tar.gz: 0cfb7dc00f8eacff31bff96cfedee003427e9e9e4677f8a382f7aa28e0148de5ca902ca576697aeba54c3b28a19a3302f451fb0944dc96464908742a9328287f
|
data/CHANGELOG.md
CHANGED
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.
|
data/lib/order_optimizer/sku.rb
CHANGED
@@ -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
|
data/lib/order_optimizer.rb
CHANGED
@@ -10,21 +10,39 @@ class OrderOptimizer
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def cheapest_order(required_qty:)
|
13
|
-
|
14
|
-
.
|
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
|
-
|
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
|
-
|
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.
|
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-
|
12
|
+
date: 2019-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|