Skip to content

Commit ace50ed

Browse files
committed
Orders are made up of optimal pack combinations
1 parent 4b96f15 commit ace50ed

File tree

2 files changed

+96
-61
lines changed

2 files changed

+96
-61
lines changed

lib/order_line.rb

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,65 +11,124 @@ def initialize(order_qty, order_item)
1111
end
1212

1313
def optimal(product)
14+
puts "Quantity: #{order_qty}"
1415
pack_qtys = []
1516
@order_item.packs(product).each { |p| pack_qtys << [p.qty, p.price] }
17+
puts "Packs: #{pack_qtys}"
1618

17-
whole_packs = whole_packs(pack_qtys, [], order_qty)
19+
# Get Exact matches
20+
exact_matches = []
21+
pack_qtys.sort { |a, b| b <=> a }.each do |p, v|
22+
next unless (order_qty / p) * p == order_qty
23+
exact_matches << [order_qty / p, p, v]
24+
end
25+
# puts exact_matches
1826

19-
return whole_packs[0] if whole_packs[1].zero?
27+
# Check for the optimim price
28+
price_check = []
29+
exact_matches.each do |x, y, z|
30+
val = (x * z)
2031

21-
total_packs = left_over_items(pack_qtys, whole_packs[0], whole_packs[1])
32+
price_check << [x, y, z, val]
33+
end
34+
# puts price_check
2235

23-
total_packs[0]
24-
end
36+
numbers = price_check.select { |x| x[3] }.map
2537

26-
def present_line(packs)
27-
sub_total = []
28-
sub_item = []
29-
packs.each do |k, v, i|
30-
sub_total << k * i
31-
sub_item << "#{k}x #{v} packs @ #{i}"
38+
# Get Part matches
39+
partial_matches = []
40+
pack_qtys.sort { |a, b| b <=> a }.each do |p, v|
41+
partial_matches << [order_qty / p, p, v]
42+
end
43+
puts "partial: #{partial_matches}"
44+
45+
# Top up part matches
46+
exact_partial = []
47+
partial_matches.each do |x, y, z|
48+
val = order_qty - (x * y)
49+
puts "VAL: #{val}"
50+
pack_qtys.detect do |a|
51+
# To be optimised: a.include?(val) does not cooperate with Money
52+
if a[0] == val || a[0] * 3 == val
53+
exact_partial << [x, y, z]
54+
exact_partial << [val / a[0], a[0], a[1]]
55+
end
56+
end
3257
end
58+
puts "exact partial: #{exact_partial}"
3359

34-
@line_total = sub_total.inject(0) { |sum, x| sum + x }
60+
# Check for the optimim partial price
61+
partial_price_check = []
62+
exact_partial.each do |x, y, z|
63+
val = (x * z)
3564

36-
sub_item.each { |s| s }
37-
end
65+
partial_price_check << [x, y, z, val]
66+
end
67+
# puts partial_price_check
3868

39-
def presenter_line_total
40-
@line_total
41-
end
69+
partial_numbers = partial_price_check.select { |x| x[3] }.map
4270

43-
private
71+
partial_price_array = []
72+
partial_numbers.each do |f|
73+
partial_price_array << f
74+
end
75+
# puts partial_price_array
4476

45-
def whole_packs(pack_qtys, packs, left_over_qty)
46-
pack_qtys.sort { |a, b| b <=> a }.each do |p, v|
47-
volume = left_over_qty.to_f / p
77+
puts "Exact Match: #{exact_matches}"
78+
# Array for best price line: 1x 9pk @16 = $16
79+
puts "Best Exact Price: #{numbers.min}"
80+
puts "Partial Match: #{exact_partial}"
81+
puts "Best Partial Price: #{partial_price_array}"
4882

49-
next unless volume >= 1
83+
return calculate_best(numbers, partial_price_array) unless price_check.empty?
5084

51-
left_over_qty -= volume.to_int * p
85+
calculate_best(numbers.min, partial_price_array)
86+
end
5287

53-
packs << [volume.to_int, p, v]
88+
def calculate_best(exact, partial)
89+
if exact.nil?
90+
puts "\nThis is it #{partial}"
91+
return partial
5492
end
5593

56-
[packs, left_over_qty]
57-
end
94+
if partial.empty?
95+
puts "\nThis is it #{exact}"
96+
return exact
97+
end
5898

59-
def left_over_items(pack_qtys, packs, left_over_qty)
60-
pack_qtys.sort { |a, b| a <=> b }.each do |p, v|
61-
break if left_over_qty.zero?
99+
sub_total = []
100+
partial.each do |_x, _y, _z, val|
101+
sub_total << val
102+
end
62103

63-
next unless left_over_qty <= p
104+
# Find total cost of sub_items
105+
line_total = sub_total.inject(:+)
64106

65-
packs.map { |a| a[0] += 1 if a[1].to_i == p }
66-
left_over_qty = 0
107+
# Return array of the cheapest line
108+
if [exact.min[3], line_total].min == line_total
109+
puts "\nThis is it #{partial}"
110+
return partial
111+
end
67112

68-
next if packs.detect { |a| a[1] == p }
113+
puts "\nThis is it #{exact}" if [exact.min[3], line_total].min == exact
114+
exact
115+
end
69116

70-
packs << [1, p, v]
117+
def present_line(packs)
118+
puts "pACKS: #{packs}"
119+
sub_total = []
120+
sub_item = []
121+
packs.each do |k, v, i|
122+
sub_total << k * i
123+
sub_item << "#{k}x #{v} packs @ #{i}"
71124
end
72125

73-
[packs, left_over_qty]
126+
@line_total = sub_total.inject(:+)
127+
128+
sub_item.each { |s| s }
129+
end
130+
131+
def presenter_line_total
132+
@line_total
74133
end
75134
end

spec/order_line_spec.rb

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,10 @@
2525
end
2626

2727
describe '#optimal' do
28-
it 'should return total_packs[0] which is an Array' do
28+
it 'should return optimum combination of packs which is an Enumerator' do
2929
instance = OrderLine.new(12, Item.new('watermelons'))
3030

31-
expect(instance.optimal(instance.order_item.name)).to be_a Array
32-
end
33-
34-
it 'should return whole_packs[0] which is an Array' do
35-
instance = OrderLine.new(10, Item.new('watermelons'))
36-
37-
expect(instance.optimal(instance.order_item.name)).to be_a Array
31+
expect(instance.optimal(instance.order_item.name)).to be_a Enumerator
3832
end
3933
end
4034

@@ -54,22 +48,4 @@
5448
expect(instance.presenter_line_total).to be_a Money
5549
end
5650
end
57-
58-
describe '#whole_packs' do
59-
it 'should return an Array' do
60-
instance = OrderLine.new(12, Item.new('watermelons'))
61-
pack_qtys = [[3, 6.99], [5, 8.99]]
62-
63-
expect(instance.send(:whole_packs, pack_qtys, [], @order_qty)).to be_a Array
64-
end
65-
end
66-
67-
describe '#left_over_items' do
68-
it 'should return an Array' do
69-
instance = OrderLine.new(12, Item.new('watermelons'))
70-
pack_qtys = [[3, 6.99], [5, 8.99]]
71-
72-
expect(instance.send(:left_over_items, pack_qtys, [], 1)).to be_a Array
73-
end
74-
end
7551
end

0 commit comments

Comments
 (0)