fix: format all code with black

and from now on should not deviate from that...
This commit is contained in:
Lance Edgar 2025-08-31 12:59:28 -05:00
parent 925235f0d3
commit 2107e9ee1d
47 changed files with 5729 additions and 3977 deletions

View file

@ -8,36 +8,36 @@
from importlib.metadata import version as get_version from importlib.metadata import version as get_version
project = 'Sideshow' project = "Sideshow"
copyright = '2025, Lance Edgar' copyright = "2025, Lance Edgar"
author = 'Lance Edgar' author = "Lance Edgar"
release = get_version('Sideshow') release = get_version("Sideshow")
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [ extensions = [
'sphinx.ext.autodoc', "sphinx.ext.autodoc",
'sphinx.ext.intersphinx', "sphinx.ext.intersphinx",
'sphinx.ext.viewcode', "sphinx.ext.viewcode",
'sphinx.ext.todo', "sphinx.ext.todo",
'sphinxcontrib.programoutput', "sphinxcontrib.programoutput",
'enum_tools.autoenum', "enum_tools.autoenum",
] ]
templates_path = ['_templates'] templates_path = ["_templates"]
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
intersphinx_mapping = { intersphinx_mapping = {
'pyramid': ('https://docs.pylonsproject.org/projects/pyramid/en/latest/', None), "pyramid": ("https://docs.pylonsproject.org/projects/pyramid/en/latest/", None),
'python': ('https://docs.python.org/3/', None), "python": ("https://docs.python.org/3/", None),
'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None), "wuttjamaican": ("https://docs.wuttaproject.org/wuttjamaican/", None),
'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None), "wuttaweb": ("https://docs.wuttaproject.org/wuttaweb/", None),
} }
# -- Options for HTML output ------------------------------------------------- # -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'furo' html_theme = "furo"
html_static_path = ['_static'] html_static_path = ["_static"]

View file

@ -49,8 +49,9 @@ class SideshowAppProvider(base.AppProvider):
:returns: Instance of :class:`~sideshow.orders.OrderHandler`. :returns: Instance of :class:`~sideshow.orders.OrderHandler`.
""" """
if 'order_handler' not in self.__dict__: if "order_handler" not in self.__dict__:
spec = self.config.get('sideshow.orders.handler_spec', spec = self.config.get(
default='sideshow.orders:OrderHandler') "sideshow.orders.handler_spec", default="sideshow.orders:OrderHandler"
)
self.order_handler = self.app.load_object(spec)(self.config) self.order_handler = self.app.load_object(spec)(self.config)
return self.order_handler return self.order_handler

View file

@ -49,6 +49,7 @@ class NewOrderBatchHandler(BatchHandler):
After the batch has executed the :term:`order handler` takes over After the batch has executed the :term:`order handler` takes over
responsibility for the rest of the order lifecycle. responsibility for the rest of the order lifecycle.
""" """
model_class = NewOrderBatch model_class = NewOrderBatch
def get_default_store_id(self): def get_default_store_id(self):
@ -57,7 +58,7 @@ class NewOrderBatchHandler(BatchHandler):
:attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`, :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`,
or ``None``. or ``None``.
""" """
return self.config.get('sideshow.orders.default_store_id') return self.config.get("sideshow.orders.default_store_id")
def use_local_customers(self): def use_local_customers(self):
""" """
@ -65,8 +66,7 @@ class NewOrderBatchHandler(BatchHandler):
accounts should be used. This is true by default, but may be accounts should be used. This is true by default, but may be
false for :term:`external customer` lookups. false for :term:`external customer` lookups.
""" """
return self.config.get_bool('sideshow.orders.use_local_customers', return self.config.get_bool("sideshow.orders.use_local_customers", default=True)
default=True)
def use_local_products(self): def use_local_products(self):
""" """
@ -74,8 +74,7 @@ class NewOrderBatchHandler(BatchHandler):
records should be used. This is true by default, but may be records should be used. This is true by default, but may be
false for :term:`external product` lookups. false for :term:`external product` lookups.
""" """
return self.config.get_bool('sideshow.orders.use_local_products', return self.config.get_bool("sideshow.orders.use_local_products", default=True)
default=True)
def allow_unknown_products(self): def allow_unknown_products(self):
""" """
@ -86,24 +85,27 @@ class NewOrderBatchHandler(BatchHandler):
when creating an order. This can be disabled, to force user when creating an order. This can be disabled, to force user
to choose existing local/external product. to choose existing local/external product.
""" """
return self.config.get_bool('sideshow.orders.allow_unknown_products', return self.config.get_bool(
default=True) "sideshow.orders.allow_unknown_products", default=True
)
def allow_item_discounts(self): def allow_item_discounts(self):
""" """
Returns boolean indicating whether per-item discounts are Returns boolean indicating whether per-item discounts are
allowed when creating an order. allowed when creating an order.
""" """
return self.config.get_bool('sideshow.orders.allow_item_discounts', return self.config.get_bool(
default=False) "sideshow.orders.allow_item_discounts", default=False
)
def allow_item_discounts_if_on_sale(self): def allow_item_discounts_if_on_sale(self):
""" """
Returns boolean indicating whether per-item discounts are Returns boolean indicating whether per-item discounts are
allowed even when the item is already on sale. allowed even when the item is already on sale.
""" """
return self.config.get_bool('sideshow.orders.allow_item_discounts_if_on_sale', return self.config.get_bool(
default=False) "sideshow.orders.allow_item_discounts_if_on_sale", default=False
)
def get_default_item_discount(self): def get_default_item_discount(self):
""" """
@ -111,7 +113,7 @@ class NewOrderBatchHandler(BatchHandler):
:rtype: :class:`~python:decimal.Decimal` or ``None`` :rtype: :class:`~python:decimal.Decimal` or ``None``
""" """
discount = self.config.get('sideshow.orders.default_item_discount') discount = self.config.get("sideshow.orders.default_item_discount")
if discount: if discount:
return decimal.Decimal(discount) return decimal.Decimal(discount)
@ -157,8 +159,9 @@ class NewOrderBatchHandler(BatchHandler):
query = session.query(model.LocalCustomer) query = session.query(model.LocalCustomer)
# filter query # filter query
criteria = [model.LocalCustomer.full_name.ilike(f'%{word}%') criteria = [
for word in term.split()] model.LocalCustomer.full_name.ilike(f"%{word}%") for word in term.split()
]
query = query.filter(sa.and_(*criteria)) query = query.filter(sa.and_(*criteria))
# sort query # sort query
@ -170,8 +173,8 @@ class NewOrderBatchHandler(BatchHandler):
# get results # get results
def result(customer): def result(customer):
return {'value': customer.uuid.hex, return {"value": customer.uuid.hex, "label": customer.full_name}
'label': customer.full_name}
return [result(c) for c in customers] return [result(c) for c in customers]
def init_batch(self, batch, session=None, progress=None, **kwargs): def init_batch(self, batch, session=None, progress=None, **kwargs):
@ -251,22 +254,25 @@ class NewOrderBatchHandler(BatchHandler):
batch.local_customer = None batch.local_customer = None
customer = batch.pending_customer customer = batch.pending_customer
if not customer: if not customer:
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user or batch.created_by) status=enum.PendingCustomerStatus.PENDING,
created_by=user or batch.created_by,
)
session.add(customer) session.add(customer)
batch.pending_customer = customer batch.pending_customer = customer
fields = [ fields = [
'full_name', "full_name",
'first_name', "first_name",
'last_name', "last_name",
'phone_number', "phone_number",
'email_address', "email_address",
] ]
for key in fields: for key in fields:
setattr(customer, key, customer_info.get(key)) setattr(customer, key, customer_info.get(key))
if 'full_name' not in customer_info: if "full_name" not in customer_info:
customer.full_name = self.app.make_full_name(customer.first_name, customer.full_name = self.app.make_full_name(
customer.last_name) customer.first_name, customer.last_name
)
self.refresh_batch_from_pending_customer(batch) self.refresh_batch_from_pending_customer(batch)
else: else:
@ -361,14 +367,18 @@ class NewOrderBatchHandler(BatchHandler):
# filter query # filter query
criteria = [] criteria = []
for word in term.split(): for word in term.split():
criteria.append(sa.or_( criteria.append(
model.LocalProduct.brand_name.ilike(f'%{word}%'), sa.or_(
model.LocalProduct.description.ilike(f'%{word}%'))) model.LocalProduct.brand_name.ilike(f"%{word}%"),
model.LocalProduct.description.ilike(f"%{word}%"),
)
)
query = query.filter(sa.and_(*criteria)) query = query.filter(sa.and_(*criteria))
# sort query # sort query
query = query.order_by(model.LocalProduct.brand_name, query = query.order_by(
model.LocalProduct.description) model.LocalProduct.brand_name, model.LocalProduct.description
)
# get data # get data
# TODO: need max_results option # TODO: need max_results option
@ -376,8 +386,8 @@ class NewOrderBatchHandler(BatchHandler):
# get results # get results
def result(product): def result(product):
return {'value': product.uuid.hex, return {"value": product.uuid.hex, "label": product.full_description}
'label': product.full_description}
return [result(c) for c in products] return [result(c) for c in products]
def get_default_uom_choices(self): def get_default_uom_choices(self):
@ -392,8 +402,7 @@ class NewOrderBatchHandler(BatchHandler):
corresponding to the UOM code and label, respectively. corresponding to the UOM code and label, respectively.
""" """
enum = self.app.enum enum = self.app.enum
return [{'key': key, 'value': val} return [{"key": key, "value": val} for key, val in enum.ORDER_UOM.items()]
for key, val in enum.ORDER_UOM.items()]
def get_product_info_external(self, session, product_id, user=None): def get_product_info_external(self, session, product_id, user=None):
""" """
@ -514,20 +523,20 @@ class NewOrderBatchHandler(BatchHandler):
* ``product_id`` will be set to the product UUID as string * ``product_id`` will be set to the product UUID as string
""" """
return { return {
'product_id': product.uuid.hex, "product_id": product.uuid.hex,
'scancode': product.scancode, "scancode": product.scancode,
'brand_name': product.brand_name, "brand_name": product.brand_name,
'description': product.description, "description": product.description,
'size': product.size, "size": product.size,
'full_description': product.full_description, "full_description": product.full_description,
'weighed': product.weighed, "weighed": product.weighed,
'special_order': product.special_order, "special_order": product.special_order,
'department_id': product.department_id, "department_id": product.department_id,
'department_name': product.department_name, "department_name": product.department_name,
'case_size': product.case_size, "case_size": product.case_size,
'unit_price_reg': product.unit_price_reg, "unit_price_reg": product.unit_price_reg,
'vendor_name': product.vendor_name, "vendor_name": product.vendor_name,
'vendor_item_code': product.vendor_item_code, "vendor_item_code": product.vendor_item_code,
} }
def get_past_orders(self, batch): def get_past_orders(self, batch):
@ -605,36 +614,51 @@ class NewOrderBatchHandler(BatchHandler):
products[product.uuid] = self.normalize_local_product(product) products[product.uuid] = self.normalize_local_product(product)
elif item.product_id and item.product_id not in products: elif item.product_id and item.product_id not in products:
products[item.product_id] = self.get_product_info_external( products[item.product_id] = self.get_product_info_external(
session, item.product_id, user=user) session, item.product_id, user=user
)
products = list(products.values()) products = list(products.values())
for product in products: for product in products:
price = product['unit_price_reg'] price = product["unit_price_reg"]
if 'unit_price_reg_display' not in product: if "unit_price_reg_display" not in product:
product['unit_price_reg_display'] = self.app.render_currency(price) product["unit_price_reg_display"] = self.app.render_currency(price)
if 'unit_price_quoted' not in product: if "unit_price_quoted" not in product:
product['unit_price_quoted'] = price product["unit_price_quoted"] = price
if 'unit_price_quoted_display' not in product: if "unit_price_quoted_display" not in product:
product['unit_price_quoted_display'] = product['unit_price_reg_display'] product["unit_price_quoted_display"] = product["unit_price_reg_display"]
if ('case_price_quoted' not in product if (
and product.get('unit_price_quoted') is not None "case_price_quoted" not in product
and product.get('case_size') is not None): and product.get("unit_price_quoted") is not None
product['case_price_quoted'] = product['unit_price_quoted'] * product['case_size'] and product.get("case_size") is not None
):
product["case_price_quoted"] = (
product["unit_price_quoted"] * product["case_size"]
)
if ('case_price_quoted_display' not in product if (
and 'case_price_quoted' in product): "case_price_quoted_display" not in product
product['case_price_quoted_display'] = self.app.render_currency( and "case_price_quoted" in product
product['case_price_quoted']) ):
product["case_price_quoted_display"] = self.app.render_currency(
product["case_price_quoted"]
)
return products return products
def add_item(self, batch, product_info, order_qty, order_uom, def add_item(
discount_percent=None, user=None): self,
batch,
product_info,
order_qty,
order_uom,
discount_percent=None,
user=None,
):
""" """
Add a new item/row to the batch, for given product and quantity. Add a new item/row to the batch, for given product and quantity.
@ -695,23 +719,25 @@ class NewOrderBatchHandler(BatchHandler):
raise TypeError("unknown/pending product not allowed for new orders") raise TypeError("unknown/pending product not allowed for new orders")
row.product_id = None row.product_id = None
row.local_product = None row.local_product = None
pending = model.PendingProduct(status=enum.PendingProductStatus.PENDING, pending = model.PendingProduct(
created_by=user or batch.created_by) status=enum.PendingProductStatus.PENDING,
created_by=user or batch.created_by,
)
fields = [ fields = [
'scancode', "scancode",
'brand_name', "brand_name",
'description', "description",
'size', "size",
'weighed', "weighed",
'department_id', "department_id",
'department_name', "department_name",
'special_order', "special_order",
'vendor_name', "vendor_name",
'vendor_item_code', "vendor_item_code",
'case_size', "case_size",
'unit_cost', "unit_cost",
'unit_price_reg', "unit_price_reg",
'notes', "notes",
] ]
for key in fields: for key in fields:
setattr(pending, key, product_info.get(key)) setattr(pending, key, product_info.get(key))
@ -735,8 +761,9 @@ class NewOrderBatchHandler(BatchHandler):
session.flush() session.flush()
return row return row
def update_item(self, row, product_info, order_qty, order_uom, def update_item(
discount_percent=None, user=None): self, row, product_info, order_qty, order_uom, discount_percent=None, user=None
):
""" """
Update an item/row, per given product and quantity. Update an item/row, per given product and quantity.
@ -794,25 +821,27 @@ class NewOrderBatchHandler(BatchHandler):
row.local_product = None row.local_product = None
pending = row.pending_product pending = row.pending_product
if not pending: if not pending:
pending = model.PendingProduct(status=enum.PendingProductStatus.PENDING, pending = model.PendingProduct(
created_by=user or row.batch.created_by) status=enum.PendingProductStatus.PENDING,
created_by=user or row.batch.created_by,
)
session.add(pending) session.add(pending)
row.pending_product = pending row.pending_product = pending
fields = [ fields = [
'scancode', "scancode",
'brand_name', "brand_name",
'description', "description",
'size', "size",
'weighed', "weighed",
'department_id', "department_id",
'department_name', "department_name",
'special_order', "special_order",
'vendor_name', "vendor_name",
'vendor_item_code', "vendor_item_code",
'case_size', "case_size",
'unit_cost', "unit_cost",
'unit_price_reg', "unit_price_reg",
'notes', "notes",
] ]
for key in fields: for key in fields:
setattr(pending, key, product_info.get(key)) setattr(pending, key, product_info.get(key))
@ -885,8 +914,8 @@ class NewOrderBatchHandler(BatchHandler):
row.unit_price_quoted = None row.unit_price_quoted = None
row.case_price_quoted = None row.case_price_quoted = None
if row.unit_price_sale is not None and ( if row.unit_price_sale is not None and (
not row.sale_ends not row.sale_ends or row.sale_ends > datetime.datetime.now()
or row.sale_ends > datetime.datetime.now()): ):
row.unit_price_quoted = row.unit_price_sale row.unit_price_quoted = row.unit_price_sale
else: else:
row.unit_price_quoted = row.unit_price_reg row.unit_price_quoted = row.unit_price_reg
@ -906,17 +935,17 @@ class NewOrderBatchHandler(BatchHandler):
row.total_price = row.unit_price_quoted * row.order_qty row.total_price = row.unit_price_quoted * row.order_qty
if row.total_price is not None: if row.total_price is not None:
if row.discount_percent and self.allow_item_discounts(): if row.discount_percent and self.allow_item_discounts():
row.total_price = (float(row.total_price) row.total_price = (
* (100 - float(row.discount_percent)) float(row.total_price) * (100 - float(row.discount_percent)) / 100.0
/ 100.0) )
row.total_price = decimal.Decimal(f'{row.total_price:0.2f}') row.total_price = decimal.Decimal(f"{row.total_price:0.2f}")
# update batch if total price changed # update batch if total price changed
if row.total_price != old_total: if row.total_price != old_total:
batch = row.batch batch = row.batch
batch.total_price = ((batch.total_price or 0) batch.total_price = (
+ (row.total_price or 0) (batch.total_price or 0) + (row.total_price or 0) - (old_total or 0)
- (old_total or 0)) )
# all ok # all ok
row.status_code = row.STATUS_OK row.status_code = row.STATUS_OK
@ -1046,8 +1075,7 @@ class NewOrderBatchHandler(BatchHandler):
are "effective" - i.e. rows with other status codes will not are "effective" - i.e. rows with other status codes will not
be created as proper order items. be created as proper order items.
""" """
return [row for row in batch.rows return [row for row in batch.rows if row.status_code == row.STATUS_OK]
if row.status_code == row.STATUS_OK]
def execute(self, batch, user=None, progress=None, **kwargs): def execute(self, batch, user=None, progress=None, **kwargs):
""" """
@ -1181,48 +1209,47 @@ class NewOrderBatchHandler(BatchHandler):
session = self.app.get_session(batch) session = self.app.get_session(batch)
batch_fields = [ batch_fields = [
'store_id', "store_id",
'customer_id', "customer_id",
'local_customer', "local_customer",
'pending_customer', "pending_customer",
'customer_name', "customer_name",
'phone_number', "phone_number",
'email_address', "email_address",
'total_price', "total_price",
] ]
row_fields = [ row_fields = [
'product_id', "product_id",
'local_product', "local_product",
'pending_product', "pending_product",
'product_scancode', "product_scancode",
'product_brand', "product_brand",
'product_description', "product_description",
'product_size', "product_size",
'product_weighed', "product_weighed",
'department_id', "department_id",
'department_name', "department_name",
'vendor_name', "vendor_name",
'vendor_item_code', "vendor_item_code",
'case_size', "case_size",
'order_qty', "order_qty",
'order_uom', "order_uom",
'unit_cost', "unit_cost",
'unit_price_quoted', "unit_price_quoted",
'case_price_quoted', "case_price_quoted",
'unit_price_reg', "unit_price_reg",
'unit_price_sale', "unit_price_sale",
'sale_ends', "sale_ends",
'discount_percent', "discount_percent",
'total_price', "total_price",
'special_order', "special_order",
] ]
# make order # make order
kw = dict([(field, getattr(batch, field)) kw = dict([(field, getattr(batch, field)) for field in batch_fields])
for field in batch_fields]) kw["order_id"] = batch.id
kw['order_id'] = batch.id kw["created_by"] = user
kw['created_by'] = user
order = model.Order(**kw) order = model.Order(**kw)
session.add(order) session.add(order)
session.flush() session.flush()
@ -1230,16 +1257,16 @@ class NewOrderBatchHandler(BatchHandler):
def convert(row, i): def convert(row, i):
# make order item # make order item
kw = dict([(field, getattr(row, field)) kw = dict([(field, getattr(row, field)) for field in row_fields])
for field in row_fields])
item = model.OrderItem(**kw) item = model.OrderItem(**kw)
order.items.append(item) order.items.append(item)
# set item status # set item status
self.set_initial_item_status(item, user) self.set_initial_item_status(item, user)
self.app.progress_loop(convert, rows, progress, self.app.progress_loop(
message="Converting batch rows to order items") convert, rows, progress, message="Converting batch rows to order items"
)
session.flush() session.flush()
return order return order

View file

@ -35,6 +35,5 @@ from wuttjamaican.cli import make_typer
sideshow_typer = make_typer( sideshow_typer = make_typer(
name='sideshow', name="sideshow", help="Sideshow -- Case/Special Order Tracker"
help="Sideshow -- Case/Special Order Tracker"
) )

View file

