Skip to content

Commit 0e511f7

Browse files
committed
back to classes
1 parent 4219cdd commit 0e511f7

File tree

2 files changed

+142
-96
lines changed

2 files changed

+142
-96
lines changed

domain_model.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
1-
def allocate(order, stock, shipments):
2-
allocation = {}
3-
for source in shipments + [stock]:
4-
allocation.update(allocation_from(order, source))
5-
return allocation
6-
7-
def allocation_from(order, source):
8-
return {
9-
sku: source
10-
for sku, quantity in order.items()
11-
if sku in source
12-
and source[sku] > quantity
13-
}
1+
class Order(dict):
2+
3+
def allocate(self, stock, shipments):
4+
self.allocation = self.find_allocation(stock, shipments)
5+
self.decrement_source_quantities()
6+
7+
def find_allocation(self, stock, shipments):
8+
ordered_sources = [stock] + sorted(shipments)
9+
allocation = {}
10+
for source in reversed(ordered_sources):
11+
allocation.update(source.allocation_for(self))
12+
return allocation
13+
14+
def decrement_source_quantities(self):
15+
for sku, source in self.allocation.items():
16+
source[sku] -= self[sku]
17+
18+
19+
20+
class Stock(dict):
21+
22+
def allocation_for(self, order):
23+
return {
24+
sku: self
25+
for sku, quantity in order.items()
26+
if sku in self
27+
and self[sku] > quantity
28+
}
29+
30+
31+
class Shipment(Stock):
32+
33+
def __init__(self, d, eta):
34+
self.eta = eta
35+
super().__init__(d)
36+
37+
def __lt__(self, other):
38+
return self.eta < other.eta
39+
40+

test_allocation.py

Lines changed: 102 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,172 @@
1-
from domain_model import allocate
1+
from domain_model import Order, Stock, Shipment
2+
from datetime import date, timedelta
3+
4+
today = date.today()
5+
tomorrow = today + timedelta(days=1)
6+
later = tomorrow + timedelta(days=10)
27

38

49
def test_can_allocate_to_stock():
5-
order = {'a-sku': 10}
6-
stock = {'a-sku': 1000}
10+
order = Order({'a-sku': 10})
11+
stock = Stock({'a-sku': 1000})
712

8-
allocations = allocate(order, stock, shipments=[])
13+
order.allocate(stock, shipments=[])
914

10-
assert allocations['a-sku'] == stock
15+
assert order.allocation['a-sku'] == stock
16+
assert stock['a-sku'] == 990
1117

1218

1319
def test_can_allocate_to_shipment():
14-
order = {'a-sku': 10}
15-
shipment = {'a-sku': 1000}
20+
order = Order({'a-sku': 10})
21+
shipment = Shipment({'a-sku': 1000}, eta=tomorrow)
1622

17-
allocations = allocate(order, stock={}, shipments=[shipment])
23+
order.allocate(stock=Stock({}), shipments=[shipment])
1824

19-
assert allocations['a-sku'] == shipment
25+
assert order.allocation['a-sku'] == shipment
26+
assert shipment['a-sku'] == 990
2027

2128

2229
def test_ignores_irrelevant_stock():
23-
order = {'sku1': 10}
24-
stock = {'sku2': 1000}
25-
shipment = {'sku1': 1000}
30+
order = Order({'sku1': 10})
31+
stock = Stock({'sku2': 1000})
32+
shipment = Shipment({'sku1': 1000}, eta=tomorrow)
33+
34+
order.allocate(stock=stock, shipments=[shipment])
2635

27-
allocations = allocate(order, stock=stock, shipments=[shipment])
36+
assert order.allocation['sku1'] == shipment
2837

29-
assert allocations['sku1'] == shipment
3038

3139

3240
def test_can_allocate_to_correct_shipment():
33-
order = {'sku2': 10}
34-
shipment1 = {'sku1': 1000}
35-
shipment2 = {'sku2': 1000}
41+
order = Order({'sku2': 10})
42+
shipment1 = Shipment({'sku1': 1000}, eta=tomorrow)
43+
shipment2 = Shipment({'sku2': 1000}, eta=tomorrow)
3644

37-
allocations = allocate(order, stock={}, shipments=[shipment1, shipment2])
45+
order.allocate(stock=Stock({}), shipments=[shipment1, shipment2])
3846

39-
assert allocations['sku2'] == shipment2
47+
assert order.allocation['sku2'] == shipment2
4048

4149

