feat: move lookup logic to handler; improve support for external lookup

This commit is contained in:
Lance Edgar 2025-01-12 22:03:31 -06:00
parent f8f4933ca1
commit 811a37995d
7 changed files with 553 additions and 151 deletions

View file

@ -50,6 +50,34 @@ class TestNewOrderBatchHandler(DataTestCase):
config.setdefault('sideshow.orders.allow_unknown_products', 'false')
self.assertFalse(handler.allow_unknown_products())
def test_autocomplete_customers_external(self):
handler = self.make_handler()
self.assertRaises(NotImplementedError, handler.autocomplete_customers_external,
self.session, 'jack')
def test_autocomplete_cutomers_local(self):
model = self.app.model
handler = self.make_handler()
# empty results by default
self.assertEqual(handler.autocomplete_customers_local(self.session, 'foo'), [])
# add a customer
customer = model.LocalCustomer(full_name="Chuck Norris")
self.session.add(customer)
self.session.flush()
# search for chuck finds chuck
results = handler.autocomplete_customers_local(self.session, 'chuck')
self.assertEqual(len(results), 1)
self.assertEqual(results[0], {
'value': customer.uuid.hex,
'label': "Chuck Norris",
})
# search for sally finds nothing
self.assertEqual(handler.autocomplete_customers_local(self.session, 'sally'), [])
def test_set_customer(self):
model = self.app.model
handler = self.make_handler()
@ -146,6 +174,83 @@ class TestNewOrderBatchHandler(DataTestCase):
self.assertIsNone(batch.phone_number)
self.assertIsNone(batch.email_address)
def test_autocomplete_products_external(self):
handler = self.make_handler()
self.assertRaises(NotImplementedError, handler.autocomplete_products_external,
self.session, 'cheese')
def test_autocomplete_products_local(self):
model = self.app.model
handler = self.make_handler()
# empty results by default
self.assertEqual(handler.autocomplete_products_local(self.session, 'foo'), [])
# add a product
product = model.LocalProduct(brand_name="Bragg's", description="Vinegar")
self.session.add(product)
self.session.flush()
# search for vinegar finds product
results = handler.autocomplete_products_local(self.session, 'vinegar')
self.assertEqual(len(results), 1)
self.assertEqual(results[0], {
'value': product.uuid.hex,
'label': "Bragg's Vinegar",
})
# search for brag finds product
results = handler.autocomplete_products_local(self.session, 'brag')
self.assertEqual(len(results), 1)
self.assertEqual(results[0], {
'value': product.uuid.hex,
'label': "Bragg's Vinegar",
})
# search for juice finds nothing
self.assertEqual(handler.autocomplete_products_local(self.session, 'juice'), [])
def test_get_product_info_external(self):
handler = self.make_handler()
self.assertRaises(NotImplementedError, handler.get_product_info_external,
self.session, '07430500132')
def test_get_product_info_local(self):
model = self.app.model
handler = self.make_handler()
user = model.User(username='barney')
self.session.add(user)
local = model.LocalProduct(scancode='07430500132',
brand_name='Bragg',
description='Vinegar',
size='32oz',
case_size=12,
unit_price_reg=decimal.Decimal('5.99'))
self.session.add(local)
self.session.flush()
batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch)
# typical, for local product
info = handler.get_product_info_local(self.session, local.uuid.hex)
self.assertEqual(info['product_id'], local.uuid.hex)
self.assertEqual(info['scancode'], '07430500132')
self.assertEqual(info['brand_name'], 'Bragg')
self.assertEqual(info['description'], 'Vinegar')
self.assertEqual(info['size'], '32oz')
self.assertEqual(info['full_description'], 'Bragg Vinegar 32oz')
self.assertEqual(info['case_size'], 12)
self.assertEqual(info['unit_price_reg'], decimal.Decimal('5.99'))
# error if no product_id
self.assertRaises(ValueError, handler.get_product_info_local, self.session, None)
# error if product not found
mock_uuid = self.app.make_true_uuid()
self.assertRaises(ValueError, handler.get_product_info_local, self.session, mock_uuid.hex)
def test_add_item(self):
model = self.app.model
enum = self.app.enum
@ -719,10 +824,8 @@ class TestNewOrderBatchHandler(DataTestCase):
self.session.add(row)
self.session.flush()
# STATUS_OK
row = handler.make_row(product_id=42, order_qty=1, order_uom=enum.ORDER_UOM_UNIT)
handler.add_row(batch, row)
self.session.add(row)
self.session.commit()
row = handler.add_item(batch, {'scancode': '07430500132'}, 1, enum.ORDER_UOM_UNIT)
self.session.flush()
# only 1 effective row
rows = handler.get_effective_rows(batch)

View file

