fix: add "other" menu entry, plus docs/tests
This commit is contained in:
parent
ce54ca6bd6
commit
c2f76f6c97
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
0
docs/_static/.keepme
vendored
Normal file
0
docs/_static/.keepme
vendored
Normal file
6
docs/api/sideshow_corepos.batch.neworder.rst
Normal file
6
docs/api/sideshow_corepos.batch.neworder.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos.batch.neworder``
|
||||||
|
===================================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos.batch.neworder
|
||||||
|
:members:
|
6
docs/api/sideshow_corepos.batch.rst
Normal file
6
docs/api/sideshow_corepos.batch.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos.batch``
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos.batch
|
||||||
|
:members:
|
6
docs/api/sideshow_corepos.rst
Normal file
6
docs/api/sideshow_corepos.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos``
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos
|
||||||
|
:members:
|
6
docs/api/sideshow_corepos.web.app.rst
Normal file
6
docs/api/sideshow_corepos.web.app.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos.web.app``
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos.web.app
|
||||||
|
:members:
|
6
docs/api/sideshow_corepos.web.menus.rst
Normal file
6
docs/api/sideshow_corepos.web.menus.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos.web.menus``
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos.web.menus
|
||||||
|
:members:
|
6
docs/api/sideshow_corepos.web.rst
Normal file
6
docs/api/sideshow_corepos.web.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos.web``
|
||||||
|
========================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos.web
|
||||||
|
:members:
|
6
docs/api/sideshow_corepos.web.views.rst
Normal file
6
docs/api/sideshow_corepos.web.views.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow_corepos.web.views``
|
||||||
|
==============================
|
||||||
|
|
||||||
|
.. automodule:: sideshow_corepos.web.views
|
||||||
|
:members:
|
40
docs/conf.py
Normal file
40
docs/conf.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# For the full list of built-in configuration values, see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
|
from importlib.metadata import version as get_version
|
||||||
|
|
||||||
|
project = 'Sideshow-COREPOS'
|
||||||
|
copyright = '2025, Lance Edgar'
|
||||||
|
author = 'Lance Edgar'
|
||||||
|
release = '0.1'
|
||||||
|
release = get_version('Sideshow-COREPOS')
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.intersphinx',
|
||||||
|
'sphinx.ext.viewcode',
|
||||||
|
'sphinx.ext.todo',
|
||||||
|
]
|
||||||
|
|
||||||
|
templates_path = ['_templates']
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
intersphinx_mapping = {
|
||||||
|
'sideshow': ('https://rattailproject.org/docs/sideshow/', None),
|
||||||
|
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
|
html_theme = 'furo'
|
||||||
|
html_static_path = ['_static']
|
22
docs/index.rst
Normal file
22
docs/index.rst
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
Sideshow-COREPOS
|
||||||
|
================
|
||||||
|
|
||||||
|
This is `Sideshow`_ with integration for `CORE-POS`_.
|
||||||
|
|
||||||
|
.. _Sideshow: https://pypi.org/project/Sideshow/
|
||||||
|
|
||||||
|
.. _CORE-POS: https://www.core-pos.com/
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:caption: API
|
||||||
|
|
||||||
|
api/sideshow_corepos
|
||||||
|
api/sideshow_corepos.batch
|
||||||
|
api/sideshow_corepos.batch.neworder
|
||||||
|
api/sideshow_corepos.web
|
||||||
|
api/sideshow_corepos.web.app
|
||||||
|
api/sideshow_corepos.web.menus
|
||||||
|
api/sideshow_corepos.web.views
|
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
|
set BUILDDIR=_build
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.https://www.sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
|
@ -34,8 +34,9 @@ from sideshow.batch import neworder as base
|
||||||
|
|
||||||
class NewOrderBatchHandler(base.NewOrderBatchHandler):
|
class NewOrderBatchHandler(base.NewOrderBatchHandler):
|
||||||
"""
|
"""
|
||||||
Custom batch handler which can use CORE-POS as external data
|
Custom :term:`handler` for :term:`new order batches <new order
|
||||||
source for customers and products.
|
batch>` which can use CORE-POS as external data source for
|
||||||
|
customers and products.
|
||||||
|
|
||||||
See parent class
|
See parent class
|
||||||
:class:`~sideshow:sideshow.batch.neworder.NewOrderBatchHandler`
|
:class:`~sideshow:sideshow.batch.neworder.NewOrderBatchHandler`
|
||||||
|
@ -96,7 +97,7 @@ class NewOrderBatchHandler(base.NewOrderBatchHandler):
|
||||||
.options(orm.joinedload(op_model.CustomerClassic.member_info))\
|
.options(orm.joinedload(op_model.CustomerClassic.member_info))\
|
||||||
.one()
|
.one()
|
||||||
except orm.exc.NoResultFound:
|
except orm.exc.NoResultFound:
|
||||||
raise ValueError(f"CORE-POS Customer not found: {customer_id}")
|
raise ValueError(f"CORE-POS Customer not found: {batch.customer_id}")
|
||||||
|
|
||||||
batch.customer_name = str(customer)
|
batch.customer_name = str(customer)
|
||||||
batch.phone_number = customer.member_info.phone
|
batch.phone_number = customer.member_info.phone
|
||||||
|
@ -132,19 +133,16 @@ class NewOrderBatchHandler(base.NewOrderBatchHandler):
|
||||||
# get results
|
# get results
|
||||||
def result(product):
|
def result(product):
|
||||||
return {'value': product.upc,
|
return {'value': product.upc,
|
||||||
'label': product.formatted_name}
|
'label': self.app.make_full_name(product.brand,
|
||||||
|
product.description,
|
||||||
|
product.size)}
|
||||||
results = [result(c) for c in products]
|
results = [result(c) for c in products]
|
||||||
|
|
||||||
op_session.close()
|
op_session.close()
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def get_product_info_external(self, session, product_id, user=None):
|
def get_product_info_external(self, session, product_id, user=None):
|
||||||
"""
|
""" """
|
||||||
Returns basic info for an :term:`external product` as pertains
|
|
||||||
to ordering.
|
|
||||||
|
|
||||||
There is no default logic here; subclass must implement.
|
|
||||||
"""
|
|
||||||
corepos = self.app.get_corepos_handler()
|
corepos = self.app.get_corepos_handler()
|
||||||
op_model = corepos.get_model_office_op()
|
op_model = corepos.get_model_office_op()
|
||||||
op_session = corepos.make_session_office_op()
|
op_session = corepos.make_session_office_op()
|
||||||
|
@ -162,7 +160,6 @@ class NewOrderBatchHandler(base.NewOrderBatchHandler):
|
||||||
'brand_name': product.brand,
|
'brand_name': product.brand,
|
||||||
'description': product.description,
|
'description': product.description,
|
||||||
'size': product.size,
|
'size': product.size,
|
||||||
# 'full_description': product.formatted_name,
|
|
||||||
'full_description': self.app.make_full_name(product.brand,
|
'full_description': self.app.make_full_name(product.brand,
|
||||||
product.description,
|
product.description,
|
||||||
product.size),
|
product.size),
|
||||||
|
|
|
@ -35,7 +35,6 @@ def main(global_config, **settings):
|
||||||
"""
|
"""
|
||||||
# prefer Sideshow templates over wuttaweb
|
# prefer Sideshow templates over wuttaweb
|
||||||
settings.setdefault('mako.directories', [
|
settings.setdefault('mako.directories', [
|
||||||
# 'sideshow_corepos.web:templates',
|
|
||||||
'sideshow.web:templates',
|
'sideshow.web:templates',
|
||||||
'wuttaweb:templates',
|
'wuttaweb:templates',
|
||||||
])
|
])
|
||||||
|
@ -63,6 +62,6 @@ def make_wsgi_app():
|
||||||
|
|
||||||
def make_asgi_app():
|
def make_asgi_app():
|
||||||
"""
|
"""
|
||||||
Make and return the ASGI app.
|
Make and return the ASGI app (generic entry point).
|
||||||
"""
|
"""
|
||||||
return base.make_asgi_app(main)
|
return base.make_asgi_app(main)
|
||||||
|
|
|
@ -34,7 +34,7 @@ class SideshowMenuHandler(base.SideshowMenuHandler):
|
||||||
|
|
||||||
def make_customers_menu(self, request, **kwargs):
|
def make_customers_menu(self, request, **kwargs):
|
||||||
"""
|
"""
|
||||||
This adds the entry for CORE-POS Members..
|
This adds the entry for CORE-POS Members.
|
||||||
"""
|
"""
|
||||||
menu = super().make_customers_menu(request, **kwargs)
|
menu = super().make_customers_menu(request, **kwargs)
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class SideshowMenuHandler(base.SideshowMenuHandler):
|
||||||
|
|
||||||
def make_products_menu(self, request, **kwargs):
|
def make_products_menu(self, request, **kwargs):
|
||||||
"""
|
"""
|
||||||
This adds the entry for CORE-POS Products..
|
This adds the entry for CORE-POS Products.
|
||||||
"""
|
"""
|
||||||
menu = super().make_products_menu(request, **kwargs)
|
menu = super().make_products_menu(request, **kwargs)
|
||||||
|
|
||||||
|
@ -65,3 +65,22 @@ class SideshowMenuHandler(base.SideshowMenuHandler):
|
||||||
])
|
])
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
|
def make_other_menu(self, request, **kwargs):
|
||||||
|
"""
|
||||||
|
This adds the entry for CORE Office.
|
||||||
|
"""
|
||||||
|
menu = super().make_other_menu(request, **kwargs)
|
||||||
|
|
||||||
|
corepos = self.app.get_corepos_handler()
|
||||||
|
url = corepos.get_office_url()
|
||||||
|
if url:
|
||||||
|
menu['items'].extend([
|
||||||
|
{
|
||||||
|
'title': "CORE Office",
|
||||||
|
'url': url,
|
||||||
|
'target': '_blank',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return menu
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
"""
|
"""
|
||||||
Sideshow-COREPOS - custom views
|
Sideshow-COREPOS - custom views
|
||||||
|
|
||||||
|
This adds config for readonly views for CORE-POS members and products.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
261
tests/batch/test_neworder.py
Normal file
261
tests/batch/test_neworder.py
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import decimal
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from corepos.db.office_op import model as op_model, Session as OpSession
|
||||||
|
|
||||||
|
from wuttjamaican.testing import DataTestCase
|
||||||
|
|
||||||
|
from sideshow_corepos.batch import neworder as mod
|
||||||
|
|
||||||
|
|
||||||
|
class TestNewOrderBatchHandler(DataTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.op_engine = sa.create_engine('sqlite://')
|
||||||
|
self.config.core_office_op_engines = {'default': self.op_engine}
|
||||||
|
self.config.core_office_op_engine = self.op_engine
|
||||||
|
|
||||||
|
op_model.Base.metadata.create_all(bind=self.op_engine)
|
||||||
|
|
||||||
|
self.op_session = OpSession(bind=self.op_engine)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.op_session.close()
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def make_config(self, **kwargs):
|
||||||
|
config = super().make_config(**kwargs)
|
||||||
|
config.setdefault('wutta.enum_spec', 'sideshow.enum')
|
||||||
|
return config
|
||||||
|
|
||||||
|
def make_handler(self):
|
||||||
|
return mod.NewOrderBatchHandler(self.config)
|
||||||
|
|
||||||
|
def test_autocomplete_cutomers_external(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# empty results by default
|
||||||
|
self.assertEqual(handler.autocomplete_customers_external(self.session, 'foo'), [])
|
||||||
|
|
||||||
|
# add a member
|
||||||
|
member = op_model.MemberInfo(card_number=42)
|
||||||
|
self.op_session.add(member)
|
||||||
|
customer = op_model.CustomerClassic(first_name="Chuck", last_name="Norris",
|
||||||
|
last_change=datetime.datetime.now())
|
||||||
|
member.customers.append(customer)
|
||||||
|
self.op_session.add(customer)
|
||||||
|
self.op_session.flush()
|
||||||
|
|
||||||
|
# search for chuck finds chuck
|
||||||
|
results = handler.autocomplete_customers_external(self.session, 'chuck')
|
||||||
|
self.assertEqual(len(results), 1)
|
||||||
|
self.assertEqual(results[0], {
|
||||||
|
'value': '42',
|
||||||
|
'label': "Chuck Norris",
|
||||||
|
})
|
||||||
|
|
||||||
|
# search for sally finds nothing
|
||||||
|
self.assertEqual(handler.autocomplete_customers_external(self.session, 'sally'), [])
|
||||||
|
|
||||||
|
def test_refresh_batch_from_external_customer(self):
|
||||||
|
model = self.app.model
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
user = model.User(username='barney')
|
||||||
|
self.session.add(user)
|
||||||
|
self.session.flush()
|
||||||
|
|
||||||
|
batch = handler.make_batch(self.session, created_by=user)
|
||||||
|
self.session.add(batch)
|
||||||
|
self.session.flush()
|
||||||
|
|
||||||
|
# add a member
|
||||||
|
member = op_model.MemberInfo(card_number=42, phone='555-1234', email='chuck@example.com')
|
||||||
|
self.op_session.add(member)
|
||||||
|
customer = op_model.CustomerClassic(first_name="Chuck", last_name="Norris",
|
||||||
|
last_change=datetime.datetime.now())
|
||||||
|
member.customers.append(customer)
|
||||||
|
self.op_session.add(customer)
|
||||||
|
self.op_session.flush()
|
||||||
|
|
||||||
|
# error if invalid customer_id
|
||||||
|
batch.customer_id = 'BreakThings!'
|
||||||
|
self.assertRaises(ValueError, handler.refresh_batch_from_external_customer, batch)
|
||||||
|
|
||||||
|
# error if customer not found
|
||||||
|
batch.customer_id = '9999'
|
||||||
|
self.assertRaises(ValueError, handler.refresh_batch_from_external_customer, batch)
|
||||||
|
|
||||||
|
# batch should reflect customer info
|
||||||
|
batch.customer_id = '42'
|
||||||
|
self.assertIsNone(batch.customer_name)
|
||||||
|
self.assertIsNone(batch.phone_number)
|
||||||
|
self.assertIsNone(batch.email_address)
|
||||||
|
handler.refresh_batch_from_external_customer(batch)
|
||||||
|
self.assertEqual(batch.customer_name, "Chuck Norris")
|
||||||
|
self.assertEqual(batch.phone_number, '555-1234')
|
||||||
|
self.assertEqual(batch.email_address, 'chuck@example.com')
|
||||||
|
|
||||||
|
def test_autocomplete_products_local(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# empty results by default
|
||||||
|
self.assertEqual(handler.autocomplete_products_external(self.session, 'foo'), [])
|
||||||
|
|
||||||
|
# add a product
|
||||||
|
product = op_model.Product(upc='07430500132', brand="Bragg's",
|
||||||
|
description="Vinegar", size='32oz')
|
||||||
|
self.op_session.add(product)
|
||||||
|
self.op_session.commit()
|
||||||
|
|
||||||
|
# search for vinegar finds product
|
||||||
|
results = handler.autocomplete_products_external(self.session, 'vinegar')
|
||||||
|
self.assertEqual(len(results), 1)
|
||||||
|
self.assertEqual(results[0], {
|
||||||
|
'value': '07430500132',
|
||||||
|
'label': "Bragg's Vinegar 32oz",
|
||||||
|
})
|
||||||
|
|
||||||
|
# search for brag finds product
|
||||||
|
results = handler.autocomplete_products_external(self.session, 'brag')
|
||||||
|
self.assertEqual(len(results), 1)
|
||||||
|
self.assertEqual(results[0], {
|
||||||
|
'value': '07430500132',
|
||||||
|
'label': "Bragg's Vinegar 32oz",
|
||||||
|
})
|
||||||
|
|
||||||
|
# search for juice finds nothing
|
||||||
|
self.assertEqual(handler.autocomplete_products_external(self.session, 'juice'), [])
|
||||||
|
|
||||||
|
def test_get_case_size_for_external_product(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# null
|
||||||
|
product = op_model.Product(upc='07430500132', brand="Bragg's",
|
||||||
|
description="Vinegar", size='32oz')
|
||||||
|
self.op_session.add(product)
|
||||||
|
self.op_session.commit()
|
||||||
|
self.op_session.refresh(product)
|
||||||
|
self.assertIsNone(handler.get_case_size_for_external_product(product))
|
||||||
|
|
||||||
|
# typical
|
||||||
|
vendor = op_model.Vendor(id=42, name='Acme Distributors')
|
||||||
|
self.op_session.add(vendor)
|
||||||
|
item = op_model.VendorItem(vendor=vendor, sku='1234', units=12.34,
|
||||||
|
vendor_item_id=1)
|
||||||
|
product.vendor_items.append(item)
|
||||||
|
self.op_session.commit()
|
||||||
|
self.op_session.refresh(product)
|
||||||
|
self.assertEqual(handler.get_case_size_for_external_product(product),
|
||||||
|
decimal.Decimal('12.3400'))
|
||||||
|
|
||||||
|
def test_get_unit_price_reg_for_external_product(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# null
|
||||||
|
product = op_model.Product(upc='07430500132', brand="Bragg's",
|
||||||
|
description="Vinegar", size='32oz')
|
||||||
|
self.op_session.add(product)
|
||||||
|
self.op_session.commit()
|
||||||
|
self.op_session.refresh(product)
|
||||||
|
self.assertIsNone(handler.get_unit_price_reg_for_external_product(product))
|
||||||
|
|
||||||
|
# typical
|
||||||
|
product.normal_price = 4.19
|
||||||
|
self.op_session.commit()
|
||||||
|
self.op_session.refresh(product)
|
||||||
|
self.assertEqual(handler.get_unit_price_reg_for_external_product(product),
|
||||||
|
decimal.Decimal('4.19'))
|
||||||
|
|
||||||
|
def test_get_product_info_external(self):
|
||||||
|
model = self.app.model
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
user = model.User(username='barney')
|
||||||
|
self.session.add(user)
|
||||||
|
batch = handler.make_batch(self.session, created_by=user)
|
||||||
|
self.session.add(batch)
|
||||||
|
self.session.flush()
|
||||||
|
|
||||||
|
vendor = op_model.Vendor(id=42, name='Acme Distributors')
|
||||||
|
self.op_session.add(vendor)
|
||||||
|
product = op_model.Product(upc='07430500132', brand="Bragg",
|
||||||
|
description="Vinegar", size='32oz',
|
||||||
|
normal_price=4.19)
|
||||||
|
item = op_model.VendorItem(vendor=vendor, sku='1234', units=12.34,
|
||||||
|
vendor_item_id=1)
|
||||||
|
product.vendor_items.append(item)
|
||||||
|
self.op_session.add(product)
|
||||||
|
self.op_session.commit()
|
||||||
|
|
||||||
|
# typical
|
||||||
|
info = handler.get_product_info_external(self.session, '07430500132')
|
||||||
|
self.assertEqual(info['product_id'], '07430500132')
|
||||||
|
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'], decimal.Decimal('12.3400'))
|
||||||
|
self.assertEqual(info['unit_price_reg'], decimal.Decimal('4.19'))
|
||||||
|
|
||||||
|
# error if no product_id
|
||||||
|
self.assertRaises(ValueError, handler.get_product_info_external, self.session, None)
|
||||||
|
|
||||||
|
# error if product not found
|
||||||
|
self.assertRaises(ValueError, handler.get_product_info_external, self.session, 'BADUPC')
|
||||||
|
|
||||||
|
def test_refresh_row_from_external_product(self):
|
||||||
|
model = self.app.model
|
||||||
|
enum = self.app.enum
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
user = model.User(username='barney')
|
||||||
|
self.session.add(user)
|
||||||
|
batch = handler.make_batch(self.session, created_by=user)
|
||||||
|
self.session.add(batch)
|
||||||
|
row = handler.make_row(order_qty=1, order_uom=enum.ORDER_UOM_UNIT)
|
||||||
|
handler.add_row(batch, row)
|
||||||
|
self.session.add(row)
|
||||||
|
self.session.flush()
|
||||||
|
|
||||||
|
vendor = op_model.Vendor(id=42, name='Acme Distributors')
|
||||||
|
self.op_session.add(vendor)
|
||||||
|
product = op_model.Product(upc='07430500132', brand="Bragg",
|
||||||
|
description="Vinegar", size='32oz',
|
||||||
|
normal_price=4.19)
|
||||||
|
item = op_model.VendorItem(vendor=vendor, sku='1234', units=12.34,
|
||||||
|
vendor_item_id=1)
|
||||||
|
product.vendor_items.append(item)
|
||||||
|
self.op_session.add(product)
|
||||||
|
self.op_session.commit()
|
||||||
|
|
||||||
|
# error if invalid product_id
|
||||||
|
row.product_id = 'BreakThings!'
|
||||||
|
self.assertRaises(ValueError, handler.refresh_row_from_external_product, row)
|
||||||
|
|
||||||
|
# error if product not found
|
||||||
|
row.product_id = '9999'
|
||||||
|
self.assertRaises(ValueError, handler.refresh_row_from_external_product, row)
|
||||||
|
|
||||||
|
# row should reflect product info
|
||||||
|
row.product_id = '07430500132'
|
||||||
|
self.assertIsNone(row.product_scancode)
|
||||||
|
self.assertIsNone(row.product_brand)
|
||||||
|
self.assertIsNone(row.product_description)
|
||||||
|
self.assertIsNone(row.product_size)
|
||||||
|
self.assertIsNone(row.case_size)
|
||||||
|
self.assertIsNone(row.unit_price_reg)
|
||||||
|
handler.refresh_row_from_external_product(row)
|
||||||
|
self.assertEqual(row.product_scancode, '07430500132')
|
||||||
|
self.assertEqual(row.product_brand, "Bragg")
|
||||||
|
self.assertEqual(row.product_description, "Vinegar")
|
||||||
|
self.assertEqual(row.product_size, "32oz")
|
||||||
|
self.assertEqual(row.case_size, decimal.Decimal('12.3400'))
|
||||||
|
self.assertEqual(row.unit_price_reg, decimal.Decimal('4.19'))
|
32
tests/web/test_app.py
Normal file
32
tests/web/test_app.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from wuttjamaican.testing import FileTestCase, ConfigTestCase
|
||||||
|
|
||||||
|
from asgiref.wsgi import WsgiToAsgi
|
||||||
|
from pyramid.router import Router
|
||||||
|
|
||||||
|
from sideshow_corepos.web import app as mod
|
||||||
|
|
||||||
|
|
||||||
|
class TestMain(FileTestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
global_config = None
|
||||||
|
myconf = self.write_file('my.conf', '')
|
||||||
|
settings = {'wutta.config': myconf}
|
||||||
|
app = mod.main(global_config, **settings)
|
||||||
|
self.assertIsInstance(app, Router)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMakeWsgiApp(ConfigTestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
wsgi = mod.make_wsgi_app()
|
||||||
|
self.assertIsInstance(wsgi, Router)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMakeAsgiApp(ConfigTestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
asgi = mod.make_asgi_app()
|
||||||
|
self.assertIsInstance(asgi, WsgiToAsgi)
|
11
tests/web/test_init.py
Normal file
11
tests/web/test_init.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from wuttaweb.testing import WebTestCase
|
||||||
|
|
||||||
|
from sideshow_corepos import web as mod
|
||||||
|
|
||||||
|
|
||||||
|
class TestIncludeme(WebTestCase):
|
||||||
|
|
||||||
|
def test_coverage(self):
|
||||||
|
mod.includeme(self.pyramid_config)
|
51
tests/web/test_menus.py
Normal file
51
tests/web/test_menus.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from wuttaweb.testing import WebTestCase
|
||||||
|
|
||||||
|
from sideshow_corepos.web import menus as mod
|
||||||
|
|
||||||
|
|
||||||
|
class TestSideshowMenuHandler(WebTestCase):
|
||||||
|
|
||||||
|
def make_handler(self):
|
||||||
|
return mod.SideshowMenuHandler(self.config)
|
||||||
|
|
||||||
|
def test_make_customers_menu(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
menu = handler.make_customers_menu(self.request)
|
||||||
|
item = menu['items'][-1]
|
||||||
|
self.assertEqual(item, {
|
||||||
|
'title': "CORE-POS Members",
|
||||||
|
'route': 'corepos_members',
|
||||||
|
'perm': 'corepos_members.list',
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_make_products_menu(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
menu = handler.make_products_menu(self.request)
|
||||||
|
item = menu['items'][-1]
|
||||||
|
self.assertEqual(item, {
|
||||||
|
'title': "CORE-POS Products",
|
||||||
|
'route': 'corepos_products',
|
||||||
|
'perm': 'corepos_products.list',
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_make_other_menu(self):
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# no url configured by default
|
||||||
|
menu = handler.make_other_menu(self.request)
|
||||||
|
if menu['items']:
|
||||||
|
item = menu['items'][-1]
|
||||||
|
self.assertNotEqual(item['title'], "CORE Office")
|
||||||
|
|
||||||
|
# entry added if url configured
|
||||||
|
self.config.setdefault('corepos.office.url', 'http://localhost/fannie/')
|
||||||
|
menu = handler.make_other_menu(self.request)
|
||||||
|
item = menu['items'][-1]
|
||||||
|
self.assertEqual(item, {
|
||||||
|
'title': "CORE Office",
|
||||||
|
# nb. trailing slash gets stripped
|
||||||
|
'url': 'http://localhost/fannie',
|
||||||
|
'target': '_blank',
|
||||||
|
})
|
Loading…
Reference in a new issue