@ -38,8 +38,10 @@ def install(
""" """
config = ctx.parent.wutta_config config = ctx.parent.wutta_config
app = config.get_app() app = config.get_app()
install = app.get_install_handler(pkg_name='sideshow', install = app.get_install_handler(
pkg_name="sideshow",
app_title="Sideshow", app_title="Sideshow",
pypi_name='Sideshow', pypi_name="Sideshow",
egg_name='Sideshow') egg_name="Sideshow",
)
install.run() install.run()

View file

@ -33,26 +33,31 @@ class SideshowConfig(WuttaConfigExtension):
This establishes some config defaults specific to Sideshow. This establishes some config defaults specific to Sideshow.
""" """
key = 'sideshow'
key = "sideshow"
def configure(self, config): def configure(self, config):
""" """ """ """
# app info # app info
config.setdefault(f'{config.appname}.app_title', "Sideshow") config.setdefault(f"{config.appname}.app_title", "Sideshow")
config.setdefault(f'{config.appname}.app_dist', "Sideshow") config.setdefault(f"{config.appname}.app_dist", "Sideshow")
# app model, enum # app model, enum
config.setdefault(f'{config.appname}.model_spec', 'sideshow.db.model') config.setdefault(f"{config.appname}.model_spec", "sideshow.db.model")
config.setdefault(f'{config.appname}.enum_spec', 'sideshow.enum') config.setdefault(f"{config.appname}.enum_spec", "sideshow.enum")
# batch handlers # batch handlers
config.setdefault(f'{config.appname}.batch.neworder.handler.default_spec', config.setdefault(
'sideshow.batch.neworder:NewOrderBatchHandler') f"{config.appname}.batch.neworder.handler.default_spec",
"sideshow.batch.neworder:NewOrderBatchHandler",
)
# web app menu # web app menu
config.setdefault(f'{config.appname}.web.menus.handler.default_spec', config.setdefault(
'sideshow.web.menus:SideshowMenuHandler') f"{config.appname}.web.menus.handler.default_spec",
"sideshow.web.menus:SideshowMenuHandler",
)
# web app libcache # web app libcache
config.setdefault('wuttaweb.static_libcache.module', 'sideshow.web.static') config.setdefault("wuttaweb.static_libcache.module", "sideshow.web.static")

View file

@ -5,6 +5,7 @@ Revises: a4273360d379
Create Date: 2025-02-19 19:36:30.308840 Create Date: 2025-02-19 19:36:30.308840
""" """
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
@ -13,8 +14,8 @@ import wuttjamaican.db.util
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '13af2ffbc0e0' revision: str = "13af2ffbc0e0"
down_revision: Union[str, None] = 'a4273360d379' down_revision: Union[str, None] = "a4273360d379"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
@ -22,20 +23,32 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# sideshow_batch_neworder_row # sideshow_batch_neworder_row
op.add_column('sideshow_batch_neworder_row', sa.Column('vendor_name', sa.String(length=50), nullable=True)) op.add_column(
op.add_column('sideshow_batch_neworder_row', sa.Column('vendor_item_code', sa.String(length=20), nullable=True)) "sideshow_batch_neworder_row",
sa.Column("vendor_name", sa.String(length=50), nullable=True),
)
op.add_column(
"sideshow_batch_neworder_row",
sa.Column("vendor_item_code", sa.String(length=20), nullable=True),
)
# sideshow_order_item # sideshow_order_item
op.add_column('sideshow_order_item', sa.Column('vendor_name', sa.String(length=50), nullable=True)) op.add_column(
op.add_column('sideshow_order_item', sa.Column('vendor_item_code', sa.String(length=20), nullable=True)) "sideshow_order_item",
sa.Column("vendor_name", sa.String(length=50), nullable=True),
)
op.add_column(
"sideshow_order_item",
sa.Column("vendor_item_code", sa.String(length=20), nullable=True),
)
def downgrade() -> None: def downgrade() -> None:
# sideshow_order_item # sideshow_order_item
op.drop_column('sideshow_order_item', 'vendor_item_code') op.drop_column("sideshow_order_item", "vendor_item_code")
op.drop_column('sideshow_order_item', 'vendor_name') op.drop_column("sideshow_order_item", "vendor_name")
# sideshow_batch_neworder_row # sideshow_batch_neworder_row
op.drop_column('sideshow_batch_neworder_row', 'vendor_item_code') op.drop_column("sideshow_batch_neworder_row", "vendor_item_code")
op.drop_column('sideshow_batch_neworder_row', 'vendor_name') op.drop_column("sideshow_batch_neworder_row", "vendor_name")

View file

@ -5,6 +5,7 @@ Revises:
Create Date: 2024-12-30 18:53:51.358163 Create Date: 2024-12-30 18:53:51.358163
""" """
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
@ -14,253 +15,364 @@ import wuttjamaican.db.util
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = '7a6df83afbd4' revision: str = "7a6df83afbd4"
down_revision: Union[str, None] = None down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = ('sideshow',) branch_labels: Union[str, Sequence[str], None] = ("sideshow",)
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# enums # enums
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus').create(op.get_bind()) sa.Enum("PENDING", "READY", "RESOLVED", name="pendingcustomerstatus").create(
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus').create(op.get_bind()) op.get_bind()
)
sa.Enum("PENDING", "READY", "RESOLVED", name="pendingproductstatus").create(
op.get_bind()
)
# sideshow_customer_pending # sideshow_customer_pending
op.create_table('sideshow_customer_pending', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_customer_pending",
sa.Column('customer_id', sa.String(length=20), nullable=True), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('full_name', sa.String(length=100), nullable=True), sa.Column("customer_id", sa.String(length=20), nullable=True),
sa.Column('first_name', sa.String(length=50), nullable=True), sa.Column("full_name", sa.String(length=100), nullable=True),
sa.Column('last_name', sa.String(length=50), nullable=True), sa.Column("first_name", sa.String(length=50), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True), sa.Column("last_name", sa.String(length=50), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True), sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column('status', postgresql.ENUM('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus', create_type=False), nullable=False), sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column('created', sa.DateTime(timezone=True), nullable=False), sa.Column(
sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), "status",
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_customer_pending_created_by_uuid_user')), postgresql.ENUM(
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_customer_pending')) "PENDING",
"READY",
"RESOLVED",
name="pendingcustomerstatus",
create_type=False,
),
nullable=False,
),
sa.Column("created", sa.DateTime(timezone=True), nullable=False),
sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(
["created_by_uuid"],
["user.uuid"],
name=op.f("fk_sideshow_customer_pending_created_by_uuid_user"),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_customer_pending")),
) )
# sideshow_customer_local # sideshow_customer_local
op.create_table('sideshow_customer_local', op.create_table(
sa.Column('full_name', sa.String(length=100), nullable=True), "sideshow_customer_local",
sa.Column('first_name', sa.String(length=50), nullable=True), sa.Column("full_name", sa.String(length=100), nullable=True),
sa.Column('last_name', sa.String(length=50), nullable=True), sa.Column("first_name", sa.String(length=50), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True), sa.Column("last_name", sa.String(length=50), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True), sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column('external_id', sa.String(length=20), nullable=True), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_customer_local')) sa.Column("external_id", sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_customer_local")),
) )
# sideshow_product_pending # sideshow_product_pending
op.create_table('sideshow_product_pending', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_product_pending",
sa.Column('product_id', sa.String(length=20), nullable=True), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('scancode', sa.String(length=14), nullable=True), sa.Column("product_id", sa.String(length=20), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True), sa.Column("scancode", sa.String(length=14), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True), sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column('brand_name', sa.String(length=100), nullable=True), sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True), sa.Column("brand_name", sa.String(length=100), nullable=True),
sa.Column('size', sa.String(length=30), nullable=True), sa.Column("description", sa.String(length=255), nullable=True),
sa.Column('weighed', sa.Boolean(), nullable=True), sa.Column("size", sa.String(length=30), nullable=True),
sa.Column('vendor_name', sa.String(length=50), nullable=True), sa.Column("weighed", sa.Boolean(), nullable=True),
sa.Column('vendor_item_code', sa.String(length=20), nullable=True), sa.Column("vendor_name", sa.String(length=50), nullable=True),
sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), sa.Column("vendor_item_code", sa.String(length=20), nullable=True),
sa.Column('case_size', sa.Numeric(precision=9, scale=4), nullable=True), sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True),
sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("case_size", sa.Numeric(precision=9, scale=4), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True), sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('notes', sa.Text(), nullable=True), sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column('status', postgresql.ENUM('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus', create_type=False), nullable=False), sa.Column("notes", sa.Text(), nullable=True),
sa.Column('created', sa.DateTime(timezone=True), nullable=False), sa.Column(
sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), "status",
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_product_pending_created_by_uuid_user')), postgresql.ENUM(
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_product_pending')) "PENDING",
"READY",
"RESOLVED",
name="pendingproductstatus",
create_type=False,
),
nullable=False,
),
sa.Column("created", sa.DateTime(timezone=True), nullable=False),
sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(
["created_by_uuid"],
["user.uuid"],
name=op.f("fk_sideshow_product_pending_created_by_uuid_user"),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_product_pending")),
) )
# sideshow_product_local # sideshow_product_local
op.create_table('sideshow_product_local', op.create_table(
sa.Column('scancode', sa.String(length=14), nullable=True), "sideshow_product_local",
sa.Column('brand_name', sa.String(length=100), nullable=True), sa.Column("scancode", sa.String(length=14), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True), sa.Column("brand_name", sa.String(length=100), nullable=True),
sa.Column('size', sa.String(length=30), nullable=True), sa.Column("description", sa.String(length=255), nullable=True),
sa.Column('weighed', sa.Boolean(), nullable=True), sa.Column("size", sa.String(length=30), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True), sa.Column("weighed", sa.Boolean(), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True), sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True), sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column('vendor_name', sa.String(length=50), nullable=True), sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column('vendor_item_code', sa.String(length=20), nullable=True), sa.Column("vendor_name", sa.String(length=50), nullable=True),
sa.Column('case_size', sa.Numeric(precision=9, scale=4), nullable=True), sa.Column("vendor_item_code", sa.String(length=20), nullable=True),
sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), sa.Column("case_size", sa.Numeric(precision=9, scale=4), nullable=True),
sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True),
sa.Column('notes', sa.Text(), nullable=True), sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("notes", sa.Text(), nullable=True),
sa.Column('external_id', sa.String(length=20), nullable=True), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_product_local')) sa.Column("external_id", sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_product_local")),
) )
# sideshow_order # sideshow_order
op.create_table('sideshow_order', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_order",
sa.Column('order_id', sa.Integer(), nullable=False), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('store_id', sa.String(length=10), nullable=True), sa.Column("order_id", sa.Integer(), nullable=False),
sa.Column('customer_id', sa.String(length=20), nullable=True), sa.Column("store_id", sa.String(length=10), nullable=True),
sa.Column('local_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("customer_id", sa.String(length=20), nullable=True),
sa.Column('pending_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("local_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('customer_name', sa.String(length=100), nullable=True), sa.Column("pending_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True), sa.Column("customer_name", sa.String(length=100), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True), sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column('total_price', sa.Numeric(precision=10, scale=3), nullable=True), sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column('created', sa.DateTime(timezone=True), nullable=False), sa.Column("total_price", sa.Numeric(precision=10, scale=3), nullable=True),
sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("created", sa.DateTime(timezone=True), nullable=False),
sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid'], name=op.f('fk_order_local_customer_uuid_local_customer')), sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid'], name=op.f('fk_order_pending_customer_uuid_pending_customer')), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_order_created_by_uuid_user')), ["local_customer_uuid"],
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_order')) ["sideshow_customer_local.uuid"],
name=op.f("fk_order_local_customer_uuid_local_customer"),
),
sa.ForeignKeyConstraint(
["pending_customer_uuid"],
["sideshow_customer_pending.uuid"],
name=op.f("fk_order_pending_customer_uuid_pending_customer"),
),
sa.ForeignKeyConstraint(
["created_by_uuid"],
["user.uuid"],
name=op.f("fk_order_created_by_uuid_user"),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_order")),
) )
# sideshow_order_item # sideshow_order_item
op.create_table('sideshow_order_item', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_order_item",
sa.Column('order_uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=False), sa.Column("order_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('product_id', sa.String(length=20), nullable=True), sa.Column("sequence", sa.Integer(), nullable=False),
sa.Column('local_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("product_id", sa.String(length=20), nullable=True),
sa.Column('pending_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("local_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('product_scancode', sa.String(length=14), nullable=True), sa.Column("pending_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('product_brand', sa.String(length=100), nullable=True), sa.Column("product_scancode", sa.String(length=14), nullable=True),
sa.Column('product_description', sa.String(length=255), nullable=True), sa.Column("product_brand", sa.String(length=100), nullable=True),
sa.Column('product_size', sa.String(length=30), nullable=True), sa.Column("product_description", sa.String(length=255), nullable=True),
sa.Column('product_weighed', sa.Boolean(), nullable=True), sa.Column("product_size", sa.String(length=30), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True), sa.Column("product_weighed", sa.Boolean(), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True), sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True), sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column('case_size', sa.Numeric(precision=10, scale=4), nullable=True), sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column('order_qty', sa.Numeric(precision=10, scale=4), nullable=False), sa.Column("case_size", sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('order_uom', sa.String(length=10), nullable=False), sa.Column("order_qty", sa.Numeric(precision=10, scale=4), nullable=False),
sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), sa.Column("order_uom", sa.String(length=10), nullable=False),
sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True),
sa.Column('unit_price_sale', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('sale_ends', sa.DateTime(timezone=True), nullable=True), sa.Column("unit_price_sale", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('unit_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("sale_ends", sa.DateTime(timezone=True), nullable=True),
sa.Column('case_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=True), sa.Column("case_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("discount_percent", sa.Numeric(precision=5, scale=3), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=False), sa.Column("total_price", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('paid_amount', sa.Numeric(precision=8, scale=3), nullable=False), sa.Column("status_code", sa.Integer(), nullable=False),
sa.Column('payment_transaction_number', sa.String(length=20), nullable=True), sa.Column("paid_amount", sa.Numeric(precision=8, scale=3), nullable=False),
sa.ForeignKeyConstraint(['order_uuid'], ['sideshow_order.uuid'], name=op.f('fk_sideshow_order_item_order_uuid_order')), sa.Column("payment_transaction_number", sa.String(length=20), nullable=True),
sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid'], name=op.f('fk_sideshow_order_item_local_product_uuid_local_product')), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid'], name=op.f('fk_sideshow_order_item_pending_product_uuid_pending_product')), ["order_uuid"],
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_order_item')) ["sideshow_order.uuid"],
name=op.f("fk_sideshow_order_item_order_uuid_order"),
),
sa.ForeignKeyConstraint(
["local_product_uuid"],
["sideshow_product_local.uuid"],
name=op.f("fk_sideshow_order_item_local_product_uuid_local_product"),
),
sa.ForeignKeyConstraint(
["pending_product_uuid"],
["sideshow_product_pending.uuid"],
name=op.f("fk_sideshow_order_item_pending_product_uuid_pending_product"),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_order_item")),
) )
# sideshow_order_item_event # sideshow_order_item_event
op.create_table('sideshow_order_item_event', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_order_item_event",
sa.Column('item_uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('type_code', sa.Integer(), nullable=False), sa.Column("item_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('occurred', sa.DateTime(timezone=True), nullable=False), sa.Column("type_code", sa.Integer(), nullable=False),
sa.Column('actor_uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("occurred", sa.DateTime(timezone=True), nullable=False),
sa.Column('note', sa.Text(), nullable=True), sa.Column("actor_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(['actor_uuid'], ['user.uuid'], name=op.f('fk_sideshow_order_item_event_actor_uuid_user')), sa.Column("note", sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['item_uuid'], ['sideshow_order_item.uuid'], name=op.f('fk_sideshow_order_item_event_item_uuid_sideshow_order_item')), sa.ForeignKeyConstraint(
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_order_item_event')) ["actor_uuid"],
["user.uuid"],
name=op.f("fk_sideshow_order_item_event_actor_uuid_user"),
),
sa.ForeignKeyConstraint(
["item_uuid"],
["sideshow_order_item.uuid"],
name=op.f("fk_sideshow_order_item_event_item_uuid_sideshow_order_item"),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_order_item_event")),
) )
# sideshow_batch_neworder # sideshow_batch_neworder
op.create_table('sideshow_batch_neworder', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_batch_neworder",
sa.Column('id', sa.Integer(), nullable=False), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True), sa.Column("id", sa.Integer(), nullable=False),
sa.Column('notes', sa.Text(), nullable=True), sa.Column("description", sa.String(length=255), nullable=True),
sa.Column('row_count', sa.Integer(), nullable=True), sa.Column("notes", sa.Text(), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=True), sa.Column("row_count", sa.Integer(), nullable=True),
sa.Column('status_text', sa.String(length=255), nullable=True), sa.Column("status_code", sa.Integer(), nullable=True),
sa.Column('created', sa.DateTime(timezone=True), nullable=False), sa.Column("status_text", sa.String(length=255), nullable=True),
sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("created", sa.DateTime(timezone=True), nullable=False),
sa.Column('executed', sa.DateTime(timezone=True), nullable=True), sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('executed_by_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("executed", sa.DateTime(timezone=True), nullable=True),
sa.Column('store_id', sa.String(length=10), nullable=True), sa.Column("executed_by_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('customer_id', sa.String(length=20), nullable=True), sa.Column("store_id", sa.String(length=10), nullable=True),
sa.Column('local_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("customer_id", sa.String(length=20), nullable=True),
sa.Column('pending_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("local_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('customer_name', sa.String(length=100), nullable=True), sa.Column("pending_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True), sa.Column("customer_name", sa.String(length=100), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True), sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column('total_price', sa.Numeric(precision=10, scale=3), nullable=True), sa.Column("email_address", sa.String(length=255), nullable=True),
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_batch_neworder_created_by_uuid_user')), sa.Column("total_price", sa.Numeric(precision=10, scale=3), nullable=True),
sa.ForeignKeyConstraint(['executed_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_batch_neworder_executed_by_uuid_user')), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid'], name=op.f('fk_sideshow_batch_neworder_local_customer_uuid_local_customer')), ["created_by_uuid"],
sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid'], name=op.f('fk_sideshow_batch_neworder_pending_customer_uuid_pending_customer')), ["user.uuid"],
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_batch_neworder')) name=op.f("fk_sideshow_batch_neworder_created_by_uuid_user"),
),
sa.ForeignKeyConstraint(
["executed_by_uuid"],
["user.uuid"],
name=op.f("fk_sideshow_batch_neworder_executed_by_uuid_user"),
),
sa.ForeignKeyConstraint(
["local_customer_uuid"],
["sideshow_customer_local.uuid"],
name=op.f("fk_sideshow_batch_neworder_local_customer_uuid_local_customer"),
),
sa.ForeignKeyConstraint(
["pending_customer_uuid"],
["sideshow_customer_pending.uuid"],
name=op.f(
"fk_sideshow_batch_neworder_pending_customer_uuid_pending_customer"
),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_batch_neworder")),
) )
# sideshow_batch_neworder_row # sideshow_batch_neworder_row
op.create_table('sideshow_batch_neworder_row', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_batch_neworder_row",
sa.Column('batch_uuid', wuttjamaican.db.util.UUID(), nullable=False), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=False), sa.Column("batch_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('status_text', sa.String(length=255), nullable=True), sa.Column("sequence", sa.Integer(), nullable=False),
sa.Column('modified', sa.DateTime(timezone=True), nullable=True), sa.Column("status_text", sa.String(length=255), nullable=True),
sa.Column('product_id', sa.String(length=20), nullable=True), sa.Column("modified", sa.DateTime(timezone=True), nullable=True),
sa.Column('local_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("product_id", sa.String(length=20), nullable=True),
sa.Column('pending_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), sa.Column("local_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('product_scancode', sa.String(length=14), nullable=True), sa.Column("pending_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('product_brand', sa.String(length=100), nullable=True), sa.Column("product_scancode", sa.String(length=14), nullable=True),
sa.Column('product_description', sa.String(length=255), nullable=True), sa.Column("product_brand", sa.String(length=100), nullable=True),
sa.Column('product_size', sa.String(length=30), nullable=True), sa.Column("product_description", sa.String(length=255), nullable=True),
sa.Column('product_weighed', sa.Boolean(), nullable=True), sa.Column("product_size", sa.String(length=30), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True), sa.Column("product_weighed", sa.Boolean(), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True), sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True), sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column('case_size', sa.Numeric(precision=10, scale=4), nullable=True), sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column('order_qty', sa.Numeric(precision=10, scale=4), nullable=False), sa.Column("case_size", sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('order_uom', sa.String(length=10), nullable=False), sa.Column("order_qty", sa.Numeric(precision=10, scale=4), nullable=False),
sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), sa.Column("order_uom", sa.String(length=10), nullable=False),
sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True),
sa.Column('unit_price_sale', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('sale_ends', sa.DateTime(timezone=True), nullable=True), sa.Column("unit_price_sale", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('unit_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("sale_ends", sa.DateTime(timezone=True), nullable=True),
sa.Column('case_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("unit_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=True), sa.Column("case_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True), sa.Column("discount_percent", sa.Numeric(precision=5, scale=3), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=True), sa.Column("total_price", sa.Numeric(precision=8, scale=3), nullable=True),
sa.ForeignKeyConstraint(['batch_uuid'], ['sideshow_batch_neworder.uuid'], name=op.f('fk_sideshow_batch_neworder_row_batch_uuid_batch_neworder')), sa.Column("status_code", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid'], name=op.f('fk_sideshow_batch_neworder_row_local_product_uuid_local_product')), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid'], name=op.f('fk_sideshow_batch_neworder_row_pending_product_uuid_pending_product')), ["batch_uuid"],
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_batch_neworder_row')) ["sideshow_batch_neworder.uuid"],
name=op.f("fk_sideshow_batch_neworder_row_batch_uuid_batch_neworder"),
),
sa.ForeignKeyConstraint(
["local_product_uuid"],
["sideshow_product_local.uuid"],
name=op.f(
"fk_sideshow_batch_neworder_row_local_product_uuid_local_product"
),
),
sa.ForeignKeyConstraint(
["pending_product_uuid"],
["sideshow_product_pending.uuid"],
name=op.f(
"fk_sideshow_batch_neworder_row_pending_product_uuid_pending_product"
),
),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_batch_neworder_row")),
) )
def downgrade() -> None: def downgrade() -> None:
# sideshow_batch_neworder* # sideshow_batch_neworder*
op.drop_table('sideshow_batch_neworder_row') op.drop_table("sideshow_batch_neworder_row")
op.drop_table('sideshow_batch_neworder') op.drop_table("sideshow_batch_neworder")
# sideshow_order_item_event # sideshow_order_item_event
op.drop_table('sideshow_order_item_event') op.drop_table("sideshow_order_item_event")
# sideshow_order_item # sideshow_order_item
op.drop_table('sideshow_order_item') op.drop_table("sideshow_order_item")
# sideshow_order # sideshow_order
op.drop_table('sideshow_order') op.drop_table("sideshow_order")
# sideshow_product_local # sideshow_product_local
op.drop_table('sideshow_product_local') op.drop_table("sideshow_product_local")
# sideshow_product_pending # sideshow_product_pending
op.drop_table('sideshow_product_pending') op.drop_table("sideshow_product_pending")
# sideshow_customer_local # sideshow_customer_local
op.drop_table('sideshow_customer_local') op.drop_table("sideshow_customer_local")
# sideshow_customer_pending # sideshow_customer_pending
op.drop_table('sideshow_customer_pending') op.drop_table("sideshow_customer_pending")
# enums # enums
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus').drop(op.get_bind()) sa.Enum("PENDING", "READY", "RESOLVED", name="pendingproductstatus").drop(
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus').drop(op.get_bind()) op.get_bind()
)
sa.Enum("PENDING", "READY", "RESOLVED", name="pendingcustomerstatus").drop(
op.get_bind()
)

View file

@ -5,6 +5,7 @@ Revises: 7a6df83afbd4
Create Date: 2025-01-27 17:48:20.638664 Create Date: 2025-01-27 17:48:20.638664
""" """
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
@ -13,8 +14,8 @@ import wuttjamaican.db.util
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = 'a4273360d379' revision: str = "a4273360d379"
down_revision: Union[str, None] = '7a6df83afbd4' down_revision: Union[str, None] = "7a6df83afbd4"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
@ -22,18 +23,19 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
# sideshow_store # sideshow_store
op.create_table('sideshow_store', op.create_table(
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), "sideshow_store",
sa.Column('store_id', sa.String(length=10), nullable=False), sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False), sa.Column("store_id", sa.String(length=10), nullable=False),
sa.Column('archived', sa.Boolean(), nullable=False), sa.Column("name", sa.String(length=100), nullable=False),
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_store')), sa.Column("archived", sa.Boolean(), nullable=False),
sa.UniqueConstraint('store_id', name=op.f('uq_sideshow_store_store_id')), sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_store")),
sa.UniqueConstraint('name', name=op.f('uq_sideshow_store_name')) sa.UniqueConstraint("store_id", name=op.f("uq_sideshow_store_store_id")),
sa.UniqueConstraint("name", name=op.f("uq_sideshow_store_name")),
) )
def downgrade() -> None: def downgrade() -> None:
# sideshow_store # sideshow_store
op.drop_table('sideshow_store') op.drop_table("sideshow_store")

View file

@ -5,6 +5,7 @@ Revises: 13af2ffbc0e0
Create Date: 2025-02-20 12:08:27.374172 Create Date: 2025-02-20 12:08:27.374172
""" """
from typing import Sequence, Union from typing import Sequence, Union
from alembic import op from alembic import op
@ -13,8 +14,8 @@ import wuttjamaican.db.util
from alembic_postgresql_enum import TableReference from alembic_postgresql_enum import TableReference
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = 'fd8a2527bd30' revision: str = "fd8a2527bd30"
down_revision: Union[str, None] = '13af2ffbc0e0' down_revision: Union[str, None] = "13af2ffbc0e0"
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
@ -23,19 +24,31 @@ def upgrade() -> None:
# pendingcustomerstatus # pendingcustomerstatus
op.sync_enum_values( op.sync_enum_values(
enum_schema='public', enum_schema="public",
enum_name='pendingcustomerstatus', enum_name="pendingcustomerstatus",
new_values=['PENDING', 'READY', 'RESOLVED', 'IGNORED'], new_values=["PENDING", "READY", "RESOLVED", "IGNORED"],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_customer_pending', column_name='status')], affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_customer_pending",
column_name="status",
)
],
enum_values_to_rename=[], enum_values_to_rename=[],
) )
# pendingproductstatus # pendingproductstatus
op.sync_enum_values( op.sync_enum_values(
enum_schema='public', enum_schema="public",
enum_name='pendingproductstatus', enum_name="pendingproductstatus",
new_values=['PENDING', 'READY', 'RESOLVED', 'IGNORED'], new_values=["PENDING", "READY", "RESOLVED", "IGNORED"],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_product_pending', column_name='status')], affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_product_pending",
column_name="status",
)
],
enum_values_to_rename=[], enum_values_to_rename=[],
) )
@ -44,18 +57,30 @@ def downgrade() -> None:
# pendingproductstatus # pendingproductstatus
op.sync_enum_values( op.sync_enum_values(
enum_schema='public', enum_schema="public",
enum_name='pendingproductstatus', enum_name="pendingproductstatus",
new_values=['PENDING', 'READY', 'RESOLVED'], new_values=["PENDING", "READY", "RESOLVED"],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_product_pending', column_name='status')], affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_product_pending",
column_name="status",
)
],
enum_values_to_rename=[], enum_values_to_rename=[],
) )
# pendingcustomerstatus # pendingcustomerstatus
op.sync_enum_values( op.sync_enum_values(
enum_schema='public', enum_schema="public",
enum_name='pendingcustomerstatus', enum_name="pendingcustomerstatus",
new_values=['PENDING', 'READY', 'RESOLVED'], new_values=["PENDING", "READY", "RESOLVED"],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_customer_pending', column_name='status')], affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_customer_pending",
column_name="status",
)
],
enum_values_to_rename=[], enum_values_to_rename=[],
) )

View file