@ -177,7 +177,9 @@ class TestOrderView(WebTestCase):
def test_customer_autocomplete(self):
model = self.app.model
handler = self.make_handler()
view = self.make_view()
view.batch_handler = handler
with patch.object(view, 'Session', return_value=self.session):
@ -205,9 +207,16 @@ class TestOrderView(WebTestCase):
result = view.customer_autocomplete()
self.assertEqual(result, [])
# external lookup not implemented by default
with patch.object(handler, 'use_local_customers', return_value=False):
with patch.object(self.request, 'GET', new={'term': 'sally'}, create=True):
self.assertRaises(NotImplementedError, view.customer_autocomplete)
def test_product_autocomplete(self):
model = self.app.model
handler = self.make_handler()
view = self.make_view()
view.batch_handler = handler
with patch.object(view, 'Session', return_value=self.session):
@ -244,6 +253,11 @@ class TestOrderView(WebTestCase):
result = view.product_autocomplete()
self.assertEqual(result, [])
# external lookup not implemented by default
with patch.object(handler, 'use_local_products', return_value=False):
with patch.object(self.request, 'GET', new={'term': 'juice'}, create=True):
self.assertRaises(NotImplementedError, view.product_autocomplete)
def test_get_pending_product_required_fields(self):
model = self.app.model
view = self.make_view()
@ -529,20 +543,27 @@ class TestOrderView(WebTestCase):
self.assertEqual(context['case_size'], 12)
self.assertEqual(context['unit_price_reg'], 5.99)
# error if local product missing
mock_uuid = self.app.make_true_uuid()
context = view.get_product_info(batch, {'product_id': mock_uuid.hex})
self.assertEqual(context, {'error': "Product not found"})
# error if no product_id
context = view.get_product_info(batch, {})
self.assertEqual(context, {'error': "Must specify a product ID"})
# external lookup not implemented (yet)
# error if product not found
mock_uuid = self.app.make_true_uuid()
self.assertRaises(ValueError, view.get_product_info,
batch, {'product_id': mock_uuid.hex})
with patch.object(handler, 'use_local_products', return_value=False):
# external lookup not implemented by default
self.assertRaises(NotImplementedError, view.get_product_info,
batch, {'product_id': '42'})
# external lookup may return its own error
with patch.object(handler, 'get_product_info_external',
return_value={'error': "something smells fishy"}):
context = view.get_product_info(batch, {'product_id': '42'})
self.assertEqual(context, {'error': "something smells fishy"})
def test_add_item(self):
model = self.app.model
enum = self.app.enum
@ -970,33 +991,47 @@ class TestOrderView(WebTestCase):
# the next few tests will morph 2nd row..
def refresh_external(row):
row.product_scancode = '012345'
row.product_brand = 'Acme'
row.product_description = 'Bricks'
row.product_size = '1 ton'
row.product_weighed = True
row.department_id = 1
row.department_name = "Bricks & Mortar"
row.special_order = False
row.case_size = None
row.unit_cost = decimal.Decimal('599.99')
row.unit_price_reg = decimal.Decimal('999.99')
# typical, external product
row2.product_id = '42'
with patch.object(handler, 'use_local_products', return_value=False):
data = view.normalize_row(row2)
with patch.object(handler, 'refresh_row_from_external_product', new=refresh_external):
handler.update_item(row2, '42', 1, enum.ORDER_UOM_UNIT)
data = view.normalize_row(row2)
self.assertEqual(data['uuid'], row2.uuid.hex)
self.assertEqual(data['sequence'], 2)
self.assertEqual(data['product_id'], '42')
self.assertIsNone(data['product_scancode'])
self.assertNotIn('product_full_description', data) # TODO
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['order_qty'], 1)
self.assertEqual(data['order_uom'], 'EA')
self.assertEqual(data['order_qty_display'], '1 Units')
self.assertEqual(data['unit_price_reg'], 3.29)
self.assertEqual(data['unit_price_reg_display'], '$3.29')
self.assertEqual(data['unit_price_reg'], 999.99)
self.assertEqual(data['unit_price_reg_display'], '$999.99')
self.assertNotIn('unit_price_sale', data)
self.assertNotIn('unit_price_sale_display', data)
self.assertNotIn('sale_ends', data)
self.assertNotIn('sale_ends_display', data)
self.assertEqual(data['unit_price_quoted'], 3.29)
self.assertEqual(data['unit_price_quoted_display'], '$3.29')
self.assertEqual(data['unit_price_quoted'], 999.99)
self.assertEqual(data['unit_price_quoted_display'], '$999.99')
self.assertIsNone(data['case_price_quoted'])
self.assertEqual(data['case_price_quoted_display'], '')
self.assertEqual(data['total_price'], 3.29)
self.assertEqual(data['total_price_display'], '$3.29')
self.assertIsNone(data['special_order'])
self.assertEqual(data['total_price'], 999.99)
self.assertEqual(data['total_price_display'], '$999.99')
self.assertFalse(data['special_order'])
self.assertEqual(data['status_code'], row2.STATUS_OK)
self.assertNotIn('pending_product', data)