order_optimizer 0.4.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5868b03e4f28cfec6001c9e53e61ffba0907c6d626d4ee950596bf4b1f154075
4
- data.tar.gz: 739453efda8bcfe9e015f91a47b0cf8945d0c9270fe8f18dc6241a71c5d35e63
3
+ metadata.gz: 21b6b1f9358d26572d0f3fe4d2a237c2da2a217bbb5f2d241d7a98683a9a43e1
4
+ data.tar.gz: 29987d3e12a263c8f18acdcf6c1972dcfa91204ece002e1a778ccc72a38a2843
5
5
  SHA512:
6
- metadata.gz: eebd782f5bc6971227292700da42edb7c30a3bca2d19573f65f901311b708aa91fd422ad1ca51a17798d28351e89a020eb1857b7ff7671892874f9e67fbe1429
7
- data.tar.gz: d4f80534e745ed6723468c7bf7b49ce5fd5d45fb6db915b756601aa7b00894d10550baeb16bba5da96f4bc24c01e5638ab7ab79ac07ca6eb0bb6576aa4648663
6
+ metadata.gz: 32b08adac0ba68f5133e60506b72a2f969d2e5617fd02ab16e922a05088e0afb81294041d6cbf6762dc70e8877a522733dc6160bb5583a30f744d67668efbee7
7
+ data.tar.gz: e8808b19221863663091640750d3765fccbf9dea0a4191f8cb9b1d13c46f49a3fa47f7ba8f906efaaa7bf5f3a6f7f13873ccdf19a59fcd32e81025b409676300
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # OrderOptimizer Gem Changelog
2
2
 
3
+ ## 0.5.0 (2021-04-15)
4
+ - Add `maximum_quantity` for SKUs
5
+
3
6
  ## 0.4.1 (2021-02-15)
4
7
 
5
8
  - Fix issue with floating point conversion [#3]
data/README.md CHANGED
@@ -72,7 +72,7 @@ cheapest_exact_order_for_58_units.total # => 322
72
72
  cheapest_exact_order_for_58_units.skus # => { '10-pack' => 5, '1-pack' => 8 }
73
73
  ```
74
74
 
75
- It is possible to define dicount prices a minimum quantity:
75
+ It is possible to define discount prices with a minimum quantity:
76
76
 
77
77
  ```ruby
78
78
  order_optimizer = OrderOptimizer.new(
@@ -100,6 +100,17 @@ order_optimizer.cheapest_order(required_qty: 29).skus
100
100
  #=> { '20-pack' => 1, '10-discount' => 10 }
101
101
  ```
102
102
 
103
+ It is also possible to define discount prices with a maximum quantity:
104
+ ```ruby
105
+ order_optimizer = OrderOptimizer.new(
106
+ '1-pack' => { quantity: 1, price_per_unit: 9 },
107
+ '10-pack' => { quantity: 10, price_per_unit: 8, max_quantity: 20 },
108
+ )
109
+
110
+ order_optimizer.cheapest_order(required_qty: 30).skus
111
+ #=> { '10-pack' => 2, '1-pack' => 10 }
112
+ ```
113
+
103
114
  If you're just interested in all possible order combinations:
104
115
 
105
116
  ```ruby
@@ -32,19 +32,19 @@ class OrderOptimizer
32
32
 
33
33
  skus.each do |sku|
34
34
  orders.reject(&:complete?).each do |order|
35
- count, remainder = count_and_remainder_for_sku(order.missing_qty, sku)
35
+ count, remainder, skip_increase = count_and_remainder_for_sku(order.missing_qty, sku)
36
36
 
37
37
  orders << order.dup.add(sku, count: count) unless count.zero?
38
- orders << order.dup.add(sku, count: count + 1) unless remainder.zero?
38
+ orders << order.dup.add(sku, count: count + 1) unless remainder.zero? || skip_increase
39
39
  end
40
40
 
41
- count, remainder = count_and_remainder_for_sku(required_qty, sku)
41
+ count, remainder, skip_increase = count_and_remainder_for_sku(required_qty, sku)
42
42
 
43
43
  unless count.zero?
44
44
  orders << OrderOptimizer::Order.new(required_qty: required_qty).add(sku, count: count)
45
45
  end
46
46
 
47
- unless remainder.zero?
47
+ unless remainder.zero? || skip_increase
48
48
  orders << OrderOptimizer::Order.new(required_qty: required_qty).add(sku, count: count + 1)
49
49
  end
50
50
  end
@@ -55,12 +55,18 @@ class OrderOptimizer
55
55
  def count_and_remainder_for_sku(quantity, sku)
56
56
  count, remainder = quantity.divmod(sku.quantity)
57
57
 
58
- if sku.min_quantity && count * sku.quantity < sku.min_quantity
58
+ if sku.max_quantity && count * sku.quantity > sku.max_quantity
59
+ new_count = (sku.max_quantity / sku.quantity).ceil
60
+ remainder = (count - new_count) * sku.quantity
61
+
62
+ [new_count, [remainder, 0].max, true]
63
+ elsif sku.min_quantity && count * sku.quantity < sku.min_quantity
59
64
  new_count = (sku.min_quantity / sku.quantity).ceil
60
65
  remainder -= (new_count - count) * sku.quantity
61
- [new_count, [remainder, 0].max]
66
+
67
+ [new_count, [remainder, 0].max, false]
62
68
  else
63
- [count, remainder]
69
+ [count, remainder, false]
64
70
  end
65
71
  end
66
72
  end
@@ -1,16 +1,19 @@
1
1
  class OrderOptimizer
2
2
  class Sku
3
- attr_reader :id, :quantity, :price_per_unit, :price_per_sku, :min_quantity
3
+ attr_reader :id, :quantity, :price_per_unit, :price_per_sku, :min_quantity, :max_quantity
4
4
 
5
- def initialize(id, quantity:, price_per_sku: nil, price_per_unit: nil, min_quantity: nil)
5
+ def initialize(id, quantity:, price_per_sku: nil, price_per_unit: nil, min_quantity: nil, max_quantity: nil)
6
6
  @id = id
7
7
  @quantity = BigDecimal(quantity, 2)
8
8
  @min_quantity = BigDecimal(min_quantity, 2) if min_quantity
9
+ @max_quantity = BigDecimal(max_quantity, 2) if max_quantity
9
10
 
10
- unless price_per_unit || price_per_sku
11
- raise ':price_per_sku or :price_per_unit must be set'
11
+ if min_quantity && max_quantity && (min_quantity > max_quantity)
12
+ raise ArgumentError, "min_quantity can't be larger than max_quantity"
12
13
  end
13
14
 
15
+ raise ArgumentError, ':price_per_sku or :price_per_unit must be set' unless price_per_unit || price_per_sku
16
+
14
17
  @price_per_unit = BigDecimal(price_per_unit, 2) if price_per_unit
15
18
  @price_per_unit ||= BigDecimal(price_per_sku, 2) / quantity
16
19
 
@@ -1,3 +1,3 @@
1
1
  class OrderOptimizer
2
- VERSION = "0.4.1".freeze
2
+ VERSION = "0.5.0".freeze
3
3
  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.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - crispymtn
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-02-15 00:00:00.000000000 Z
13
+ date: 2021-04-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  requirements: []
104
- rubygems_version: 3.2.3
104
+ rubygems_version: 3.2.15
105
105
  signing_key:
106
106
  specification_version: 4
107
107
  summary: Helps to optimize orders if the goods are offered in different pack sizes