@ -47,10 +47,11 @@ class NewOrderBatch(model.BatchMixin, model.Base):
Generic batch attributes (undocumented below) are inherited from Generic batch attributes (undocumented below) are inherited from
:class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchMixin`. :class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchMixin`.
""" """
__tablename__ = 'sideshow_batch_neworder'
__batchrow_class__ = 'NewOrderBatchRow'
batch_type = 'neworder' __tablename__ = "sideshow_batch_neworder"
__batchrow_class__ = "NewOrderBatchRow"
batch_type = "neworder"
""" """
Official :term:`batch type` key. Official :term:`batch type` key.
""" """
@ -58,72 +59,102 @@ class NewOrderBatch(model.BatchMixin, model.Base):
@declared_attr @declared_attr
def __table_args__(cls): def __table_args__(cls):
return cls.__default_table_args__() + ( return cls.__default_table_args__() + (
sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid']), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid']), ["local_customer_uuid"], ["sideshow_customer_local.uuid"]
),
sa.ForeignKeyConstraint(
["pending_customer_uuid"], ["sideshow_customer_pending.uuid"]
),
) )
STATUS_OK = 1 STATUS_OK = 1
STATUS = { STATUS = {
STATUS_OK : "ok", STATUS_OK: "ok",
} }
store_id = sa.Column(sa.String(length=10), nullable=True, doc=""" store_id = sa.Column(
sa.String(length=10),
nullable=True,
doc="""
ID of the store to which the order pertains, if applicable. ID of the store to which the order pertains, if applicable.
""") """,
)
customer_id = sa.Column(sa.String(length=20), nullable=True, doc=""" customer_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Proper account ID for the :term:`external customer` to which the Proper account ID for the :term:`external customer` to which the
order pertains, if applicable. order pertains, if applicable.
See also :attr:`local_customer` and :attr:`pending_customer`. See also :attr:`local_customer` and :attr:`pending_customer`.
""") """,
)
local_customer_uuid = sa.Column(model.UUID(), nullable=True) local_customer_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr @declared_attr
def local_customer(cls): def local_customer(cls):
return orm.relationship( return orm.relationship(
'LocalCustomer', "LocalCustomer",
back_populates='new_order_batches', back_populates="new_order_batches",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.customers.LocalCustomer` record :class:`~sideshow.db.model.customers.LocalCustomer` record
for the order, if applicable. for the order, if applicable.
See also :attr:`customer_id` and :attr:`pending_customer`. See also :attr:`customer_id` and :attr:`pending_customer`.
""") """,
)
pending_customer_uuid = sa.Column(model.UUID(), nullable=True) pending_customer_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr @declared_attr
def pending_customer(cls): def pending_customer(cls):
return orm.relationship( return orm.relationship(
'PendingCustomer', "PendingCustomer",
back_populates='new_order_batches', back_populates="new_order_batches",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.customers.PendingCustomer` :class:`~sideshow.db.model.customers.PendingCustomer`
record for the order, if applicable. record for the order, if applicable.
See also :attr:`customer_id` and :attr:`local_customer`. See also :attr:`customer_id` and :attr:`local_customer`.
""") """,
)
customer_name = sa.Column(sa.String(length=100), nullable=True, doc=""" customer_name = sa.Column(
sa.String(length=100),
nullable=True,
doc="""
Name for the customer account. Name for the customer account.
""") """,
)
phone_number = sa.Column(sa.String(length=20), nullable=True, doc=""" phone_number = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Phone number for the customer. Phone number for the customer.
""") """,
)
email_address = sa.Column(sa.String(length=255), nullable=True, doc=""" email_address = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Email address for the customer. Email address for the customer.
""") """,
)
total_price = sa.Column(sa.Numeric(precision=10, scale=3), nullable=True, doc=""" total_price = sa.Column(
sa.Numeric(precision=10, scale=3),
nullable=True,
doc="""
Full price (not including tax etc.) for all items on the order. Full price (not including tax etc.) for all items on the order.
""") """,
)
class NewOrderBatchRow(model.BatchRowMixin, model.Base): class NewOrderBatchRow(model.BatchRowMixin, model.Base):
@ -134,14 +165,19 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
Generic row attributes (undocumented below) are inherited from Generic row attributes (undocumented below) are inherited from
:class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchRowMixin`. :class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchRowMixin`.
""" """
__tablename__ = 'sideshow_batch_neworder_row'
__tablename__ = "sideshow_batch_neworder_row"
__batch_class__ = NewOrderBatch __batch_class__ = NewOrderBatch
@declared_attr @declared_attr
def __table_args__(cls): def __table_args__(cls):
return cls.__default_table_args__() + ( return cls.__default_table_args__() + (
sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid']), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid']), ["local_product_uuid"], ["sideshow_product_local.uuid"]
),
sa.ForeignKeyConstraint(
["pending_product_uuid"], ["sideshow_product_pending.uuid"]
),
) )
STATUS_OK = 1 STATUS_OK = 1
@ -164,52 +200,61 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
""" """
STATUS = { STATUS = {
STATUS_OK : "ok", STATUS_OK: "ok",
STATUS_MISSING_PRODUCT : "missing product", STATUS_MISSING_PRODUCT: "missing product",
STATUS_MISSING_ORDER_QTY : "missing order qty/uom", STATUS_MISSING_ORDER_QTY: "missing order qty/uom",
} }
""" """
Dict of possible status code -> label options. Dict of possible status code -> label options.
""" """
product_id = sa.Column(sa.String(length=20), nullable=True, doc=""" product_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Proper ID for the :term:`external product` which the order item Proper ID for the :term:`external product` which the order item
represents, if applicable. represents, if applicable.
See also :attr:`local_product` and :attr:`pending_product`. See also :attr:`local_product` and :attr:`pending_product`.
""") """,
)
local_product_uuid = sa.Column(model.UUID(), nullable=True) local_product_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr @declared_attr
def local_product(cls): def local_product(cls):
return orm.relationship( return orm.relationship(
'LocalProduct', "LocalProduct",
back_populates='new_order_batch_rows', back_populates="new_order_batch_rows",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.products.LocalProduct` record :class:`~sideshow.db.model.products.LocalProduct` record
for the order item, if applicable. for the order item, if applicable.
See also :attr:`product_id` and :attr:`pending_product`. See also :attr:`product_id` and :attr:`pending_product`.
""") """,
)
pending_product_uuid = sa.Column(model.UUID(), nullable=True) pending_product_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr @declared_attr
def pending_product(cls): def pending_product(cls):
return orm.relationship( return orm.relationship(
'PendingProduct', "PendingProduct",
back_populates='new_order_batch_rows', back_populates="new_order_batch_rows",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.products.PendingProduct` record :class:`~sideshow.db.model.products.PendingProduct` record
for the order item, if applicable. for the order item, if applicable.
See also :attr:`product_id` and :attr:`local_product`. See also :attr:`product_id` and :attr:`local_product`.
""") """,
)
product_scancode = sa.Column(sa.String(length=14), nullable=True, doc=""" product_scancode = sa.Column(
sa.String(length=14),
nullable=True,
doc="""
Scancode for the product, as string. Scancode for the product, as string.
.. note:: .. note::
@ -221,61 +266,109 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
That may change eventually, depending on POS integration That may change eventually, depending on POS integration
scenarios that come up. Maybe a config option to declare scenarios that come up. Maybe a config option to declare
whether check digit should be included or not, etc. whether check digit should be included or not, etc.
""") """,
)
product_brand = sa.Column(sa.String(length=100), nullable=True, doc=""" product_brand = sa.Column(
sa.String(length=100),
nullable=True,
doc="""
Brand name for the product - up to 100 chars. Brand name for the product - up to 100 chars.
""") """,
)
product_description = sa.Column(sa.String(length=255), nullable=True, doc=""" product_description = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Description for the product - up to 255 chars. Description for the product - up to 255 chars.
""") """,
)
product_size = sa.Column(sa.String(length=30), nullable=True, doc=""" product_size = sa.Column(
sa.String(length=30),
nullable=True,
doc="""
Size of the product, as string - up to 30 chars. Size of the product, as string - up to 30 chars.
""") """,
)
product_weighed = sa.Column(sa.Boolean(), nullable=True, doc=""" product_weighed = sa.Column(
sa.Boolean(),
nullable=True,
doc="""
Flag indicating the product is sold by weight; default is null. Flag indicating the product is sold by weight; default is null.
""") """,
)
department_id = sa.Column(sa.String(length=10), nullable=True, doc=""" department_id = sa.Column(
sa.String(length=10),
nullable=True,
doc="""
ID of the department to which the product belongs, if known. ID of the department to which the product belongs, if known.
""") """,
)
department_name = sa.Column(sa.String(length=30), nullable=True, doc=""" department_name = sa.Column(
sa.String(length=30),
nullable=True,
doc="""
Name of the department to which the product belongs, if known. Name of the department to which the product belongs, if known.
""") """,
)
special_order = sa.Column(sa.Boolean(), nullable=True, doc=""" special_order = sa.Column(
sa.Boolean(),
nullable=True,
doc="""
Flag indicating the item is a "special order" - e.g. something not Flag indicating the item is a "special order" - e.g. something not
normally carried by the store. Default is null. normally carried by the store. Default is null.
""") """,
)
vendor_name = sa.Column(sa.String(length=50), nullable=True, doc=""" vendor_name = sa.Column(
sa.String(length=50),
nullable=True,
doc="""
Name of vendor from which product may be purchased, if known. See Name of vendor from which product may be purchased, if known. See
also :attr:`vendor_item_code`. also :attr:`vendor_item_code`.
""") """,
)
vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc=""" vendor_item_code = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Item code (SKU) to use when ordering this product from the vendor Item code (SKU) to use when ordering this product from the vendor
identified by :attr:`vendor_name`, if known. identified by :attr:`vendor_name`, if known.
""") """,
)
case_size = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc=""" case_size = sa.Column(
sa.Numeric(precision=10, scale=4),
nullable=True,
doc="""
Case pack count for the product, if known. Case pack count for the product, if known.
If this is not set, then customer cannot order a "case" of the item. If this is not set, then customer cannot order a "case" of the item.
""") """,
)
order_qty = sa.Column(sa.Numeric(precision=10, scale=4), nullable=False, doc=""" order_qty = sa.Column(
sa.Numeric(precision=10, scale=4),
nullable=False,
doc="""
Quantity (as decimal) of product being ordered. Quantity (as decimal) of product being ordered.
This must be interpreted along with :attr:`order_uom` to determine This must be interpreted along with :attr:`order_uom` to determine
the *complete* order quantity, e.g. "2 cases". the *complete* order quantity, e.g. "2 cases".
""") """,
)
order_uom = sa.Column(sa.String(length=10), nullable=False, doc=""" order_uom = sa.Column(
sa.String(length=10),
nullable=False,
doc="""
Code indicating the unit of measure for product being ordered. Code indicating the unit of measure for product being ordered.
This should be one of the codes from This should be one of the codes from
@ -284,31 +377,51 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
Sideshow will treat :data:`~sideshow.enum.ORDER_UOM_CASE` Sideshow will treat :data:`~sideshow.enum.ORDER_UOM_CASE`
differently but :data:`~sideshow.enum.ORDER_UOM_UNIT` and others differently but :data:`~sideshow.enum.ORDER_UOM_UNIT` and others
are all treated the same (i.e. "unit" is assumed). are all treated the same (i.e. "unit" is assumed).
""") """,
)
unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc=""" unit_cost = sa.Column(
sa.Numeric(precision=9, scale=5),
nullable=True,
doc="""
Cost of goods amount for one "unit" (not "case") of the product, Cost of goods amount for one "unit" (not "case") of the product,
as decimal to 4 places. as decimal to 4 places.
""") """,
)
unit_price_reg = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_reg = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Regular price for the item unit. Unless a sale is in effect, Regular price for the item unit. Unless a sale is in effect,
:attr:`unit_price_quoted` will typically match this value. :attr:`unit_price_quoted` will typically match this value.
""") """,
)
unit_price_sale = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_sale = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Sale price for the item unit, if applicable. If set, then Sale price for the item unit, if applicable. If set, then
:attr:`unit_price_quoted` will typically match this value. See :attr:`unit_price_quoted` will typically match this value. See
also :attr:`sale_ends`. also :attr:`sale_ends`.
""") """,
)
sale_ends = sa.Column(sa.DateTime(timezone=True), nullable=True, doc=""" sale_ends = sa.Column(
sa.DateTime(timezone=True),
nullable=True,
doc="""
End date/time for the sale in effect, if any. End date/time for the sale in effect, if any.
This is only relevant if :attr:`unit_price_sale` is set. This is only relevant if :attr:`unit_price_sale` is set.
""") """,
)
unit_price_quoted = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_quoted = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Quoted price for the item unit. This is the "effective" unit Quoted price for the item unit. This is the "effective" unit
price, which is used to calculate :attr:`total_price`. price, which is used to calculate :attr:`total_price`.
@ -317,21 +430,33 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
:attr:`unit_price_sale`. :attr:`unit_price_sale`.
See also :attr:`case_price_quoted`, if applicable. See also :attr:`case_price_quoted`, if applicable.
""") """,
)
case_price_quoted = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" case_price_quoted = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Quoted price for a "case" of the item, if applicable. Quoted price for a "case" of the item, if applicable.
This is mostly for display purposes; :attr:`unit_price_quoted` is This is mostly for display purposes; :attr:`unit_price_quoted` is
used for calculations. used for calculations.
""") """,
)
discount_percent = sa.Column(sa.Numeric(precision=5, scale=3), nullable=True, doc=""" discount_percent = sa.Column(
sa.Numeric(precision=5, scale=3),
nullable=True,
doc="""
Discount percent to apply when calculating :attr:`total_price`, if Discount percent to apply when calculating :attr:`total_price`, if
applicable. applicable.
""") """,
)
total_price = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" total_price = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Full price (not including tax etc.) which the customer is quoted Full price (not including tax etc.) which the customer is quoted
for the order item. for the order item.
@ -342,7 +467,8 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
* :attr:`order_uom` * :attr:`order_uom`
* :attr:`case_size` * :attr:`case_size`
* :attr:`discount_percent` * :attr:`discount_percent`
""") """,
)
def __str__(self): def __str__(self):
return str(self.pending_product or self.product_description or "") return str(self.pending_product or self.product_description or "")

View file