4250
def test_allocates_to_stock_in_preference_to_shipment():
43-
order = {'sku1': 10}
44-
stock = {'sku1': 1000}
45-
shipment = {'sku1': 1000}
51+
order = Order({'sku1': 10})
52+
stock = Stock({'sku1': 1000})
53+
shipment = Shipment({'sku1': 1000}, eta=tomorrow)
4654

47-
allocations = allocate(order, stock, shipments=[shipment])
55+
order.allocate(stock, shipments=[shipment])
4856

49-
assert allocations['sku1'] == stock
57+
assert order.allocation['sku1'] == stock
58+
assert stock['sku1'] == 990
59+
assert shipment['sku1'] == 1000
5060

5161

5262
def test_can_allocate_multiple_lines_to_wh():
53-
order = {'sku1': 5, 'sku2': 10}
54-
stock = {'sku1': 1000, 'sku2': 1000}
63+
order = Order({'sku1': 5, 'sku2': 10})
64+
stock = Stock({'sku1': 1000, 'sku2': 1000})
5565

56-
allocations = allocate(order, stock, shipments=[])
57-
assert allocations['sku1'] == stock
58-
assert allocations['sku2'] == stock
66+
order.allocate(stock, shipments=[])
67+
assert order.allocation['sku1'] == stock
68+
assert order.allocation['sku2'] == stock
69+
assert stock['sku1'] == 995
70+
assert stock['sku2'] == 990
5971

6072

6173
def test_can_allocate_multiple_lines_to_shipment():
62-
order = {'sku1': 5, 'sku2': 10}
63-
shipment = {'sku1': 1000, 'sku2': 1000}
74+
order = Order({'sku1': 5, 'sku2': 10})
75+
shipment = Shipment({'sku1': 1000, 'sku2': 1000}, eta=tomorrow)
6476

65-
allocations = allocate(order, stock={}, shipments=[shipment])
77+
order.allocate(stock=Stock({}), shipments=[shipment])
6678

67-
assert allocations['sku1'] == shipment
68-
assert allocations['sku2'] == shipment
79+
assert order.allocation['sku1'] == shipment
80+
assert order.allocation['sku2'] == shipment
81+
assert shipment['sku1'] == 995
82+
assert shipment['sku2'] == 990
6983

7084

7185
def test_can_allocate_to_both():
72-
order = {'sku1': 5, 'sku2': 10}
73-
shipment = {'sku2': 1000}
74-
stock = {'sku1': 1000}
86+
order = Order({'sku1': 5, 'sku2': 10})
87+
shipment = Shipment({'sku2': 1000}, eta=tomorrow)
88+
stock = Stock({'sku1': 1000})
7589

76-
allocations = allocate(order, stock, shipments=[shipment])
90+
order.allocate(stock, shipments=[shipment])
7791

78-
assert allocations['sku1'] == stock
79-
assert allocations['sku2'] == shipment
92+
assert order.allocation['sku1'] == stock
93+
assert order.allocation['sku2'] == shipment
94+
assert stock['sku1'] == 995
95+
assert shipment['sku2'] == 990
8096

8197

8298
def test_can_allocate_to_both_preferring_stock():
83-
order = {'sku1': 1, 'sku2': 2, 'sku3': 3, 'sku4': 4}
84-
shipment = {'sku1': 1000, 'sku2': 1000, 'sku3': 1000}
85-
stock = {'sku3': 1000, 'sku4': 1000}
99+
order = Order({'sku1': 1, 'sku2': 2, 'sku3': 3, 'sku4': 4})
100+
shipment = Shipment({'sku1': 1000, 'sku2': 1000, 'sku3': 1000}, eta=tomorrow)
101+
stock = Stock({'sku3': 1000, 'sku4': 1000})
86102

87-
allocations = allocate(order, stock, shipments=[shipment])
103+
order.allocate(stock, shipments=[shipment])
88104

89-
assert allocations['sku1'] == shipment
90-
assert allocations['sku2'] == shipment
91-
assert allocations['sku3'] == stock
92-
assert allocations['sku4'] == stock
105+
assert order.allocation['sku1'] == shipment
106+
assert order.allocation['sku2'] == shipment
107+
assert order.allocation['sku3'] == stock
108+
assert order.allocation['sku4'] == stock
109+
assert shipment['sku1'] == 999
110+
assert shipment['sku2'] == 998
111+
assert shipment['sku3'] == 1000
112+
assert stock['sku3'] == 997
113+
assert stock['sku4'] == 996
93114

