feat: add basic support to "resolve" a pending product
This commit is contained in:
parent
6b4bc3da10
commit
1ee398e8fb
17 changed files with 780 additions and 69 deletions
|
@ -1219,7 +1219,7 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.assertEqual(local.full_name, "Chuck Norris")
|
||||
self.assertEqual(local.phone_number, '555-1234')
|
||||
|
||||
def test_make_local_products(self):
|
||||
def test_process_pending_products(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
handler = self.make_handler()
|
||||
|
@ -1253,7 +1253,7 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.assertEqual(self.session.query(model.LocalProduct).count(), 1)
|
||||
self.assertIsNotNone(row2.pending_product)
|
||||
self.assertIsNone(row2.local_product)
|
||||
handler.make_local_products(batch, batch.rows)
|
||||
handler.process_pending_products(batch, batch.rows)
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 0)
|
||||
self.assertEqual(self.session.query(model.LocalProduct).count(), 2)
|
||||
self.assertIsNone(row2.pending_product)
|
||||
|
@ -1266,7 +1266,7 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.assertEqual(local.unit_price_reg, decimal.Decimal('5.99'))
|
||||
|
||||
# trying again does nothing
|
||||
handler.make_local_products(batch, batch.rows)
|
||||
handler.process_pending_products(batch, batch.rows)
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 0)
|
||||
self.assertEqual(self.session.query(model.LocalProduct).count(), 2)
|
||||
self.assertIsNone(row2.pending_product)
|
||||
|
@ -1291,24 +1291,26 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
), 1, enum.ORDER_UOM_UNIT)
|
||||
self.session.flush()
|
||||
|
||||
# should do nothing if local products disabled
|
||||
# should update status if using external products
|
||||
with patch.object(handler, 'use_local_products', return_value=False):
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 1)
|
||||
self.assertEqual(self.session.query(model.LocalProduct).count(), 2)
|
||||
self.assertIsNotNone(row.pending_product)
|
||||
self.assertEqual(row.pending_product.status, enum.PendingProductStatus.PENDING)
|
||||
self.assertIsNone(row.local_product)
|
||||
handler.make_local_products(batch, batch.rows)
|
||||
handler.process_pending_products(batch, batch.rows)
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 1)
|
||||
self.assertEqual(self.session.query(model.LocalProduct).count(), 2)
|
||||
self.assertIsNotNone(row.pending_product)
|
||||
self.assertEqual(row.pending_product.status, enum.PendingProductStatus.READY)
|
||||
self.assertIsNone(row.local_product)
|
||||
|
||||
# but things happen by default, since local products enabled
|
||||
# but if using local products (the default), pending is converted to local
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 1)
|
||||
self.assertEqual(self.session.query(model.LocalProduct).count(), 2)
|
||||
self.assertIsNotNone(row.pending_product)
|
||||
self.assertIsNone(row.local_product)
|
||||
handler.make_local_products(batch, batch.rows)
|
||||
handler.process_pending_products(batch, batch.rows)
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 0)
|
||||
self.assertEqual(self.session.query(model.LocalProduct).count(), 3)
|
||||
self.assertIsNone(row.pending_product)
|
||||
|
|
|
@ -70,6 +70,75 @@ class TestOrderHandler(DataTestCase):
|
|||
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_EXPIRED), 'warning')
|
||||
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INACTIVE), 'warning')
|
||||
|
||||
def test_resolve_pending_product(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
handler = self.make_handler()
|
||||
|
||||
# sample data
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
pending = model.PendingProduct(description='vinegar', unit_price_reg=5.99,
|
||||
status=enum.PendingProductStatus.PENDING,
|
||||
created_by=user)
|
||||
self.session.add(pending)
|
||||
order = model.Order(order_id=100, customer_name="Fred Flintstone", created_by=user)
|
||||
item = model.OrderItem(pending_product=pending,
|
||||
order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
|
||||
status_code=enum.ORDER_ITEM_STATUS_READY)
|
||||
order.items.append(item)
|
||||
self.session.add(order)
|
||||
self.session.flush()
|
||||
|
||||
info = {
|
||||
'product_id': '07430500132',
|
||||
'scancode': '07430500132',
|
||||
'brand_name': "Bragg's",
|
||||
'description': "Apple Cider Vinegar",
|
||||
'size': "32oz",
|
||||
'weighed': False,
|
||||
'department_id': None,
|
||||
'department_name': None,
|
||||
'special_order': False,
|
||||
'vendor_name': None,
|
||||
'vendor_item_code': None,
|
||||
'case_size': 12,
|
||||
'unit_cost': 2.99,
|
||||
'unit_price_reg': 5.99,
|
||||
}
|
||||
|
||||
# first try fails b/c pending status
|
||||
self.assertEqual(len(item.events), 0)
|
||||
self.assertRaises(ValueError, handler.resolve_pending_product, pending, info, user)
|
||||
|
||||
# resolves okay if ready status
|
||||
pending.status = enum.PendingProductStatus.READY
|
||||
handler.resolve_pending_product(pending, info, user)
|
||||
self.assertEqual(len(item.events), 1)
|
||||
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED)
|
||||
self.assertIsNone(item.events[0].note)
|
||||
|
||||
# more sample data
|
||||
pending2 = model.PendingProduct(description='vinegar', unit_price_reg=5.99,
|
||||
status=enum.PendingProductStatus.READY,
|
||||
created_by=user)
|
||||
self.session.add(pending2)
|
||||
order2 = model.Order(order_id=101, customer_name="Wilma Flintstone", created_by=user)
|
||||
item2 = model.OrderItem(pending_product=pending2,
|
||||
order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
|
||||
status_code=enum.ORDER_ITEM_STATUS_READY)
|
||||
order2.items.append(item2)
|
||||
self.session.add(order2)
|
||||
self.session.flush()
|
||||
|
||||
# resolve with extra note
|
||||
handler.resolve_pending_product(pending2, info, user, note='hello world')
|
||||
self.assertEqual(len(item2.events), 2)
|
||||
self.assertEqual(item2.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED)
|
||||
self.assertIsNone(item2.events[0].note)
|
||||
self.assertEqual(item2.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED)
|
||||
self.assertEqual(item2.events[1].note, "hello world")
|
||||
|
||||
def test_process_placement(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
|
|
|
@ -1144,6 +1144,8 @@ class TestOrderView(WebTestCase):
|
|||
row.department_id = 1
|
||||
row.department_name = "Bricks & Mortar"
|
||||
row.special_order = False
|
||||
row.vendor_name = 'Acme Distributors'
|
||||
row.vendor_item_code = '1234'
|
||||
row.case_size = None
|
||||
row.unit_cost = decimal.Decimal('599.99')
|
||||
row.unit_price_reg = decimal.Decimal('999.99')
|
||||
|
@ -1159,7 +1161,8 @@ class TestOrderView(WebTestCase):
|
|||
self.assertEqual(data['product_scancode'], '012345')
|
||||
self.assertEqual(data['product_full_description'], 'Acme Bricks 1 ton')
|
||||
self.assertIsNone(data['case_size'])
|
||||
self.assertNotIn('vendor_name', data) # TODO
|
||||
self.assertEqual(data['vendor_name'], 'Acme Distributors')
|
||||
self.assertEqual(data['vendor_item_code'], '1234')
|
||||
self.assertEqual(data['order_qty'], 1)
|
||||
self.assertEqual(data['order_uom'], 'EA')
|
||||
self.assertEqual(data['order_qty_display'], '1 Units')
|
||||
|
|
|
@ -132,6 +132,19 @@ class TestPendingProductView(WebTestCase):
|
|||
self.assertIn('brand_name', grid.linked_columns)
|
||||
self.assertIn('description', grid.linked_columns)
|
||||
|
||||
def test_grid_row_class(self):
|
||||
enum = self.app.enum
|
||||
model = self.app.model
|
||||
view = self.make_view()
|
||||
product = model.PendingProduct()
|
||||
|
||||
# null by default
|
||||
self.assertIsNone(view.grid_row_class(product, {}, 1))
|
||||
|
||||
# warning for ignored
|
||||
product.status = enum.PendingProductStatus.IGNORED
|
||||
self.assertEqual(view.grid_row_class(product, {}, 1), 'has-background-warning')
|
||||
|
||||
def test_configure_form(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
|
@ -141,7 +154,6 @@ class TestPendingProductView(WebTestCase):
|
|||
with patch.object(view, 'creating', new=True):
|
||||
form = view.make_form(model_class=model.PendingProduct)
|
||||
view.configure_form(form)
|
||||
self.assertNotIn('status', form)
|
||||
self.assertNotIn('created', form)
|
||||
self.assertNotIn('created_by', form)
|
||||
|
||||
|
@ -216,6 +228,39 @@ class TestPendingProductView(WebTestCase):
|
|||
self.assertEqual(len(grid.actions), 1)
|
||||
self.assertEqual(grid.actions[0].key, 'view')
|
||||
|
||||
def test_get_template_context(self):
|
||||
enum = self.app.enum
|
||||
model = self.app.model
|
||||
view = self.make_view()
|
||||
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING)
|
||||
orig_context = {'instance': product}
|
||||
|
||||
# local setting omitted by default
|
||||
context = view.get_template_context(orig_context)
|
||||
self.assertNotIn('use_local_products', context)
|
||||
|
||||
# still omitted even though 'viewing'
|
||||
with patch.object(view, 'viewing', new=True):
|
||||
context = view.get_template_context(orig_context)
|
||||
self.assertNotIn('use_local_products', context)
|
||||
|
||||
# still omitted even though correct status
|
||||
product.status = enum.PendingProductStatus.READY
|
||||
context = view.get_template_context(orig_context)
|
||||
self.assertNotIn('use_local_products', context)
|
||||
|
||||
# no longer omitted if user has perm
|
||||
with patch.object(self.request, 'is_root', new=True):
|
||||
context = view.get_template_context(orig_context)
|
||||
self.assertIn('use_local_products', context)
|
||||
# nb. true by default
|
||||
self.assertTrue(context['use_local_products'])
|
||||
|
||||
# accurately reflects config
|
||||
self.config.setdefault('sideshow.orders.use_local_products', 'false')
|
||||
context = view.get_template_context(orig_context)
|
||||
self.assertFalse(context['use_local_products'])
|
||||
|
||||
def test_delete_instance(self):
|
||||
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}')
|
||||
model = self.app.model
|
||||
|
@ -259,3 +304,117 @@ class TestPendingProductView(WebTestCase):
|
|||
view.delete_instance(product)
|
||||
self.session.flush()
|
||||
self.assertEqual(self.session.query(model.PendingProduct).count(), 0)
|
||||
|
||||
def test_resolve(self):
|
||||
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}')
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
# sample data
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
|
||||
created_by=user)
|
||||
self.session.add(product)
|
||||
self.session.flush()
|
||||
|
||||
info = {
|
||||
'product_id': '07430500132',
|
||||
'scancode': '07430500132',
|
||||
'brand_name': "Bragg's",
|
||||
'description': "Apple Cider Vinegar",
|
||||
'size': "32oz",
|
||||
'weighed': False,
|
||||
'department_id': None,
|
||||
'department_name': None,
|
||||
'special_order': False,
|
||||
'vendor_name': None,
|
||||
'vendor_item_code': None,
|
||||
'case_size': 12,
|
||||
'unit_cost': 2.99,
|
||||
'unit_price_reg': 5.99,
|
||||
}
|
||||
|
||||
with patch.object(view, 'Session', return_value=self.session):
|
||||
with patch.object(self.request, 'user', new=user):
|
||||
with patch.object(self.request, 'matchdict', new={'uuid': product.uuid}):
|
||||
|
||||
# flash error if wrong status
|
||||
result = view.resolve()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertTrue(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(self.request.session.pop_flash('error'),
|
||||
["pending product does not have 'ready' status!"])
|
||||
|
||||
# flash error if product_id not specified
|
||||
product.status = enum.PendingProductStatus.READY
|
||||
result = view.resolve()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertTrue(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(self.request.session.pop_flash('error'),
|
||||
["must specify valid product_id"])
|
||||
|
||||
# more sample data
|
||||
order = model.Order(order_id=100, created_by=user,
|
||||
customer_name="Fred Flintstone")
|
||||
item = model.OrderItem(pending_product=product,
|
||||
order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
|
||||
status_code=enum.ORDER_ITEM_STATUS_READY)
|
||||
order.items.append(item)
|
||||
self.session.add(order)
|
||||
|
||||
# product + order items updated
|
||||
self.assertIsNone(product.product_id)
|
||||
self.assertEqual(product.status, enum.PendingProductStatus.READY)
|
||||
self.assertIsNone(item.product_id)
|
||||
batch_handler = NewOrderBatchHandler(self.config)
|
||||
with patch.object(batch_handler, 'get_product_info_external',
|
||||
return_value=info):
|
||||
with patch.object(self.app, 'get_batch_handler',
|
||||
return_value=batch_handler):
|
||||
with patch.object(self.request, 'POST',
|
||||
new={'product_id': '07430500132'}):
|
||||
with patch.object(batch_handler, 'get_product_info_external',
|
||||
return_value=info):
|
||||
result = view.resolve()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertFalse(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(product.product_id, '07430500132')
|
||||
self.assertEqual(product.status, enum.PendingProductStatus.RESOLVED)
|
||||
self.assertEqual(item.product_id, '07430500132')
|
||||
|
||||
def test_ignore(self):
|
||||
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}')
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
# sample data
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
|
||||
created_by=user)
|
||||
self.session.add(product)
|
||||
self.session.flush()
|
||||
|
||||
with patch.object(view, 'Session', return_value=self.session):
|
||||
with patch.object(self.request, 'user', new=user):
|
||||
with patch.object(self.request, 'matchdict', new={'uuid': product.uuid}):
|
||||
|
||||
# flash error if wrong status
|
||||
result = view.ignore()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertTrue(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(self.request.session.pop_flash('error'),
|
||||
["pending product does not have 'ready' status!"])
|
||||
|
||||
# product updated
|
||||
product.status = enum.PendingProductStatus.READY
|
||||
self.assertIsNone(product.product_id)
|
||||
self.assertEqual(product.status, enum.PendingProductStatus.READY)
|
||||
result = view.ignore()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertFalse(self.request.session.peek_flash('error'))
|
||||
self.assertIsNone(product.product_id)
|
||||
self.assertEqual(product.status, enum.PendingProductStatus.IGNORED)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue