Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c885341

Browse files
committed
start on a Product model with an allocate fn [product_aggregate]
1 parent 7959ae6 commit c885341

File tree

10 files changed

+75
-206
lines changed

10 files changed

+75
-206
lines changed

src/allocation/adapters/orm.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@
1717
Column('orderid', String(255)),
1818
)
1919

20+
products = Table(
21+
'products', metadata,
22+
Column('sku', String(255), primary_key=True),
23+
# Column('version_number', Integer, nullable=False, default=0),
24+
)
25+
2026
batches = Table(
2127
'batches', metadata,
2228
Column('id', Integer, primary_key=True, autoincrement=True),
2329
Column('reference', String(255)),
24-
Column('sku', String(255)),
30+
Column('sku', ForeignKey('products.sku')),
2531
Column('_purchased_quantity', Integer, nullable=False),
2632
Column('eta', Date, nullable=True),
2733
)
@@ -36,10 +42,13 @@
3642

3743
def start_mappers():
3844
lines_mapper = mapper(model.OrderLine, order_lines)
39-
mapper(model.Batch, batches, properties={
45+
batches_mapper = mapper(model.Batch, batches, properties={
4046
'_allocations': relationship(
4147
lines_mapper,
4248
secondary=allocations,
4349
collection_class=set,
4450
)
4551
})
52+
mapper(model.Product, products, properties={
53+
'batches': relationship(batches_mapper)
54+
})

src/allocation/adapters/repository.py

+6-10
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import abc
22
from allocation.domain import model
33

4-
54
class AbstractRepository(abc.ABC):
65

76
@abc.abstractmethod
8-
def add(self, batch: model.Batch):
7+
def add(self, product: model.Product):
98
raise NotImplementedError
109

1110
@abc.abstractmethod
12-
def get(self, reference) -> model.Batch:
11+
def get(self, sku) -> model.Product:
1312
raise NotImplementedError
1413

1514

@@ -19,11 +18,8 @@ class SqlAlchemyRepository(AbstractRepository):
1918
def __init__(self, session):
2019
self.session = session
2120

22-
def add(self, batch):
23-
self.session.add(batch)
24-
25-
def get(self, reference):
26-
return self.session.query(model.Batch).filter_by(reference=reference).one()
21+
def add(self, product):
22+
self.session.add(product)
2723

28-
def list(self):
29-
return self.session.query(model.Batch).all()
24+
def get(self, sku):
25+
return self.session.query(model.Product).filter_by(sku=sku).first()

src/allocation/domain/model.py

+15-9
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@ class OutOfStock(Exception):
88
pass
99

1010

11-
def allocate(line: OrderLine, batches: List[Batch]) -> str:
12-
try:
13-
batch = next(
14-
b for b in sorted(batches) if b.can_allocate(line)
15-
)
16-
batch.allocate(line)
17-
return batch.reference
18-
except StopIteration:
19-
raise OutOfStock(f'Out of stock for sku {line.sku}')
11+
class Product:
12+
13+
def __init__(self, sku: str, batches: List[Batch]):
14+
self.sku = sku
15+
self.batches = batches
16+
17+
def allocate(self, line: OrderLine) -> str:
18+
try:
19+
batch = next(
20+
b for b in sorted(self.batches) if b.can_allocate(line)
21+
)
22+
batch.allocate(line)
23+
return batch.reference
24+
except StopIteration:
25+
raise OutOfStock(f'Out of stock for sku {line.sku}')
2026

2127

2228
@dataclass(unsafe_hash=True)

src/allocation/service_layer/services.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ class InvalidSku(Exception):
1111
pass
1212

1313

14-
def is_valid_sku(sku, batches):
15-
return sku in {b.sku for b in batches}
16-
17-
1814
def add_batch(
1915
ref: str, sku: str, qty: int, eta: Optional[date],
2016
uow: unit_of_work.AbstractUnitOfWork
2117
):
2218
with uow:
23-
uow.batches.add(model.Batch(ref, sku, qty, eta))
19+
product = uow.products.get(sku=sku)
20+
if product is None:
21+
product = model.Product(sku, batches=[])
22+
uow.products.add(product)
23+
product.batches.append(model.Batch(ref, sku, qty, eta))
2424
uow.commit()
2525

2626

@@ -30,9 +30,9 @@ def allocate(
3030
) -> str:
3131
line = OrderLine(orderid, sku, qty)
3232
with uow:
33-
batches = uow.batches.list()
34-
if not is_valid_sku(line.sku, batches):
33+
product = uow.products.get(sku=line.sku)
34+
if product is None:
3535
raise InvalidSku(f'Invalid sku {line.sku}')
36-
batchref = model.allocate(line, batches)
36+
batchref = product.allocate(line)
3737
uow.commit()
3838
return batchref

src/allocation/service_layer/unit_of_work.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111

1212
class AbstractUnitOfWork(abc.ABC):
13-
batches: repository.AbstractRepository
13+
products: repository.AbstractRepository
1414

1515
def __enter__(self) -> AbstractUnitOfWork:
1616
return self
@@ -39,7 +39,7 @@ def __init__(self, session_factory=DEFAULT_SESSION_FACTORY):
3939

4040
def __enter__(self):
4141
self.session = self.session_factory() # type: Session
42-
self.batches = repository.SqlAlchemyRepository(self.session)
42+
self.products = repository.SqlAlchemyRepository(self.session)
4343
return super().__enter__()
4444

4545
def __exit__(self, *args):

tests/integration/test_orm.py

-90
This file was deleted.

tests/integration/test_repository.py

-64
This file was deleted.

tests/integration/test_uow.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55

66
def insert_batch(session, ref, sku, qty, eta):
7+
session.execute(
8+
'INSERT INTO products (sku) VALUES (:sku)',
9+
dict(sku=sku),
10+
)
711
session.execute(
812
'INSERT INTO batches (reference, sku, _purchased_quantity, eta)'
913
' VALUES (:ref, :sku, :qty, :eta)',
@@ -31,9 +35,9 @@ def test_uow_can_retrieve_a_batch_and_allocate_to_it(session_factory):
3135

3236
uow = unit_of_work.SqlAlchemyUnitOfWork(session_factory)
3337
with uow:
34-
batch = uow.batches.get(reference='batch1')
38+
product = uow.products.get(sku='HIPSTER-WORKBENCH')
3539
line = model.OrderLine('o1', 'HIPSTER-WORKBENCH', 10)
36-
batch.allocate(line)
40+
product.allocate(line)
3741
uow.commit()
3842

3943
batchref = get_allocated_batch_ref(session, 'o1', 'HIPSTER-WORKBENCH')
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from datetime import date, timedelta
22
import pytest
3-
from allocation.domain.model import allocate, OrderLine, Batch, OutOfStock
3+
from allocation.domain.model import Product, OrderLine, Batch, OutOfStock
44

55
today = date.today()
66
tomorrow = today + timedelta(days=1)
77
later = tomorrow + timedelta(days=10)
88

9-
def test_prefers_current_stock_batches_to_shipments():
9+
def test_prefers_warehouse_batches_to_shipments():
1010
in_stock_batch = Batch("in-stock-batch", "RETRO-CLOCK", 100, eta=None)
1111
shipment_batch = Batch("shipment-batch", "RETRO-CLOCK", 100, eta=tomorrow)
12+
product = Product(sku="RETRO-CLOCK", batches=[in_stock_batch, shipment_batch])
1213
line = OrderLine("oref", "RETRO-CLOCK", 10)
1314

14-
allocate(line, [in_stock_batch, shipment_batch])
15+
product.allocate(line)
1516

1617
assert in_stock_batch.available_quantity == 90
1718
assert shipment_batch.available_quantity == 100
@@ -21,9 +22,10 @@ def test_prefers_earlier_batches():
2122
earliest = Batch("speedy-batch", "MINIMALIST-SPOON", 100, eta=today)
2223
medium = Batch("normal-batch", "MINIMALIST-SPOON", 100, eta=tomorrow)
2324
latest = Batch("slow-batch", "MINIMALIST-SPOON", 100, eta=later)
25+
product = Product(sku="MINIMALIST-SPOON", batches=[medium, earliest, latest])
2426
line = OrderLine("order1", "MINIMALIST-SPOON", 10)
2527

26-
allocate(line, [medium, earliest, latest])
28+
product.allocate(line)
2729

2830
assert earliest.available_quantity == 90
2931
assert medium.available_quantity == 100
@@ -34,13 +36,15 @@ def test_returns_allocated_batch_ref():
3436
in_stock_batch = Batch("in-stock-batch-ref", "HIGHBROW-POSTER", 100, eta=None)
3537
shipment_batch = Batch("shipment-batch-ref", "HIGHBROW-POSTER", 100, eta=tomorrow)
3638
line = OrderLine("oref", "HIGHBROW-POSTER", 10)
37-
allocation = allocate(line, [in_stock_batch, shipment_batch])
39+
product = Product(sku="HIGHBROW-POSTER", batches=[in_stock_batch, shipment_batch])
40+
allocation = product.allocate(line)
3841
assert allocation == in_stock_batch.reference
3942

4043

4144
def test_raises_out_of_stock_exception_if_cannot_allocate():
4245
batch = Batch('batch1', 'SMALL-FORK', 10, eta=today)
43-
allocate(OrderLine('order1', 'SMALL-FORK', 10), [batch])
46+
product = Product(sku="SMALL-FORK", batches=[batch])
47+
product.allocate(OrderLine('order1', 'SMALL-FORK', 10))
4448

4549
with pytest.raises(OutOfStock, match='SMALL-FORK'):
46-
allocate(OrderLine('order2', 'SMALL-FORK', 1), [batch])
50+
product.allocate(OrderLine('order2', 'SMALL-FORK', 1))

0 commit comments

Comments
 (0)