94115

95-
def test_allocated_to_first_suitable_shipment_in_list():
96-
order = {'sku1': 10, 'sku2': 10}
97-
shipment1 = {'sku1': 1000, 'sku2': 1000}
98-
shipment2 = {'sku1': 1000, 'sku2': 1000}
99-
stock = {}
116+
def test_allocated_to_earliest_suitable_shipment_in_list():
117+
order = Order({'sku1': 10, 'sku2': 10})
118+
shipment1 = Shipment({'sku1': 1000, 'sku2': 1000}, eta=today)
119+
shipment2 = Shipment({'sku1': 1000, 'sku2': 1000}, eta=tomorrow)
120+
stock = Stock({})
100121

101-
allocations = allocate(order, stock, shipments=[shipment1, shipment2])
122+
order.allocate(stock, shipments=[shipment1, shipment2])
102123

103-
assert allocations['sku1'] == shipment1
104-
assert allocations['sku2'] == shipment1
124+
assert order.allocation['sku1'] == shipment1
125+
assert order.allocation['sku2'] == shipment1
105126

106127

107-
def test_still_preserves_ordering_if_split_across_shipments():
108-
order = {'sku1': 10, 'sku2': 10, 'sku3': 10}
109-
shipment1 = {'sku1': 1000}
110-
shipment2 = {'sku2': 1000, 'sku3': 1000}
111-
shipment3 = {'sku2': 1000, 'sku3': 1000}
112-
stock = {}
128+
def test_still_chooses_earliest_if_split_across_shipments():
129+
order = Order({'sku1': 10, 'sku2': 10, 'sku3': 10})
130+
shipment1 = Shipment({'sku1': 1000}, eta=today)
131+
shipment2 = Shipment({'sku2': 1000, 'sku3': 1000}, eta=tomorrow)
132+
shipment3 = Shipment({'sku2': 1000, 'sku3': 1000}, eta=later)
133+
stock = Stock({})
113134

114-
allocations = allocate(order, stock, shipments=[shipment1, shipment2, shipment3])
135+
order.allocate(stock, shipments=[shipment2, shipment3, shipment1])
115136

116-
assert allocations['sku1'] == shipment1
117-
assert allocations['sku2'] == shipment2
118-
assert allocations['sku3'] == shipment2
137+
assert order.allocation['sku1'] == shipment1
138+
assert order.allocation['sku2'] == shipment2
139+
assert order.allocation['sku3'] == shipment2
119140

120141

121142
def test_stock_not_quite_enough_means_we_use_shipment():
122-
order = {'sku1': 10, 'sku2': 10}
123-
stock = {'sku1': 10, 'sku2': 5}
124-
shipment = {
143+
order = Order({'sku1': 10, 'sku2': 10})
144+
stock = Stock({'sku1': 10, 'sku2': 5})
145+
shipment = Shipment({
125146
'sku1': 1000,
126147
'sku2': 1000,
127-
}
148+
}, eta=tomorrow)
128149

129-
allocations = allocate(order, stock, shipments=[shipment])
150+
order.allocate(stock, shipments=[shipment])
130151

131-
assert allocations['sku1'] == shipment
132-
assert allocations['sku2'] == shipment
152+
assert order.allocation['sku1'] == shipment
153+
assert order.allocation['sku2'] == shipment
133154

134155

135156
def test_cannot_allocate_if_insufficent_quantity_in_stock():
136-
order = {'a-sku': 10}
137-
stock = {'a-sku': 5}
157+
order = Order({'a-sku': 10})
158+
stock = Stock({'a-sku': 5})
138159

139-
allocations = allocate(order, stock, shipments=[])
160+
order.allocate(stock, shipments=[])
140161

141-
assert 'a-sku' not in allocations
162+
assert 'a-sku' not in order.allocation
142163

143164

144165
def test_cannot_allocate_if_insufficent_quantity_in_shipment():
145-
order = {'a-sku': 10}
146-
shipment = {
147-
'a-sku': 5,
148-
}
166+
order = Order({'a-sku': 10})
167+
shipment = Shipment({'a-sku': 5}, eta=tomorrow)
149168

150-
allocations = allocate(order, stock={}, shipments=[shipment])
169+
order.allocate(stock=Stock({}), shipments=[shipment])
151170

152-
assert 'a-sku' not in allocations
171+
assert 'a-sku' not in order.allocation
153172

0 commit comments

Comments
 (0)