@ -42,25 +42,45 @@ class CustomerMixin:
* :class:`PendingCustomer` * :class:`PendingCustomer`
""" """
full_name = sa.Column(sa.String(length=100), nullable=True, doc=""" full_name = sa.Column(
sa.String(length=100),
nullable=True,
doc="""
Full display name for the customer account. Full display name for the customer account.
""") """,
)
first_name = sa.Column(sa.String(length=50), nullable=True, doc=""" first_name = sa.Column(
sa.String(length=50),
nullable=True,
doc="""
First name of the customer. First name of the customer.
""") """,
)
last_name = sa.Column(sa.String(length=50), nullable=True, doc=""" last_name = sa.Column(
sa.String(length=50),
nullable=True,
doc="""
Last name of the customer. Last name of the customer.
""") """,
)
phone_number = sa.Column(sa.String(length=20), nullable=True, doc=""" phone_number = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Phone number for the customer. Phone number for the customer.
""") """,
)
email_address = sa.Column(sa.String(length=255), nullable=True, doc=""" email_address = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Email address for the customer. Email address for the customer.
""") """,
)
def __str__(self): def __str__(self):
return self.full_name or "" return self.full_name or ""
@ -78,35 +98,42 @@ class LocalCustomer(CustomerMixin, model.Base):
:term:`pending customer` is executed, a new record is added to :term:`pending customer` is executed, a new record is added to
this local customers table, for lookup next time. this local customers table, for lookup next time.
""" """
__tablename__ = 'sideshow_customer_local'
__tablename__ = "sideshow_customer_local"
uuid = model.uuid_column() uuid = model.uuid_column()
external_id = sa.Column(sa.String(length=20), nullable=True, doc=""" external_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
ID of the proper customer account associated with this record, if ID of the proper customer account associated with this record, if
applicable. applicable.
""") """,
)
orders = orm.relationship( orders = orm.relationship(
'Order', "Order",
order_by='Order.order_id.desc()', order_by="Order.order_id.desc()",
back_populates='local_customer', back_populates="local_customer",
cascade_backrefs=False, cascade_backrefs=False,
doc=""" doc="""
List of :class:`~sideshow.db.model.orders.Order` records List of :class:`~sideshow.db.model.orders.Order` records
associated with this customer. associated with this customer.
""") """,
)
new_order_batches = orm.relationship( new_order_batches = orm.relationship(
'NewOrderBatch', "NewOrderBatch",
order_by='NewOrderBatch.id.desc()', order_by="NewOrderBatch.id.desc()",
back_populates='local_customer', back_populates="local_customer",
cascade_backrefs=False, cascade_backrefs=False,
doc=""" doc="""
List of List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch` :class:`~sideshow.db.model.batch.neworder.NewOrderBatch`
records associated with this customer. records associated with this customer.
""") """,
)
class PendingCustomer(CustomerMixin, model.Base): class PendingCustomer(CustomerMixin, model.Base):
@ -121,25 +148,38 @@ class PendingCustomer(CustomerMixin, model.Base):
is executed, a new record is added to the :term:`local customers is executed, a new record is added to the :term:`local customers
<local customer>` table, for lookup next time. <local customer>` table, for lookup next time.
""" """
__tablename__ = 'sideshow_customer_pending'
__tablename__ = "sideshow_customer_pending"
uuid = model.uuid_column() uuid = model.uuid_column()
customer_id = sa.Column(sa.String(length=20), nullable=True, doc=""" customer_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
ID of the proper customer account associated with this record, if ID of the proper customer account associated with this record, if
applicable. applicable.
""") """,
)
status = sa.Column(sa.Enum(PendingCustomerStatus), nullable=False, doc=""" status = sa.Column(
sa.Enum(PendingCustomerStatus),
nullable=False,
doc="""
Status code for the customer record. Status code for the customer record.
""") """,
)
created = sa.Column(sa.DateTime(timezone=True), nullable=False, created = sa.Column(
default=datetime.datetime.now, doc=""" sa.DateTime(timezone=True),
nullable=False,
default=datetime.datetime.now,
doc="""
Timestamp when the customer record was created. Timestamp when the customer record was created.
""") """,
)
created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False) created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False)
created_by = orm.relationship( created_by = orm.relationship(
model.User, model.User,
cascade_backrefs=False, cascade_backrefs=False,
@ -147,25 +187,28 @@ class PendingCustomer(CustomerMixin, model.Base):
Reference to the Reference to the
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
created the customer record. created the customer record.
""") """,
)
orders = orm.relationship( orders = orm.relationship(
'Order', "Order",
order_by='Order.order_id.desc()', order_by="Order.order_id.desc()",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='pending_customer', back_populates="pending_customer",
doc=""" doc="""
List of :class:`~sideshow.db.model.orders.Order` records List of :class:`~sideshow.db.model.orders.Order` records
associated with this customer. associated with this customer.
""") """,
)
new_order_batches = orm.relationship( new_order_batches = orm.relationship(
'NewOrderBatch', "NewOrderBatch",
order_by='NewOrderBatch.id.desc()', order_by="NewOrderBatch.id.desc()",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='pending_customer', back_populates="pending_customer",
doc=""" doc="""
List of List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch` :class:`~sideshow.db.model.batch.neworder.NewOrderBatch`
records associated with this customer. records associated with this customer.
""") """,
)

View file

@ -41,93 +41,134 @@ class Order(model.Base):
Usually, orders are created by way of a Usually, orders are created by way of a
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch`. :class:`~sideshow.db.model.batch.neworder.NewOrderBatch`.
""" """
__tablename__ = 'sideshow_order'
__tablename__ = "sideshow_order"
# TODO: this feels a bit hacky yet but it does avoid problems # TODO: this feels a bit hacky yet but it does avoid problems
# showing the Orders grid for a PendingCustomer # showing the Orders grid for a PendingCustomer
__colanderalchemy_config__ = { __colanderalchemy_config__ = {
'excludes': ['items'], "excludes": ["items"],
} }
uuid = model.uuid_column() uuid = model.uuid_column()
order_id = sa.Column(sa.Integer(), nullable=False, doc=""" order_id = sa.Column(
sa.Integer(),
nullable=False,
doc="""
Unique ID for the order. Unique ID for the order.
When the order is created from New Order Batch, this order ID will When the order is created from New Order Batch, this order ID will
match the batch ID. match the batch ID.
""") """,
)
store_id = sa.Column(sa.String(length=10), nullable=True, doc=""" store_id = sa.Column(
sa.String(length=10),
nullable=True,
doc="""
ID of the store to which the order pertains, if applicable. ID of the store to which the order pertains, if applicable.
""") """,
)
store = orm.relationship( store = orm.relationship(
'Store', "Store",
primaryjoin='Store.store_id == Order.store_id', primaryjoin="Store.store_id == Order.store_id",
foreign_keys='Order.store_id', foreign_keys="Order.store_id",
doc=""" doc="""
Reference to the :class:`~sideshow.db.model.stores.Store` Reference to the :class:`~sideshow.db.model.stores.Store`
record, if applicable. record, if applicable.
""") """,
)
customer_id = sa.Column(sa.String(length=20), nullable=True, doc=""" customer_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Proper account ID for the :term:`external customer` to which the Proper account ID for the :term:`external customer` to which the
order pertains, if applicable. order pertains, if applicable.
See also :attr:`local_customer` and :attr:`pending_customer`. See also :attr:`local_customer` and :attr:`pending_customer`.
""") """,
)
local_customer_uuid = model.uuid_fk_column('sideshow_customer_local.uuid', nullable=True) local_customer_uuid = model.uuid_fk_column(
"sideshow_customer_local.uuid", nullable=True
)
local_customer = orm.relationship( local_customer = orm.relationship(
'LocalCustomer', "LocalCustomer",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='orders', back_populates="orders",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.customers.LocalCustomer` record :class:`~sideshow.db.model.customers.LocalCustomer` record
for the order, if applicable. for the order, if applicable.
See also :attr:`customer_id` and :attr:`pending_customer`. See also :attr:`customer_id` and :attr:`pending_customer`.
""") """,
)
pending_customer_uuid = model.uuid_fk_column('sideshow_customer_pending.uuid', nullable=True) pending_customer_uuid = model.uuid_fk_column(
"sideshow_customer_pending.uuid", nullable=True
)
pending_customer = orm.relationship( pending_customer = orm.relationship(
'PendingCustomer', "PendingCustomer",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='orders', back_populates="orders",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.customers.PendingCustomer` record :class:`~sideshow.db.model.customers.PendingCustomer` record
for the order, if applicable. for the order, if applicable.
See also :attr:`customer_id` and :attr:`local_customer`. See also :attr:`customer_id` and :attr:`local_customer`.
""") """,
)
customer_name = sa.Column(sa.String(length=100), nullable=True, doc=""" customer_name = sa.Column(
sa.String(length=100),
nullable=True,
doc="""
Name for the customer account. Name for the customer account.
""") """,
)
phone_number = sa.Column(sa.String(length=20), nullable=True, doc=""" phone_number = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Phone number for the customer. Phone number for the customer.
""") """,
)
email_address = sa.Column(sa.String(length=255), nullable=True, doc=""" email_address = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Email address for the customer. Email address for the customer.
""") """,
)
total_price = sa.Column(sa.Numeric(precision=10, scale=3), nullable=True, doc=""" total_price = sa.Column(
sa.Numeric(precision=10, scale=3),
nullable=True,
doc="""
Full price (not including tax etc.) for all items on the order. Full price (not including tax etc.) for all items on the order.
""") """,
)
created = sa.Column(sa.DateTime(timezone=True), nullable=False, default=datetime.datetime.now, doc=""" created = sa.Column(
sa.DateTime(timezone=True),
nullable=False,
default=datetime.datetime.now,
doc="""
Timestamp when the order was created. Timestamp when the order was created.
If the order is created via New Order Batch, this will match the If the order is created via New Order Batch, this will match the
batch execution timestamp. batch execution timestamp.
""") """,
)
created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False) created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False)
created_by = orm.relationship( created_by = orm.relationship(
model.User, model.User,
cascade_backrefs=False, cascade_backrefs=False,
@ -135,17 +176,19 @@ class Order(model.Base):
Reference to the Reference to the
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
created the order. created the order.
""") """,
)
items = orm.relationship( items = orm.relationship(
'OrderItem', "OrderItem",
collection_class=ordering_list('sequence', count_from=1), collection_class=ordering_list("sequence", count_from=1),
cascade='all, delete-orphan', cascade="all, delete-orphan",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='order', back_populates="order",
doc=""" doc="""
List of :class:`OrderItem` records belonging to the order. List of :class:`OrderItem` records belonging to the order.
""") """,
)
def __str__(self): def __str__(self):
return str(self.order_id) return str(self.order_id)
@ -159,58 +202,77 @@ class OrderItem(model.Base):
:class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
records. records.
""" """
__tablename__ = 'sideshow_order_item'
__tablename__ = "sideshow_order_item"
uuid = model.uuid_column() uuid = model.uuid_column()
order_uuid = model.uuid_fk_column('sideshow_order.uuid', nullable=False) order_uuid = model.uuid_fk_column("sideshow_order.uuid", nullable=False)
order = orm.relationship( order = orm.relationship(
Order, Order,
cascade_backrefs=False, cascade_backrefs=False,
back_populates='items', back_populates="items",
doc=""" doc="""
Reference to the :class:`Order` to which the item belongs. Reference to the :class:`Order` to which the item belongs.
""") """,
)
sequence = sa.Column(sa.Integer(), nullable=False, doc=""" sequence = sa.Column(
sa.Integer(),
nullable=False,
doc="""
1-based numeric sequence for the item, i.e. its line number within 1-based numeric sequence for the item, i.e. its line number within
the order. the order.
""") """,
)
product_id = sa.Column(sa.String(length=20), nullable=True, doc=""" product_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Proper ID for the :term:`external product` which the order item Proper ID for the :term:`external product` which the order item
represents, if applicable. represents, if applicable.
See also :attr:`local_product` and :attr:`pending_product`. See also :attr:`local_product` and :attr:`pending_product`.
""") """,
)
local_product_uuid = model.uuid_fk_column('sideshow_product_local.uuid', nullable=True) local_product_uuid = model.uuid_fk_column(
"sideshow_product_local.uuid", nullable=True
)
local_product = orm.relationship( local_product = orm.relationship(
'LocalProduct', "LocalProduct",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='order_items', back_populates="order_items",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.products.LocalProduct` record for :class:`~sideshow.db.model.products.LocalProduct` record for
the order item, if applicable. the order item, if applicable.
See also :attr:`product_id` and :attr:`pending_product`. See also :attr:`product_id` and :attr:`pending_product`.
""") """,
)
pending_product_uuid = model.uuid_fk_column('sideshow_product_pending.uuid', nullable=True) pending_product_uuid = model.uuid_fk_column(
"sideshow_product_pending.uuid", nullable=True
)
pending_product = orm.relationship( pending_product = orm.relationship(
'PendingProduct', "PendingProduct",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='order_items', back_populates="order_items",
doc=""" doc="""
Reference to the Reference to the
:class:`~sideshow.db.model.products.PendingProduct` record for :class:`~sideshow.db.model.products.PendingProduct` record for
the order item, if applicable. the order item, if applicable.
See also :attr:`product_id` and :attr:`local_product`. See also :attr:`product_id` and :attr:`local_product`.
""") """,
)
product_scancode = sa.Column(sa.String(length=14), nullable=True, doc=""" product_scancode = sa.Column(
sa.String(length=14),
nullable=True,
doc="""
Scancode for the product, as string. Scancode for the product, as string.
.. note:: .. note::
@ -222,109 +284,189 @@ class OrderItem(model.Base):
That may change eventually, depending on POS integration That may change eventually, depending on POS integration
scenarios that come up. Maybe a config option to declare scenarios that come up. Maybe a config option to declare
whether check digit should be included or not, etc. whether check digit should be included or not, etc.
""") """,
)
product_brand = sa.Column(sa.String(length=100), nullable=True, doc=""" product_brand = sa.Column(
sa.String(length=100),
nullable=True,
doc="""
Brand name for the product - up to 100 chars. Brand name for the product - up to 100 chars.
""") """,
)
product_description = sa.Column(sa.String(length=255), nullable=True, doc=""" product_description = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Description for the product - up to 255 chars. Description for the product - up to 255 chars.
""") """,
)
product_size = sa.Column(sa.String(length=30), nullable=True, doc=""" product_size = sa.Column(
sa.String(length=30),
nullable=True,
doc="""
Size of the product, as string - up to 30 chars. Size of the product, as string - up to 30 chars.
""") """,
)
product_weighed = sa.Column(sa.Boolean(), nullable=True, doc=""" product_weighed = sa.Column(
sa.Boolean(),
nullable=True,
doc="""
Flag indicating the product is sold by weight; default is null. Flag indicating the product is sold by weight; default is null.
""") """,
)
department_id = sa.Column(sa.String(length=10), nullable=True, doc=""" department_id = sa.Column(
sa.String(length=10),
nullable=True,
doc="""
ID of the department to which the product belongs, if known. ID of the department to which the product belongs, if known.
""") """,
)
department_name = sa.Column(sa.String(length=30), nullable=True, doc=""" department_name = sa.Column(
sa.String(length=30),
nullable=True,
doc="""
Name of the department to which the product belongs, if known. Name of the department to which the product belongs, if known.
""") """,
)
special_order = sa.Column(sa.Boolean(), nullable=True, doc=""" special_order = sa.Column(
sa.Boolean(),
nullable=True,
doc="""
Flag indicating the item is a "special order" - e.g. something not Flag indicating the item is a "special order" - e.g. something not
normally carried by the store. Default is null. normally carried by the store. Default is null.
""") """,
)
vendor_name = sa.Column(sa.String(length=50), nullable=True, doc=""" vendor_name = sa.Column(
sa.String(length=50),
nullable=True,
doc="""
Name of vendor from which product may be purchased, if known. See Name of vendor from which product may be purchased, if known. See
also :attr:`vendor_item_code`. also :attr:`vendor_item_code`.
""") """,
)
vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc=""" vendor_item_code = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Item code (SKU) to use when ordering this product from the vendor Item code (SKU) to use when ordering this product from the vendor
identified by :attr:`vendor_name`, if known. identified by :attr:`vendor_name`, if known.
""") """,
)
case_size = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc=""" case_size = sa.Column(
sa.Numeric(precision=10, scale=4),
nullable=True,
doc="""
Case pack count for the product, if known. Case pack count for the product, if known.
""") """,
)
order_qty = sa.Column(sa.Numeric(precision=10, scale=4), nullable=False, doc=""" order_qty = sa.Column(
sa.Numeric(precision=10, scale=4),
nullable=False,
doc="""
Quantity (as decimal) of product being ordered. Quantity (as decimal) of product being ordered.
This must be interpreted along with :attr:`order_uom` to determine This must be interpreted along with :attr:`order_uom` to determine
the *complete* order quantity, e.g. "2 cases". the *complete* order quantity, e.g. "2 cases".
""") """,
)
order_uom = sa.Column(sa.String(length=10), nullable=False, doc=""" order_uom = sa.Column(
sa.String(length=10),
nullable=False,
doc="""
Code indicating the unit of measure for product being ordered. Code indicating the unit of measure for product being ordered.
This should be one of the codes from This should be one of the codes from
:data:`~sideshow.enum.ORDER_UOM`. :data:`~sideshow.enum.ORDER_UOM`.
""") """,
)
unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc=""" unit_cost = sa.Column(
sa.Numeric(precision=9, scale=5),
nullable=True,
doc="""
Cost of goods amount for one "unit" (not "case") of the product, Cost of goods amount for one "unit" (not "case") of the product,
as decimal to 4 places. as decimal to 4 places.
""") """,
)
unit_price_reg = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_reg = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Regular price for the item unit. Unless a sale is in effect, Regular price for the item unit. Unless a sale is in effect,
:attr:`unit_price_quoted` will typically match this value. :attr:`unit_price_quoted` will typically match this value.
""") """,
)
unit_price_sale = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_sale = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Sale price for the item unit, if applicable. If set, then Sale price for the item unit, if applicable. If set, then
:attr:`unit_price_quoted` will typically match this value. See :attr:`unit_price_quoted` will typically match this value. See
also :attr:`sale_ends`. also :attr:`sale_ends`.
""") """,
)
sale_ends = sa.Column(sa.DateTime(timezone=True), nullable=True, doc=""" sale_ends = sa.Column(
sa.DateTime(timezone=True),
nullable=True,
doc="""
End date/time for the sale in effect, if any. End date/time for the sale in effect, if any.
This is only relevant if :attr:`unit_price_sale` is set. This is only relevant if :attr:`unit_price_sale` is set.
""") """,
)
unit_price_quoted = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_quoted = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Quoted price for the item unit. This is the "effective" unit Quoted price for the item unit. This is the "effective" unit
price, which is used to calculate :attr:`total_price`. price, which is used to calculate :attr:`total_price`.
This price does *not* reflect the :attr:`discount_percent`. It This price does *not* reflect the :attr:`discount_percent`. It
normally should match either :attr:`unit_price_reg` or normally should match either :attr:`unit_price_reg` or
:attr:`unit_price_sale`. :attr:`unit_price_sale`.
""") """,
)
case_price_quoted = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" case_price_quoted = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Quoted price for a "case" of the item, if applicable. Quoted price for a "case" of the item, if applicable.
This is mostly for display purposes; :attr:`unit_price_quoted` is This is mostly for display purposes; :attr:`unit_price_quoted` is
used for calculations. used for calculations.
""") """,
)
discount_percent = sa.Column(sa.Numeric(precision=5, scale=3), nullable=True, doc=""" discount_percent = sa.Column(
sa.Numeric(precision=5, scale=3),
nullable=True,
doc="""
Discount percent to apply when calculating :attr:`total_price`, if Discount percent to apply when calculating :attr:`total_price`, if
applicable. applicable.
""") """,
)
total_price = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" total_price = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Full price (not including tax etc.) which the customer is quoted Full price (not including tax etc.) which the customer is quoted
for the order item. for the order item.
@ -335,41 +477,57 @@ class OrderItem(model.Base):
* :attr:`order_uom` * :attr:`order_uom`
* :attr:`case_size` * :attr:`case_size`
* :attr:`discount_percent` * :attr:`discount_percent`
""") """,
)
status_code = sa.Column(sa.Integer(), nullable=False, doc=""" status_code = sa.Column(
sa.Integer(),
nullable=False,
doc="""
Code indicating current status for the order item. Code indicating current status for the order item.
""") """,
)
paid_amount = sa.Column(sa.Numeric(precision=8, scale=3), nullable=False, default=0, doc=""" paid_amount = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=False,
default=0,
doc="""
Amount which the customer has paid toward the :attr:`total_price` Amount which the customer has paid toward the :attr:`total_price`
of the item. of the item.
""") """,
)
payment_transaction_number = sa.Column(sa.String(length=20), nullable=True, doc=""" payment_transaction_number = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Transaction number in which payment for the order was taken, if Transaction number in which payment for the order was taken, if
applicable/known. applicable/known.
""") """,
)
events = orm.relationship( events = orm.relationship(
'OrderItemEvent', "OrderItemEvent",
order_by='OrderItemEvent.occurred, OrderItemEvent.uuid', order_by="OrderItemEvent.occurred, OrderItemEvent.uuid",
cascade='all, delete-orphan', cascade="all, delete-orphan",
cascade_backrefs=False, cascade_backrefs=False,
back_populates='item', back_populates="item",
doc=""" doc="""
List of :class:`OrderItemEvent` records for the item. List of :class:`OrderItemEvent` records for the item.
""") """,
)
@property @property
def full_description(self): def full_description(self):
""" """ """ """
fields = [ fields = [
self.product_brand or '', self.product_brand or "",
self.product_description or '', self.product_description or "",
self.product_size or ''] self.product_size or "",
]
fields = [f.strip() for f in fields if f.strip()] fields = [f.strip() for f in fields if f.strip()]
return ' '.join(fields) return " ".join(fields)
def __str__(self): def __str__(self):
return self.full_description return self.full_description
@ -379,8 +537,8 @@ class OrderItem(model.Base):
Convenience method to add a new :class:`OrderItemEvent` for Convenience method to add a new :class:`OrderItemEvent` for
the item. the item.
""" """
kwargs['type_code'] = type_code kwargs["type_code"] = type_code
kwargs['actor'] = user kwargs["actor"] = user
self.events.append(OrderItemEvent(**kwargs)) self.events.append(OrderItemEvent(**kwargs))
@ -388,37 +546,53 @@ class OrderItemEvent(model.Base):
""" """
An event in the life of an :term:`order item`. An event in the life of an :term:`order item`.
""" """
__tablename__ = 'sideshow_order_item_event'
__tablename__ = "sideshow_order_item_event"
uuid = model.uuid_column() uuid = model.uuid_column()
item_uuid = model.uuid_fk_column('sideshow_order_item.uuid', nullable=False) item_uuid = model.uuid_fk_column("sideshow_order_item.uuid", nullable=False)
item = orm.relationship( item = orm.relationship(
OrderItem, OrderItem,
cascade_backrefs=False, cascade_backrefs=False,
back_populates='events', back_populates="events",
doc=""" doc="""
Reference to the :class:`OrderItem` to which the event Reference to the :class:`OrderItem` to which the event
pertains. pertains.
""") """,
)
type_code = sa.Column(sa.Integer, nullable=False, doc=""" type_code = sa.Column(
sa.Integer,
nullable=False,
doc="""
Code indicating the type of event; values must be defined in Code indicating the type of event; values must be defined in
:data:`~sideshow.enum.ORDER_ITEM_EVENT`. :data:`~sideshow.enum.ORDER_ITEM_EVENT`.
""") """,
)
occurred = sa.Column(sa.DateTime(timezone=True), nullable=False, default=datetime.datetime.now, doc=""" occurred = sa.Column(
sa.DateTime(timezone=True),
nullable=False,
default=datetime.datetime.now,
doc="""
Date and time when the event occurred. Date and time when the event occurred.
""") """,
)
actor_uuid = model.uuid_fk_column('user.uuid', nullable=False) actor_uuid = model.uuid_fk_column("user.uuid", nullable=False)
actor = orm.relationship( actor = orm.relationship(
model.User, model.User,
doc=""" doc="""
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
performed the action. performed the action.
""") """,
)
note = sa.Column(sa.Text(), nullable=True, doc=""" note = sa.Column(
sa.Text(),
nullable=True,
doc="""
Optional note recorded for the event. Optional note recorded for the event.
""") """,
)

View file

@ -42,7 +42,10 @@ class ProductMixin:
* :class:`PendingProduct` * :class:`PendingProduct`
""" """
scancode = sa.Column(sa.String(length=14), nullable=True, doc=""" scancode = sa.Column(
sa.String(length=14),
nullable=True,
doc="""
Scancode for the product, as string. Scancode for the product, as string.
.. note:: .. note::
@ -54,73 +57,123 @@ class ProductMixin:
That may change eventually, depending on POS integration That may change eventually, depending on POS integration
scenarios that come up. Maybe a config option to declare scenarios that come up. Maybe a config option to declare
whether check digit should be included or not, etc. whether check digit should be included or not, etc.
""") """,
)
brand_name = sa.Column(sa.String(length=100), nullable=True, doc=""" brand_name = sa.Column(
sa.String(length=100),
nullable=True,
doc="""
Brand name for the product - up to 100 chars. Brand name for the product - up to 100 chars.
""") """,
)
description = sa.Column(sa.String(length=255), nullable=True, doc=""" description = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Description for the product - up to 255 chars. Description for the product - up to 255 chars.
""") """,
)
size = sa.Column(sa.String(length=30), nullable=True, doc=""" size = sa.Column(
sa.String(length=30),
nullable=True,
doc="""
Size of the product, as string - up to 30 chars. Size of the product, as string - up to 30 chars.
""") """,
)
weighed = sa.Column(sa.Boolean(), nullable=True, doc=""" weighed = sa.Column(
sa.Boolean(),
nullable=True,
doc="""
Flag indicating the product is sold by weight; default is null. Flag indicating the product is sold by weight; default is null.
""") """,
)
department_id = sa.Column(sa.String(length=10), nullable=True, doc=""" department_id = sa.Column(
sa.String(length=10),
nullable=True,
doc="""
ID of the department to which the product belongs, if known. ID of the department to which the product belongs, if known.
""") """,
)
department_name = sa.Column(sa.String(length=30), nullable=True, doc=""" department_name = sa.Column(
sa.String(length=30),
nullable=True,
doc="""
Name of the department to which the product belongs, if known. Name of the department to which the product belongs, if known.
""") """,
)
special_order = sa.Column(sa.Boolean(), nullable=True, doc=""" special_order = sa.Column(
sa.Boolean(),
nullable=True,
doc="""
Flag indicating the item is a "special order" - e.g. something not Flag indicating the item is a "special order" - e.g. something not
normally carried by the store. Default is null. normally carried by the store. Default is null.
""") """,
)
vendor_name = sa.Column(sa.String(length=50), nullable=True, doc=""" vendor_name = sa.Column(
sa.String(length=50),
nullable=True,
doc="""
Name of vendor from which product may be purchased, if known. See Name of vendor from which product may be purchased, if known. See
also :attr:`vendor_item_code`. also :attr:`vendor_item_code`.
""") """,
)
vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc=""" vendor_item_code = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
Item code (SKU) to use when ordering this product from the vendor Item code (SKU) to use when ordering this product from the vendor
identified by :attr:`vendor_name`, if known. identified by :attr:`vendor_name`, if known.
""") """,
)
case_size = sa.Column(sa.Numeric(precision=9, scale=4), nullable=True, doc=""" case_size = sa.Column(
sa.Numeric(precision=9, scale=4),
nullable=True,
doc="""
Case pack count for the product, if known. Case pack count for the product, if known.
""") """,
)
unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc=""" unit_cost = sa.Column(
sa.Numeric(precision=9, scale=5),
nullable=True,
doc="""
Cost of goods amount for one "unit" (not "case") of the product, Cost of goods amount for one "unit" (not "case") of the product,
as decimal to 4 places. as decimal to 4 places.
""") """,
)
unit_price_reg = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" unit_price_reg = sa.Column(
sa.Numeric(precision=8, scale=3),
nullable=True,
doc="""
Regular price for a "unit" of the product. Regular price for a "unit" of the product.
""") """,
)
notes = sa.Column(sa.Text(), nullable=True, doc=""" notes = sa.Column(
sa.Text(),
nullable=True,
doc="""
Arbitrary notes regarding the product, if applicable. Arbitrary notes regarding the product, if applicable.
""") """,
)
@property @property
def full_description(self): def full_description(self):
""" """ """ """
fields = [ fields = [self.brand_name or "", self.description or "", self.size or ""]
self.brand_name or '',
self.description or '',
self.size or '']
fields = [f.strip() for f in fields if f.strip()] fields = [f.strip() for f in fields if f.strip()]
return ' '.join(fields) return " ".join(fields)
def __str__(self): def __str__(self):
return self.full_description return self.full_description
@ -139,33 +192,40 @@ class LocalProduct(ProductMixin, model.Base):
record(s) will be added to this local products table, for lookup record(s) will be added to this local products table, for lookup
next time. next time.
""" """
__tablename__ = 'sideshow_product_local'
__tablename__ = "sideshow_product_local"
uuid = model.uuid_column() uuid = model.uuid_column()
external_id = sa.Column(sa.String(length=20), nullable=True, doc=""" external_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
ID of the true external product associated with this record, if ID of the true external product associated with this record, if
applicable. applicable.
""") """,
)
order_items = orm.relationship( order_items = orm.relationship(
'OrderItem', "OrderItem",
back_populates='local_product', back_populates="local_product",
cascade_backrefs=False, cascade_backrefs=False,
doc=""" doc="""
List of :class:`~sideshow.db.model.orders.OrderItem` records List of :class:`~sideshow.db.model.orders.OrderItem` records
associated with this product. associated with this product.
""") """,
)
new_order_batch_rows = orm.relationship( new_order_batch_rows = orm.relationship(
'NewOrderBatchRow', "NewOrderBatchRow",
back_populates='local_product', back_populates="local_product",
cascade_backrefs=False, cascade_backrefs=False,
doc=""" doc="""
List of List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
records associated with this product. records associated with this product.
""") """,
)
class PendingProduct(ProductMixin, model.Base): class PendingProduct(ProductMixin, model.Base):
@ -180,25 +240,38 @@ class PendingProduct(ProductMixin, model.Base):
is executed, new record(s) will be added to the :term:`local is executed, new record(s) will be added to the :term:`local
products <local product>` table, for lookup next time. products <local product>` table, for lookup next time.
""" """
__tablename__ = 'sideshow_product_pending'
__tablename__ = "sideshow_product_pending"
uuid = model.uuid_column() uuid = model.uuid_column()
product_id = sa.Column(sa.String(length=20), nullable=True, doc=""" product_id = sa.Column(
sa.String(length=20),
nullable=True,
doc="""
ID of the :term:`external product` associated with this record, if ID of the :term:`external product` associated with this record, if
applicable/known. applicable/known.
""") """,
)
status = sa.Column(sa.Enum(PendingProductStatus), nullable=False, doc=""" status = sa.Column(
sa.Enum(PendingProductStatus),
nullable=False,
doc="""
Status code for the product record. Status code for the product record.
""") """,
)
created = sa.Column(sa.DateTime(timezone=True), nullable=False, created = sa.Column(
default=datetime.datetime.now, doc=""" sa.DateTime(timezone=True),
nullable=False,
default=datetime.datetime.now,
doc="""
Timestamp when the product record was created. Timestamp when the product record was created.
""") """,
)
created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False) created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False)
created_by = orm.relationship( created_by = orm.relationship(
model.User, model.User,
cascade_backrefs=False, cascade_backrefs=False,
@ -206,23 +279,26 @@ class PendingProduct(ProductMixin, model.Base):
Reference to the Reference to the
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
created the product record. created the product record.
""") """,
)
order_items = orm.relationship( order_items = orm.relationship(
'OrderItem', "OrderItem",
back_populates='pending_product', back_populates="pending_product",
cascade_backrefs=False, cascade_backrefs=False,
doc=""" doc="""
List of :class:`~sideshow.db.model.orders.OrderItem` records List of :class:`~sideshow.db.model.orders.OrderItem` records
associated with this product. associated with this product.
""") """,
)
new_order_batch_rows = orm.relationship( new_order_batch_rows = orm.relationship(
'NewOrderBatchRow', "NewOrderBatchRow",
back_populates='pending_product', back_populates="pending_product",
cascade_backrefs=False, cascade_backrefs=False,
doc=""" doc="""
List of List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
records associated with this product. records associated with this product.
""") """,
)

View file

@ -33,22 +33,38 @@ class Store(model.Base):
""" """
Represents a physical location for the business. Represents a physical location for the business.
""" """
__tablename__ = 'sideshow_store'
__tablename__ = "sideshow_store"
uuid = model.uuid_column() uuid = model.uuid_column()
store_id = sa.Column(sa.String(length=10), nullable=False, unique=True, doc=""" store_id = sa.Column(
sa.String(length=10),
nullable=False,
unique=True,
doc="""
Unique ID for the store. Unique ID for the store.
""") """,
)
name = sa.Column(sa.String(length=100), nullable=False, unique=True, doc=""" name = sa.Column(
sa.String(length=100),
nullable=False,
unique=True,
doc="""
Display name for the store (must be unique!). Display name for the store (must be unique!).
""") """,
)
archived = sa.Column(sa.Boolean(), nullable=False, default=False, doc=""" archived = sa.Column(
sa.Boolean(),
nullable=False,
default=False,
doc="""
Indicates the store has been "retired" essentially, and mostly Indicates the store has been "retired" essentially, and mostly
hidden from view. hidden from view.
""") """,
)
def __str__(self): def __str__(self):
return self.get_display() return self.get_display()
@ -57,6 +73,6 @@ class Store(model.Base):
""" """
Returns the display string for the store, e.g. "001 Acme Goods". Returns the display string for the store, e.g. "001 Acme Goods".
""" """
return ' '.join([(self.store_id or '').strip(), return " ".join(
(self.name or '').strip()])\ [(self.store_id or "").strip(), (self.name or "").strip()]
.strip() ).strip()

View file

@ -30,7 +30,7 @@ from collections import OrderedDict
from wuttjamaican.enum import * from wuttjamaican.enum import *
ORDER_UOM_CASE = 'CS' ORDER_UOM_CASE = "CS"
""" """
UOM code for ordering a "case" of product. UOM code for ordering a "case" of product.
@ -38,7 +38,7 @@ Sideshow will treat "case" orders somewhat differently as compared to
"unit" orders. "unit" orders.
""" """
ORDER_UOM_UNIT = 'EA' ORDER_UOM_UNIT = "EA"
""" """
UOM code for ordering a "unit" of product. UOM code for ordering a "unit" of product.
@ -47,7 +47,7 @@ the same by Sideshow, whereas "case" orders are treated somewhat
differently. differently.
""" """
ORDER_UOM_KILOGRAM = 'KG' ORDER_UOM_KILOGRAM = "KG"
""" """
UOM code for ordering a "kilogram" of product. UOM code for ordering a "kilogram" of product.
@ -57,7 +57,7 @@ e.g. :attr:`~sideshow.db.model.orders.OrderItem.product_weighed` is
true. true.
""" """
ORDER_UOM_POUND = 'LB' ORDER_UOM_POUND = "LB"
""" """
UOM code for ordering a "pound" of product. UOM code for ordering a "pound" of product.
@ -67,12 +67,14 @@ e.g. :attr:`~sideshow.db.model.orders.OrderItem.product_weighed` is
true. true.
""" """
ORDER_UOM = OrderedDict([ ORDER_UOM = OrderedDict(
[
(ORDER_UOM_CASE, "Cases"), (ORDER_UOM_CASE, "Cases"),
(ORDER_UOM_UNIT, "Units"), (ORDER_UOM_UNIT, "Units"),
(ORDER_UOM_KILOGRAM, "Kilograms"), (ORDER_UOM_KILOGRAM, "Kilograms"),
(ORDER_UOM_POUND, "Pounds"), (ORDER_UOM_POUND, "Pounds"),
]) ]
)
""" """
Dict of possible code -> label options for ordering unit of measure. Dict of possible code -> label options for ordering unit of measure.
@ -88,10 +90,11 @@ class PendingCustomerStatus(Enum):
Enum values for Enum values for
:attr:`sideshow.db.model.customers.PendingCustomer.status`. :attr:`sideshow.db.model.customers.PendingCustomer.status`.
""" """
PENDING = 'pending'
READY = 'ready' PENDING = "pending"
RESOLVED = 'resolved' READY = "ready"
IGNORED = 'ignored' RESOLVED = "resolved"
IGNORED = "ignored"
class PendingProductStatus(Enum): class PendingProductStatus(Enum):
@ -99,10 +102,11 @@ class PendingProductStatus(Enum):
Enum values for Enum values for
:attr:`sideshow.db.model.products.PendingProduct.status`. :attr:`sideshow.db.model.products.PendingProduct.status`.
""" """
PENDING = 'pending'
READY = 'ready' PENDING = "pending"
RESOLVED = 'resolved' READY = "ready"
IGNORED = 'ignored' RESOLVED = "resolved"
IGNORED = "ignored"
######################################## ########################################
@ -206,7 +210,8 @@ ORDER_ITEM_STATUS_INACTIVE = 950
Indicates the order item has become inactive. Indicates the order item has become inactive.
""" """
ORDER_ITEM_STATUS = OrderedDict([ ORDER_ITEM_STATUS = OrderedDict(
[
(ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"), (ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"),
(ORDER_ITEM_STATUS_INITIATED, "initiated"), (ORDER_ITEM_STATUS_INITIATED, "initiated"),
(ORDER_ITEM_STATUS_PAID_BEFORE, "paid"), (ORDER_ITEM_STATUS_PAID_BEFORE, "paid"),
@ -223,7 +228,8 @@ ORDER_ITEM_STATUS = OrderedDict([
(ORDER_ITEM_STATUS_RESTOCKED, "restocked"), (ORDER_ITEM_STATUS_RESTOCKED, "restocked"),
(ORDER_ITEM_STATUS_EXPIRED, "expired"), (ORDER_ITEM_STATUS_EXPIRED, "expired"),
(ORDER_ITEM_STATUS_INACTIVE, "inactive"), (ORDER_ITEM_STATUS_INACTIVE, "inactive"),
]) ]
)
""" """
Dict of possible code -> label options for :term:`order item` status. Dict of possible code -> label options for :term:`order item` status.
@ -366,7 +372,8 @@ Arbitrary event type which does not signify anything in particular.
If used, the event should be given an explanatory note. If used, the event should be given an explanatory note.
""" """
ORDER_ITEM_EVENT = OrderedDict([ ORDER_ITEM_EVENT = OrderedDict(
[
(ORDER_ITEM_EVENT_INITIATED, "initiated"), (ORDER_ITEM_EVENT_INITIATED, "initiated"),
(ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"), (ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"),
(ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"), (ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"),
@ -388,7 +395,8 @@ ORDER_ITEM_EVENT = OrderedDict([
(ORDER_ITEM_EVENT_EXPIRED, "expired"), (ORDER_ITEM_EVENT_EXPIRED, "expired"),
(ORDER_ITEM_EVENT_INACTIVE, "inactive"), (ORDER_ITEM_EVENT_INACTIVE, "inactive"),
(ORDER_ITEM_EVENT_OTHER, "other"), (ORDER_ITEM_EVENT_OTHER, "other"),
]) ]
)
""" """
Dict of possible code -> label options for :term:`order item` event Dict of possible code -> label options for :term:`order item` event
types. types.

View file

@ -42,8 +42,7 @@ class OrderHandler(GenericHandler):
Returns boolean indicating whether the ``store_id`` field Returns boolean indicating whether the ``store_id`` field
should be exposed at all. This is false by default. should be exposed at all. This is false by default.
""" """
return self.config.get_bool('sideshow.orders.expose_store_id', return self.config.get_bool("sideshow.orders.expose_store_id", default=False)
default=False)
def get_order_qty_uom_text(self, order_qty, order_uom, case_size=None, html=False): def get_order_qty_uom_text(self, order_qty, order_uom, case_size=None, html=False):
""" """
@ -69,15 +68,15 @@ class OrderHandler(GenericHandler):
if order_uom == enum.ORDER_UOM_CASE: if order_uom == enum.ORDER_UOM_CASE:
if case_size is None: if case_size is None:
case_qty = unit_qty = '??' case_qty = unit_qty = "??"
else: else:
case_qty = self.app.render_quantity(case_size) case_qty = self.app.render_quantity(case_size)
unit_qty = self.app.render_quantity(order_qty * case_size) unit_qty = self.app.render_quantity(order_qty * case_size)
CS = enum.ORDER_UOM[enum.ORDER_UOM_CASE] CS = enum.ORDER_UOM[enum.ORDER_UOM_CASE]
EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT] EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT]
order_qty = self.app.render_quantity(order_qty) order_qty = self.app.render_quantity(order_qty)
times = '&times;' if html else 'x' times = "&times;" if html else "x"
return (f"{order_qty} {CS} ({times} {case_qty} = {unit_qty} {EA})") return f"{order_qty} {CS} ({times} {case_qty} = {unit_qty} {EA})"
# units # units
unit_qty = self.app.render_quantity(order_qty) unit_qty = self.app.render_quantity(order_qty)
@ -97,13 +96,15 @@ class OrderHandler(GenericHandler):
``None``. ``None``.
""" """
enum = self.app.enum enum = self.app.enum
if status_code in (enum.ORDER_ITEM_STATUS_CANCELED, if status_code in (
enum.ORDER_ITEM_STATUS_CANCELED,
enum.ORDER_ITEM_STATUS_REFUND_PENDING, enum.ORDER_ITEM_STATUS_REFUND_PENDING,
enum.ORDER_ITEM_STATUS_REFUNDED, enum.ORDER_ITEM_STATUS_REFUNDED,
enum.ORDER_ITEM_STATUS_RESTOCKED, enum.ORDER_ITEM_STATUS_RESTOCKED,
enum.ORDER_ITEM_STATUS_EXPIRED, enum.ORDER_ITEM_STATUS_EXPIRED,
enum.ORDER_ITEM_STATUS_INACTIVE): enum.ORDER_ITEM_STATUS_INACTIVE,
return 'warning' ):
return "warning"
def resolve_pending_product(self, pending_product, product_info, user, note=None): def resolve_pending_product(self, pending_product, product_info, user, note=None):
""" """
@ -152,35 +153,39 @@ class OrderHandler(GenericHandler):
raise ValueError("pending product does not have 'ready' status") raise ValueError("pending product does not have 'ready' status")
info = product_info info = product_info
pending_product.product_id = info['product_id'] pending_product.product_id = info["product_id"]
pending_product.status = enum.PendingProductStatus.RESOLVED pending_product.status = enum.PendingProductStatus.RESOLVED
items = session.query(model.OrderItem)\ items = (
.filter(model.OrderItem.pending_product == pending_product)\ session.query(model.OrderItem)
.filter(model.OrderItem.product_id == None)\ .filter(model.OrderItem.pending_product == pending_product)
.filter(model.OrderItem.product_id == None)
.all() .all()
)
for item in items: for item in items:
item.product_id = info['product_id'] item.product_id = info["product_id"]
item.product_scancode = info['scancode'] item.product_scancode = info["scancode"]
item.product_brand = info['brand_name'] item.product_brand = info["brand_name"]
item.product_description = info['description'] item.product_description = info["description"]
item.product_size = info['size'] item.product_size = info["size"]
item.product_weighed = info['weighed'] item.product_weighed = info["weighed"]
item.department_id = info['department_id'] item.department_id = info["department_id"]
item.department_name = info['department_name'] item.department_name = info["department_name"]
item.special_order = info['special_order'] item.special_order = info["special_order"]
item.vendor_name = info['vendor_name'] item.vendor_name = info["vendor_name"]
item.vendor_item_code = info['vendor_item_code'] item.vendor_item_code = info["vendor_item_code"]
item.case_size = info['case_size'] item.case_size = info["case_size"]
item.unit_cost = info['unit_cost'] item.unit_cost = info["unit_cost"]
item.unit_price_reg = info['unit_price_reg'] item.unit_price_reg = info["unit_price_reg"]
item.add_event(enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED, user) item.add_event(enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED, user)
if note: if note:
item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, user, note=note) item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, user, note=note)
def process_placement(self, items, user, vendor_name=None, po_number=None, note=None): def process_placement(
self, items, user, vendor_name=None, po_number=None, note=None
):
""" """
Process the "placement" step for the given order items. Process the "placement" step for the given order items.
@ -221,8 +226,15 @@ class OrderHandler(GenericHandler):
item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, user, note=note) item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, user, note=note)
item.status_code = enum.ORDER_ITEM_STATUS_PLACED item.status_code = enum.ORDER_ITEM_STATUS_PLACED
def process_receiving(self, items, user, vendor_name=None, def process_receiving(
invoice_number=None, po_number=None, note=None): self,
items,
user,
vendor_name=None,
invoice_number=None,
po_number=None,
note=None,
):
""" """
Process the "receiving" step for the given order items. Process the "receiving" step for the given order items.
@ -252,7 +264,9 @@ class OrderHandler(GenericHandler):
received = None received = None
if invoice_number and po_number and vendor_name: if invoice_number and po_number and vendor_name:
received = f"invoice {invoice_number} (PO {po_number}) from vendor {vendor_name}" received = (
f"invoice {invoice_number} (PO {po_number}) from vendor {vendor_name}"
)
elif invoice_number and vendor_name: elif invoice_number and vendor_name:
received = f"invoice {invoice_number} from vendor {vendor_name}" received = f"invoice {invoice_number} from vendor {vendor_name}"
elif po_number and vendor_name: elif po_number and vendor_name:

View file

@ -31,8 +31,10 @@ class WebTestCase(base.WebTestCase):
def make_config(self, **kwargs): def make_config(self, **kwargs):
config = super().make_config(**kwargs) config = super().make_config(**kwargs)
config.setdefault('wutta.model_spec', 'sideshow.db.model') config.setdefault("wutta.model_spec", "sideshow.db.model")
config.setdefault('wutta.enum_spec', 'sideshow.enum') config.setdefault("wutta.enum_spec", "sideshow.enum")
config.setdefault(f'{config.appname}.batch.neworder.handler.default_spec', config.setdefault(
'sideshow.batch.neworder:NewOrderBatchHandler') f"{config.appname}.batch.neworder.handler.default_spec",
"sideshow.batch.neworder:NewOrderBatchHandler",
)
return config return config

View file

@ -26,6 +26,6 @@ Sideshow web app
def includeme(config): def includeme(config):
config.include('sideshow.web.static') config.include("sideshow.web.static")
config.include('wuttaweb.subscribers') config.include("wuttaweb.subscribers")
config.include('sideshow.web.views') config.include("sideshow.web.views")

View file

@ -32,17 +32,20 @@ def main(global_config, **settings):
Make and return the WSGI app (Paste entry point). Make and return the WSGI app (Paste entry point).
""" """
# prefer Sideshow templates over wuttaweb # prefer Sideshow templates over wuttaweb
settings.setdefault('mako.directories', [ settings.setdefault(
'sideshow.web:templates', "mako.directories",
'wuttaweb:templates', [
]) "sideshow.web:templates",
"wuttaweb:templates",
],
)
# make config objects # make config objects
wutta_config = base.make_wutta_config(settings) wutta_config = base.make_wutta_config(settings)
pyramid_config = base.make_pyramid_config(settings) pyramid_config = base.make_pyramid_config(settings)
# bring in the rest of Sideshow # bring in the rest of Sideshow
pyramid_config.include('sideshow.web') pyramid_config.include("sideshow.web")
return pyramid_config.make_wsgi_app() return pyramid_config.make_wsgi_app()

View file

@ -48,7 +48,7 @@ class OrderRef(ObjectRef):
def get_object_url(self, order): def get_object_url(self, order):
""" """ """ """
return self.request.route_url('orders.view', uuid=order.uuid) return self.request.route_url("orders.view", uuid=order.uuid)
class LocalCustomerRef(ObjectRef): class LocalCustomerRef(ObjectRef):
@ -73,7 +73,7 @@ class LocalCustomerRef(ObjectRef):
def get_object_url(self, customer): def get_object_url(self, customer):
""" """ """ """
return self.request.route_url('local_customers.view', uuid=customer.uuid) return self.request.route_url("local_customers.view", uuid=customer.uuid)
class PendingCustomerRef(ObjectRef): class PendingCustomerRef(ObjectRef):
@ -98,7 +98,7 @@ class PendingCustomerRef(ObjectRef):
def get_object_url(self, customer): def get_object_url(self, customer):
""" """ """ """
return self.request.route_url('pending_customers.view', uuid=customer.uuid) return self.request.route_url("pending_customers.view", uuid=customer.uuid)
class LocalProductRef(ObjectRef): class LocalProductRef(ObjectRef):
@ -122,7 +122,7 @@ class LocalProductRef(ObjectRef):
def get_object_url(self, product): def get_object_url(self, product):
""" """ """ """
return self.request.route_url('local_products.view', uuid=product.uuid) return self.request.route_url("local_products.view", uuid=product.uuid)
class PendingProductRef(ObjectRef): class PendingProductRef(ObjectRef):
@ -147,4 +147,4 @@ class PendingProductRef(ObjectRef):
def get_object_url(self, product): def get_object_url(self, product):
""" """ """ """
return self.request.route_url('pending_products.view', uuid=product.uuid) return self.request.route_url("pending_products.view", uuid=product.uuid)

View file

@ -48,45 +48,45 @@ class SideshowMenuHandler(base.MenuHandler):
Generate the Orders menu. Generate the Orders menu.
""" """
return { return {
'title': "Orders", "title": "Orders",
'type': 'menu', "type": "menu",
'items': [ "items": [
{ {
'title': "Create New Order", "title": "Create New Order",
'route': 'orders.create', "route": "orders.create",
'perm': 'orders.create', "perm": "orders.create",
}, },
{'type': 'sep'}, {"type": "sep"},
{ {
'title': "Placement", "title": "Placement",
'route': 'order_items_placement', "route": "order_items_placement",
'perm': 'order_items_placement.list', "perm": "order_items_placement.list",
}, },
{ {
'title': "Receiving", "title": "Receiving",
'route': 'order_items_receiving', "route": "order_items_receiving",
'perm': 'order_items_receiving.list', "perm": "order_items_receiving.list",
}, },
{ {
'title': "Contact", "title": "Contact",
'route': 'order_items_contact', "route": "order_items_contact",
'perm': 'order_items_contact.list', "perm": "order_items_contact.list",
}, },
{ {
'title': "Delivery", "title": "Delivery",
'route': 'order_items_delivery', "route": "order_items_delivery",
'perm': 'order_items_delivery.list', "perm": "order_items_delivery.list",
}, },
{'type': 'sep'}, {"type": "sep"},
{ {
'title': "All Order Items", "title": "All Order Items",
'route': 'order_items', "route": "order_items",
'perm': 'order_items.list', "perm": "order_items.list",
}, },
{ {
'title': "All Orders", "title": "All Orders",
'route': 'orders', "route": "orders",
'perm': 'orders.list', "perm": "orders.list",
}, },
], ],
} }
@ -96,18 +96,18 @@ class SideshowMenuHandler(base.MenuHandler):
Generate the Customers menu. Generate the Customers menu.
""" """
return { return {
'title': "Customers", "title": "Customers",
'type': 'menu', "type": "menu",
'items': [ "items": [
{ {
'title': "Local Customers", "title": "Local Customers",
'route': 'local_customers', "route": "local_customers",
'perm': 'local_customers.list', "perm": "local_customers.list",
}, },
{ {
'title': "Pending Customers", "title": "Pending Customers",
'route': 'pending_customers', "route": "pending_customers",
'perm': 'pending_customers.list', "perm": "pending_customers.list",
}, },
], ],
} }
@ -117,18 +117,18 @@ class SideshowMenuHandler(base.MenuHandler):
Generate the Products menu. Generate the Products menu.
""" """
return { return {
'title': "Products", "title": "Products",
'type': 'menu', "type": "menu",
'items': [ "items": [
{ {
'title': "Local Products", "title": "Local Products",
'route': 'local_products', "route": "local_products",
'perm': 'local_products.list', "perm": "local_products.list",
}, },
{ {
'title': "Pending Products", "title": "Pending Products",
'route': 'pending_products', "route": "pending_products",
'perm': 'pending_products.list', "perm": "pending_products.list",
}, },
], ],
} }
@ -138,13 +138,13 @@ class SideshowMenuHandler(base.MenuHandler):
Generate the Batch menu. Generate the Batch menu.
""" """
return { return {
'title': "Batches", "title": "Batches",
'type': 'menu', "type": "menu",
'items': [ "items": [
{ {
'title': "New Orders", "title": "New Orders",
'route': 'neworder_batches', "route": "neworder_batches",
'perm': 'neworder_batches.list', "perm": "neworder_batches.list",
}, },
], ],
} }
@ -154,20 +154,23 @@ class SideshowMenuHandler(base.MenuHandler):
Generate the "Other" menu. Generate the "Other" menu.
""" """
return { return {
'title': "Other", "title": "Other",
'type': 'menu', "type": "menu",
'items': [], "items": [],
} }
def make_admin_menu(self, request, **kwargs): def make_admin_menu(self, request, **kwargs):
""" """ """ """
kwargs['include_people'] = True kwargs["include_people"] = True
menu = super().make_admin_menu(request, **kwargs) menu = super().make_admin_menu(request, **kwargs)
menu['items'].insert(0, { menu["items"].insert(
'title': "Stores", 0,
'route': 'stores', {
'perm': 'stores.list', "title": "Stores",
}) "route": "stores",
"perm": "stores.list",
},
)
return menu return menu

View file

@ -28,12 +28,12 @@ from fanstatic import Library, Resource
# # libcache # # libcache
libcache = Library('sideshow_libcache', 'libcache') libcache = Library("sideshow_libcache", "libcache")
vue_js = Resource(libcache, 'vue-2.6.14.min.js') vue_js = Resource(libcache, "vue-2.6.14.min.js")
vue_resource_js = Resource(libcache, 'vue-resource-1.5.3.min.js') vue_resource_js = Resource(libcache, "vue-resource-1.5.3.min.js")
buefy_js = Resource(libcache, 'buefy-0.9.25.min.js') buefy_js = Resource(libcache, "buefy-0.9.25.min.js")
buefy_css = Resource(libcache, 'buefy-0.9.25.min.css') buefy_css = Resource(libcache, "buefy-0.9.25.min.css")
fontawesome_js = Resource(libcache, 'fontawesome-5.3.1-all.min.js') fontawesome_js = Resource(libcache, "fontawesome-5.3.1-all.min.js")
# bb_vue_js = Resource(libcache, 'vue.esm-browser-3.3.11.prod.js') # bb_vue_js = Resource(libcache, 'vue.esm-browser-3.3.11.prod.js')
# bb_oruga_js = Resource(libcache, 'oruga-0.8.10.js') # bb_oruga_js = Resource(libcache, 'oruga-0.8.10.js')
# bb_oruga_bulma_js = Resource(libcache, 'oruga-bulma-0.3.0.js') # bb_oruga_bulma_js = Resource(libcache, 'oruga-bulma-0.3.0.js')
@ -44,5 +44,5 @@ fontawesome_js = Resource(libcache, 'fontawesome-5.3.1-all.min.js')
def includeme(config): def includeme(config):
config.include('wuttaweb.static') config.include("wuttaweb.static")
config.add_static_view('sideshow', 'sideshow.web:static', cache_max_age=3600) config.add_static_view("sideshow", "sideshow.web:static", cache_max_age=3600)

View file

@ -30,15 +30,18 @@ from wuttaweb.views import essential
def includeme(config): def includeme(config):
# core views for wuttaweb # core views for wuttaweb
essential.defaults(config, **{ essential.defaults(
'wuttaweb.views.common': 'sideshow.web.views.common', config,
}) **{
"wuttaweb.views.common": "sideshow.web.views.common",
},
)
# sideshow views # sideshow views
config.include('sideshow.web.views.stores') config.include("sideshow.web.views.stores")
config.include('sideshow.web.views.customers') config.include("sideshow.web.views.customers")
config.include('sideshow.web.views.products') config.include("sideshow.web.views.products")
config.include('sideshow.web.views.orders') config.include("sideshow.web.views.orders")
# batch views # batch views
config.include('sideshow.web.views.batch.neworder') config.include("sideshow.web.views.batch.neworder")

View file

@ -52,78 +52,79 @@ class NewOrderBatchView(BatchMasterView):
since those should be handled by since those should be handled by
:class:`~sideshow.web.views.orders.OrderView` instead. :class:`~sideshow.web.views.orders.OrderView` instead.
""" """
model_class = NewOrderBatch model_class = NewOrderBatch
model_title = "New Order Batch" model_title = "New Order Batch"
model_title_plural = "New Order Batches" model_title_plural = "New Order Batches"
route_prefix = 'neworder_batches' route_prefix = "neworder_batches"
url_prefix = '/batch/neworder' url_prefix = "/batch/neworder"
creatable = False creatable = False
editable = False editable = False
labels = { labels = {
'store_id': "Store ID", "store_id": "Store ID",
'customer_id': "Customer ID", "customer_id": "Customer ID",
} }
grid_columns = [ grid_columns = [
'id', "id",
'store_id', "store_id",
'customer_id', "customer_id",
'customer_name', "customer_name",
'phone_number', "phone_number",
'email_address', "email_address",
'total_price', "total_price",
'row_count', "row_count",
'created', "created",
'created_by', "created_by",
'executed', "executed",
] ]
filter_defaults = { filter_defaults = {
'executed': {'active': True, 'verb': 'is_null'}, "executed": {"active": True, "verb": "is_null"},
} }
form_fields = [ form_fields = [
'id', "id",
'store_id', "store_id",
'customer_id', "customer_id",
'local_customer', "local_customer",
'pending_customer', "pending_customer",
'customer_name', "customer_name",
'phone_number', "phone_number",
'email_address', "email_address",
'total_price', "total_price",
'row_count', "row_count",
'status_code', "status_code",
'created', "created",
'created_by', "created_by",
'executed', "executed",
'executed_by', "executed_by",
] ]
row_labels = { row_labels = {
'product_scancode': "Scancode", "product_scancode": "Scancode",
'product_brand': "Brand", "product_brand": "Brand",
'product_description': "Description", "product_description": "Description",
'product_size': "Size", "product_size": "Size",
'order_uom': "Order UOM", "order_uom": "Order UOM",
} }
row_grid_columns = [ row_grid_columns = [
'sequence', "sequence",
'product_scancode', "product_scancode",
'product_brand', "product_brand",
'product_description', "product_description",
'product_size', "product_size",
'special_order', "special_order",
'unit_price_quoted', "unit_price_quoted",
'case_size', "case_size",
'case_price_quoted', "case_price_quoted",
'order_qty', "order_qty",
'order_uom', "order_uom",
'discount_percent', "discount_percent",
'total_price', "total_price",
'status_code', "status_code",
] ]
def __init__(self, request, context=None): def __init__(self, request, context=None):
@ -141,10 +142,10 @@ class NewOrderBatchView(BatchMasterView):
# store_id # store_id
if not self.order_handler.expose_store_id(): if not self.order_handler.expose_store_id():
g.remove('store_id') g.remove("store_id")
# total_price # total_price
g.set_renderer('total_price', 'currency') g.set_renderer("total_price", "currency")
def configure_form(self, f): def configure_form(self, f):
""" """ """ """
@ -152,16 +153,16 @@ class NewOrderBatchView(BatchMasterView):
# store_id # store_id
if not self.order_handler.expose_store_id(): if not self.order_handler.expose_store_id():
f.remove('store_id') f.remove("store_id")
# local_customer # local_customer
f.set_node('local_customer', LocalCustomerRef(self.request)) f.set_node("local_customer", LocalCustomerRef(self.request))
# pending_customer # pending_customer
f.set_node('pending_customer', PendingCustomerRef(self.request)) f.set_node("pending_customer", PendingCustomerRef(self.request))
# total_price # total_price
f.set_node('total_price', WuttaMoney(self.request)) f.set_node("total_price", WuttaMoney(self.request))
def configure_row_grid(self, g): def configure_row_grid(self, g):
""" """ """ """
@ -170,22 +171,22 @@ class NewOrderBatchView(BatchMasterView):
# TODO # TODO
# order_uom # order_uom
#g.set_renderer('order_uom', self.grid_render_enum, enum=enum.ORDER_UOM) # g.set_renderer('order_uom', self.grid_render_enum, enum=enum.ORDER_UOM)
# unit_price_quoted # unit_price_quoted
g.set_label('unit_price_quoted', "Unit Price", column_only=True) g.set_label("unit_price_quoted", "Unit Price", column_only=True)
g.set_renderer('unit_price_quoted', 'currency') g.set_renderer("unit_price_quoted", "currency")
# case_price_quoted # case_price_quoted
g.set_label('case_price_quoted', "Case Price", column_only=True) g.set_label("case_price_quoted", "Case Price", column_only=True)
g.set_renderer('case_price_quoted', 'currency') g.set_renderer("case_price_quoted", "currency")
# discount_percent # discount_percent
g.set_renderer('discount_percent', 'percent') g.set_renderer("discount_percent", "percent")
g.set_label('discount_percent', "Disc. %", column_only=True) g.set_label("discount_percent", "Disc. %", column_only=True)
# total_price # total_price
g.set_renderer('total_price', 'currency') g.set_renderer("total_price", "currency")
def get_xref_buttons(self, batch): def get_xref_buttons(self, batch):
""" """
@ -197,14 +198,19 @@ class NewOrderBatchView(BatchMasterView):
model = self.app.model model = self.app.model
session = self.Session() session = self.Session()
if batch.executed and self.request.has_perm('orders.view'): if batch.executed and self.request.has_perm("orders.view"):
order = session.query(model.Order)\ order = (
.filter(model.Order.order_id == batch.id)\ session.query(model.Order)
.filter(model.Order.order_id == batch.id)
.first() .first()
)
if order: if order:
url = self.request.route_url('orders.view', uuid=order.uuid) url = self.request.route_url("orders.view", uuid=order.uuid)
buttons.append( buttons.append(
self.make_button("View the Order", primary=True, icon_left='eye', url=url)) self.make_button(
"View the Order", primary=True, icon_left="eye", url=url
)
)
return buttons return buttons
@ -212,7 +218,7 @@ class NewOrderBatchView(BatchMasterView):
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()
NewOrderBatchView = kwargs.get('NewOrderBatchView', base['NewOrderBatchView']) NewOrderBatchView = kwargs.get("NewOrderBatchView", base["NewOrderBatchView"])
NewOrderBatchView.defaults(config) NewOrderBatchView.defaults(config)

View file

@ -46,54 +46,55 @@ class CommonView(base.CommonView):
auth = self.app.get_auth_handler() auth = self.app.get_auth_handler()
admin = model.Role(name="Order Admin") admin = model.Role(name="Order Admin")
admin.notes = ("this role was auto-created; " admin.notes = (
"you can change or remove it as needed.") "this role was auto-created; " "you can change or remove it as needed."
)
session.add(admin) session.add(admin)
user.roles.append(admin) user.roles.append(admin)
order_admin_perms = [ order_admin_perms = [
'local_customers.list', "local_customers.list",
'local_customers.view', "local_customers.view",
'local_products.list', "local_products.list",
'local_products.view', "local_products.view",
'neworder_batches.list', "neworder_batches.list",
'neworder_batches.view', "neworder_batches.view",
'order_items.add_note', "order_items.add_note",
'order_items.change_status', "order_items.change_status",
'order_items.list', "order_items.list",
'order_items.view', "order_items.view",
'order_items_contact.add_note', "order_items_contact.add_note",
'order_items_contact.change_status', "order_items_contact.change_status",
'order_items_contact.list', "order_items_contact.list",
'order_items_contact.process_contact', "order_items_contact.process_contact",
'order_items_contact.view', "order_items_contact.view",
'order_items_delivery.add_note', "order_items_delivery.add_note",
'order_items_delivery.change_status', "order_items_delivery.change_status",
'order_items_delivery.list', "order_items_delivery.list",
'order_items_delivery.process_delivery', "order_items_delivery.process_delivery",
'order_items_delivery.process_restock', "order_items_delivery.process_restock",
'order_items_delivery.view', "order_items_delivery.view",
'order_items_placement.add_note', "order_items_placement.add_note",
'order_items_placement.change_status', "order_items_placement.change_status",
'order_items_placement.list', "order_items_placement.list",
'order_items_placement.process_placement', "order_items_placement.process_placement",
'order_items_placement.view', "order_items_placement.view",
'order_items_receiving.add_note', "order_items_receiving.add_note",
'order_items_receiving.change_status', "order_items_receiving.change_status",
'order_items_receiving.list', "order_items_receiving.list",
'order_items_receiving.process_receiving', "order_items_receiving.process_receiving",
'order_items_receiving.process_reorder', "order_items_receiving.process_reorder",
'order_items_receiving.view', "order_items_receiving.view",
'orders.configure', "orders.configure",
'orders.create', "orders.create",
'orders.create_unknown_product', "orders.create_unknown_product",
'orders.list', "orders.list",
'orders.view', "orders.view",
'pending_customers.list', "pending_customers.list",
'pending_customers.view', "pending_customers.view",
'pending_products.list', "pending_products.list",
'pending_products.view', "pending_products.view",
] ]
for perm in order_admin_perms: for perm in order_admin_perms:
@ -101,4 +102,4 @@ class CommonView(base.CommonView):
def includeme(config): def includeme(config):
base.defaults(config, **{'CommonView': CommonView}) base.defaults(config, **{"CommonView": CommonView})

View file

@ -44,35 +44,36 @@ class LocalCustomerView(MasterView):
* ``/local/customers/XXX/edit`` * ``/local/customers/XXX/edit``
* ``/local/customers/XXX/delete`` * ``/local/customers/XXX/delete``
""" """
model_class = LocalCustomer model_class = LocalCustomer
model_title = "Local Customer" model_title = "Local Customer"
route_prefix = 'local_customers' route_prefix = "local_customers"
url_prefix = '/local/customers' url_prefix = "/local/customers"
labels = { labels = {
'external_id': "External ID", "external_id": "External ID",
} }
grid_columns = [ grid_columns = [
'external_id', "external_id",
'full_name', "full_name",
'first_name', "first_name",
'last_name', "last_name",
'phone_number', "phone_number",
'email_address', "email_address",
] ]
sort_defaults = 'full_name' sort_defaults = "full_name"
form_fields = [ form_fields = [
'external_id', "external_id",
'full_name', "full_name",
'first_name', "first_name",
'last_name', "last_name",
'phone_number', "phone_number",
'email_address', "email_address",
'orders', "orders",
'new_order_batches', "new_order_batches",
] ]
def configure_grid(self, g): def configure_grid(self, g):
@ -80,11 +81,11 @@ class LocalCustomerView(MasterView):
super().configure_grid(g) super().configure_grid(g)
# links # links
g.set_link('full_name') g.set_link("full_name")
g.set_link('first_name') g.set_link("first_name")
g.set_link('last_name') g.set_link("last_name")
g.set_link('phone_number') g.set_link("phone_number")
g.set_link('email_address') g.set_link("email_address")
def configure_form(self, f): def configure_form(self, f):
""" """ """ """
@ -93,25 +94,25 @@ class LocalCustomerView(MasterView):
# external_id # external_id
if self.creating: if self.creating:
f.remove('external_id') f.remove("external_id")
else: else:
f.set_readonly('external_id') f.set_readonly("external_id")
# full_name # full_name
if self.creating or self.editing: if self.creating or self.editing:
f.remove('full_name') f.remove("full_name")
# orders # orders
if self.creating or self.editing: if self.creating or self.editing:
f.remove('orders') f.remove("orders")
else: else:
f.set_grid('orders', self.make_orders_grid(customer)) f.set_grid("orders", self.make_orders_grid(customer))
# new_order_batches # new_order_batches
if self.creating or self.editing: if self.creating or self.editing:
f.remove('new_order_batches') f.remove("new_order_batches")
else: else:
f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer)) f.set_grid("new_order_batches", self.make_new_order_batches_grid(customer))
def make_orders_grid(self, customer): def make_orders_grid(self, customer):
""" """
@ -120,24 +121,28 @@ class LocalCustomerView(MasterView):
model = self.app.model model = self.app.model
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.orders', grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order, model_class=model.Order,
data=customer.orders, data=customer.orders,
columns=[ columns=[
'order_id', "order_id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
], ],
labels={ labels={
'order_id': "Order ID", "order_id": "Order ID",
}) },
grid.set_renderer('total_price', grid.render_currency) )
grid.set_renderer("total_price", grid.render_currency)
if self.request.has_perm('orders.view'): if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) url = lambda order, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "orders.view", uuid=order.uuid
grid.set_link('order_id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid return grid
@ -148,28 +153,32 @@ class LocalCustomerView(MasterView):
model = self.app.model model = self.app.model
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch, model_class=model.NewOrderBatch,
data=customer.new_order_batches, data=customer.new_order_batches,
columns=[ columns=[
'id', "id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
'executed', "executed",
], ],
labels={ labels={
'id': "Batch ID", "id": "Batch ID",
}, },
renderers={ renderers={
'id': 'batch_id', "id": "batch_id",
'total_price': 'currency', "total_price": "currency",
}) },
)
if self.request.has_perm('neworder_batches.view'): if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) url = lambda batch, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "neworder_batches.view", uuid=batch.uuid
grid.set_link('id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid return grid
@ -178,8 +187,9 @@ class LocalCustomerView(MasterView):
enum = self.app.enum enum = self.app.enum
customer = super().objectify(form) customer = super().objectify(form)
customer.full_name = self.app.make_full_name(customer.first_name, customer.full_name = self.app.make_full_name(
customer.last_name) customer.first_name, customer.last_name
)
return customer return customer
@ -198,41 +208,42 @@ class PendingCustomerView(MasterView):
* ``/pending/customers/XXX/edit`` * ``/pending/customers/XXX/edit``
* ``/pending/customers/XXX/delete`` * ``/pending/customers/XXX/delete``
""" """
model_class = PendingCustomer model_class = PendingCustomer
model_title = "Pending Customer" model_title = "Pending Customer"
route_prefix = 'pending_customers' route_prefix = "pending_customers"
url_prefix = '/pending/customers' url_prefix = "/pending/customers"
labels = { labels = {
'customer_id': "Customer ID", "customer_id": "Customer ID",
} }
grid_columns = [ grid_columns = [
'full_name', "full_name",
'first_name', "first_name",
'last_name', "last_name",
'phone_number', "phone_number",
'email_address', "email_address",
'customer_id', "customer_id",
'status', "status",
'created', "created",
'created_by', "created_by",
] ]
sort_defaults = 'full_name' sort_defaults = "full_name"
form_fields = [ form_fields = [
'customer_id', "customer_id",
'full_name', "full_name",
'first_name', "first_name",
'last_name', "last_name",
'phone_number', "phone_number",
'email_address', "email_address",
'status', "status",
'created', "created",
'created_by', "created_by",
'orders', "orders",
'new_order_batches', "new_order_batches",
] ]
def configure_grid(self, g): def configure_grid(self, g):
@ -241,14 +252,14 @@ class PendingCustomerView(MasterView):
enum = self.app.enum enum = self.app.enum
# status # status
g.set_renderer('status', self.grid_render_enum, enum=enum.PendingCustomerStatus) g.set_renderer("status", self.grid_render_enum, enum=enum.PendingCustomerStatus)
# links # links
g.set_link('full_name') g.set_link("full_name")
g.set_link('first_name') g.set_link("first_name")
g.set_link('last_name') g.set_link("last_name")
g.set_link('phone_number') g.set_link("phone_number")
g.set_link('email_address') g.set_link("email_address")
def configure_form(self, f): def configure_form(self, f):
""" """ """ """
@ -258,41 +269,41 @@ class PendingCustomerView(MasterView):
# customer_id # customer_id
if self.creating: if self.creating:
f.remove('customer_id') f.remove("customer_id")
else: else:
f.set_readonly('customer_id') f.set_readonly("customer_id")
# status # status
if self.creating: if self.creating:
f.remove('status') f.remove("status")
else: else:
f.set_node('status', WuttaEnum(self.request, enum.PendingCustomerStatus)) f.set_node("status", WuttaEnum(self.request, enum.PendingCustomerStatus))
f.set_readonly('status') f.set_readonly("status")
# created # created
if self.creating: if self.creating:
f.remove('created') f.remove("created")
else: else:
f.set_readonly('created') f.set_readonly("created")
# created_by # created_by
if self.creating: if self.creating:
f.remove('created_by') f.remove("created_by")
else: else:
f.set_node('created_by', UserRef(self.request)) f.set_node("created_by", UserRef(self.request))
f.set_readonly('created_by') f.set_readonly("created_by")
# orders # orders
if self.creating or self.editing: if self.creating or self.editing:
f.remove('orders') f.remove("orders")
else: else:
f.set_grid('orders', self.make_orders_grid(customer)) f.set_grid("orders", self.make_orders_grid(customer))
# new_order_batches # new_order_batches
if self.creating or self.editing: if self.creating or self.editing:
f.remove('new_order_batches') f.remove("new_order_batches")
else: else:
f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer)) f.set_grid("new_order_batches", self.make_new_order_batches_grid(customer))
def make_orders_grid(self, customer): def make_orders_grid(self, customer):
""" """
@ -301,24 +312,28 @@ class PendingCustomerView(MasterView):
model = self.app.model model = self.app.model
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.orders', grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order, model_class=model.Order,
data=customer.orders, data=customer.orders,
columns=[ columns=[
'order_id', "order_id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
], ],
labels={ labels={
'order_id': "Order ID", "order_id": "Order ID",
}) },
grid.set_renderer('total_price', grid.render_currency) )
grid.set_renderer("total_price", grid.render_currency)
if self.request.has_perm('orders.view'): if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) url = lambda order, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "orders.view", uuid=order.uuid
grid.set_link('order_id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid return grid
@ -329,28 +344,32 @@ class PendingCustomerView(MasterView):
model = self.app.model model = self.app.model
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch, model_class=model.NewOrderBatch,
data=customer.new_order_batches, data=customer.new_order_batches,
columns=[ columns=[
'id', "id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
'executed', "executed",
], ],
labels={ labels={
'id': "Batch ID", "id": "Batch ID",
}, },
renderers={ renderers={
'id': 'batch_id', "id": "batch_id",
'total_price': 'currency', "total_price": "currency",
}) },
)
if self.request.has_perm('neworder_batches.view'): if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) url = lambda batch, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "neworder_batches.view", uuid=batch.uuid
grid.set_link('id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid return grid
@ -371,16 +390,20 @@ class PendingCustomerView(MasterView):
# avoid deleting if still referenced by order(s) # avoid deleting if still referenced by order(s)
for order in customer.orders: for order in customer.orders:
self.request.session.flash(f"Cannot delete {model_title} still attached " self.request.session.flash(
"to Order(s)", 'warning') f"Cannot delete {model_title} still attached " "to Order(s)", "warning"
raise self.redirect(self.get_action_url('view', customer)) )
raise self.redirect(self.get_action_url("view", customer))
# avoid deleting if still referenced by new order batch(es) # avoid deleting if still referenced by new order batch(es)
for batch in customer.new_order_batches: for batch in customer.new_order_batches:
if not batch.executed: if not batch.executed:
self.request.session.flash(f"Cannot delete {model_title} still attached " self.request.session.flash(
"to New Order Batch(es)", 'warning') f"Cannot delete {model_title} still attached "
raise self.redirect(self.get_action_url('view', customer)) "to New Order Batch(es)",
"warning",
)
raise self.redirect(self.get_action_url("view", customer))
# go ahead and delete per usual # go ahead and delete per usual
super().delete_instance(customer) super().delete_instance(customer)
@ -389,10 +412,10 @@ class PendingCustomerView(MasterView):
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()
LocalCustomerView = kwargs.get('LocalCustomerView', base['LocalCustomerView']) LocalCustomerView = kwargs.get("LocalCustomerView", base["LocalCustomerView"])
LocalCustomerView.defaults(config) LocalCustomerView.defaults(config)
PendingCustomerView = kwargs.get('PendingCustomerView', base['PendingCustomerView']) PendingCustomerView = kwargs.get("PendingCustomerView", base["PendingCustomerView"])
PendingCustomerView.defaults(config) PendingCustomerView.defaults(config)

File diff suppressed because it is too large Load diff

View file

@ -44,47 +44,48 @@ class LocalProductView(MasterView):
* ``/local/products/XXX/edit`` * ``/local/products/XXX/edit``
* ``/local/products/XXX/delete`` * ``/local/products/XXX/delete``
""" """
model_class = LocalProduct model_class = LocalProduct
model_title = "Local Product" model_title = "Local Product"
route_prefix = 'local_products' route_prefix = "local_products"
url_prefix = '/local/products' url_prefix = "/local/products"
labels = { labels = {
'external_id': "External ID", "external_id": "External ID",
'department_id': "Department ID", "department_id": "Department ID",
} }
grid_columns = [ grid_columns = [
'scancode', "scancode",
'brand_name', "brand_name",
'description', "description",
'size', "size",
'department_name', "department_name",
'special_order', "special_order",
'case_size', "case_size",
'unit_cost', "unit_cost",
'unit_price_reg', "unit_price_reg",
] ]
sort_defaults = 'scancode' sort_defaults = "scancode"
form_fields = [ form_fields = [
'external_id', "external_id",
'scancode', "scancode",
'brand_name', "brand_name",
'description', "description",
'size', "size",
'department_id', "department_id",
'department_name', "department_name",
'special_order', "special_order",
'vendor_name', "vendor_name",
'vendor_item_code', "vendor_item_code",
'case_size', "case_size",
'unit_cost', "unit_cost",
'unit_price_reg', "unit_price_reg",
'notes', "notes",
'orders', "orders",
'new_order_batches', "new_order_batches",
] ]
def configure_grid(self, g): def configure_grid(self, g):
@ -92,17 +93,17 @@ class LocalProductView(MasterView):
super().configure_grid(g) super().configure_grid(g)
# unit_cost # unit_cost
g.set_renderer('unit_cost', 'currency', scale=4) g.set_renderer("unit_cost", "currency", scale=4)
# unit_price_reg # unit_price_reg
g.set_label('unit_price_reg', "Reg. Price", column_only=True) g.set_label("unit_price_reg", "Reg. Price", column_only=True)
g.set_renderer('unit_price_reg', 'currency') g.set_renderer("unit_price_reg", "currency")
# links # links
g.set_link('scancode') g.set_link("scancode")
g.set_link('brand_name') g.set_link("brand_name")
g.set_link('description') g.set_link("description")
g.set_link('size') g.set_link("size")
def configure_form(self, f): def configure_form(self, f):
""" """ """ """
@ -112,40 +113,40 @@ class LocalProductView(MasterView):
# external_id # external_id
if self.creating: if self.creating:
f.remove('external_id') f.remove("external_id")
else: else:
f.set_readonly('external_id') f.set_readonly("external_id")
# TODO: should not have to explicitly mark these nodes # TODO: should not have to explicitly mark these nodes
# as required=False.. i guess i do for now b/c i am # as required=False.. i guess i do for now b/c i am
# totally overriding the node from colanderlachemy # totally overriding the node from colanderlachemy
# case_size # case_size
f.set_node('case_size', WuttaQuantity(self.request)) f.set_node("case_size", WuttaQuantity(self.request))
f.set_required('case_size', False) f.set_required("case_size", False)
# unit_cost # unit_cost
f.set_node('unit_cost', WuttaMoney(self.request, scale=4)) f.set_node("unit_cost", WuttaMoney(self.request, scale=4))
f.set_required('unit_cost', False) f.set_required("unit_cost", False)
# unit_price_reg # unit_price_reg
f.set_node('unit_price_reg', WuttaMoney(self.request)) f.set_node("unit_price_reg", WuttaMoney(self.request))
f.set_required('unit_price_reg', False) f.set_required("unit_price_reg", False)
# notes # notes
f.set_widget('notes', 'notes') f.set_widget("notes", "notes")
# orders # orders
if self.creating or self.editing: if self.creating or self.editing:
f.remove('orders') f.remove("orders")
else: else:
f.set_grid('orders', self.make_orders_grid(product)) f.set_grid("orders", self.make_orders_grid(product))
# new_order_batches # new_order_batches
if self.creating or self.editing: if self.creating or self.editing:
f.remove('new_order_batches') f.remove("new_order_batches")
else: else:
f.set_grid('new_order_batches', self.make_new_order_batches_grid(product)) f.set_grid("new_order_batches", self.make_new_order_batches_grid(product))
def make_orders_grid(self, product): def make_orders_grid(self, product):
""" """
@ -157,26 +158,30 @@ class LocalProductView(MasterView):
orders = set([item.order for item in product.order_items]) orders = set([item.order for item in product.order_items])
orders = sorted(orders, key=lambda order: order.order_id) orders = sorted(orders, key=lambda order: order.order_id)
grid = self.make_grid(key=f'{route_prefix}.view.orders', grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order, model_class=model.Order,
data=orders, data=orders,
columns=[ columns=[
'order_id', "order_id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
], ],
labels={ labels={
'order_id': "Order ID", "order_id": "Order ID",
}, },
renderers={ renderers={
'total_price': 'currency', "total_price": "currency",
}) },
)
if self.request.has_perm('orders.view'): if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) url = lambda order, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "orders.view", uuid=order.uuid
grid.set_link('order_id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid return grid
@ -190,28 +195,32 @@ class LocalProductView(MasterView):
batches = set([row.batch for row in product.new_order_batch_rows]) batches = set([row.batch for row in product.new_order_batch_rows])
batches = sorted(batches, key=lambda batch: batch.id) batches = sorted(batches, key=lambda batch: batch.id)
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch, model_class=model.NewOrderBatch,
data=batches, data=batches,
columns=[ columns=[
'id', "id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
'executed', "executed",
], ],
labels={ labels={
'id': "Batch ID", "id": "Batch ID",
'status_code': "Status", "status_code": "Status",
}, },
renderers={ renderers={
'id': 'batch_id', "id": "batch_id",
}) },
)
if self.request.has_perm('neworder_batches.view'): if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) url = lambda batch, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "neworder_batches.view", uuid=batch.uuid
grid.set_link('id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid return grid
@ -230,57 +239,57 @@ class PendingProductView(MasterView):
* ``/pending/products/XXX/edit`` * ``/pending/products/XXX/edit``
* ``/pending/products/XXX/delete`` * ``/pending/products/XXX/delete``
""" """
model_class = PendingProduct model_class = PendingProduct
model_title = "Pending Product" model_title = "Pending Product"
route_prefix = 'pending_products' route_prefix = "pending_products"
url_prefix = '/pending/products' url_prefix = "/pending/products"
labels = { labels = {
'department_id': "Department ID", "department_id": "Department ID",
'product_id': "Product ID", "product_id": "Product ID",
} }
grid_columns = [ grid_columns = [
'scancode', "scancode",
'department_name', "department_name",
'brand_name', "brand_name",
'description', "description",
'size', "size",
'unit_cost', "unit_cost",
'case_size', "case_size",
'unit_price_reg', "unit_price_reg",
'special_order', "special_order",
'status', "status",
'created', "created",
'created_by', "created_by",
] ]
sort_defaults = ('created', 'desc') sort_defaults = ("created", "desc")
filter_defaults = { filter_defaults = {
'status': {'active': True, "status": {"active": True, "value": PendingProductStatus.READY.name},
'value': PendingProductStatus.READY.name},
} }
form_fields = [ form_fields = [
'product_id', "product_id",
'scancode', "scancode",
'department_id', "department_id",
'department_name', "department_name",
'brand_name', "brand_name",
'description', "description",
'size', "size",
'vendor_name', "vendor_name",
'vendor_item_code', "vendor_item_code",
'unit_cost', "unit_cost",
'case_size', "case_size",
'unit_price_reg', "unit_price_reg",
'special_order', "special_order",
'notes', "notes",
'created', "created",
'created_by', "created_by",
'orders', "orders",
'new_order_batches', "new_order_batches",
] ]
def configure_grid(self, g): def configure_grid(self, g):
@ -289,26 +298,26 @@ class PendingProductView(MasterView):
enum = self.app.enum enum = self.app.enum
# unit_cost # unit_cost
g.set_renderer('unit_cost', 'currency', scale=4) g.set_renderer("unit_cost", "currency", scale=4)
# unit_price_reg # unit_price_reg
g.set_label('unit_price_reg', "Reg. Price", column_only=True) g.set_label("unit_price_reg", "Reg. Price", column_only=True)
g.set_renderer('unit_price_reg', 'currency') g.set_renderer("unit_price_reg", "currency")
# status # status
g.set_enum('status', enum.PendingProductStatus) g.set_enum("status", enum.PendingProductStatus)
# links # links
g.set_link('scancode') g.set_link("scancode")
g.set_link('brand_name') g.set_link("brand_name")
g.set_link('description') g.set_link("description")
g.set_link('size') g.set_link("size")
def grid_row_class(self, product, data, i): def grid_row_class(self, product, data, i):
""" """ """ """
enum = self.app.enum enum = self.app.enum
if product.status == enum.PendingProductStatus.IGNORED: if product.status == enum.PendingProductStatus.IGNORED:
return 'has-background-warning' return "has-background-warning"
def configure_form(self, f): def configure_form(self, f):
""" """ """ """
@ -318,40 +327,40 @@ class PendingProductView(MasterView):
# product_id # product_id
if self.creating: if self.creating:
f.remove('product_id') f.remove("product_id")
else: else:
f.set_readonly('product_id') f.set_readonly("product_id")
# unit_price_reg # unit_price_reg
f.set_node('unit_price_reg', WuttaMoney(self.request)) f.set_node("unit_price_reg", WuttaMoney(self.request))
# notes # notes
f.set_widget('notes', 'notes') f.set_widget("notes", "notes")
# created # created
if self.creating: if self.creating:
f.remove('created') f.remove("created")
else: else:
f.set_readonly('created') f.set_readonly("created")
# created_by # created_by
if self.creating: if self.creating:
f.remove('created_by') f.remove("created_by")
else: else:
f.set_node('created_by', UserRef(self.request)) f.set_node("created_by", UserRef(self.request))
f.set_readonly('created_by') f.set_readonly("created_by")
# orders # orders
if self.creating or self.editing: if self.creating or self.editing:
f.remove('orders') f.remove("orders")
else: else:
f.set_grid('orders', self.make_orders_grid(product)) f.set_grid("orders", self.make_orders_grid(product))
# new_order_batches # new_order_batches
if self.creating or self.editing: if self.creating or self.editing:
f.remove('new_order_batches') f.remove("new_order_batches")
else: else:
f.set_grid('new_order_batches', self.make_new_order_batches_grid(product)) f.set_grid("new_order_batches", self.make_new_order_batches_grid(product))
def make_orders_grid(self, product): def make_orders_grid(self, product):
""" """
@ -363,26 +372,30 @@ class PendingProductView(MasterView):
orders = set([item.order for item in product.order_items]) orders = set([item.order for item in product.order_items])
orders = sorted(orders, key=lambda order: order.order_id) orders = sorted(orders, key=lambda order: order.order_id)
grid = self.make_grid(key=f'{route_prefix}.view.orders', grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order, model_class=model.Order,
data=orders, data=orders,
columns=[ columns=[
'order_id', "order_id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
], ],
labels={ labels={
'order_id': "Order ID", "order_id": "Order ID",
}, },
renderers={ renderers={
'total_price': 'currency', "total_price": "currency",
}) },
)
if self.request.has_perm('orders.view'): if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) url = lambda order, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "orders.view", uuid=order.uuid
grid.set_link('order_id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid return grid
@ -396,28 +409,32 @@ class PendingProductView(MasterView):
batches = set([row.batch for row in product.new_order_batch_rows]) batches = set([row.batch for row in product.new_order_batch_rows])
batches = sorted(batches, key=lambda batch: batch.id) batches = sorted(batches, key=lambda batch: batch.id)
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch, model_class=model.NewOrderBatch,
data=batches, data=batches,
columns=[ columns=[
'id', "id",
'total_price', "total_price",
'created', "created",
'created_by', "created_by",
'executed', "executed",
], ],
labels={ labels={
'id': "Batch ID", "id": "Batch ID",
'status_code': "Status", "status_code": "Status",
}, },
renderers={ renderers={
'id': 'batch_id', "id": "batch_id",
}) },
)
if self.request.has_perm('neworder_batches.view'): if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) url = lambda batch, i: self.request.route_url(
grid.add_action('view', icon='eye', url=url) "neworder_batches.view", uuid=batch.uuid
grid.set_link('id') )
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid return grid
@ -426,11 +443,12 @@ class PendingProductView(MasterView):
enum = self.app.enum enum = self.app.enum
if self.viewing: if self.viewing:
product = context['instance'] product = context["instance"]
if (product.status == enum.PendingProductStatus.READY if product.status == enum.PendingProductStatus.READY and self.has_any_perm(
and self.has_any_perm('resolve', 'ignore')): "resolve", "ignore"
handler = self.app.get_batch_handler('neworder') ):
context['use_local_products'] = handler.use_local_products() handler = self.app.get_batch_handler("neworder")
context["use_local_products"] = handler.use_local_products()
return context return context
@ -441,9 +459,12 @@ class PendingProductView(MasterView):
for row in product.new_order_batch_rows: for row in product.new_order_batch_rows:
if not row.batch.executed: if not row.batch.executed:
model_title = self.get_model_title() model_title = self.get_model_title()
self.request.session.flash(f"Cannot delete {model_title} still attached " self.request.session.flash(
"to New Order Batch(es)", 'warning') f"Cannot delete {model_title} still attached "
raise self.redirect(self.get_action_url('view', product)) "to New Order Batch(es)",
"warning",
)
raise self.redirect(self.get_action_url("view", product))
# go ahead and delete per usual # go ahead and delete per usual
super().delete_instance(product) super().delete_instance(product)
@ -469,21 +490,23 @@ class PendingProductView(MasterView):
product = self.get_instance() product = self.get_instance()
if product.status != enum.PendingProductStatus.READY: if product.status != enum.PendingProductStatus.READY:
self.request.session.flash("pending product does not have 'ready' status!", 'error') self.request.session.flash(
return self.redirect(self.get_action_url('view', product)) "pending product does not have 'ready' status!", "error"
)
return self.redirect(self.get_action_url("view", product))
product_id = self.request.POST.get('product_id') product_id = self.request.POST.get("product_id")
if not product_id: if not product_id:
self.request.session.flash("must specify valid product_id", 'error') self.request.session.flash("must specify valid product_id", "error")
return self.redirect(self.get_action_url('view', product)) return self.redirect(self.get_action_url("view", product))
batch_handler = self.app.get_batch_handler('neworder') batch_handler = self.app.get_batch_handler("neworder")
order_handler = self.app.get_order_handler() order_handler = self.app.get_order_handler()
info = batch_handler.get_product_info_external(session, product_id) info = batch_handler.get_product_info_external(session, product_id)
order_handler.resolve_pending_product(product, info, self.request.user) order_handler.resolve_pending_product(product, info, self.request.user)
return self.redirect(self.get_action_url('view', product)) return self.redirect(self.get_action_url("view", product))
def ignore(self): def ignore(self):
""" """
@ -499,11 +522,13 @@ class PendingProductView(MasterView):
product = self.get_instance() product = self.get_instance()
if product.status != enum.PendingProductStatus.READY: if product.status != enum.PendingProductStatus.READY:
self.request.session.flash("pending product does not have 'ready' status!", 'error') self.request.session.flash(
return self.redirect(self.get_action_url('view', product)) "pending product does not have 'ready' status!", "error"
)
return self.redirect(self.get_action_url("view", product))
product.status = enum.PendingProductStatus.IGNORED product.status = enum.PendingProductStatus.IGNORED
return self.redirect(self.get_action_url('view', product)) return self.redirect(self.get_action_url("view", product))
@classmethod @classmethod
def defaults(cls, config): def defaults(cls, config):
@ -519,35 +544,45 @@ class PendingProductView(MasterView):
model_title = cls.get_model_title() model_title = cls.get_model_title()
# resolve # resolve
config.add_wutta_permission(permission_prefix, config.add_wutta_permission(
f'{permission_prefix}.resolve', permission_prefix, f"{permission_prefix}.resolve", f"Resolve {model_title}"
f"Resolve {model_title}") )
config.add_route(f'{route_prefix}.resolve', config.add_route(
f'{instance_url_prefix}/resolve', f"{route_prefix}.resolve",
request_method='POST') f"{instance_url_prefix}/resolve",
config.add_view(cls, attr='resolve', request_method="POST",
route_name=f'{route_prefix}.resolve', )
permission=f'{permission_prefix}.resolve') config.add_view(
cls,
attr="resolve",
route_name=f"{route_prefix}.resolve",
permission=f"{permission_prefix}.resolve",
)
# ignore # ignore
config.add_wutta_permission(permission_prefix, config.add_wutta_permission(
f'{permission_prefix}.ignore', permission_prefix, f"{permission_prefix}.ignore", f"Ignore {model_title}"
f"Ignore {model_title}") )
config.add_route(f'{route_prefix}.ignore', config.add_route(
f'{instance_url_prefix}/ignore', f"{route_prefix}.ignore",
request_method='POST') f"{instance_url_prefix}/ignore",
config.add_view(cls, attr='ignore', request_method="POST",
route_name=f'{route_prefix}.ignore', )
permission=f'{permission_prefix}.ignore') config.add_view(
cls,
attr="ignore",
route_name=f"{route_prefix}.ignore",
permission=f"{permission_prefix}.ignore",
)
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()
LocalProductView = kwargs.get('LocalProductView', base['LocalProductView']) LocalProductView = kwargs.get("LocalProductView", base["LocalProductView"])
LocalProductView.defaults(config) LocalProductView.defaults(config)
PendingProductView = kwargs.get('PendingProductView', base['PendingProductView']) PendingProductView = kwargs.get("PendingProductView", base["PendingProductView"])
PendingProductView.defaults(config) PendingProductView.defaults(config)

View file

@ -43,51 +43,51 @@ class StoreView(MasterView):
* ``/stores/XXX/edit`` * ``/stores/XXX/edit``
* ``/stores/XXX/delete`` * ``/stores/XXX/delete``
""" """
model_class = Store model_class = Store
labels = { labels = {
'store_id': "Store ID", "store_id": "Store ID",
} }
filter_defaults = { filter_defaults = {
'archived': {'active': True, 'verb': 'is_false'}, "archived": {"active": True, "verb": "is_false"},
} }
sort_defaults = 'store_id' sort_defaults = "store_id"
def configure_grid(self, g): def configure_grid(self, g):
""" """ """ """
super().configure_grid(g) super().configure_grid(g)
# links # links
g.set_link('store_id') g.set_link("store_id")
g.set_link('name') g.set_link("name")
def grid_row_class(self, store, data, i): def grid_row_class(self, store, data, i):
""" """ """ """
if store.archived: if store.archived:
return 'has-background-warning' return "has-background-warning"
def configure_form(self, f): def configure_form(self, f):
""" """ """ """
super().configure_form(f) super().configure_form(f)
# store_id # store_id
f.set_validator('store_id', self.unique_store_id) f.set_validator("store_id", self.unique_store_id)
# name # name
f.set_validator('name', self.unique_name) f.set_validator("name", self.unique_name)
def unique_store_id(self, node, value): def unique_store_id(self, node, value):
""" """ """ """
model = self.app.model model = self.app.model
session = self.Session() session = self.Session()
query = session.query(model.Store)\ query = session.query(model.Store).filter(model.Store.store_id == value)
.filter(model.Store.store_id == value)
if self.editing: if self.editing:
uuid = self.request.matchdict['uuid'] uuid = self.request.matchdict["uuid"]
query = query.filter(model.Store.uuid != uuid) query = query.filter(model.Store.uuid != uuid)
if query.count(): if query.count():
@ -98,11 +98,10 @@ class StoreView(MasterView):
model = self.app.model model = self.app.model
session = self.Session() session = self.Session()
query = session.query(model.Store)\ query = session.query(model.Store).filter(model.Store.name == value)
.filter(model.Store.name == value)
if self.editing: if self.editing:
uuid = self.request.matchdict['uuid'] uuid = self.request.matchdict["uuid"]
query = query.filter(model.Store.uuid != uuid) query = query.filter(model.Store.uuid != uuid)
if query.count(): if query.count():
@ -112,7 +111,7 @@ class StoreView(MasterView):
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()
StoreView = kwargs.get('StoreView', base['StoreView']) StoreView = kwargs.get("StoreView", base["StoreView"])
StoreView.defaults(config) StoreView.defaults(config)

View file

@ -15,14 +15,14 @@ def release(c, skip_tests=False):
Release a new version of Sideshow Release a new version of Sideshow
""" """
if not skip_tests: if not skip_tests:
c.run('pytest') c.run("pytest")
# rebuild pkg # rebuild pkg
if os.path.exists('dist'): if os.path.exists("dist"):
shutil.rmtree('dist') shutil.rmtree("dist")
if os.path.exists('Sideshow.egg-info'): if os.path.exists("Sideshow.egg-info"):
shutil.rmtree('Sideshow.egg-info') shutil.rmtree("Sideshow.egg-info")
c.run('python -m build --sdist') c.run("python -m build --sdist")
# upload # upload
c.run('twine upload dist/*') c.run("twine upload dist/*")

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,6 @@ class TestInstall(ConfigTestCase):
def test_run(self): def test_run(self):
ctx = MagicMock(params={}) ctx = MagicMock(params={})
ctx.parent.wutta_config = self.config ctx.parent.wutta_config = self.config
with patch.object(InstallHandler, 'run') as run: with patch.object(InstallHandler, "run") as run:
mod.install(ctx) mod.install(ctx)
run.assert_called_once_with() run.assert_called_once_with()

View file

@ -16,8 +16,6 @@ class TestNewOrderBatchRow(DataTestCase):
row = mod.NewOrderBatchRow(product_description="Vinegar") row = mod.NewOrderBatchRow(product_description="Vinegar")
self.assertEqual(str(row), "Vinegar") self.assertEqual(str(row), "Vinegar")
product = PendingProduct(brand_name="Bragg", product = PendingProduct(brand_name="Bragg", description="Vinegar", size="32oz")
description="Vinegar",
size="32oz")
row = mod.NewOrderBatchRow(pending_product=product) row = mod.NewOrderBatchRow(pending_product=product)
self.assertEqual(str(row), "Bragg Vinegar 32oz") self.assertEqual(str(row), "Bragg Vinegar 32oz")

View file

@ -21,7 +21,7 @@ class TestOrderItem(DataTestCase):
def make_config(self, **kw): def make_config(self, **kw):
config = super().make_config(**kw) config = super().make_config(**kw)
config.setdefault('wutta.enum_spec', 'sideshow.enum') config.setdefault("wutta.enum_spec", "sideshow.enum")
return config return config
def test_full_description(self): def test_full_description(self):
@ -32,9 +32,9 @@ class TestOrderItem(DataTestCase):
item = mod.OrderItem(product_description="Vinegar") item = mod.OrderItem(product_description="Vinegar")
self.assertEqual(item.full_description, "Vinegar") self.assertEqual(item.full_description, "Vinegar")
item = mod.OrderItem(product_brand='Bragg', item = mod.OrderItem(
product_description='Vinegar', product_brand="Bragg", product_description="Vinegar", product_size="32oz"
product_size='32oz') )
self.assertEqual(item.full_description, "Bragg Vinegar 32oz") self.assertEqual(item.full_description, "Bragg Vinegar 32oz")
def test_str(self): def test_str(self):
@ -45,15 +45,15 @@ class TestOrderItem(DataTestCase):
item = mod.OrderItem(product_description="Vinegar") item = mod.OrderItem(product_description="Vinegar")
self.assertEqual(str(item), "Vinegar") self.assertEqual(str(item), "Vinegar")
item = mod.OrderItem(product_brand='Bragg', item = mod.OrderItem(
product_description='Vinegar', product_brand="Bragg", product_description="Vinegar", product_size="32oz"
product_size='32oz') )
self.assertEqual(str(item), "Bragg Vinegar 32oz") self.assertEqual(str(item), "Bragg Vinegar 32oz")
def test_add_event(self): def test_add_event(self):
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
user = model.User(username='barney') user = model.User(username="barney")
item = mod.OrderItem() item = mod.OrderItem()
self.assertEqual(item.events, []) self.assertEqual(item.events, [])
item.add_event(enum.ORDER_ITEM_EVENT_INITIATED, user) item.add_event(enum.ORDER_ITEM_EVENT_INITIATED, user)

View file

@ -20,9 +20,9 @@ class TestPendingProduct(DataTestCase):
product = mod.PendingProduct(size="32oz") product = mod.PendingProduct(size="32oz")
self.assertEqual(str(product), "32oz") self.assertEqual(str(product), "32oz")
product = mod.PendingProduct(brand_name="Bragg", product = mod.PendingProduct(
description="Vinegar", brand_name="Bragg", description="Vinegar", size="32oz"
size="32oz") )
self.assertEqual(str(product), "Bragg Vinegar 32oz") self.assertEqual(str(product), "Bragg Vinegar 32oz")
def test_full_description(self): def test_full_description(self):
@ -38,7 +38,7 @@ class TestPendingProduct(DataTestCase):
product = mod.PendingProduct(size="32oz") product = mod.PendingProduct(size="32oz")
self.assertEqual(product.full_description, "32oz") self.assertEqual(product.full_description, "32oz")
product = mod.PendingProduct(brand_name="Bragg", product = mod.PendingProduct(
description="Vinegar", brand_name="Bragg", description="Vinegar", size="32oz"
size="32oz") )
self.assertEqual(product.full_description, "Bragg Vinegar 32oz") self.assertEqual(product.full_description, "Bragg Vinegar 32oz")

View file

@ -13,5 +13,5 @@ class TestSideshowConfig(TestCase):
config = WuttaConfig(files=[]) config = WuttaConfig(files=[])
ext = mod.SideshowConfig() ext = mod.SideshowConfig()
ext.configure(config) ext.configure(config)
self.assertEqual(config.get('wutta.app_title'), "Sideshow") self.assertEqual(config.get("wutta.app_title"), "Sideshow")
self.assertEqual(config.get('wutta.app_dist'), "Sideshow") self.assertEqual(config.get("wutta.app_dist"), "Sideshow")

View file

@ -9,8 +9,8 @@ class TestOrderHandler(DataTestCase):
def make_config(self, **kwargs): def make_config(self, **kwargs):
config = super().make_config(**kwargs) config = super().make_config(**kwargs)
config.setdefault('wutta.model_spec', 'sideshow.db.model') config.setdefault("wutta.model_spec", "sideshow.db.model")
config.setdefault('wutta.enum_spec', 'sideshow.enum') config.setdefault("wutta.enum_spec", "sideshow.enum")
return config return config
def make_handler(self): def make_handler(self):
@ -23,7 +23,7 @@ class TestOrderHandler(DataTestCase):
self.assertFalse(handler.expose_store_id()) self.assertFalse(handler.expose_store_id())
# config can enable # config can enable
self.config.setdefault('sideshow.orders.expose_store_id', 'true') self.config.setdefault("sideshow.orders.expose_store_id", "true")
self.assertTrue(handler.expose_store_id()) self.assertTrue(handler.expose_store_id())
def test_get_order_qty_uom_text(self): def test_get_order_qty_uom_text(self):
@ -35,7 +35,9 @@ class TestOrderHandler(DataTestCase):
self.assertEqual(text, "2 Cases (x 12 = 24 Units)") self.assertEqual(text, "2 Cases (x 12 = 24 Units)")
# typical w/ html # typical w/ html
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_CASE, case_size=12, html=True) text = handler.get_order_qty_uom_text(
2, enum.ORDER_UOM_CASE, case_size=12, html=True
)
self.assertEqual(text, "2 Cases (&times; 12 = 24 Units)") self.assertEqual(text, "2 Cases (&times; 12 = 24 Units)")
# unknown case size # unknown case size
@ -55,20 +57,39 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# typical # typical
self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INITIATED)) self.assertIsNone(
handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INITIATED)
)
self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_READY)) self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_READY))
self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_PLACED)) self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_PLACED))
self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RECEIVED)) self.assertIsNone(
self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CONTACTED)) handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RECEIVED)
)
self.assertIsNone(
handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CONTACTED)
)
self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_PAID)) self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_PAID))
# warning # warning
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CANCELED), 'warning') self.assertEqual(
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUND_PENDING), 'warning') handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CANCELED), "warning"
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUNDED), 'warning') )
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RESTOCKED), 'warning') self.assertEqual(
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_EXPIRED), 'warning') handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUND_PENDING),
self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INACTIVE), 'warning') "warning",
)
self.assertEqual(
handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUNDED), "warning"
)
self.assertEqual(
handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RESTOCKED), "warning"
)
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): def test_resolve_pending_product(self):
model = self.app.model model = self.app.model
@ -76,65 +97,87 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
pending = model.PendingProduct(description='vinegar', unit_price_reg=5.99, pending = model.PendingProduct(
description="vinegar",
unit_price_reg=5.99,
status=enum.PendingProductStatus.PENDING, status=enum.PendingProductStatus.PENDING,
created_by=user) created_by=user,
)
self.session.add(pending) self.session.add(pending)
order = model.Order(order_id=100, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item = model.OrderItem(pending_product=pending, order_id=100, customer_name="Fred Flintstone", created_by=user
order_qty=1, order_uom=enum.ORDER_UOM_UNIT, )
status_code=enum.ORDER_ITEM_STATUS_READY) 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) order.items.append(item)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
info = { info = {
'product_id': '07430500132', "product_id": "07430500132",
'scancode': '07430500132', "scancode": "07430500132",
'brand_name': "Bragg's", "brand_name": "Bragg's",
'description': "Apple Cider Vinegar", "description": "Apple Cider Vinegar",
'size': "32oz", "size": "32oz",
'weighed': False, "weighed": False,
'department_id': None, "department_id": None,
'department_name': None, "department_name": None,
'special_order': False, "special_order": False,
'vendor_name': None, "vendor_name": None,
'vendor_item_code': None, "vendor_item_code": None,
'case_size': 12, "case_size": 12,
'unit_cost': 2.99, "unit_cost": 2.99,
'unit_price_reg': 5.99, "unit_price_reg": 5.99,
} }
# first try fails b/c pending status # first try fails b/c pending status
self.assertEqual(len(item.events), 0) self.assertEqual(len(item.events), 0)
self.assertRaises(ValueError, handler.resolve_pending_product, pending, info, user) self.assertRaises(
ValueError, handler.resolve_pending_product, pending, info, user
)
# resolves okay if ready status # resolves okay if ready status
pending.status = enum.PendingProductStatus.READY pending.status = enum.PendingProductStatus.READY
handler.resolve_pending_product(pending, info, user) handler.resolve_pending_product(pending, info, user)
self.assertEqual(len(item.events), 1) self.assertEqual(len(item.events), 1)
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED) self.assertEqual(
item.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED
)
self.assertIsNone(item.events[0].note) self.assertIsNone(item.events[0].note)
# more sample data # more sample data
pending2 = model.PendingProduct(description='vinegar', unit_price_reg=5.99, pending2 = model.PendingProduct(
description="vinegar",
unit_price_reg=5.99,
status=enum.PendingProductStatus.READY, status=enum.PendingProductStatus.READY,
created_by=user) created_by=user,
)
self.session.add(pending2) self.session.add(pending2)
order2 = model.Order(order_id=101, customer_name="Wilma Flintstone", created_by=user) order2 = model.Order(
item2 = model.OrderItem(pending_product=pending2, order_id=101, customer_name="Wilma Flintstone", created_by=user
order_qty=1, order_uom=enum.ORDER_UOM_UNIT, )
status_code=enum.ORDER_ITEM_STATUS_READY) 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) order2.items.append(item2)
self.session.add(order2) self.session.add(order2)
self.session.flush() self.session.flush()
# resolve with extra note # resolve with extra note
handler.resolve_pending_product(pending2, info, user, note='hello world') handler.resolve_pending_product(pending2, info, user, note="hello world")
self.assertEqual(len(item2.events), 2) self.assertEqual(len(item2.events), 2)
self.assertEqual(item2.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED) self.assertEqual(
item2.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED
)
self.assertIsNone(item2.events[0].note) self.assertIsNone(item2.events[0].note)
self.assertEqual(item2.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED) self.assertEqual(item2.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED)
self.assertEqual(item2.events[1].note, "hello world") self.assertEqual(item2.events[1].note, "hello world")
@ -145,17 +188,28 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_READY) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_READY,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_READY) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_READY,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_READY) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_READY,
)
order.items.append(item3) order.items.append(item3)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
@ -165,8 +219,9 @@ class TestOrderHandler(DataTestCase):
self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_READY) self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_READY)
self.assertEqual(len(item1.events), 0) self.assertEqual(len(item1.events), 0)
self.assertEqual(len(item2.events), 0) self.assertEqual(len(item2.events), 0)
handler.process_placement([item1, item2], user, handler.process_placement(
vendor_name="Acme Dist", po_number='ACME123') [item1, item2], user, vendor_name="Acme Dist", po_number="ACME123"
)
self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_PLACED)
self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_PLACED)
self.assertEqual(len(item1.events), 1) self.assertEqual(len(item1.events), 1)
@ -193,23 +248,40 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_PLACED) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_PLACED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_PLACED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item3) order.items.append(item3)
item4 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item4 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_PLACED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item4) order.items.append(item4)
item5 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item5 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_PLACED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item5) order.items.append(item5)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
@ -217,17 +289,26 @@ class TestOrderHandler(DataTestCase):
# all info provided # all info provided
self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_PLACED)
self.assertEqual(len(item1.events), 0) self.assertEqual(len(item1.events), 0)
handler.process_receiving([item1], user, vendor_name="Acme Dist", handler.process_receiving(
invoice_number='INV123', po_number='123') [item1],
user,
vendor_name="Acme Dist",
invoice_number="INV123",
po_number="123",
)
self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_RECEIVED)
self.assertEqual(len(item1.events), 1) self.assertEqual(len(item1.events), 1)
self.assertEqual(item1.events[0].note, "invoice INV123 (PO 123) from vendor Acme Dist") self.assertEqual(
item1.events[0].note, "invoice INV123 (PO 123) from vendor Acme Dist"
)
self.assertEqual(item1.events[0].type_code, enum.ORDER_ITEM_EVENT_RECEIVED) self.assertEqual(item1.events[0].type_code, enum.ORDER_ITEM_EVENT_RECEIVED)
# missing PO number # missing PO number
self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_PLACED)
self.assertEqual(len(item2.events), 0) self.assertEqual(len(item2.events), 0)
handler.process_receiving([item2], user, vendor_name="Acme Dist", invoice_number='INV123') handler.process_receiving(
[item2], user, vendor_name="Acme Dist", invoice_number="INV123"
)
self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_RECEIVED)
self.assertEqual(len(item2.events), 1) self.assertEqual(len(item2.events), 1)
self.assertEqual(item2.events[0].note, "invoice INV123 from vendor Acme Dist") self.assertEqual(item2.events[0].note, "invoice INV123 from vendor Acme Dist")
@ -236,7 +317,9 @@ class TestOrderHandler(DataTestCase):
# missing invoice number # missing invoice number
self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_PLACED)
self.assertEqual(len(item3.events), 0) self.assertEqual(len(item3.events), 0)
handler.process_receiving([item3], user, vendor_name="Acme Dist", po_number='123') handler.process_receiving(
[item3], user, vendor_name="Acme Dist", po_number="123"
)
self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_RECEIVED)
self.assertEqual(len(item3.events), 1) self.assertEqual(len(item3.events), 1)
self.assertEqual(item3.events[0].note, "PO 123 from vendor Acme Dist") self.assertEqual(item3.events[0].note, "PO 123 from vendor Acme Dist")
@ -268,17 +351,28 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_PLACED) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_PLACED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_PLACED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_PLACED,
)
order.items.append(item3) order.items.append(item3)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
@ -315,17 +409,28 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item3) order.items.append(item3)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
@ -362,17 +467,28 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item3) order.items.append(item3)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
@ -389,8 +505,12 @@ class TestOrderHandler(DataTestCase):
self.assertEqual(len(item2.events), 1) self.assertEqual(len(item2.events), 1)
self.assertIsNone(item1.events[0].note) self.assertIsNone(item1.events[0].note)
self.assertIsNone(item2.events[0].note) self.assertIsNone(item2.events[0].note)
self.assertEqual(item1.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED) self.assertEqual(
self.assertEqual(item2.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED) item1.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED
)
self.assertEqual(
item2.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED
)
# update last item, with extra note # update last item, with extra note
self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_RECEIVED)
@ -400,7 +520,9 @@ class TestOrderHandler(DataTestCase):
self.assertEqual(len(item3.events), 2) self.assertEqual(len(item3.events), 2)
self.assertIsNone(item3.events[0].note) self.assertIsNone(item3.events[0].note)
self.assertEqual(item3.events[1].note, "extra note") self.assertEqual(item3.events[1].note, "extra note")
self.assertEqual(item3.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED) self.assertEqual(
item3.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED
)
self.assertEqual(item3.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED) self.assertEqual(item3.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED)
def test_process_delivery(self): def test_process_delivery(self):
@ -409,17 +531,28 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item3) order.items.append(item3)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
@ -456,17 +589,28 @@ class TestOrderHandler(DataTestCase):
handler = self.make_handler() handler = self.make_handler()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) order = model.Order(
item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, order_id=42, customer_name="Fred Flintstone", created_by=user
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) )
item1 = model.OrderItem(
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item1) order.items.append(item1)
item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item2 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item2) order.items.append(item2)
item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, item3 = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_RECEIVED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_RECEIVED,
)
order.items.append(item3) order.items.append(item3)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()

View file

@ -17,10 +17,10 @@ class TestOrderRef(WebTestCase):
self.assertIsNot(sorted_query, query) self.assertIsNot(sorted_query, query)
def test_get_object_url(self): def test_get_object_url(self):
self.pyramid_config.add_route('orders.view', '/orders/{uuid}') self.pyramid_config.add_route("orders.view", "/orders/{uuid}")
model = self.app.model model = self.app.model
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, created_by=user) order = model.Order(order_id=42, created_by=user)
self.session.add(order) self.session.add(order)
@ -29,7 +29,7 @@ class TestOrderRef(WebTestCase):
typ = mod.OrderRef(self.request, session=self.session) typ = mod.OrderRef(self.request, session=self.session)
url = typ.get_object_url(order) url = typ.get_object_url(order)
self.assertIsNotNone(url) self.assertIsNotNone(url)
self.assertIn(f'/orders/{order.uuid}', url) self.assertIn(f"/orders/{order.uuid}", url)
class TestLocalCustomerRef(WebTestCase): class TestLocalCustomerRef(WebTestCase):
@ -43,7 +43,7 @@ class TestLocalCustomerRef(WebTestCase):
self.assertIsNot(sorted_query, query) self.assertIsNot(sorted_query, query)
def test_get_object_url(self): def test_get_object_url(self):
self.pyramid_config.add_route('local_customers.view', '/local/customers/{uuid}') self.pyramid_config.add_route("local_customers.view", "/local/customers/{uuid}")
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
@ -54,7 +54,7 @@ class TestLocalCustomerRef(WebTestCase):
typ = mod.LocalCustomerRef(self.request, session=self.session) typ = mod.LocalCustomerRef(self.request, session=self.session)
url = typ.get_object_url(customer) url = typ.get_object_url(customer)
self.assertIsNotNone(url) self.assertIsNotNone(url)
self.assertIn(f'/local/customers/{customer.uuid}', url) self.assertIn(f"/local/customers/{customer.uuid}", url)
class TestPendingCustomerRef(WebTestCase): class TestPendingCustomerRef(WebTestCase):
@ -68,21 +68,24 @@ class TestPendingCustomerRef(WebTestCase):
self.assertIsNot(sorted_query, query) self.assertIsNot(sorted_query, query)
def test_get_object_url(self): def test_get_object_url(self):
self.pyramid_config.add_route('pending_customers.view', '/pending/customers/{uuid}') self.pyramid_config.add_route(
"pending_customers.view", "/pending/customers/{uuid}"
)
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
self.session.commit() self.session.commit()
typ = mod.PendingCustomerRef(self.request, session=self.session) typ = mod.PendingCustomerRef(self.request, session=self.session)
url = typ.get_object_url(customer) url = typ.get_object_url(customer)
self.assertIsNotNone(url) self.assertIsNotNone(url)
self.assertIn(f'/pending/customers/{customer.uuid}', url) self.assertIn(f"/pending/customers/{customer.uuid}", url)
class TestLocalProductRef(WebTestCase): class TestLocalProductRef(WebTestCase):
@ -96,7 +99,7 @@ class TestLocalProductRef(WebTestCase):
self.assertIsNot(sorted_query, query) self.assertIsNot(sorted_query, query)
def test_get_object_url(self): def test_get_object_url(self):
self.pyramid_config.add_route('local_products.view', '/local/products/{uuid}') self.pyramid_config.add_route("local_products.view", "/local/products/{uuid}")
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
@ -107,7 +110,7 @@ class TestLocalProductRef(WebTestCase):
typ = mod.LocalProductRef(self.request, session=self.session) typ = mod.LocalProductRef(self.request, session=self.session)
url = typ.get_object_url(product) url = typ.get_object_url(product)
self.assertIsNotNone(url) self.assertIsNotNone(url)
self.assertIn(f'/local/products/{product.uuid}', url) self.assertIn(f"/local/products/{product.uuid}", url)
class TestPendingProductRef(WebTestCase): class TestPendingProductRef(WebTestCase):
@ -121,18 +124,21 @@ class TestPendingProductRef(WebTestCase):
self.assertIsNot(sorted_query, query) self.assertIsNot(sorted_query, query)
def test_get_object_url(self): def test_get_object_url(self):
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}') self.pyramid_config.add_route(
"pending_products.view", "/pending/products/{uuid}"
)
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
self.session.commit() self.session.commit()
typ = mod.PendingProductRef(self.request, session=self.session) typ = mod.PendingProductRef(self.request, session=self.session)
url = typ.get_object_url(product) url = typ.get_object_url(product)
self.assertIsNotNone(url) self.assertIsNotNone(url)
self.assertIn(f'/pending/products/{product.uuid}', url) self.assertIn(f"/pending/products/{product.uuid}", url)

View file

@ -13,7 +13,7 @@ from sideshow.web import app as mod
class TestMain(DataTestCase): class TestMain(DataTestCase):
def test_coverage(self): def test_coverage(self):
app = mod.main({}, **{'wutta_config': self.config}) app = mod.main({}, **{"wutta_config": self.config})
self.assertIsInstance(app, Router) self.assertIsInstance(app, Router)

View file

@ -9,12 +9,15 @@ class TestSideshowMenuHandler(WebTestCase):
def test_make_menus(self): def test_make_menus(self):
handler = mod.SideshowMenuHandler(self.config) handler = mod.SideshowMenuHandler(self.config)
menus = handler.make_menus(self.request) menus = handler.make_menus(self.request)
titles = [menu['title'] for menu in menus] titles = [menu["title"] for menu in menus]
self.assertEqual(titles, [ self.assertEqual(
'Orders', titles,
'Customers', [
'Products', "Orders",
'Batches', "Customers",
'Other', "Products",
'Admin', "Batches",
]) "Other",
"Admin",
],
)

View file

@ -33,16 +33,16 @@ class TestNewOrderBatchView(WebTestCase):
# store_id not exposed by default # store_id not exposed by default
grid = view.make_grid(model_class=model.NewOrderBatch) grid = view.make_grid(model_class=model.NewOrderBatch)
self.assertIn('store_id', grid.columns) self.assertIn("store_id", grid.columns)
view.configure_grid(grid) view.configure_grid(grid)
self.assertNotIn('store_id', grid.columns) self.assertNotIn("store_id", grid.columns)
# store_id is exposed if configured # store_id is exposed if configured
self.config.setdefault('sideshow.orders.expose_store_id', 'true') self.config.setdefault("sideshow.orders.expose_store_id", "true")
grid = view.make_grid(model_class=model.NewOrderBatch) grid = view.make_grid(model_class=model.NewOrderBatch)
self.assertIn('store_id', grid.columns) self.assertIn("store_id", grid.columns)
view.configure_grid(grid) view.configure_grid(grid)
self.assertIn('store_id', grid.columns) self.assertIn("store_id", grid.columns)
def test_configure_form(self): def test_configure_form(self):
model = self.app.model model = self.app.model
@ -50,74 +50,85 @@ class TestNewOrderBatchView(WebTestCase):
view = self.make_view() view = self.make_view()
handler = view.batch_handler handler = view.batch_handler
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
batch = handler.make_batch(self.session, pending_customer=customer, created_by=user) batch = handler.make_batch(
self.session, pending_customer=customer, created_by=user
)
self.session.add(batch) self.session.add(batch)
self.session.commit() self.session.commit()
# viewing # viewing
with patch.object(view, 'viewing', new=True): with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=batch) form = view.make_form(model_instance=batch)
view.configure_form(form) view.configure_form(form)
schema = form.get_schema() schema = form.get_schema()
self.assertIsInstance(schema['pending_customer'].typ, PendingCustomerRef) self.assertIsInstance(schema["pending_customer"].typ, PendingCustomerRef)
self.assertIsInstance(schema['total_price'].typ, WuttaMoney) self.assertIsInstance(schema["total_price"].typ, WuttaMoney)
# store_id not exposed by default # store_id not exposed by default
form = view.make_form(model_instance=batch) form = view.make_form(model_instance=batch)
self.assertIn('store_id', form) self.assertIn("store_id", form)
view.configure_form(form) view.configure_form(form)
self.assertNotIn('store_id', form) self.assertNotIn("store_id", form)
# store_id is exposed if configured # store_id is exposed if configured
self.config.setdefault('sideshow.orders.expose_store_id', 'true') self.config.setdefault("sideshow.orders.expose_store_id", "true")
form = view.make_form(model_instance=batch) form = view.make_form(model_instance=batch)
self.assertIn('store_id', form) self.assertIn("store_id", form)
view.configure_form(form) view.configure_form(form)
self.assertIn('store_id', form) self.assertIn("store_id", form)
def test_configure_row_grid(self): def test_configure_row_grid(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
grid = view.make_grid(model_class=model.NewOrderBatchRow) grid = view.make_grid(model_class=model.NewOrderBatchRow)
self.assertNotIn('total_price', grid.renderers) self.assertNotIn("total_price", grid.renderers)
view.configure_row_grid(grid) view.configure_row_grid(grid)
self.assertIn('total_price', grid.renderers) self.assertIn("total_price", grid.renderers)
def test_get_xref_buttons(self): def test_get_xref_buttons(self):
self.pyramid_config.add_route('orders.view', '/orders/{uuid}') self.pyramid_config.add_route("orders.view", "/orders/{uuid}")
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
handler = view.batch_handler handler = view.batch_handler
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
# 1st batch has no order # 1st batch has no order
batch = handler.make_batch(self.session, pending_customer=customer, created_by=user) batch = handler.make_batch(
self.session, pending_customer=customer, created_by=user
)
self.session.add(batch) self.session.add(batch)
self.session.flush() self.session.flush()
buttons = view.get_xref_buttons(batch) buttons = view.get_xref_buttons(batch)
self.assertEqual(len(buttons), 0) self.assertEqual(len(buttons), 0)
# 2nd batch is executed; has order # 2nd batch is executed; has order
batch = handler.make_batch(self.session, pending_customer=customer, created_by=user, batch = handler.make_batch(
executed=datetime.datetime.now(), executed_by=user) self.session,
pending_customer=customer,
created_by=user,
executed=datetime.datetime.now(),
executed_by=user,
)
self.session.add(batch) self.session.add(batch)
self.session.flush() self.session.flush()
order = model.Order(order_id=batch.id, created_by=user) order = model.Order(order_id=batch.id, created_by=user)
self.session.add(order) self.session.add(order)
self.session.flush() self.session.flush()
with patch.object(view, 'Session', return_value=self.session): with patch.object(view, "Session", return_value=self.session):
# nb. this also requires perm # nb. this also requires perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
buttons = view.get_xref_buttons(batch) buttons = view.get_xref_buttons(batch)
self.assertEqual(len(buttons), 1) self.assertEqual(len(buttons), 1)

View file

@ -19,11 +19,11 @@ class TestCommonView(WebTestCase):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
self.session.flush() self.session.flush()
self.assertEqual(len(user.roles), 0) self.assertEqual(len(user.roles), 0)
view.setup_enhance_admin_user(user) view.setup_enhance_admin_user(user)
self.assertEqual(len(user.roles), 1) self.assertEqual(len(user.roles), 1)
self.assertEqual(user.roles[0].name, 'Order Admin') self.assertEqual(user.roles[0].name, "Order Admin")

View file

@ -25,43 +25,43 @@ class TestLocalCustomerView(WebTestCase):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
grid = view.make_grid(model_class=model.LocalCustomer) grid = view.make_grid(model_class=model.LocalCustomer)
self.assertNotIn('full_name', grid.linked_columns) self.assertNotIn("full_name", grid.linked_columns)
view.configure_grid(grid) view.configure_grid(grid)
self.assertIn('full_name', grid.linked_columns) self.assertIn("full_name", grid.linked_columns)
def test_configure_form(self): def test_configure_form(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
# creating # creating
with patch.object(view, 'creating', new=True): with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.LocalCustomer) form = view.make_form(model_class=model.LocalCustomer)
view.configure_form(form) view.configure_form(form)
self.assertNotIn('external_id', form) self.assertNotIn("external_id", form)
self.assertNotIn('full_name', form) self.assertNotIn("full_name", form)
self.assertNotIn('orders', form) self.assertNotIn("orders", form)
self.assertNotIn('new_order_batches', form) self.assertNotIn("new_order_batches", form)
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.LocalCustomer() customer = model.LocalCustomer()
self.session.add(customer) self.session.add(customer)
self.session.commit() self.session.commit()
# viewing # viewing
with patch.object(view, 'viewing', new=True): with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=customer) form = view.make_form(model_instance=customer)
view.configure_form(form) view.configure_form(form)
self.assertIn('external_id', form) self.assertIn("external_id", form)
self.assertIn('full_name', form) self.assertIn("full_name", form)
self.assertIn('orders', form) self.assertIn("orders", form)
self.assertIn('new_order_batches', form) self.assertIn("new_order_batches", form)
def test_make_orders_grid(self): def test_make_orders_grid(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.LocalCustomer() customer = model.LocalCustomer()
self.session.add(customer) self.session.add(customer)
@ -74,21 +74,23 @@ class TestLocalCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_orders_grid(customer) grid = view.make_orders_grid(customer)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_make_new_order_batches_grid(self): def test_make_new_order_batches_grid(self):
model = self.app.model model = self.app.model
handler = NewOrderBatchHandler(self.config) handler = NewOrderBatchHandler(self.config)
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.LocalCustomer() customer = model.LocalCustomer()
self.session.add(customer) self.session.add(customer)
batch = handler.make_batch(self.session, local_customer=customer, created_by=user) batch = handler.make_batch(
self.session, local_customer=customer, created_by=user
)
self.session.add(batch) self.session.add(batch)
self.session.commit() self.session.commit()
@ -97,31 +99,36 @@ class TestLocalCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_new_order_batches_grid(customer) grid = view.make_new_order_batches_grid(customer)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_objectify(self): def test_objectify(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
self.session.commit() self.session.commit()
with patch.object(view, 'creating', new=True): with patch.object(view, "creating", new=True):
with patch.object(self.request, 'user', new=user): with patch.object(self.request, "user", new=user):
form = view.make_model_form() form = view.make_model_form()
with patch.object(form, 'validated', create=True, new={ with patch.object(
'first_name': 'Chuck', form,
'last_name': 'Norris', "validated",
}): create=True,
new={
"first_name": "Chuck",
"last_name": "Norris",
},
):
customer = view.objectify(form) customer = view.objectify(form)
self.assertIsInstance(customer, model.LocalCustomer) self.assertIsInstance(customer, model.LocalCustomer)
self.assertEqual(customer.first_name, 'Chuck') self.assertEqual(customer.first_name, "Chuck")
self.assertEqual(customer.last_name, 'Norris') self.assertEqual(customer.last_name, "Norris")
self.assertEqual(customer.full_name, 'Chuck Norris') self.assertEqual(customer.full_name, "Chuck Norris")
class TestPendingCustomerView(WebTestCase): class TestPendingCustomerView(WebTestCase):
@ -135,7 +142,7 @@ class TestPendingCustomerView(WebTestCase):
# nb. mostly just getting coverage here # nb. mostly just getting coverage here
grid = view.make_grid(model_class=model.PendingCustomer) grid = view.make_grid(model_class=model.PendingCustomer)
view.configure_grid(grid) view.configure_grid(grid)
self.assertIn('full_name', grid.linked_columns) self.assertIn("full_name", grid.linked_columns)
def test_configure_form(self): def test_configure_form(self):
model = self.app.model model = self.app.model
@ -143,41 +150,43 @@ class TestPendingCustomerView(WebTestCase):
view = self.make_view() view = self.make_view()
# creating # creating
with patch.object(view, 'creating', new=True): with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.PendingCustomer) form = view.make_form(model_class=model.PendingCustomer)
view.configure_form(form) view.configure_form(form)
self.assertNotIn('status', form) self.assertNotIn("status", form)
self.assertNotIn('created', form) self.assertNotIn("created", form)
self.assertNotIn('created_by', form) self.assertNotIn("created_by", form)
self.assertNotIn('orders', form) self.assertNotIn("orders", form)
self.assertNotIn('new_order_batches', form) self.assertNotIn("new_order_batches", form)
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
self.session.commit() self.session.commit()
# viewing # viewing
with patch.object(view, 'viewing', new=True): with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=customer) form = view.make_form(model_instance=customer)
view.configure_form(form) view.configure_form(form)
self.assertIn('status', form) self.assertIn("status", form)
self.assertIn('created', form) self.assertIn("created", form)
self.assertIn('created_by', form) self.assertIn("created_by", form)
self.assertIn('orders', form) self.assertIn("orders", form)
self.assertIn('new_order_batches', form) self.assertIn("new_order_batches", form)
def test_make_orders_grid(self): def test_make_orders_grid(self):
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
order = model.Order(order_id=42, pending_customer=customer, created_by=user) order = model.Order(order_id=42, pending_customer=customer, created_by=user)
self.session.add(order) self.session.add(order)
@ -188,10 +197,10 @@ class TestPendingCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_orders_grid(customer) grid = view.make_orders_grid(customer)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_make_new_order_batches_grid(self): def test_make_new_order_batches_grid(self):
model = self.app.model model = self.app.model
@ -199,12 +208,15 @@ class TestPendingCustomerView(WebTestCase):
handler = NewOrderBatchHandler(self.config) handler = NewOrderBatchHandler(self.config)
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
batch = handler.make_batch(self.session, pending_customer=customer, created_by=user) batch = handler.make_batch(
self.session, pending_customer=customer, created_by=user
)
self.session.add(batch) self.session.add(batch)
self.session.commit() self.session.commit()
@ -213,44 +225,54 @@ class TestPendingCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_new_order_batches_grid(customer) grid = view.make_new_order_batches_grid(customer)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_objectify(self): def test_objectify(self):
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
self.session.commit() self.session.commit()
with patch.object(view, 'creating', new=True): with patch.object(view, "creating", new=True):
with patch.object(self.request, 'user', new=user): with patch.object(self.request, "user", new=user):
form = view.make_model_form() form = view.make_model_form()
with patch.object(form, 'validated', create=True, new={ with patch.object(
'full_name': "Fred Flinstone", form,
}): "validated",
create=True,
new={
"full_name": "Fred Flinstone",
},
):
customer = view.objectify(form) customer = view.objectify(form)
self.assertIsInstance(customer, model.PendingCustomer) self.assertIsInstance(customer, model.PendingCustomer)
self.assertIs(customer.created_by, user) self.assertIs(customer.created_by, user)
self.assertEqual(customer.status, enum.PendingCustomerStatus.PENDING) self.assertEqual(
customer.status, enum.PendingCustomerStatus.PENDING
)
def test_delete_instance(self): def test_delete_instance(self):
self.pyramid_config.add_route('pending_customers.view', '/pending/customers/{uuid}') self.pyramid_config.add_route(
"pending_customers.view", "/pending/customers/{uuid}"
)
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
handler = NewOrderBatchHandler(self.config) handler = NewOrderBatchHandler(self.config)
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
# 1st customer is standalone, will be deleted # 1st customer is standalone, will be deleted
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
self.session.flush() self.session.flush()
self.assertEqual(self.session.query(model.PendingCustomer).count(), 1) self.assertEqual(self.session.query(model.PendingCustomer).count(), 1)
@ -259,10 +281,13 @@ class TestPendingCustomerView(WebTestCase):
self.assertEqual(self.session.query(model.PendingCustomer).count(), 0) self.assertEqual(self.session.query(model.PendingCustomer).count(), 0)
# 2nd customer is attached to new order batch, will not be deleted # 2nd customer is attached to new order batch, will not be deleted
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
batch = handler.make_batch(self.session, created_by=user, pending_customer=customer) batch = handler.make_batch(
self.session, created_by=user, pending_customer=customer
)
self.session.add(batch) self.session.add(batch)
self.session.flush() self.session.flush()
self.assertEqual(self.session.query(model.PendingCustomer).count(), 1) self.assertEqual(self.session.query(model.PendingCustomer).count(), 1)
@ -280,8 +305,9 @@ class TestPendingCustomerView(WebTestCase):
self.assertEqual(self.session.query(model.PendingCustomer).count(), 0) self.assertEqual(self.session.query(model.PendingCustomer).count(), 0)
# 3rd customer is attached to order, will not be deleted # 3rd customer is attached to order, will not be deleted
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, customer = model.PendingCustomer(
created_by=user) status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer) self.session.add(customer)
order = model.Order(order_id=42, created_by=user, pending_customer=customer) order = model.Order(order_id=42, created_by=user, pending_customer=customer)
self.session.add(order) self.session.add(order)

File diff suppressed because it is too large Load diff

View file

@ -25,53 +25,56 @@ class TestLocalProductView(WebTestCase):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
grid = view.make_grid(model_class=model.LocalProduct) grid = view.make_grid(model_class=model.LocalProduct)
self.assertNotIn('scancode', grid.linked_columns) self.assertNotIn("scancode", grid.linked_columns)
self.assertNotIn('brand_name', grid.linked_columns) self.assertNotIn("brand_name", grid.linked_columns)
self.assertNotIn('description', grid.linked_columns) self.assertNotIn("description", grid.linked_columns)
view.configure_grid(grid) view.configure_grid(grid)
self.assertIn('scancode', grid.linked_columns) self.assertIn("scancode", grid.linked_columns)
self.assertIn('brand_name', grid.linked_columns) self.assertIn("brand_name", grid.linked_columns)
self.assertIn('description', grid.linked_columns) self.assertIn("description", grid.linked_columns)
def test_configure_form(self): def test_configure_form(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
# creating # creating
with patch.object(view, 'creating', new=True): with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.LocalProduct) form = view.make_form(model_class=model.LocalProduct)
self.assertIn('external_id', form) self.assertIn("external_id", form)
view.configure_form(form) view.configure_form(form)
self.assertNotIn('external_id', form) self.assertNotIn("external_id", form)
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
product = model.LocalProduct() product = model.LocalProduct()
self.session.add(product) self.session.add(product)
self.session.commit() self.session.commit()
# viewing # viewing
with patch.object(view, 'viewing', new=True): with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=product) form = view.make_form(model_instance=product)
self.assertNotIn('external_id', form.readonly_fields) self.assertNotIn("external_id", form.readonly_fields)
self.assertNotIn('local_products.view.orders', form.grid_vue_context) self.assertNotIn("local_products.view.orders", form.grid_vue_context)
view.configure_form(form) view.configure_form(form)
self.assertIn('external_id', form.readonly_fields) self.assertIn("external_id", form.readonly_fields)
self.assertIn('local_products.view.orders', form.grid_vue_context) self.assertIn("local_products.view.orders", form.grid_vue_context)
def test_make_orders_grid(self): def test_make_orders_grid(self):
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_id=42, created_by=user) order = model.Order(order_id=42, customer_id=42, created_by=user)
product = model.LocalProduct() product = model.LocalProduct()
self.session.add(product) self.session.add(product)
item = model.OrderItem(local_product=product, item = model.OrderItem(
order_qty=1, order_uom=enum.ORDER_UOM_UNIT, local_product=product,
status_code=enum.ORDER_ITEM_STATUS_INITIATED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_INITIATED,
)
order.items.append(item) order.items.append(item)
self.session.add(order) self.session.add(order)
self.session.commit() self.session.commit()
@ -81,10 +84,10 @@ class TestLocalProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_orders_grid(product) grid = view.make_orders_grid(product)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_make_new_order_batches_grid(self): def test_make_new_order_batches_grid(self):
model = self.app.model model = self.app.model
@ -92,14 +95,15 @@ class TestLocalProductView(WebTestCase):
handler = NewOrderBatchHandler(self.config) handler = NewOrderBatchHandler(self.config)
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
batch = handler.make_batch(self.session, created_by=user) batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch) self.session.add(batch)
product = model.LocalProduct() product = model.LocalProduct()
self.session.add(product) self.session.add(product)
row = handler.make_row(local_product=product, row = handler.make_row(
order_qty=1, order_uom=enum.ORDER_UOM_UNIT) local_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT
)
handler.add_row(batch, row) handler.add_row(batch, row)
self.session.commit() self.session.commit()
@ -108,10 +112,10 @@ class TestLocalProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_new_order_batches_grid(product) grid = view.make_new_order_batches_grid(product)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
class TestPendingProductView(WebTestCase): class TestPendingProductView(WebTestCase):
@ -124,13 +128,13 @@ class TestPendingProductView(WebTestCase):
view = self.make_view() view = self.make_view()
# nb. mostly just getting coverage here # nb. mostly just getting coverage here
grid = view.make_grid(model_class=model.PendingProduct) grid = view.make_grid(model_class=model.PendingProduct)
self.assertNotIn('scancode', grid.linked_columns) self.assertNotIn("scancode", grid.linked_columns)
self.assertNotIn('brand_name', grid.linked_columns) self.assertNotIn("brand_name", grid.linked_columns)
self.assertNotIn('description', grid.linked_columns) self.assertNotIn("description", grid.linked_columns)
view.configure_grid(grid) view.configure_grid(grid)
self.assertIn('scancode', grid.linked_columns) self.assertIn("scancode", grid.linked_columns)
self.assertIn('brand_name', grid.linked_columns) self.assertIn("brand_name", grid.linked_columns)
self.assertIn('description', grid.linked_columns) self.assertIn("description", grid.linked_columns)
def test_grid_row_class(self): def test_grid_row_class(self):
enum = self.app.enum enum = self.app.enum
@ -143,7 +147,7 @@ class TestPendingProductView(WebTestCase):
# warning for ignored # warning for ignored
product.status = enum.PendingProductStatus.IGNORED product.status = enum.PendingProductStatus.IGNORED
self.assertEqual(view.grid_row_class(product, {}, 1), 'has-background-warning') self.assertEqual(view.grid_row_class(product, {}, 1), "has-background-warning")
def test_configure_form(self): def test_configure_form(self):
model = self.app.model model = self.app.model
@ -151,41 +155,46 @@ class TestPendingProductView(WebTestCase):
view = self.make_view() view = self.make_view()
# creating # creating
with patch.object(view, 'creating', new=True): with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.PendingProduct) form = view.make_form(model_class=model.PendingProduct)
view.configure_form(form) view.configure_form(form)
self.assertNotIn('created', form) self.assertNotIn("created", form)
self.assertNotIn('created_by', form) self.assertNotIn("created_by", form)
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
self.session.commit() self.session.commit()
# viewing # viewing
with patch.object(view, 'viewing', new=True): with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=product) form = view.make_form(model_instance=product)
view.configure_form(form) view.configure_form(form)
self.assertIn('status', form) self.assertIn("status", form)
self.assertIn('created', form) self.assertIn("created", form)
self.assertIn('created_by', form) self.assertIn("created_by", form)
def test_make_orders_grid(self): def test_make_orders_grid(self):
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
order = model.Order(order_id=42, customer_id=42, created_by=user) order = model.Order(order_id=42, customer_id=42, created_by=user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
item = model.OrderItem(pending_product=product, item = model.OrderItem(
order_qty=1, order_uom=enum.ORDER_UOM_UNIT, pending_product=product,
status_code=enum.ORDER_ITEM_STATUS_INITIATED) order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_INITIATED,
)
order.items.append(item) order.items.append(item)
self.session.add(order) self.session.add(order)
self.session.commit() self.session.commit()
@ -195,10 +204,10 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_orders_grid(product) grid = view.make_orders_grid(product)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_make_new_order_batches_grid(self): def test_make_new_order_batches_grid(self):
model = self.app.model model = self.app.model
@ -206,15 +215,17 @@ class TestPendingProductView(WebTestCase):
handler = NewOrderBatchHandler(self.config) handler = NewOrderBatchHandler(self.config)
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
batch = handler.make_batch(self.session, created_by=user) batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch) self.session.add(batch)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
row = handler.make_row(pending_product=product, row = handler.make_row(
order_qty=1, order_uom=enum.ORDER_UOM_UNIT) pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT
)
handler.add_row(batch, row) handler.add_row(batch, row)
self.session.commit() self.session.commit()
@ -223,57 +234,60 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0) self.assertEqual(len(grid.actions), 0)
# with view perm # with view perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
grid = view.make_new_order_batches_grid(product) grid = view.make_new_order_batches_grid(product)
self.assertEqual(len(grid.actions), 1) self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view') self.assertEqual(grid.actions[0].key, "view")
def test_get_template_context(self): def test_get_template_context(self):
enum = self.app.enum enum = self.app.enum
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING) product = model.PendingProduct(status=enum.PendingProductStatus.PENDING)
orig_context = {'instance': product} orig_context = {"instance": product}
# local setting omitted by default # local setting omitted by default
context = view.get_template_context(orig_context) context = view.get_template_context(orig_context)
self.assertNotIn('use_local_products', context) self.assertNotIn("use_local_products", context)
# still omitted even though 'viewing' # still omitted even though 'viewing'
with patch.object(view, 'viewing', new=True): with patch.object(view, "viewing", new=True):
context = view.get_template_context(orig_context) context = view.get_template_context(orig_context)
self.assertNotIn('use_local_products', context) self.assertNotIn("use_local_products", context)
# still omitted even though correct status # still omitted even though correct status
product.status = enum.PendingProductStatus.READY product.status = enum.PendingProductStatus.READY
context = view.get_template_context(orig_context) context = view.get_template_context(orig_context)
self.assertNotIn('use_local_products', context) self.assertNotIn("use_local_products", context)
# no longer omitted if user has perm # no longer omitted if user has perm
with patch.object(self.request, 'is_root', new=True): with patch.object(self.request, "is_root", new=True):
context = view.get_template_context(orig_context) context = view.get_template_context(orig_context)
self.assertIn('use_local_products', context) self.assertIn("use_local_products", context)
# nb. true by default # nb. true by default
self.assertTrue(context['use_local_products']) self.assertTrue(context["use_local_products"])
# accurately reflects config # accurately reflects config
self.config.setdefault('sideshow.orders.use_local_products', 'false') self.config.setdefault("sideshow.orders.use_local_products", "false")
context = view.get_template_context(orig_context) context = view.get_template_context(orig_context)
self.assertFalse(context['use_local_products']) self.assertFalse(context["use_local_products"])
def test_delete_instance(self): def test_delete_instance(self):
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}') self.pyramid_config.add_route(
"pending_products.view", "/pending/products/{uuid}"
)
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
handler = NewOrderBatchHandler(self.config) handler = NewOrderBatchHandler(self.config)
view = self.make_view() view = self.make_view()
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
# 1st product is standalone, will be deleted # 1st product is standalone, will be deleted
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
self.session.flush() self.session.flush()
self.assertEqual(self.session.query(model.PendingProduct).count(), 1) self.assertEqual(self.session.query(model.PendingProduct).count(), 1)
@ -284,11 +298,13 @@ class TestPendingProductView(WebTestCase):
# 2nd product is attached to new order batch, will not be deleted # 2nd product is attached to new order batch, will not be deleted
batch = handler.make_batch(self.session, created_by=user) batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch) self.session.add(batch)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
row = handler.make_row(pending_product=product, row = handler.make_row(
order_qty=1, order_uom=enum.ORDER_UOM_UNIT) pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT
)
handler.add_row(batch, row) handler.add_row(batch, row)
self.session.flush() self.session.flush()
self.assertEqual(self.session.query(model.PendingProduct).count(), 1) self.assertEqual(self.session.query(model.PendingProduct).count(), 1)
@ -306,61 +322,74 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(self.session.query(model.PendingProduct).count(), 0) self.assertEqual(self.session.query(model.PendingProduct).count(), 0)
def test_resolve(self): def test_resolve(self):
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}') self.pyramid_config.add_route(
"pending_products.view", "/pending/products/{uuid}"
)
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
self.session.flush() self.session.flush()
info = { info = {
'product_id': '07430500132', "product_id": "07430500132",
'scancode': '07430500132', "scancode": "07430500132",
'brand_name': "Bragg's", "brand_name": "Bragg's",
'description': "Apple Cider Vinegar", "description": "Apple Cider Vinegar",
'size': "32oz", "size": "32oz",
'weighed': False, "weighed": False,
'department_id': None, "department_id": None,
'department_name': None, "department_name": None,
'special_order': False, "special_order": False,
'vendor_name': None, "vendor_name": None,
'vendor_item_code': None, "vendor_item_code": None,
'case_size': 12, "case_size": 12,
'unit_cost': 2.99, "unit_cost": 2.99,
'unit_price_reg': 5.99, "unit_price_reg": 5.99,
} }
with patch.object(view, 'Session', return_value=self.session): with patch.object(view, "Session", return_value=self.session):
with patch.object(self.request, 'user', new=user): with patch.object(self.request, "user", new=user):
with patch.object(self.request, 'matchdict', new={'uuid': product.uuid}): with patch.object(
self.request, "matchdict", new={"uuid": product.uuid}
):
# flash error if wrong status # flash error if wrong status
result = view.resolve() result = view.resolve()
self.assertIsInstance(result, HTTPFound) self.assertIsInstance(result, HTTPFound)
self.assertTrue(self.request.session.peek_flash('error')) self.assertTrue(self.request.session.peek_flash("error"))
self.assertEqual(self.request.session.pop_flash('error'), self.assertEqual(
["pending product does not have 'ready' status!"]) self.request.session.pop_flash("error"),
["pending product does not have 'ready' status!"],
)
# flash error if product_id not specified # flash error if product_id not specified
product.status = enum.PendingProductStatus.READY product.status = enum.PendingProductStatus.READY
result = view.resolve() result = view.resolve()
self.assertIsInstance(result, HTTPFound) self.assertIsInstance(result, HTTPFound)
self.assertTrue(self.request.session.peek_flash('error')) self.assertTrue(self.request.session.peek_flash("error"))
self.assertEqual(self.request.session.pop_flash('error'), self.assertEqual(
["must specify valid product_id"]) self.request.session.pop_flash("error"),
["must specify valid product_id"],
)
# more sample data # more sample data
order = model.Order(order_id=100, created_by=user, order = model.Order(
customer_name="Fred Flintstone") 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, item = model.OrderItem(
status_code=enum.ORDER_ITEM_STATUS_READY) pending_product=product,
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_READY,
)
order.items.append(item) order.items.append(item)
self.session.add(order) self.session.add(order)
@ -369,45 +398,58 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(product.status, enum.PendingProductStatus.READY) self.assertEqual(product.status, enum.PendingProductStatus.READY)
self.assertIsNone(item.product_id) self.assertIsNone(item.product_id)
batch_handler = NewOrderBatchHandler(self.config) batch_handler = NewOrderBatchHandler(self.config)
with patch.object(batch_handler, 'get_product_info_external', with patch.object(
return_value=info): batch_handler, "get_product_info_external", return_value=info
with patch.object(self.app, 'get_batch_handler', ):
return_value=batch_handler): with patch.object(
with patch.object(self.request, 'POST', self.app, "get_batch_handler", return_value=batch_handler
new={'product_id': '07430500132'}): ):
with patch.object(batch_handler, 'get_product_info_external', with patch.object(
return_value=info): self.request, "POST", new={"product_id": "07430500132"}
):
with patch.object(
batch_handler,
"get_product_info_external",
return_value=info,
):
result = view.resolve() result = view.resolve()
self.assertIsInstance(result, HTTPFound) self.assertIsInstance(result, HTTPFound)
self.assertFalse(self.request.session.peek_flash('error')) self.assertFalse(self.request.session.peek_flash("error"))
self.assertEqual(product.product_id, '07430500132') self.assertEqual(product.product_id, "07430500132")
self.assertEqual(product.status, enum.PendingProductStatus.RESOLVED) self.assertEqual(product.status, enum.PendingProductStatus.RESOLVED)
self.assertEqual(item.product_id, '07430500132') self.assertEqual(item.product_id, "07430500132")
def test_ignore(self): def test_ignore(self):
self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}') self.pyramid_config.add_route(
"pending_products.view", "/pending/products/{uuid}"
)
model = self.app.model model = self.app.model
enum = self.app.enum enum = self.app.enum
view = self.make_view() view = self.make_view()
# sample data # sample data
user = model.User(username='barney') user = model.User(username="barney")
self.session.add(user) self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, product = model.PendingProduct(
created_by=user) status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product) self.session.add(product)
self.session.flush() self.session.flush()
with patch.object(view, 'Session', return_value=self.session): with patch.object(view, "Session", return_value=self.session):
with patch.object(self.request, 'user', new=user): with patch.object(self.request, "user", new=user):
with patch.object(self.request, 'matchdict', new={'uuid': product.uuid}): with patch.object(
self.request, "matchdict", new={"uuid": product.uuid}
):
# flash error if wrong status # flash error if wrong status
result = view.ignore() result = view.ignore()
self.assertIsInstance(result, HTTPFound) self.assertIsInstance(result, HTTPFound)
self.assertTrue(self.request.session.peek_flash('error')) self.assertTrue(self.request.session.peek_flash("error"))
self.assertEqual(self.request.session.pop_flash('error'), self.assertEqual(
["pending product does not have 'ready' status!"]) self.request.session.pop_flash("error"),
["pending product does not have 'ready' status!"],
)
# product updated # product updated
product.status = enum.PendingProductStatus.READY product.status = enum.PendingProductStatus.READY
@ -415,6 +457,6 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(product.status, enum.PendingProductStatus.READY) self.assertEqual(product.status, enum.PendingProductStatus.READY)
result = view.ignore() result = view.ignore()
self.assertIsInstance(result, HTTPFound) self.assertIsInstance(result, HTTPFound)
self.assertFalse(self.request.session.peek_flash('error')) self.assertFalse(self.request.session.peek_flash("error"))
self.assertIsNone(product.product_id) self.assertIsNone(product.product_id)
self.assertEqual(product.status, enum.PendingProductStatus.IGNORED) self.assertEqual(product.status, enum.PendingProductStatus.IGNORED)

View file

@ -23,11 +23,11 @@ class TestStoreView(WebTestCase):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
grid = view.make_grid(model_class=model.Store) grid = view.make_grid(model_class=model.Store)
self.assertNotIn('store_id', grid.linked_columns) self.assertNotIn("store_id", grid.linked_columns)
self.assertNotIn('name', grid.linked_columns) self.assertNotIn("name", grid.linked_columns)
view.configure_grid(grid) view.configure_grid(grid)
self.assertIn('store_id', grid.linked_columns) self.assertIn("store_id", grid.linked_columns)
self.assertIn('name', grid.linked_columns) self.assertIn("name", grid.linked_columns)
def test_grid_row_class(self): def test_grid_row_class(self):
model = self.app.model model = self.app.model
@ -39,7 +39,7 @@ class TestStoreView(WebTestCase):
store = model.Store(archived=True) store = model.Store(archived=True)
self.assertTrue(store.archived) self.assertTrue(store.archived)
self.assertEqual(view.grid_row_class(store, {}, 0), 'has-background-warning') self.assertEqual(view.grid_row_class(store, {}, 0), "has-background-warning")
def test_configure_form(self): def test_configure_form(self):
model = self.app.model model = self.app.model
@ -47,48 +47,48 @@ class TestStoreView(WebTestCase):
# unique validators are set # unique validators are set
form = view.make_form(model_class=model.Store) form = view.make_form(model_class=model.Store)
self.assertNotIn('store_id', form.validators) self.assertNotIn("store_id", form.validators)
self.assertNotIn('name', form.validators) self.assertNotIn("name", form.validators)
view.configure_form(form) view.configure_form(form)
self.assertIn('store_id', form.validators) self.assertIn("store_id", form.validators)
self.assertIn('name', form.validators) self.assertIn("name", form.validators)
def test_unique_store_id(self): def test_unique_store_id(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
store = model.Store(store_id='001', name='whatever') store = model.Store(store_id="001", name="whatever")
self.session.add(store) self.session.add(store)
self.session.commit() self.session.commit()
with patch.object(view, 'Session', return_value=self.session): with patch.object(view, "Session", return_value=self.session):
# invalid if same store_id in data # invalid if same store_id in data
node = colander.SchemaNode(colander.String(), name='store_id') node = colander.SchemaNode(colander.String(), name="store_id")
self.assertRaises(colander.Invalid, view.unique_store_id, node, '001') self.assertRaises(colander.Invalid, view.unique_store_id, node, "001")
# but not if store_id belongs to current store # but not if store_id belongs to current store
with patch.object(self.request, 'matchdict', new={'uuid': store.uuid}): with patch.object(self.request, "matchdict", new={"uuid": store.uuid}):
with patch.object(view, 'editing', new=True): with patch.object(view, "editing", new=True):
node = colander.SchemaNode(colander.String(), name='store_id') node = colander.SchemaNode(colander.String(), name="store_id")
self.assertIsNone(view.unique_store_id(node, '001')) self.assertIsNone(view.unique_store_id(node, "001"))
def test_unique_name(self): def test_unique_name(self):
model = self.app.model model = self.app.model
view = self.make_view() view = self.make_view()
store = model.Store(store_id='001', name='Acme Goods') store = model.Store(store_id="001", name="Acme Goods")
self.session.add(store) self.session.add(store)
self.session.commit() self.session.commit()
with patch.object(view, 'Session', return_value=self.session): with patch.object(view, "Session", return_value=self.session):
# invalid if same name in data # invalid if same name in data
node = colander.SchemaNode(colander.String(), name='name') node = colander.SchemaNode(colander.String(), name="name")
self.assertRaises(colander.Invalid, view.unique_name, node, 'Acme Goods') self.assertRaises(colander.Invalid, view.unique_name, node, "Acme Goods")
# but not if name belongs to current store # but not if name belongs to current store
with patch.object(self.request, 'matchdict', new={'uuid': store.uuid}): with patch.object(self.request, "matchdict", new={"uuid": store.uuid}):
with patch.object(view, 'editing', new=True): with patch.object(view, "editing", new=True):
node = colander.SchemaNode(colander.String(), name='name') node = colander.SchemaNode(colander.String(), name="name")
self.assertIsNone(view.unique_name(node, 'Acme Goods')) self.assertIsNone(view.unique_name(node, "Acme Goods"))