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

View file

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

View file

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

View file

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

View file

@ -31,15 +31,17 @@ from .base import sideshow_typer
@sideshow_typer.command()
def install(
ctx: typer.Context,
ctx: typer.Context,
):
"""
Install the Sideshow app
"""
config = ctx.parent.wutta_config
app = config.get_app()
install = app.get_install_handler(pkg_name='sideshow',
app_title="Sideshow",
pypi_name='Sideshow',
egg_name='Sideshow')
install = app.get_install_handler(
pkg_name="sideshow",
app_title="Sideshow",
pypi_name="Sideshow",
egg_name="Sideshow",
)
install.run()

View file

@ -33,26 +33,31 @@ class SideshowConfig(WuttaConfigExtension):
This establishes some config defaults specific to Sideshow.
"""
key = 'sideshow'
key = "sideshow"
def configure(self, config):
""" """
# app info
config.setdefault(f'{config.appname}.app_title', "Sideshow")
config.setdefault(f'{config.appname}.app_dist', "Sideshow")
config.setdefault(f"{config.appname}.app_title", "Sideshow")
config.setdefault(f"{config.appname}.app_dist", "Sideshow")
# app model, enum
config.setdefault(f'{config.appname}.model_spec', 'sideshow.db.model')
config.setdefault(f'{config.appname}.enum_spec', 'sideshow.enum')
config.setdefault(f"{config.appname}.model_spec", "sideshow.db.model")
config.setdefault(f"{config.appname}.enum_spec", "sideshow.enum")
# batch handlers
config.setdefault(f'{config.appname}.batch.neworder.handler.default_spec',
'sideshow.batch.neworder:NewOrderBatchHandler')
config.setdefault(
f"{config.appname}.batch.neworder.handler.default_spec",
"sideshow.batch.neworder:NewOrderBatchHandler",
)
# web app menu
config.setdefault(f'{config.appname}.web.menus.handler.default_spec',
'sideshow.web.menus:SideshowMenuHandler')
config.setdefault(
f"{config.appname}.web.menus.handler.default_spec",
"sideshow.web.menus:SideshowMenuHandler",
)
# 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
"""
from typing import Sequence, Union
from alembic import op
@ -13,8 +14,8 @@ import wuttjamaican.db.util
# revision identifiers, used by Alembic.
revision: str = '13af2ffbc0e0'
down_revision: Union[str, None] = 'a4273360d379'
revision: str = "13af2ffbc0e0"
down_revision: Union[str, None] = "a4273360d379"
branch_labels: 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:
# sideshow_batch_neworder_row
op.add_column('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))
op.add_column(
"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
op.add_column('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))
op.add_column(
"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:
# sideshow_order_item
op.drop_column('sideshow_order_item', 'vendor_item_code')
op.drop_column('sideshow_order_item', 'vendor_name')
op.drop_column("sideshow_order_item", "vendor_item_code")
op.drop_column("sideshow_order_item", "vendor_name")
# sideshow_batch_neworder_row
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_item_code")
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
"""
from typing import Sequence, Union
from alembic import op
@ -14,253 +15,364 @@ import wuttjamaican.db.util
# revision identifiers, used by Alembic.
revision: str = '7a6df83afbd4'
revision: str = "7a6df83afbd4"
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
def upgrade() -> None:
# enums
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus').create(op.get_bind())
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus').create(op.get_bind())
sa.Enum("PENDING", "READY", "RESOLVED", name="pendingcustomerstatus").create(
op.get_bind()
)
sa.Enum("PENDING", "READY", "RESOLVED", name="pendingproductstatus").create(
op.get_bind()
)
# sideshow_customer_pending
op.create_table('sideshow_customer_pending',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('customer_id', sa.String(length=20), nullable=True),
sa.Column('full_name', sa.String(length=100), nullable=True),
sa.Column('first_name', sa.String(length=50), nullable=True),
sa.Column('last_name', sa.String(length=50), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True),
sa.Column('status', postgresql.ENUM('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'))
)
op.create_table(
"sideshow_customer_pending",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("customer_id", sa.String(length=20), nullable=True),
sa.Column("full_name", sa.String(length=100), nullable=True),
sa.Column("first_name", sa.String(length=50), nullable=True),
sa.Column("last_name", sa.String(length=50), nullable=True),
sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column(
"status",
postgresql.ENUM(
"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
op.create_table('sideshow_customer_local',
sa.Column('full_name', sa.String(length=100), nullable=True),
sa.Column('first_name', sa.String(length=50), nullable=True),
sa.Column('last_name', sa.String(length=50), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True),
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('external_id', sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_customer_local'))
)
op.create_table(
"sideshow_customer_local",
sa.Column("full_name", sa.String(length=100), nullable=True),
sa.Column("first_name", sa.String(length=50), nullable=True),
sa.Column("last_name", sa.String(length=50), nullable=True),
sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("external_id", sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_customer_local")),
)
# sideshow_product_pending
op.create_table('sideshow_product_pending',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('product_id', sa.String(length=20), nullable=True),
sa.Column('scancode', sa.String(length=14), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True),
sa.Column('brand_name', sa.String(length=100), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('size', sa.String(length=30), nullable=True),
sa.Column('weighed', sa.Boolean(), nullable=True),
sa.Column('vendor_name', sa.String(length=50), 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('special_order', sa.Boolean(), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('status', postgresql.ENUM('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'))
)
op.create_table(
"sideshow_product_pending",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("product_id", sa.String(length=20), nullable=True),
sa.Column("scancode", sa.String(length=14), nullable=True),
sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column("brand_name", sa.String(length=100), nullable=True),
sa.Column("description", sa.String(length=255), nullable=True),
sa.Column("size", sa.String(length=30), nullable=True),
sa.Column("weighed", sa.Boolean(), nullable=True),
sa.Column("vendor_name", sa.String(length=50), 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("special_order", sa.Boolean(), nullable=True),
sa.Column("notes", sa.Text(), nullable=True),
sa.Column(
"status",
postgresql.ENUM(
"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
op.create_table('sideshow_product_local',
sa.Column('scancode', sa.String(length=14), nullable=True),
sa.Column('brand_name', sa.String(length=100), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('size', sa.String(length=30), nullable=True),
sa.Column('weighed', sa.Boolean(), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True),
sa.Column('vendor_name', sa.String(length=50), 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('notes', sa.Text(), nullable=True),
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('external_id', sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_product_local'))
)
op.create_table(
"sideshow_product_local",
sa.Column("scancode", sa.String(length=14), nullable=True),
sa.Column("brand_name", sa.String(length=100), nullable=True),
sa.Column("description", sa.String(length=255), nullable=True),
sa.Column("size", sa.String(length=30), nullable=True),
sa.Column("weighed", sa.Boolean(), nullable=True),
sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column("vendor_name", sa.String(length=50), 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("notes", sa.Text(), nullable=True),
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("external_id", sa.String(length=20), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_product_local")),
)
# sideshow_order
op.create_table('sideshow_order',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('order_id', sa.Integer(), nullable=False),
sa.Column('store_id', sa.String(length=10), nullable=True),
sa.Column('customer_id', sa.String(length=20), nullable=True),
sa.Column('local_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('pending_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('customer_name', sa.String(length=100), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True),
sa.Column('total_price', sa.Numeric(precision=10, scale=3), nullable=True),
sa.Column('created', sa.DateTime(timezone=True), nullable=False),
sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(['local_customer_uuid'], ['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'))
)
op.create_table(
"sideshow_order",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("order_id", sa.Integer(), nullable=False),
sa.Column("store_id", sa.String(length=10), nullable=True),
sa.Column("customer_id", sa.String(length=20), nullable=True),
sa.Column("local_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("pending_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("customer_name", sa.String(length=100), nullable=True),
sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column("total_price", sa.Numeric(precision=10, scale=3), nullable=True),
sa.Column("created", sa.DateTime(timezone=True), nullable=False),
sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.ForeignKeyConstraint(
["local_customer_uuid"],
["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
op.create_table('sideshow_order_item',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('order_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=False),
sa.Column('product_id', sa.String(length=20), nullable=True),
sa.Column('local_product_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('pending_product_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('product_scancode', sa.String(length=14), nullable=True),
sa.Column('product_brand', sa.String(length=100), nullable=True),
sa.Column('product_description', sa.String(length=255), nullable=True),
sa.Column('product_size', sa.String(length=30), nullable=True),
sa.Column('product_weighed', sa.Boolean(), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True),
sa.Column('case_size', sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('order_qty', sa.Numeric(precision=10, scale=4), nullable=False),
sa.Column('order_uom', sa.String(length=10), nullable=False),
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('unit_price_sale', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('sale_ends', sa.DateTime(timezone=True), nullable=True),
sa.Column('unit_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('case_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=True),
sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=False),
sa.Column('paid_amount', sa.Numeric(precision=8, scale=3), nullable=False),
sa.Column('payment_transaction_number', sa.String(length=20), nullable=True),
sa.ForeignKeyConstraint(['order_uuid'], ['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'))
)
op.create_table(
"sideshow_order_item",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("order_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("sequence", sa.Integer(), nullable=False),
sa.Column("product_id", sa.String(length=20), nullable=True),
sa.Column("local_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("pending_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("product_scancode", sa.String(length=14), nullable=True),
sa.Column("product_brand", sa.String(length=100), nullable=True),
sa.Column("product_description", sa.String(length=255), nullable=True),
sa.Column("product_size", sa.String(length=30), nullable=True),
sa.Column("product_weighed", sa.Boolean(), nullable=True),
sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column("case_size", sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column("order_qty", sa.Numeric(precision=10, scale=4), nullable=False),
sa.Column("order_uom", sa.String(length=10), nullable=False),
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("unit_price_sale", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("sale_ends", sa.DateTime(timezone=True), nullable=True),
sa.Column("unit_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("case_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("discount_percent", sa.Numeric(precision=5, scale=3), nullable=True),
sa.Column("total_price", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("status_code", sa.Integer(), nullable=False),
sa.Column("paid_amount", sa.Numeric(precision=8, scale=3), nullable=False),
sa.Column("payment_transaction_number", sa.String(length=20), nullable=True),
sa.ForeignKeyConstraint(
["order_uuid"],
["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
op.create_table('sideshow_order_item_event',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('item_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('type_code', sa.Integer(), nullable=False),
sa.Column('occurred', sa.DateTime(timezone=True), nullable=False),
sa.Column('actor_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('note', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['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'))
)
op.create_table(
"sideshow_order_item_event",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("item_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("type_code", sa.Integer(), nullable=False),
sa.Column("occurred", sa.DateTime(timezone=True), nullable=False),
sa.Column("actor_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("note", sa.Text(), nullable=True),
sa.ForeignKeyConstraint(
["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
op.create_table('sideshow_batch_neworder',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('row_count', sa.Integer(), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=True),
sa.Column('status_text', sa.String(length=255), nullable=True),
sa.Column('created', sa.DateTime(timezone=True), nullable=False),
sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('executed', sa.DateTime(timezone=True), nullable=True),
sa.Column('executed_by_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('store_id', sa.String(length=10), nullable=True),
sa.Column('customer_id', sa.String(length=20), nullable=True),
sa.Column('local_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('pending_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('customer_name', sa.String(length=100), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True),
sa.Column('email_address', sa.String(length=255), nullable=True),
sa.Column('total_price', sa.Numeric(precision=10, scale=3), nullable=True),
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], 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'))
)
op.create_table(
"sideshow_batch_neworder",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("description", sa.String(length=255), nullable=True),
sa.Column("notes", sa.Text(), nullable=True),
sa.Column("row_count", sa.Integer(), nullable=True),
sa.Column("status_code", sa.Integer(), nullable=True),
sa.Column("status_text", sa.String(length=255), nullable=True),
sa.Column("created", sa.DateTime(timezone=True), nullable=False),
sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("executed", sa.DateTime(timezone=True), nullable=True),
sa.Column("executed_by_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("store_id", sa.String(length=10), nullable=True),
sa.Column("customer_id", sa.String(length=20), nullable=True),
sa.Column("local_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("pending_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("customer_name", sa.String(length=100), nullable=True),
sa.Column("phone_number", sa.String(length=20), nullable=True),
sa.Column("email_address", sa.String(length=255), nullable=True),
sa.Column("total_price", sa.Numeric(precision=10, scale=3), nullable=True),
sa.ForeignKeyConstraint(
["created_by_uuid"],
["user.uuid"],
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
op.create_table('sideshow_batch_neworder_row',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('batch_uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=False),
sa.Column('status_text', sa.String(length=255), nullable=True),
sa.Column('modified', sa.DateTime(timezone=True), nullable=True),
sa.Column('product_id', sa.String(length=20), nullable=True),
sa.Column('local_product_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('pending_product_uuid', wuttjamaican.db.util.UUID(), nullable=True),
sa.Column('product_scancode', sa.String(length=14), nullable=True),
sa.Column('product_brand', sa.String(length=100), nullable=True),
sa.Column('product_description', sa.String(length=255), nullable=True),
sa.Column('product_size', sa.String(length=30), nullable=True),
sa.Column('product_weighed', sa.Boolean(), nullable=True),
sa.Column('department_id', sa.String(length=10), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True),
sa.Column('special_order', sa.Boolean(), nullable=True),
sa.Column('case_size', sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('order_qty', sa.Numeric(precision=10, scale=4), nullable=False),
sa.Column('order_uom', sa.String(length=10), nullable=False),
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('unit_price_sale', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('sale_ends', sa.DateTime(timezone=True), nullable=True),
sa.Column('unit_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('case_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=True),
sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['batch_uuid'], ['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'))
)
op.create_table(
"sideshow_batch_neworder_row",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("batch_uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("sequence", sa.Integer(), nullable=False),
sa.Column("status_text", sa.String(length=255), nullable=True),
sa.Column("modified", sa.DateTime(timezone=True), nullable=True),
sa.Column("product_id", sa.String(length=20), nullable=True),
sa.Column("local_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("pending_product_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("product_scancode", sa.String(length=14), nullable=True),
sa.Column("product_brand", sa.String(length=100), nullable=True),
sa.Column("product_description", sa.String(length=255), nullable=True),
sa.Column("product_size", sa.String(length=30), nullable=True),
sa.Column("product_weighed", sa.Boolean(), nullable=True),
sa.Column("department_id", sa.String(length=10), nullable=True),
sa.Column("department_name", sa.String(length=30), nullable=True),
sa.Column("special_order", sa.Boolean(), nullable=True),
sa.Column("case_size", sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column("order_qty", sa.Numeric(precision=10, scale=4), nullable=False),
sa.Column("order_uom", sa.String(length=10), nullable=False),
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("unit_price_sale", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("sale_ends", sa.DateTime(timezone=True), nullable=True),
sa.Column("unit_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("case_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("discount_percent", sa.Numeric(precision=5, scale=3), nullable=True),
sa.Column("total_price", sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column("status_code", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["batch_uuid"],
["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:
# sideshow_batch_neworder*
op.drop_table('sideshow_batch_neworder_row')
op.drop_table('sideshow_batch_neworder')
op.drop_table("sideshow_batch_neworder_row")
op.drop_table("sideshow_batch_neworder")
# sideshow_order_item_event
op.drop_table('sideshow_order_item_event')
op.drop_table("sideshow_order_item_event")
# sideshow_order_item
op.drop_table('sideshow_order_item')
op.drop_table("sideshow_order_item")
# sideshow_order
op.drop_table('sideshow_order')
op.drop_table("sideshow_order")
# sideshow_product_local
op.drop_table('sideshow_product_local')
op.drop_table("sideshow_product_local")
# sideshow_product_pending
op.drop_table('sideshow_product_pending')
op.drop_table("sideshow_product_pending")
# sideshow_customer_local
op.drop_table('sideshow_customer_local')
op.drop_table("sideshow_customer_local")
# sideshow_customer_pending
op.drop_table('sideshow_customer_pending')
op.drop_table("sideshow_customer_pending")
# enums
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus').drop(op.get_bind())
sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus').drop(op.get_bind())
sa.Enum("PENDING", "READY", "RESOLVED", name="pendingproductstatus").drop(
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
"""
from typing import Sequence, Union
from alembic import op
@ -13,8 +14,8 @@ import wuttjamaican.db.util
# revision identifiers, used by Alembic.
revision: str = 'a4273360d379'
down_revision: Union[str, None] = '7a6df83afbd4'
revision: str = "a4273360d379"
down_revision: Union[str, None] = "7a6df83afbd4"
branch_labels: 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:
# sideshow_store
op.create_table('sideshow_store',
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
sa.Column('store_id', sa.String(length=10), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('archived', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_store')),
sa.UniqueConstraint('store_id', name=op.f('uq_sideshow_store_store_id')),
sa.UniqueConstraint('name', name=op.f('uq_sideshow_store_name'))
)
op.create_table(
"sideshow_store",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("store_id", sa.String(length=10), nullable=False),
sa.Column("name", sa.String(length=100), nullable=False),
sa.Column("archived", sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_store")),
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:
# 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
"""
from typing import Sequence, Union
from alembic import op
@ -13,8 +14,8 @@ import wuttjamaican.db.util
from alembic_postgresql_enum import TableReference
# revision identifiers, used by Alembic.
revision: str = 'fd8a2527bd30'
down_revision: Union[str, None] = '13af2ffbc0e0'
revision: str = "fd8a2527bd30"
down_revision: Union[str, None] = "13af2ffbc0e0"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
@ -23,19 +24,31 @@ def upgrade() -> None:
# pendingcustomerstatus
op.sync_enum_values(
enum_schema='public',
enum_name='pendingcustomerstatus',
new_values=['PENDING', 'READY', 'RESOLVED', 'IGNORED'],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_customer_pending', column_name='status')],
enum_schema="public",
enum_name="pendingcustomerstatus",
new_values=["PENDING", "READY", "RESOLVED", "IGNORED"],
affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_customer_pending",
column_name="status",
)
],
enum_values_to_rename=[],
)
# pendingproductstatus
op.sync_enum_values(
enum_schema='public',
enum_name='pendingproductstatus',
new_values=['PENDING', 'READY', 'RESOLVED', 'IGNORED'],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_product_pending', column_name='status')],
enum_schema="public",
enum_name="pendingproductstatus",
new_values=["PENDING", "READY", "RESOLVED", "IGNORED"],
affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_product_pending",
column_name="status",
)
],
enum_values_to_rename=[],
)
@ -44,18 +57,30 @@ def downgrade() -> None:
# pendingproductstatus
op.sync_enum_values(
enum_schema='public',
enum_name='pendingproductstatus',
new_values=['PENDING', 'READY', 'RESOLVED'],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_product_pending', column_name='status')],
enum_schema="public",
enum_name="pendingproductstatus",
new_values=["PENDING", "READY", "RESOLVED"],
affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_product_pending",
column_name="status",
)
],
enum_values_to_rename=[],
)
# pendingcustomerstatus
op.sync_enum_values(
enum_schema='public',
enum_name='pendingcustomerstatus',
new_values=['PENDING', 'READY', 'RESOLVED'],
affected_columns=[TableReference(table_schema='public', table_name='sideshow_customer_pending', column_name='status')],
enum_schema="public",
enum_name="pendingcustomerstatus",
new_values=["PENDING", "READY", "RESOLVED"],
affected_columns=[
TableReference(
table_schema="public",
table_name="sideshow_customer_pending",
column_name="status",
)
],
enum_values_to_rename=[],
)

View file

@ -47,10 +47,11 @@ class NewOrderBatch(model.BatchMixin, model.Base):
Generic batch attributes (undocumented below) are inherited from
: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.
"""
@ -58,72 +59,102 @@ class NewOrderBatch(model.BatchMixin, model.Base):
@declared_attr
def __table_args__(cls):
return cls.__default_table_args__() + (
sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid']),
sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid']),
sa.ForeignKeyConstraint(
["local_customer_uuid"], ["sideshow_customer_local.uuid"]
),
sa.ForeignKeyConstraint(
["pending_customer_uuid"], ["sideshow_customer_pending.uuid"]
),
)
STATUS_OK = 1
STATUS_OK = 1
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.
""")
""",
)
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
order pertains, if applicable.
See also :attr:`local_customer` and :attr:`pending_customer`.
""")
""",
)
local_customer_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr
def local_customer(cls):
return orm.relationship(
'LocalCustomer',
back_populates='new_order_batches',
"LocalCustomer",
back_populates="new_order_batches",
doc="""
Reference to the
:class:`~sideshow.db.model.customers.LocalCustomer` record
for the order, if applicable.
See also :attr:`customer_id` and :attr:`pending_customer`.
""")
""",
)
pending_customer_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr
def pending_customer(cls):
return orm.relationship(
'PendingCustomer',
back_populates='new_order_batches',
"PendingCustomer",
back_populates="new_order_batches",
doc="""
Reference to the
:class:`~sideshow.db.model.customers.PendingCustomer`
record for the order, if applicable.
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
class NewOrderBatchRow(model.BatchRowMixin, model.Base):
@ -134,82 +165,96 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
Generic row attributes (undocumented below) are inherited from
:class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchRowMixin`.
"""
__tablename__ = 'sideshow_batch_neworder_row'
__tablename__ = "sideshow_batch_neworder_row"
__batch_class__ = NewOrderBatch
@declared_attr
def __table_args__(cls):
return cls.__default_table_args__() + (
sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid']),
sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid']),
sa.ForeignKeyConstraint(
["local_product_uuid"], ["sideshow_product_local.uuid"]
),
sa.ForeignKeyConstraint(
["pending_product_uuid"], ["sideshow_product_pending.uuid"]
),
)
STATUS_OK = 1
STATUS_OK = 1
"""
This is the default value for :attr:`status_code`. All rows are
considered "OK" if they have either a :attr:`product_id` or
:attr:`pending_product`.
"""
STATUS_MISSING_PRODUCT = 2
STATUS_MISSING_PRODUCT = 2
"""
Status code indicating the row has no :attr:`product_id` or
:attr:`pending_product` set.
"""
STATUS_MISSING_ORDER_QTY = 3
STATUS_MISSING_ORDER_QTY = 3
"""
Status code indicating the row has no :attr:`order_qty` and/or
:attr:`order_uom` set.
"""
STATUS = {
STATUS_OK : "ok",
STATUS_MISSING_PRODUCT : "missing product",
STATUS_MISSING_ORDER_QTY : "missing order qty/uom",
STATUS_OK: "ok",
STATUS_MISSING_PRODUCT: "missing product",
STATUS_MISSING_ORDER_QTY: "missing order qty/uom",
}
"""
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
represents, if applicable.
See also :attr:`local_product` and :attr:`pending_product`.
""")
""",
)
local_product_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr
def local_product(cls):
return orm.relationship(
'LocalProduct',
back_populates='new_order_batch_rows',
"LocalProduct",
back_populates="new_order_batch_rows",
doc="""
Reference to the
:class:`~sideshow.db.model.products.LocalProduct` record
for the order item, if applicable.
See also :attr:`product_id` and :attr:`pending_product`.
""")
""",
)
pending_product_uuid = sa.Column(model.UUID(), nullable=True)
@declared_attr
def pending_product(cls):
return orm.relationship(
'PendingProduct',
back_populates='new_order_batch_rows',
"PendingProduct",
back_populates="new_order_batch_rows",
doc="""
Reference to the
:class:`~sideshow.db.model.products.PendingProduct` record
for the order item, if applicable.
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.
.. note::
@ -221,61 +266,109 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
That may change eventually, depending on POS integration
scenarios that come up. Maybe a config option to declare
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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
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
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
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.
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.
This must be interpreted along with :attr:`order_uom` to determine
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.
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`
differently but :data:`~sideshow.enum.ORDER_UOM_UNIT` and others
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,
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,
: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
:attr:`unit_price_quoted` will typically match this value. See
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.
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
price, which is used to calculate :attr:`total_price`.
@ -317,21 +430,33 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
:attr:`unit_price_sale`.
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.
This is mostly for display purposes; :attr:`unit_price_quoted` is
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
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
for the order item.
@ -342,7 +467,8 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
* :attr:`order_uom`
* :attr:`case_size`
* :attr:`discount_percent`
""")
""",
)
def __str__(self):
return str(self.pending_product or self.product_description or "")

View file

@ -42,25 +42,45 @@ class CustomerMixin:
* :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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
def __str__(self):
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
this local customers table, for lookup next time.
"""
__tablename__ = 'sideshow_customer_local'
__tablename__ = "sideshow_customer_local"
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
applicable.
""")
""",
)
orders = orm.relationship(
'Order',
order_by='Order.order_id.desc()',
back_populates='local_customer',
"Order",
order_by="Order.order_id.desc()",
back_populates="local_customer",
cascade_backrefs=False,
doc="""
List of :class:`~sideshow.db.model.orders.Order` records
associated with this customer.
""")
""",
)
new_order_batches = orm.relationship(
'NewOrderBatch',
order_by='NewOrderBatch.id.desc()',
back_populates='local_customer',
"NewOrderBatch",
order_by="NewOrderBatch.id.desc()",
back_populates="local_customer",
cascade_backrefs=False,
doc="""
List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch`
records associated with this customer.
""")
""",
)
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
<local customer>` table, for lookup next time.
"""
__tablename__ = 'sideshow_customer_pending'
__tablename__ = "sideshow_customer_pending"
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
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.
""")
""",
)
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 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(
model.User,
cascade_backrefs=False,
@ -147,25 +187,28 @@ class PendingCustomer(CustomerMixin, model.Base):
Reference to the
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
created the customer record.
""")
""",
)
orders = orm.relationship(
'Order',
order_by='Order.order_id.desc()',
"Order",
order_by="Order.order_id.desc()",
cascade_backrefs=False,
back_populates='pending_customer',
back_populates="pending_customer",
doc="""
List of :class:`~sideshow.db.model.orders.Order` records
associated with this customer.
""")
""",
)
new_order_batches = orm.relationship(
'NewOrderBatch',
order_by='NewOrderBatch.id.desc()',
"NewOrderBatch",
order_by="NewOrderBatch.id.desc()",
cascade_backrefs=False,
back_populates='pending_customer',
back_populates="pending_customer",
doc="""
List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch`
records associated with this customer.
""")
""",
)

View file

@ -41,93 +41,134 @@ class Order(model.Base):
Usually, orders are created by way of a
: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
# showing the Orders grid for a PendingCustomer
__colanderalchemy_config__ = {
'excludes': ['items'],
"excludes": ["items"],
}
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.
When the order is created from New Order Batch, this order ID will
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.
""")
""",
)
store = orm.relationship(
'Store',
primaryjoin='Store.store_id == Order.store_id',
foreign_keys='Order.store_id',
"Store",
primaryjoin="Store.store_id == Order.store_id",
foreign_keys="Order.store_id",
doc="""
Reference to the :class:`~sideshow.db.model.stores.Store`
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
order pertains, if applicable.
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(
'LocalCustomer',
"LocalCustomer",
cascade_backrefs=False,
back_populates='orders',
back_populates="orders",
doc="""
Reference to the
:class:`~sideshow.db.model.customers.LocalCustomer` record
for the order, if applicable.
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(
'PendingCustomer',
"PendingCustomer",
cascade_backrefs=False,
back_populates='orders',
back_populates="orders",
doc="""
Reference to the
:class:`~sideshow.db.model.customers.PendingCustomer` record
for the order, if applicable.
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
If the order is created via New Order Batch, this will match the
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(
model.User,
cascade_backrefs=False,
@ -135,17 +176,19 @@ class Order(model.Base):
Reference to the
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
created the order.
""")
""",
)
items = orm.relationship(
'OrderItem',
collection_class=ordering_list('sequence', count_from=1),
cascade='all, delete-orphan',
"OrderItem",
collection_class=ordering_list("sequence", count_from=1),
cascade="all, delete-orphan",
cascade_backrefs=False,
back_populates='order',
back_populates="order",
doc="""
List of :class:`OrderItem` records belonging to the order.
""")
""",
)
def __str__(self):
return str(self.order_id)
@ -159,58 +202,77 @@ class OrderItem(model.Base):
:class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
records.
"""
__tablename__ = 'sideshow_order_item'
__tablename__ = "sideshow_order_item"
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,
cascade_backrefs=False,
back_populates='items',
back_populates="items",
doc="""
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
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
represents, if applicable.
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(
'LocalProduct',
"LocalProduct",
cascade_backrefs=False,
back_populates='order_items',
back_populates="order_items",
doc="""
Reference to the
:class:`~sideshow.db.model.products.LocalProduct` record for
the order item, if applicable.
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(
'PendingProduct',
"PendingProduct",
cascade_backrefs=False,
back_populates='order_items',
back_populates="order_items",
doc="""
Reference to the
:class:`~sideshow.db.model.products.PendingProduct` record for
the order item, if applicable.
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.
.. note::
@ -222,109 +284,189 @@ class OrderItem(model.Base):
That may change eventually, depending on POS integration
scenarios that come up. Maybe a config option to declare
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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
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
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
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.
""")
""",
)
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.
This must be interpreted along with :attr:`order_uom` to determine
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.
This should be one of the codes from
: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,
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,
: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
:attr:`unit_price_quoted` will typically match this value. See
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.
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
price, which is used to calculate :attr:`total_price`.
This price does *not* reflect the :attr:`discount_percent`. It
normally should match either :attr:`unit_price_reg` or
: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.
This is mostly for display purposes; :attr:`unit_price_quoted` is
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
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
for the order item.
@ -335,41 +477,57 @@ class OrderItem(model.Base):
* :attr:`order_uom`
* :attr:`case_size`
* :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.
""")
""",
)
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`
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
applicable/known.
""")
""",
)
events = orm.relationship(
'OrderItemEvent',
order_by='OrderItemEvent.occurred, OrderItemEvent.uuid',
cascade='all, delete-orphan',
"OrderItemEvent",
order_by="OrderItemEvent.occurred, OrderItemEvent.uuid",
cascade="all, delete-orphan",
cascade_backrefs=False,
back_populates='item',
back_populates="item",
doc="""
List of :class:`OrderItemEvent` records for the item.
""")
""",
)
@property
def full_description(self):
""" """
fields = [
self.product_brand or '',
self.product_description or '',
self.product_size or '']
self.product_brand or "",
self.product_description or "",
self.product_size or "",
]
fields = [f.strip() for f in fields if f.strip()]
return ' '.join(fields)
return " ".join(fields)
def __str__(self):
return self.full_description
@ -379,8 +537,8 @@ class OrderItem(model.Base):
Convenience method to add a new :class:`OrderItemEvent` for
the item.
"""
kwargs['type_code'] = type_code
kwargs['actor'] = user
kwargs["type_code"] = type_code
kwargs["actor"] = user
self.events.append(OrderItemEvent(**kwargs))
@ -388,37 +546,53 @@ class OrderItemEvent(model.Base):
"""
An event in the life of an :term:`order item`.
"""
__tablename__ = 'sideshow_order_item_event'
__tablename__ = "sideshow_order_item_event"
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(
OrderItem,
cascade_backrefs=False,
back_populates='events',
back_populates="events",
doc="""
Reference to the :class:`OrderItem` to which the event
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
: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.
""")
""",
)
actor_uuid = model.uuid_fk_column('user.uuid', nullable=False)
actor_uuid = model.uuid_fk_column("user.uuid", nullable=False)
actor = orm.relationship(
model.User,
doc="""
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
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.
""")
""",
)

View file

@ -42,7 +42,10 @@ class ProductMixin:
* :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.
.. note::
@ -54,73 +57,123 @@ class ProductMixin:
That may change eventually, depending on POS integration
scenarios that come up. Maybe a config option to declare
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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.
""")
""",
)
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
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
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
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.
""")
""",
)
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,
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.
""")
""",
)
notes = sa.Column(sa.Text(), nullable=True, doc="""
notes = sa.Column(
sa.Text(),
nullable=True,
doc="""
Arbitrary notes regarding the product, if applicable.
""")
""",
)
@property
def full_description(self):
""" """
fields = [
self.brand_name or '',
self.description or '',
self.size or '']
fields = [self.brand_name or "", self.description or "", self.size or ""]
fields = [f.strip() for f in fields if f.strip()]
return ' '.join(fields)
return " ".join(fields)
def __str__(self):
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
next time.
"""
__tablename__ = 'sideshow_product_local'
__tablename__ = "sideshow_product_local"
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
applicable.
""")
""",
)
order_items = orm.relationship(
'OrderItem',
back_populates='local_product',
"OrderItem",
back_populates="local_product",
cascade_backrefs=False,
doc="""
List of :class:`~sideshow.db.model.orders.OrderItem` records
associated with this product.
""")
""",
)
new_order_batch_rows = orm.relationship(
'NewOrderBatchRow',
back_populates='local_product',
"NewOrderBatchRow",
back_populates="local_product",
cascade_backrefs=False,
doc="""
List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
records associated with this product.
""")
""",
)
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
products <local product>` table, for lookup next time.
"""
__tablename__ = 'sideshow_product_pending'
__tablename__ = "sideshow_product_pending"
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
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.
""")
""",
)
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 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(
model.User,
cascade_backrefs=False,
@ -206,23 +279,26 @@ class PendingProduct(ProductMixin, model.Base):
Reference to the
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
created the product record.
""")
""",
)
order_items = orm.relationship(
'OrderItem',
back_populates='pending_product',
"OrderItem",
back_populates="pending_product",
cascade_backrefs=False,
doc="""
List of :class:`~sideshow.db.model.orders.OrderItem` records
associated with this product.
""")
""",
)
new_order_batch_rows = orm.relationship(
'NewOrderBatchRow',
back_populates='pending_product',
"NewOrderBatchRow",
back_populates="pending_product",
cascade_backrefs=False,
doc="""
List of
:class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
records associated with this product.
""")
""",
)

View file

@ -33,22 +33,38 @@ class Store(model.Base):
"""
Represents a physical location for the business.
"""
__tablename__ = 'sideshow_store'
__tablename__ = "sideshow_store"
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.
""")
""",
)
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!).
""")
""",
)
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
hidden from view.
""")
""",
)
def __str__(self):
return self.get_display()
@ -57,6 +73,6 @@ class Store(model.Base):
"""
Returns the display string for the store, e.g. "001 Acme Goods".
"""
return ' '.join([(self.store_id or '').strip(),
(self.name or '').strip()])\
.strip()
return " ".join(
[(self.store_id or "").strip(), (self.name or "").strip()]
).strip()

View file

@ -30,7 +30,7 @@ from collections import OrderedDict
from wuttjamaican.enum import *
ORDER_UOM_CASE = 'CS'
ORDER_UOM_CASE = "CS"
"""
UOM code for ordering a "case" of product.
@ -38,7 +38,7 @@ Sideshow will treat "case" orders somewhat differently as compared to
"unit" orders.
"""
ORDER_UOM_UNIT = 'EA'
ORDER_UOM_UNIT = "EA"
"""
UOM code for ordering a "unit" of product.
@ -47,7 +47,7 @@ the same by Sideshow, whereas "case" orders are treated somewhat
differently.
"""
ORDER_UOM_KILOGRAM = 'KG'
ORDER_UOM_KILOGRAM = "KG"
"""
UOM code for ordering a "kilogram" of product.
@ -57,7 +57,7 @@ e.g. :attr:`~sideshow.db.model.orders.OrderItem.product_weighed` is
true.
"""
ORDER_UOM_POUND = 'LB'
ORDER_UOM_POUND = "LB"
"""
UOM code for ordering a "pound" of product.
@ -67,12 +67,14 @@ e.g. :attr:`~sideshow.db.model.orders.OrderItem.product_weighed` is
true.
"""
ORDER_UOM = OrderedDict([
(ORDER_UOM_CASE, "Cases"),
(ORDER_UOM_UNIT, "Units"),
(ORDER_UOM_KILOGRAM, "Kilograms"),
(ORDER_UOM_POUND, "Pounds"),
])
ORDER_UOM = OrderedDict(
[
(ORDER_UOM_CASE, "Cases"),
(ORDER_UOM_UNIT, "Units"),
(ORDER_UOM_KILOGRAM, "Kilograms"),
(ORDER_UOM_POUND, "Pounds"),
]
)
"""
Dict of possible code -> label options for ordering unit of measure.
@ -88,10 +90,11 @@ class PendingCustomerStatus(Enum):
Enum values for
:attr:`sideshow.db.model.customers.PendingCustomer.status`.
"""
PENDING = 'pending'
READY = 'ready'
RESOLVED = 'resolved'
IGNORED = 'ignored'
PENDING = "pending"
READY = "ready"
RESOLVED = "resolved"
IGNORED = "ignored"
class PendingProductStatus(Enum):
@ -99,29 +102,30 @@ class PendingProductStatus(Enum):
Enum values for
:attr:`sideshow.db.model.products.PendingProduct.status`.
"""
PENDING = 'pending'
READY = 'ready'
RESOLVED = 'resolved'
IGNORED = 'ignored'
PENDING = "pending"
READY = "ready"
RESOLVED = "resolved"
IGNORED = "ignored"
########################################
# Order Item Status
########################################
ORDER_ITEM_STATUS_UNINITIATED = 1
ORDER_ITEM_STATUS_UNINITIATED = 1
"""
Indicates the item is "not yet initiated" - this probably is not
useful but exists as a possibility just in case.
"""
ORDER_ITEM_STATUS_INITIATED = 10
ORDER_ITEM_STATUS_INITIATED = 10
"""
Indicates the item is "initiated" (aka. created) but not yet "ready"
for buyer/PO. This may imply the price needs confirmation etc.
"""
ORDER_ITEM_STATUS_PAID_BEFORE = 50
ORDER_ITEM_STATUS_PAID_BEFORE = 50
"""
Indicates the customer has fully paid for the item, up-front before
the buyer places PO etc. It implies the item is not yet "ready" for
@ -129,101 +133,103 @@ some reason.
"""
# TODO: deprecate / remove this one
ORDER_ITEM_STATUS_PAID = ORDER_ITEM_STATUS_PAID_BEFORE
ORDER_ITEM_STATUS_PAID = ORDER_ITEM_STATUS_PAID_BEFORE
ORDER_ITEM_STATUS_READY = 100
ORDER_ITEM_STATUS_READY = 100
"""
Indicates the item is "ready" for buyer to include it on a vendor
purchase order.
"""
ORDER_ITEM_STATUS_PLACED = 200
ORDER_ITEM_STATUS_PLACED = 200
"""
Indicates the buyer has placed a vendor purchase order which includes
this item. The item is thereby "on order" until the truck arrives.
"""
ORDER_ITEM_STATUS_RECEIVED = 300
ORDER_ITEM_STATUS_RECEIVED = 300
"""
Indicates the item has been received as part of a vendor delivery.
The item is thereby "on hand" until customer comes in for pickup.
"""
ORDER_ITEM_STATUS_CONTACTED = 350
ORDER_ITEM_STATUS_CONTACTED = 350
"""
Indicates the customer has been notified that the item is "on hand"
and awaiting their pickup.
"""
ORDER_ITEM_STATUS_CONTACT_FAILED = 375
ORDER_ITEM_STATUS_CONTACT_FAILED = 375
"""
Indicates the attempt(s) to notify customer have failed. The item is
on hand but the customer does not know to pickup.
"""
ORDER_ITEM_STATUS_DELIVERED = 500
ORDER_ITEM_STATUS_DELIVERED = 500
"""
Indicates the customer has picked up the item.
"""
ORDER_ITEM_STATUS_PAID_AFTER = 550
ORDER_ITEM_STATUS_PAID_AFTER = 550
"""
Indicates the customer has fully paid for the item, as part of their
pickup. This completes the cycle for orders which require payment on
the tail end.
"""
ORDER_ITEM_STATUS_CANCELED = 900
ORDER_ITEM_STATUS_CANCELED = 900
"""
Indicates the order item has been canceled.
"""
ORDER_ITEM_STATUS_REFUND_PENDING = 910
ORDER_ITEM_STATUS_REFUND_PENDING = 910
"""
Indicates the order item has been canceled, and the customer is due a
(pending) refund.
"""
ORDER_ITEM_STATUS_REFUNDED = 920
ORDER_ITEM_STATUS_REFUNDED = 920
"""
Indicates the order item has been canceled, and the customer has been
given a refund.
"""
ORDER_ITEM_STATUS_RESTOCKED = 930
ORDER_ITEM_STATUS_RESTOCKED = 930
"""
Indicates the product has been restocked, e.g. after the order item
was canceled.
"""
ORDER_ITEM_STATUS_EXPIRED = 940
ORDER_ITEM_STATUS_EXPIRED = 940
"""
Indicates the order item and/or product has expired.
"""
ORDER_ITEM_STATUS_INACTIVE = 950
ORDER_ITEM_STATUS_INACTIVE = 950
"""
Indicates the order item has become inactive.
"""
ORDER_ITEM_STATUS = OrderedDict([
(ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"),
(ORDER_ITEM_STATUS_INITIATED, "initiated"),
(ORDER_ITEM_STATUS_PAID_BEFORE, "paid"),
(ORDER_ITEM_STATUS_READY, "ready"),
(ORDER_ITEM_STATUS_PLACED, "placed"),
(ORDER_ITEM_STATUS_RECEIVED, "received"),
(ORDER_ITEM_STATUS_CONTACTED, "contacted"),
(ORDER_ITEM_STATUS_CONTACT_FAILED, "contact failed"),
(ORDER_ITEM_STATUS_DELIVERED, "delivered"),
(ORDER_ITEM_STATUS_PAID_AFTER, "paid"),
(ORDER_ITEM_STATUS_CANCELED, "canceled"),
(ORDER_ITEM_STATUS_REFUND_PENDING, "refund pending"),
(ORDER_ITEM_STATUS_REFUNDED, "refunded"),
(ORDER_ITEM_STATUS_RESTOCKED, "restocked"),
(ORDER_ITEM_STATUS_EXPIRED, "expired"),
(ORDER_ITEM_STATUS_INACTIVE, "inactive"),
])
ORDER_ITEM_STATUS = OrderedDict(
[
(ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"),
(ORDER_ITEM_STATUS_INITIATED, "initiated"),
(ORDER_ITEM_STATUS_PAID_BEFORE, "paid"),
(ORDER_ITEM_STATUS_READY, "ready"),
(ORDER_ITEM_STATUS_PLACED, "placed"),
(ORDER_ITEM_STATUS_RECEIVED, "received"),
(ORDER_ITEM_STATUS_CONTACTED, "contacted"),
(ORDER_ITEM_STATUS_CONTACT_FAILED, "contact failed"),
(ORDER_ITEM_STATUS_DELIVERED, "delivered"),
(ORDER_ITEM_STATUS_PAID_AFTER, "paid"),
(ORDER_ITEM_STATUS_CANCELED, "canceled"),
(ORDER_ITEM_STATUS_REFUND_PENDING, "refund pending"),
(ORDER_ITEM_STATUS_REFUNDED, "refunded"),
(ORDER_ITEM_STATUS_RESTOCKED, "restocked"),
(ORDER_ITEM_STATUS_EXPIRED, "expired"),
(ORDER_ITEM_STATUS_INACTIVE, "inactive"),
]
)
"""
Dict of possible code -> label options for :term:`order item` status.
@ -237,19 +243,19 @@ These codes are referenced by:
# Order Item Event Type
########################################
ORDER_ITEM_EVENT_INITIATED = 10
ORDER_ITEM_EVENT_INITIATED = 10
"""
Indicates the item was "initiated" - this occurs when the
:term:`order` is first created.
"""
ORDER_ITEM_EVENT_PRICE_CONFIRMED = 20
ORDER_ITEM_EVENT_PRICE_CONFIRMED = 20
"""
Indicates the item's price was confirmed by a user who is authorized
to do that.
"""
ORDER_ITEM_EVENT_PAYMENT_RECEIVED = 50
ORDER_ITEM_EVENT_PAYMENT_RECEIVED = 50
"""
Indicates payment was received for the item. This may occur toward
the beginning, or toward the end, of the item's life cycle depending
@ -257,9 +263,9 @@ on app configuration etc.
"""
# TODO: deprecate / remove this
ORDER_ITEM_EVENT_PAID = ORDER_ITEM_EVENT_PAYMENT_RECEIVED
ORDER_ITEM_EVENT_PAID = ORDER_ITEM_EVENT_PAYMENT_RECEIVED
ORDER_ITEM_EVENT_READY = 100
ORDER_ITEM_EVENT_READY = 100
"""
Indicates the item has become "ready" for buyer placement on a new
vendor purchase order. Often this will occur when the :term:`order`
@ -267,128 +273,130 @@ is first created, if the data is suitable. However this may be
delayed if e.g. the price needs confirmation.
"""
ORDER_ITEM_EVENT_CUSTOMER_RESOLVED = 120
ORDER_ITEM_EVENT_CUSTOMER_RESOLVED = 120
"""
Indicates the customer for the :term:`order` has been assigned to a
"proper" (existing) account. This may happen (after the fact) if the
order was first created with a new/unknown customer.
"""
ORDER_ITEM_EVENT_PRODUCT_RESOLVED = 140
ORDER_ITEM_EVENT_PRODUCT_RESOLVED = 140
"""
Indicates the product for the :term:`order item` has been assigned to
a "proper" (existing) product record. This may happen (after the
fact) if the order was first created with a new/unknown product.
"""
ORDER_ITEM_EVENT_PLACED = 200
ORDER_ITEM_EVENT_PLACED = 200
"""
Indicates the buyer has placed a vendor purchase order which includes
this item. So the item is "on order" until the truck arrives.
"""
ORDER_ITEM_EVENT_REORDER = 210
ORDER_ITEM_EVENT_REORDER = 210
"""
Indicates the item was not received with the delivery on which it was
expected, and must be re-ordered from vendor.
"""
ORDER_ITEM_EVENT_RECEIVED = 300
ORDER_ITEM_EVENT_RECEIVED = 300
"""
Indicates the receiver has found the item while receiving a vendor
delivery. The item is set aside and is "on hand" until customer comes
in to pick it up.
"""
ORDER_ITEM_EVENT_CONTACTED = 350
ORDER_ITEM_EVENT_CONTACTED = 350
"""
Indicates the customer has been contacted, to notify them of the item
being on hand and ready for pickup.
"""
ORDER_ITEM_EVENT_CONTACT_FAILED = 375
ORDER_ITEM_EVENT_CONTACT_FAILED = 375
"""
Indicates an attempt was made to contact the customer, to notify them
of item being on hand, but the attempt failed, e.g. due to bad phone
or email on file.
"""
ORDER_ITEM_EVENT_DELIVERED = 500
ORDER_ITEM_EVENT_DELIVERED = 500
"""
Indicates the customer has picked up the item.
"""
ORDER_ITEM_EVENT_STATUS_CHANGE = 700
ORDER_ITEM_EVENT_STATUS_CHANGE = 700
"""
Indicates a manual status change was made. Such an event should
ideally contain a note with further explanation.
"""
ORDER_ITEM_EVENT_NOTE_ADDED = 750
ORDER_ITEM_EVENT_NOTE_ADDED = 750
"""
Indicates an arbitrary note was added.
"""
ORDER_ITEM_EVENT_CANCELED = 900
ORDER_ITEM_EVENT_CANCELED = 900
"""
Indicates the :term:`order item` was canceled.
"""
ORDER_ITEM_EVENT_REFUND_PENDING = 910
ORDER_ITEM_EVENT_REFUND_PENDING = 910
"""
Indicates the customer is due a (pending) refund for the item.
"""
ORDER_ITEM_EVENT_REFUNDED = 920
ORDER_ITEM_EVENT_REFUNDED = 920
"""
Indicates the customer has been refunded for the item.
"""
ORDER_ITEM_EVENT_RESTOCKED = 930
ORDER_ITEM_EVENT_RESTOCKED = 930
"""
Indicates the product has been restocked, e.g. due to the order item
being canceled.
"""
ORDER_ITEM_EVENT_EXPIRED = 940
ORDER_ITEM_EVENT_EXPIRED = 940
"""
Indicates the order item (or its product) has expired.
"""
ORDER_ITEM_EVENT_INACTIVE = 950
ORDER_ITEM_EVENT_INACTIVE = 950
"""
Indicates the order item has become inactive.
"""
ORDER_ITEM_EVENT_OTHER = 999
ORDER_ITEM_EVENT_OTHER = 999
"""
Arbitrary event type which does not signify anything in particular.
If used, the event should be given an explanatory note.
"""
ORDER_ITEM_EVENT = OrderedDict([
(ORDER_ITEM_EVENT_INITIATED, "initiated"),
(ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"),
(ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"),
(ORDER_ITEM_EVENT_READY, "ready to proceed"),
(ORDER_ITEM_EVENT_CUSTOMER_RESOLVED, "customer resolved"),
(ORDER_ITEM_EVENT_PRODUCT_RESOLVED, "product resolved"),
(ORDER_ITEM_EVENT_PLACED, "placed with vendor"),
(ORDER_ITEM_EVENT_REORDER, "marked for re-order"),
(ORDER_ITEM_EVENT_RECEIVED, "received from vendor"),
(ORDER_ITEM_EVENT_CONTACTED, "customer contacted"),
(ORDER_ITEM_EVENT_CONTACT_FAILED, "contact failed"),
(ORDER_ITEM_EVENT_DELIVERED, "delivered"),
(ORDER_ITEM_EVENT_STATUS_CHANGE, "changed status"),
(ORDER_ITEM_EVENT_NOTE_ADDED, "added note"),
(ORDER_ITEM_EVENT_CANCELED, "canceled"),
(ORDER_ITEM_EVENT_REFUND_PENDING, "refund pending"),
(ORDER_ITEM_EVENT_REFUNDED, "refunded"),
(ORDER_ITEM_EVENT_RESTOCKED, "restocked"),
(ORDER_ITEM_EVENT_EXPIRED, "expired"),
(ORDER_ITEM_EVENT_INACTIVE, "inactive"),
(ORDER_ITEM_EVENT_OTHER, "other"),
])
ORDER_ITEM_EVENT = OrderedDict(
[
(ORDER_ITEM_EVENT_INITIATED, "initiated"),
(ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"),
(ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"),
(ORDER_ITEM_EVENT_READY, "ready to proceed"),
(ORDER_ITEM_EVENT_CUSTOMER_RESOLVED, "customer resolved"),
(ORDER_ITEM_EVENT_PRODUCT_RESOLVED, "product resolved"),
(ORDER_ITEM_EVENT_PLACED, "placed with vendor"),
(ORDER_ITEM_EVENT_REORDER, "marked for re-order"),
(ORDER_ITEM_EVENT_RECEIVED, "received from vendor"),
(ORDER_ITEM_EVENT_CONTACTED, "customer contacted"),
(ORDER_ITEM_EVENT_CONTACT_FAILED, "contact failed"),
(ORDER_ITEM_EVENT_DELIVERED, "delivered"),
(ORDER_ITEM_EVENT_STATUS_CHANGE, "changed status"),
(ORDER_ITEM_EVENT_NOTE_ADDED, "added note"),
(ORDER_ITEM_EVENT_CANCELED, "canceled"),
(ORDER_ITEM_EVENT_REFUND_PENDING, "refund pending"),
(ORDER_ITEM_EVENT_REFUNDED, "refunded"),
(ORDER_ITEM_EVENT_RESTOCKED, "restocked"),
(ORDER_ITEM_EVENT_EXPIRED, "expired"),
(ORDER_ITEM_EVENT_INACTIVE, "inactive"),
(ORDER_ITEM_EVENT_OTHER, "other"),
]
)
"""
Dict of possible code -> label options for :term:`order item` event
types.

View file

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

View file

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

View file

@ -26,6 +26,6 @@ Sideshow web app
def includeme(config):
config.include('sideshow.web.static')
config.include('wuttaweb.subscribers')
config.include('sideshow.web.views')
config.include("sideshow.web.static")
config.include("wuttaweb.subscribers")
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).
"""
# prefer Sideshow templates over wuttaweb
settings.setdefault('mako.directories', [
'sideshow.web:templates',
'wuttaweb:templates',
])
settings.setdefault(
"mako.directories",
[
"sideshow.web:templates",
"wuttaweb:templates",
],
)
# make config objects
wutta_config = base.make_wutta_config(settings)
pyramid_config = base.make_pyramid_config(settings)
# bring in the rest of Sideshow
pyramid_config.include('sideshow.web')
pyramid_config.include("sideshow.web")
return pyramid_config.make_wsgi_app()

View file

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

View file

@ -28,12 +28,12 @@ from fanstatic import Library, Resource
# # libcache
libcache = Library('sideshow_libcache', 'libcache')
vue_js = Resource(libcache, 'vue-2.6.14.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_css = Resource(libcache, 'buefy-0.9.25.min.css')
fontawesome_js = Resource(libcache, 'fontawesome-5.3.1-all.min.js')
libcache = Library("sideshow_libcache", "libcache")
vue_js = Resource(libcache, "vue-2.6.14.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_css = Resource(libcache, "buefy-0.9.25.min.css")
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_oruga_js = Resource(libcache, 'oruga-0.8.10.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):
config.include('wuttaweb.static')
config.add_static_view('sideshow', 'sideshow.web:static', cache_max_age=3600)
config.include("wuttaweb.static")
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):
# core views for wuttaweb
essential.defaults(config, **{
'wuttaweb.views.common': 'sideshow.web.views.common',
})
essential.defaults(
config,
**{
"wuttaweb.views.common": "sideshow.web.views.common",
},
)
# sideshow views
config.include('sideshow.web.views.stores')
config.include('sideshow.web.views.customers')
config.include('sideshow.web.views.products')
config.include('sideshow.web.views.orders')
config.include("sideshow.web.views.stores")
config.include("sideshow.web.views.customers")
config.include("sideshow.web.views.products")
config.include("sideshow.web.views.orders")
# 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
:class:`~sideshow.web.views.orders.OrderView` instead.
"""
model_class = NewOrderBatch
model_title = "New Order Batch"
model_title_plural = "New Order Batches"
route_prefix = 'neworder_batches'
url_prefix = '/batch/neworder'
route_prefix = "neworder_batches"
url_prefix = "/batch/neworder"
creatable = False
editable = False
labels = {
'store_id': "Store ID",
'customer_id': "Customer ID",
"store_id": "Store ID",
"customer_id": "Customer ID",
}
grid_columns = [
'id',
'store_id',
'customer_id',
'customer_name',
'phone_number',
'email_address',
'total_price',
'row_count',
'created',
'created_by',
'executed',
"id",
"store_id",
"customer_id",
"customer_name",
"phone_number",
"email_address",
"total_price",
"row_count",
"created",
"created_by",
"executed",
]
filter_defaults = {
'executed': {'active': True, 'verb': 'is_null'},
"executed": {"active": True, "verb": "is_null"},
}
form_fields = [
'id',
'store_id',
'customer_id',
'local_customer',
'pending_customer',
'customer_name',
'phone_number',
'email_address',
'total_price',
'row_count',
'status_code',
'created',
'created_by',
'executed',
'executed_by',
"id",
"store_id",
"customer_id",
"local_customer",
"pending_customer",
"customer_name",
"phone_number",
"email_address",
"total_price",
"row_count",
"status_code",
"created",
"created_by",
"executed",
"executed_by",
]
row_labels = {
'product_scancode': "Scancode",
'product_brand': "Brand",
'product_description': "Description",
'product_size': "Size",
'order_uom': "Order UOM",
"product_scancode": "Scancode",
"product_brand": "Brand",
"product_description": "Description",
"product_size": "Size",
"order_uom": "Order UOM",
}
row_grid_columns = [
'sequence',
'product_scancode',
'product_brand',
'product_description',
'product_size',
'special_order',
'unit_price_quoted',
'case_size',
'case_price_quoted',
'order_qty',
'order_uom',
'discount_percent',
'total_price',
'status_code',
"sequence",
"product_scancode",
"product_brand",
"product_description",
"product_size",
"special_order",
"unit_price_quoted",
"case_size",
"case_price_quoted",
"order_qty",
"order_uom",
"discount_percent",
"total_price",
"status_code",
]
def __init__(self, request, context=None):
@ -141,10 +142,10 @@ class NewOrderBatchView(BatchMasterView):
# store_id
if not self.order_handler.expose_store_id():
g.remove('store_id')
g.remove("store_id")
# total_price
g.set_renderer('total_price', 'currency')
g.set_renderer("total_price", "currency")
def configure_form(self, f):
""" """
@ -152,16 +153,16 @@ class NewOrderBatchView(BatchMasterView):
# store_id
if not self.order_handler.expose_store_id():
f.remove('store_id')
f.remove("store_id")
# local_customer
f.set_node('local_customer', LocalCustomerRef(self.request))
f.set_node("local_customer", LocalCustomerRef(self.request))
# pending_customer
f.set_node('pending_customer', PendingCustomerRef(self.request))
f.set_node("pending_customer", PendingCustomerRef(self.request))
# total_price
f.set_node('total_price', WuttaMoney(self.request))
f.set_node("total_price", WuttaMoney(self.request))
def configure_row_grid(self, g):
""" """
@ -170,22 +171,22 @@ class NewOrderBatchView(BatchMasterView):
# TODO
# 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
g.set_label('unit_price_quoted', "Unit Price", column_only=True)
g.set_renderer('unit_price_quoted', 'currency')
g.set_label("unit_price_quoted", "Unit Price", column_only=True)
g.set_renderer("unit_price_quoted", "currency")
# case_price_quoted
g.set_label('case_price_quoted', "Case Price", column_only=True)
g.set_renderer('case_price_quoted', 'currency')
g.set_label("case_price_quoted", "Case Price", column_only=True)
g.set_renderer("case_price_quoted", "currency")
# discount_percent
g.set_renderer('discount_percent', 'percent')
g.set_label('discount_percent', "Disc. %", column_only=True)
g.set_renderer("discount_percent", "percent")
g.set_label("discount_percent", "Disc. %", column_only=True)
# total_price
g.set_renderer('total_price', 'currency')
g.set_renderer("total_price", "currency")
def get_xref_buttons(self, batch):
"""
@ -197,14 +198,19 @@ class NewOrderBatchView(BatchMasterView):
model = self.app.model
session = self.Session()
if batch.executed and self.request.has_perm('orders.view'):
order = session.query(model.Order)\
.filter(model.Order.order_id == batch.id)\
.first()
if batch.executed and self.request.has_perm("orders.view"):
order = (
session.query(model.Order)
.filter(model.Order.order_id == batch.id)
.first()
)
if order:
url = self.request.route_url('orders.view', uuid=order.uuid)
url = self.request.route_url("orders.view", uuid=order.uuid)
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
@ -212,7 +218,7 @@ class NewOrderBatchView(BatchMasterView):
def defaults(config, **kwargs):
base = globals()
NewOrderBatchView = kwargs.get('NewOrderBatchView', base['NewOrderBatchView'])
NewOrderBatchView = kwargs.get("NewOrderBatchView", base["NewOrderBatchView"])
NewOrderBatchView.defaults(config)

View file

@ -46,54 +46,55 @@ class CommonView(base.CommonView):
auth = self.app.get_auth_handler()
admin = model.Role(name="Order Admin")
admin.notes = ("this role was auto-created; "
"you can change or remove it as needed.")
admin.notes = (
"this role was auto-created; " "you can change or remove it as needed."
)
session.add(admin)
user.roles.append(admin)
order_admin_perms = [
'local_customers.list',
'local_customers.view',
'local_products.list',
'local_products.view',
'neworder_batches.list',
'neworder_batches.view',
'order_items.add_note',
'order_items.change_status',
'order_items.list',
'order_items.view',
'order_items_contact.add_note',
'order_items_contact.change_status',
'order_items_contact.list',
'order_items_contact.process_contact',
'order_items_contact.view',
'order_items_delivery.add_note',
'order_items_delivery.change_status',
'order_items_delivery.list',
'order_items_delivery.process_delivery',
'order_items_delivery.process_restock',
'order_items_delivery.view',
'order_items_placement.add_note',
'order_items_placement.change_status',
'order_items_placement.list',
'order_items_placement.process_placement',
'order_items_placement.view',
'order_items_receiving.add_note',
'order_items_receiving.change_status',
'order_items_receiving.list',
'order_items_receiving.process_receiving',
'order_items_receiving.process_reorder',
'order_items_receiving.view',
'orders.configure',
'orders.create',
'orders.create_unknown_product',
'orders.list',
'orders.view',
'pending_customers.list',
'pending_customers.view',
'pending_products.list',
'pending_products.view',
"local_customers.list",
"local_customers.view",
"local_products.list",
"local_products.view",
"neworder_batches.list",
"neworder_batches.view",
"order_items.add_note",
"order_items.change_status",
"order_items.list",
"order_items.view",
"order_items_contact.add_note",
"order_items_contact.change_status",
"order_items_contact.list",
"order_items_contact.process_contact",
"order_items_contact.view",
"order_items_delivery.add_note",
"order_items_delivery.change_status",
"order_items_delivery.list",
"order_items_delivery.process_delivery",
"order_items_delivery.process_restock",
"order_items_delivery.view",
"order_items_placement.add_note",
"order_items_placement.change_status",
"order_items_placement.list",
"order_items_placement.process_placement",
"order_items_placement.view",
"order_items_receiving.add_note",
"order_items_receiving.change_status",
"order_items_receiving.list",
"order_items_receiving.process_receiving",
"order_items_receiving.process_reorder",
"order_items_receiving.view",
"orders.configure",
"orders.create",
"orders.create_unknown_product",
"orders.list",
"orders.view",
"pending_customers.list",
"pending_customers.view",
"pending_products.list",
"pending_products.view",
]
for perm in order_admin_perms:
@ -101,4 +102,4 @@ class CommonView(base.CommonView):
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/delete``
"""
model_class = LocalCustomer
model_title = "Local Customer"
route_prefix = 'local_customers'
url_prefix = '/local/customers'
route_prefix = "local_customers"
url_prefix = "/local/customers"
labels = {
'external_id': "External ID",
"external_id": "External ID",
}
grid_columns = [
'external_id',
'full_name',
'first_name',
'last_name',
'phone_number',
'email_address',
"external_id",
"full_name",
"first_name",
"last_name",
"phone_number",
"email_address",
]
sort_defaults = 'full_name'
sort_defaults = "full_name"
form_fields = [
'external_id',
'full_name',
'first_name',
'last_name',
'phone_number',
'email_address',
'orders',
'new_order_batches',
"external_id",
"full_name",
"first_name",
"last_name",
"phone_number",
"email_address",
"orders",
"new_order_batches",
]
def configure_grid(self, g):
@ -80,11 +81,11 @@ class LocalCustomerView(MasterView):
super().configure_grid(g)
# links
g.set_link('full_name')
g.set_link('first_name')
g.set_link('last_name')
g.set_link('phone_number')
g.set_link('email_address')
g.set_link("full_name")
g.set_link("first_name")
g.set_link("last_name")
g.set_link("phone_number")
g.set_link("email_address")
def configure_form(self, f):
""" """
@ -93,25 +94,25 @@ class LocalCustomerView(MasterView):
# external_id
if self.creating:
f.remove('external_id')
f.remove("external_id")
else:
f.set_readonly('external_id')
f.set_readonly("external_id")
# full_name
if self.creating or self.editing:
f.remove('full_name')
f.remove("full_name")
# orders
if self.creating or self.editing:
f.remove('orders')
f.remove("orders")
else:
f.set_grid('orders', self.make_orders_grid(customer))
f.set_grid("orders", self.make_orders_grid(customer))
# new_order_batches
if self.creating or self.editing:
f.remove('new_order_batches')
f.remove("new_order_batches")
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):
"""
@ -120,24 +121,28 @@ class LocalCustomerView(MasterView):
model = self.app.model
route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.orders',
model_class=model.Order,
data=customer.orders,
columns=[
'order_id',
'total_price',
'created',
'created_by',
],
labels={
'order_id': "Order ID",
})
grid.set_renderer('total_price', grid.render_currency)
grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order,
data=customer.orders,
columns=[
"order_id",
"total_price",
"created",
"created_by",
],
labels={
"order_id": "Order ID",
},
)
grid.set_renderer("total_price", grid.render_currency)
if self.request.has_perm('orders.view'):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('order_id')
if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url(
"orders.view", uuid=order.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid
@ -148,28 +153,32 @@ class LocalCustomerView(MasterView):
model = self.app.model
route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches',
model_class=model.NewOrderBatch,
data=customer.new_order_batches,
columns=[
'id',
'total_price',
'created',
'created_by',
'executed',
],
labels={
'id': "Batch ID",
},
renderers={
'id': 'batch_id',
'total_price': 'currency',
})
grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch,
data=customer.new_order_batches,
columns=[
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
"id": "Batch ID",
},
renderers={
"id": "batch_id",
"total_price": "currency",
},
)
if self.request.has_perm('neworder_batches.view'):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('id')
if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url(
"neworder_batches.view", uuid=batch.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid
@ -178,8 +187,9 @@ class LocalCustomerView(MasterView):
enum = self.app.enum
customer = super().objectify(form)
customer.full_name = self.app.make_full_name(customer.first_name,
customer.last_name)
customer.full_name = self.app.make_full_name(
customer.first_name, customer.last_name
)
return customer
@ -198,41 +208,42 @@ class PendingCustomerView(MasterView):
* ``/pending/customers/XXX/edit``
* ``/pending/customers/XXX/delete``
"""
model_class = PendingCustomer
model_title = "Pending Customer"
route_prefix = 'pending_customers'
url_prefix = '/pending/customers'
route_prefix = "pending_customers"
url_prefix = "/pending/customers"
labels = {
'customer_id': "Customer ID",
"customer_id": "Customer ID",
}
grid_columns = [
'full_name',
'first_name',
'last_name',
'phone_number',
'email_address',
'customer_id',
'status',
'created',
'created_by',
"full_name",
"first_name",
"last_name",
"phone_number",
"email_address",
"customer_id",
"status",
"created",
"created_by",
]
sort_defaults = 'full_name'
sort_defaults = "full_name"
form_fields = [
'customer_id',
'full_name',
'first_name',
'last_name',
'phone_number',
'email_address',
'status',
'created',
'created_by',
'orders',
'new_order_batches',
"customer_id",
"full_name",
"first_name",
"last_name",
"phone_number",
"email_address",
"status",
"created",
"created_by",
"orders",
"new_order_batches",
]
def configure_grid(self, g):
@ -241,14 +252,14 @@ class PendingCustomerView(MasterView):
enum = self.app.enum
# status
g.set_renderer('status', self.grid_render_enum, enum=enum.PendingCustomerStatus)
g.set_renderer("status", self.grid_render_enum, enum=enum.PendingCustomerStatus)
# links
g.set_link('full_name')
g.set_link('first_name')
g.set_link('last_name')
g.set_link('phone_number')
g.set_link('email_address')
g.set_link("full_name")
g.set_link("first_name")
g.set_link("last_name")
g.set_link("phone_number")
g.set_link("email_address")
def configure_form(self, f):
""" """
@ -258,41 +269,41 @@ class PendingCustomerView(MasterView):
# customer_id
if self.creating:
f.remove('customer_id')
f.remove("customer_id")
else:
f.set_readonly('customer_id')
f.set_readonly("customer_id")
# status
if self.creating:
f.remove('status')
f.remove("status")
else:
f.set_node('status', WuttaEnum(self.request, enum.PendingCustomerStatus))
f.set_readonly('status')
f.set_node("status", WuttaEnum(self.request, enum.PendingCustomerStatus))
f.set_readonly("status")
# created
if self.creating:
f.remove('created')
f.remove("created")
else:
f.set_readonly('created')
f.set_readonly("created")
# created_by
if self.creating:
f.remove('created_by')
f.remove("created_by")
else:
f.set_node('created_by', UserRef(self.request))
f.set_readonly('created_by')
f.set_node("created_by", UserRef(self.request))
f.set_readonly("created_by")
# orders
if self.creating or self.editing:
f.remove('orders')
f.remove("orders")
else:
f.set_grid('orders', self.make_orders_grid(customer))
f.set_grid("orders", self.make_orders_grid(customer))
# new_order_batches
if self.creating or self.editing:
f.remove('new_order_batches')
f.remove("new_order_batches")
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):
"""
@ -301,24 +312,28 @@ class PendingCustomerView(MasterView):
model = self.app.model
route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.orders',
model_class=model.Order,
data=customer.orders,
columns=[
'order_id',
'total_price',
'created',
'created_by',
],
labels={
'order_id': "Order ID",
})
grid.set_renderer('total_price', grid.render_currency)
grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order,
data=customer.orders,
columns=[
"order_id",
"total_price",
"created",
"created_by",
],
labels={
"order_id": "Order ID",
},
)
grid.set_renderer("total_price", grid.render_currency)
if self.request.has_perm('orders.view'):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('order_id')
if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url(
"orders.view", uuid=order.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid
@ -329,28 +344,32 @@ class PendingCustomerView(MasterView):
model = self.app.model
route_prefix = self.get_route_prefix()
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches',
model_class=model.NewOrderBatch,
data=customer.new_order_batches,
columns=[
'id',
'total_price',
'created',
'created_by',
'executed',
],
labels={
'id': "Batch ID",
},
renderers={
'id': 'batch_id',
'total_price': 'currency',
})
grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch,
data=customer.new_order_batches,
columns=[
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
"id": "Batch ID",
},
renderers={
"id": "batch_id",
"total_price": "currency",
},
)
if self.request.has_perm('neworder_batches.view'):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('id')
if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url(
"neworder_batches.view", uuid=batch.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid
@ -371,16 +390,20 @@ class PendingCustomerView(MasterView):
# avoid deleting if still referenced by order(s)
for order in customer.orders:
self.request.session.flash(f"Cannot delete {model_title} still attached "
"to Order(s)", 'warning')
raise self.redirect(self.get_action_url('view', customer))
self.request.session.flash(
f"Cannot delete {model_title} still attached " "to Order(s)", "warning"
)
raise self.redirect(self.get_action_url("view", customer))
# avoid deleting if still referenced by new order batch(es)
for batch in customer.new_order_batches:
if not batch.executed:
self.request.session.flash(f"Cannot delete {model_title} still attached "
"to New Order Batch(es)", 'warning')
raise self.redirect(self.get_action_url('view', customer))
self.request.session.flash(
f"Cannot delete {model_title} still attached "
"to New Order Batch(es)",
"warning",
)
raise self.redirect(self.get_action_url("view", customer))
# go ahead and delete per usual
super().delete_instance(customer)
@ -389,10 +412,10 @@ class PendingCustomerView(MasterView):
def defaults(config, **kwargs):
base = globals()
LocalCustomerView = kwargs.get('LocalCustomerView', base['LocalCustomerView'])
LocalCustomerView = kwargs.get("LocalCustomerView", base["LocalCustomerView"])
LocalCustomerView.defaults(config)
PendingCustomerView = kwargs.get('PendingCustomerView', base['PendingCustomerView'])
PendingCustomerView = kwargs.get("PendingCustomerView", base["PendingCustomerView"])
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/delete``
"""
model_class = LocalProduct
model_title = "Local Product"
route_prefix = 'local_products'
url_prefix = '/local/products'
route_prefix = "local_products"
url_prefix = "/local/products"
labels = {
'external_id': "External ID",
'department_id': "Department ID",
"external_id": "External ID",
"department_id": "Department ID",
}
grid_columns = [
'scancode',
'brand_name',
'description',
'size',
'department_name',
'special_order',
'case_size',
'unit_cost',
'unit_price_reg',
"scancode",
"brand_name",
"description",
"size",
"department_name",
"special_order",
"case_size",
"unit_cost",
"unit_price_reg",
]
sort_defaults = 'scancode'
sort_defaults = "scancode"
form_fields = [
'external_id',
'scancode',
'brand_name',
'description',
'size',
'department_id',
'department_name',
'special_order',
'vendor_name',
'vendor_item_code',
'case_size',
'unit_cost',
'unit_price_reg',
'notes',
'orders',
'new_order_batches',
"external_id",
"scancode",
"brand_name",
"description",
"size",
"department_id",
"department_name",
"special_order",
"vendor_name",
"vendor_item_code",
"case_size",
"unit_cost",
"unit_price_reg",
"notes",
"orders",
"new_order_batches",
]
def configure_grid(self, g):
@ -92,17 +93,17 @@ class LocalProductView(MasterView):
super().configure_grid(g)
# unit_cost
g.set_renderer('unit_cost', 'currency', scale=4)
g.set_renderer("unit_cost", "currency", scale=4)
# unit_price_reg
g.set_label('unit_price_reg', "Reg. Price", column_only=True)
g.set_renderer('unit_price_reg', 'currency')
g.set_label("unit_price_reg", "Reg. Price", column_only=True)
g.set_renderer("unit_price_reg", "currency")
# links
g.set_link('scancode')
g.set_link('brand_name')
g.set_link('description')
g.set_link('size')
g.set_link("scancode")
g.set_link("brand_name")
g.set_link("description")
g.set_link("size")
def configure_form(self, f):
""" """
@ -112,40 +113,40 @@ class LocalProductView(MasterView):
# external_id
if self.creating:
f.remove('external_id')
f.remove("external_id")
else:
f.set_readonly('external_id')
f.set_readonly("external_id")
# TODO: should not have to explicitly mark these nodes
# as required=False.. i guess i do for now b/c i am
# totally overriding the node from colanderlachemy
# case_size
f.set_node('case_size', WuttaQuantity(self.request))
f.set_required('case_size', False)
f.set_node("case_size", WuttaQuantity(self.request))
f.set_required("case_size", False)
# unit_cost
f.set_node('unit_cost', WuttaMoney(self.request, scale=4))
f.set_required('unit_cost', False)
f.set_node("unit_cost", WuttaMoney(self.request, scale=4))
f.set_required("unit_cost", False)
# unit_price_reg
f.set_node('unit_price_reg', WuttaMoney(self.request))
f.set_required('unit_price_reg', False)
f.set_node("unit_price_reg", WuttaMoney(self.request))
f.set_required("unit_price_reg", False)
# notes
f.set_widget('notes', 'notes')
f.set_widget("notes", "notes")
# orders
if self.creating or self.editing:
f.remove('orders')
f.remove("orders")
else:
f.set_grid('orders', self.make_orders_grid(product))
f.set_grid("orders", self.make_orders_grid(product))
# new_order_batches
if self.creating or self.editing:
f.remove('new_order_batches')
f.remove("new_order_batches")
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):
"""
@ -157,26 +158,30 @@ class LocalProductView(MasterView):
orders = set([item.order for item in product.order_items])
orders = sorted(orders, key=lambda order: order.order_id)
grid = self.make_grid(key=f'{route_prefix}.view.orders',
model_class=model.Order,
data=orders,
columns=[
'order_id',
'total_price',
'created',
'created_by',
],
labels={
'order_id': "Order ID",
},
renderers={
'total_price': 'currency',
})
grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order,
data=orders,
columns=[
"order_id",
"total_price",
"created",
"created_by",
],
labels={
"order_id": "Order ID",
},
renderers={
"total_price": "currency",
},
)
if self.request.has_perm('orders.view'):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('order_id')
if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url(
"orders.view", uuid=order.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid
@ -190,28 +195,32 @@ class LocalProductView(MasterView):
batches = set([row.batch for row in product.new_order_batch_rows])
batches = sorted(batches, key=lambda batch: batch.id)
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches',
model_class=model.NewOrderBatch,
data=batches,
columns=[
'id',
'total_price',
'created',
'created_by',
'executed',
],
labels={
'id': "Batch ID",
'status_code': "Status",
},
renderers={
'id': 'batch_id',
})
grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch,
data=batches,
columns=[
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
"id": "Batch ID",
"status_code": "Status",
},
renderers={
"id": "batch_id",
},
)
if self.request.has_perm('neworder_batches.view'):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('id')
if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url(
"neworder_batches.view", uuid=batch.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid
@ -230,57 +239,57 @@ class PendingProductView(MasterView):
* ``/pending/products/XXX/edit``
* ``/pending/products/XXX/delete``
"""
model_class = PendingProduct
model_title = "Pending Product"
route_prefix = 'pending_products'
url_prefix = '/pending/products'
route_prefix = "pending_products"
url_prefix = "/pending/products"
labels = {
'department_id': "Department ID",
'product_id': "Product ID",
"department_id": "Department ID",
"product_id": "Product ID",
}
grid_columns = [
'scancode',
'department_name',
'brand_name',
'description',
'size',
'unit_cost',
'case_size',
'unit_price_reg',
'special_order',
'status',
'created',
'created_by',
"scancode",
"department_name",
"brand_name",
"description",
"size",
"unit_cost",
"case_size",
"unit_price_reg",
"special_order",
"status",
"created",
"created_by",
]
sort_defaults = ('created', 'desc')
sort_defaults = ("created", "desc")
filter_defaults = {
'status': {'active': True,
'value': PendingProductStatus.READY.name},
"status": {"active": True, "value": PendingProductStatus.READY.name},
}
form_fields = [
'product_id',
'scancode',
'department_id',
'department_name',
'brand_name',
'description',
'size',
'vendor_name',
'vendor_item_code',
'unit_cost',
'case_size',
'unit_price_reg',
'special_order',
'notes',
'created',
'created_by',
'orders',
'new_order_batches',
"product_id",
"scancode",
"department_id",
"department_name",
"brand_name",
"description",
"size",
"vendor_name",
"vendor_item_code",
"unit_cost",
"case_size",
"unit_price_reg",
"special_order",
"notes",
"created",
"created_by",
"orders",
"new_order_batches",
]
def configure_grid(self, g):
@ -289,26 +298,26 @@ class PendingProductView(MasterView):
enum = self.app.enum
# unit_cost
g.set_renderer('unit_cost', 'currency', scale=4)
g.set_renderer("unit_cost", "currency", scale=4)
# unit_price_reg
g.set_label('unit_price_reg', "Reg. Price", column_only=True)
g.set_renderer('unit_price_reg', 'currency')
g.set_label("unit_price_reg", "Reg. Price", column_only=True)
g.set_renderer("unit_price_reg", "currency")
# status
g.set_enum('status', enum.PendingProductStatus)
g.set_enum("status", enum.PendingProductStatus)
# links
g.set_link('scancode')
g.set_link('brand_name')
g.set_link('description')
g.set_link('size')
g.set_link("scancode")
g.set_link("brand_name")
g.set_link("description")
g.set_link("size")
def grid_row_class(self, product, data, i):
""" """
enum = self.app.enum
if product.status == enum.PendingProductStatus.IGNORED:
return 'has-background-warning'
return "has-background-warning"
def configure_form(self, f):
""" """
@ -318,40 +327,40 @@ class PendingProductView(MasterView):
# product_id
if self.creating:
f.remove('product_id')
f.remove("product_id")
else:
f.set_readonly('product_id')
f.set_readonly("product_id")
# unit_price_reg
f.set_node('unit_price_reg', WuttaMoney(self.request))
f.set_node("unit_price_reg", WuttaMoney(self.request))
# notes
f.set_widget('notes', 'notes')
f.set_widget("notes", "notes")
# created
if self.creating:
f.remove('created')
f.remove("created")
else:
f.set_readonly('created')
f.set_readonly("created")
# created_by
if self.creating:
f.remove('created_by')
f.remove("created_by")
else:
f.set_node('created_by', UserRef(self.request))
f.set_readonly('created_by')
f.set_node("created_by", UserRef(self.request))
f.set_readonly("created_by")
# orders
if self.creating or self.editing:
f.remove('orders')
f.remove("orders")
else:
f.set_grid('orders', self.make_orders_grid(product))
f.set_grid("orders", self.make_orders_grid(product))
# new_order_batches
if self.creating or self.editing:
f.remove('new_order_batches')
f.remove("new_order_batches")
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):
"""
@ -363,26 +372,30 @@ class PendingProductView(MasterView):
orders = set([item.order for item in product.order_items])
orders = sorted(orders, key=lambda order: order.order_id)
grid = self.make_grid(key=f'{route_prefix}.view.orders',
model_class=model.Order,
data=orders,
columns=[
'order_id',
'total_price',
'created',
'created_by',
],
labels={
'order_id': "Order ID",
},
renderers={
'total_price': 'currency',
})
grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order,
data=orders,
columns=[
"order_id",
"total_price",
"created",
"created_by",
],
labels={
"order_id": "Order ID",
},
renderers={
"total_price": "currency",
},
)
if self.request.has_perm('orders.view'):
url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('order_id')
if self.request.has_perm("orders.view"):
url = lambda order, i: self.request.route_url(
"orders.view", uuid=order.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("order_id")
return grid
@ -396,28 +409,32 @@ class PendingProductView(MasterView):
batches = set([row.batch for row in product.new_order_batch_rows])
batches = sorted(batches, key=lambda batch: batch.id)
grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches',
model_class=model.NewOrderBatch,
data=batches,
columns=[
'id',
'total_price',
'created',
'created_by',
'executed',
],
labels={
'id': "Batch ID",
'status_code': "Status",
},
renderers={
'id': 'batch_id',
})
grid = self.make_grid(
key=f"{route_prefix}.view.new_order_batches",
model_class=model.NewOrderBatch,
data=batches,
columns=[
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
"id": "Batch ID",
"status_code": "Status",
},
renderers={
"id": "batch_id",
},
)
if self.request.has_perm('neworder_batches.view'):
url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid)
grid.add_action('view', icon='eye', url=url)
grid.set_link('id')
if self.request.has_perm("neworder_batches.view"):
url = lambda batch, i: self.request.route_url(
"neworder_batches.view", uuid=batch.uuid
)
grid.add_action("view", icon="eye", url=url)
grid.set_link("id")
return grid
@ -426,11 +443,12 @@ class PendingProductView(MasterView):
enum = self.app.enum
if self.viewing:
product = context['instance']
if (product.status == enum.PendingProductStatus.READY
and self.has_any_perm('resolve', 'ignore')):
handler = self.app.get_batch_handler('neworder')
context['use_local_products'] = handler.use_local_products()
product = context["instance"]
if product.status == enum.PendingProductStatus.READY and self.has_any_perm(
"resolve", "ignore"
):
handler = self.app.get_batch_handler("neworder")
context["use_local_products"] = handler.use_local_products()
return context
@ -441,9 +459,12 @@ class PendingProductView(MasterView):
for row in product.new_order_batch_rows:
if not row.batch.executed:
model_title = self.get_model_title()
self.request.session.flash(f"Cannot delete {model_title} still attached "
"to New Order Batch(es)", 'warning')
raise self.redirect(self.get_action_url('view', product))
self.request.session.flash(
f"Cannot delete {model_title} still attached "
"to New Order Batch(es)",
"warning",
)
raise self.redirect(self.get_action_url("view", product))
# go ahead and delete per usual
super().delete_instance(product)
@ -469,21 +490,23 @@ class PendingProductView(MasterView):
product = self.get_instance()
if product.status != enum.PendingProductStatus.READY:
self.request.session.flash("pending product does not have 'ready' status!", 'error')
return self.redirect(self.get_action_url('view', product))
self.request.session.flash(
"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:
self.request.session.flash("must specify valid product_id", 'error')
return self.redirect(self.get_action_url('view', product))
self.request.session.flash("must specify valid product_id", "error")
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()
info = batch_handler.get_product_info_external(session, product_id)
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):
"""
@ -499,11 +522,13 @@ class PendingProductView(MasterView):
product = self.get_instance()
if product.status != enum.PendingProductStatus.READY:
self.request.session.flash("pending product does not have 'ready' status!", 'error')
return self.redirect(self.get_action_url('view', product))
self.request.session.flash(
"pending product does not have 'ready' status!", "error"
)
return self.redirect(self.get_action_url("view", product))
product.status = enum.PendingProductStatus.IGNORED
return self.redirect(self.get_action_url('view', product))
return self.redirect(self.get_action_url("view", product))
@classmethod
def defaults(cls, config):
@ -519,35 +544,45 @@ class PendingProductView(MasterView):
model_title = cls.get_model_title()
# resolve
config.add_wutta_permission(permission_prefix,
f'{permission_prefix}.resolve',
f"Resolve {model_title}")
config.add_route(f'{route_prefix}.resolve',
f'{instance_url_prefix}/resolve',
request_method='POST')
config.add_view(cls, attr='resolve',
route_name=f'{route_prefix}.resolve',
permission=f'{permission_prefix}.resolve')
config.add_wutta_permission(
permission_prefix, f"{permission_prefix}.resolve", f"Resolve {model_title}"
)
config.add_route(
f"{route_prefix}.resolve",
f"{instance_url_prefix}/resolve",
request_method="POST",
)
config.add_view(
cls,
attr="resolve",
route_name=f"{route_prefix}.resolve",
permission=f"{permission_prefix}.resolve",
)
# ignore
config.add_wutta_permission(permission_prefix,
f'{permission_prefix}.ignore',
f"Ignore {model_title}")
config.add_route(f'{route_prefix}.ignore',
f'{instance_url_prefix}/ignore',
request_method='POST')
config.add_view(cls, attr='ignore',
route_name=f'{route_prefix}.ignore',
permission=f'{permission_prefix}.ignore')
config.add_wutta_permission(
permission_prefix, f"{permission_prefix}.ignore", f"Ignore {model_title}"
)
config.add_route(
f"{route_prefix}.ignore",
f"{instance_url_prefix}/ignore",
request_method="POST",
)
config.add_view(
cls,
attr="ignore",
route_name=f"{route_prefix}.ignore",
permission=f"{permission_prefix}.ignore",
)
def defaults(config, **kwargs):
base = globals()
LocalProductView = kwargs.get('LocalProductView', base['LocalProductView'])
LocalProductView = kwargs.get("LocalProductView", base["LocalProductView"])
LocalProductView.defaults(config)
PendingProductView = kwargs.get('PendingProductView', base['PendingProductView'])
PendingProductView = kwargs.get("PendingProductView", base["PendingProductView"])
PendingProductView.defaults(config)

View file

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

View file

@ -15,14 +15,14 @@ def release(c, skip_tests=False):
Release a new version of Sideshow
"""
if not skip_tests:
c.run('pytest')
c.run("pytest")
# rebuild pkg
if os.path.exists('dist'):
shutil.rmtree('dist')
if os.path.exists('Sideshow.egg-info'):
shutil.rmtree('Sideshow.egg-info')
c.run('python -m build --sdist')
if os.path.exists("dist"):
shutil.rmtree("dist")
if os.path.exists("Sideshow.egg-info"):
shutil.rmtree("Sideshow.egg-info")
c.run("python -m build --sdist")
# 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):
ctx = MagicMock(params={})
ctx.parent.wutta_config = self.config
with patch.object(InstallHandler, 'run') as run:
with patch.object(InstallHandler, "run") as run:
mod.install(ctx)
run.assert_called_once_with()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,16 +33,16 @@ class TestNewOrderBatchView(WebTestCase):
# store_id not exposed by default
grid = view.make_grid(model_class=model.NewOrderBatch)
self.assertIn('store_id', grid.columns)
self.assertIn("store_id", grid.columns)
view.configure_grid(grid)
self.assertNotIn('store_id', grid.columns)
self.assertNotIn("store_id", grid.columns)
# 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)
self.assertIn('store_id', grid.columns)
self.assertIn("store_id", grid.columns)
view.configure_grid(grid)
self.assertIn('store_id', grid.columns)
self.assertIn("store_id", grid.columns)
def test_configure_form(self):
model = self.app.model
@ -50,74 +50,85 @@ class TestNewOrderBatchView(WebTestCase):
view = self.make_view()
handler = view.batch_handler
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
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.commit()
# viewing
with patch.object(view, 'viewing', new=True):
with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=batch)
view.configure_form(form)
schema = form.get_schema()
self.assertIsInstance(schema['pending_customer'].typ, PendingCustomerRef)
self.assertIsInstance(schema['total_price'].typ, WuttaMoney)
self.assertIsInstance(schema["pending_customer"].typ, PendingCustomerRef)
self.assertIsInstance(schema["total_price"].typ, WuttaMoney)
# store_id not exposed by default
form = view.make_form(model_instance=batch)
self.assertIn('store_id', form)
self.assertIn("store_id", form)
view.configure_form(form)
self.assertNotIn('store_id', form)
self.assertNotIn("store_id", form)
# 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)
self.assertIn('store_id', form)
self.assertIn("store_id", form)
view.configure_form(form)
self.assertIn('store_id', form)
self.assertIn("store_id", form)
def test_configure_row_grid(self):
model = self.app.model
view = self.make_view()
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)
self.assertIn('total_price', grid.renderers)
self.assertIn("total_price", grid.renderers)
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
enum = self.app.enum
view = self.make_view()
handler = view.batch_handler
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer)
# 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.flush()
buttons = view.get_xref_buttons(batch)
self.assertEqual(len(buttons), 0)
# 2nd batch is executed; has order
batch = handler.make_batch(self.session, pending_customer=customer, created_by=user,
executed=datetime.datetime.now(), executed_by=user)
batch = handler.make_batch(
self.session,
pending_customer=customer,
created_by=user,
executed=datetime.datetime.now(),
executed_by=user,
)
self.session.add(batch)
self.session.flush()
order = model.Order(order_id=batch.id, created_by=user)
self.session.add(order)
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
with patch.object(self.request, 'is_root', new=True):
with patch.object(self.request, "is_root", new=True):
buttons = view.get_xref_buttons(batch)
self.assertEqual(len(buttons), 1)

View file

@ -19,11 +19,11 @@ class TestCommonView(WebTestCase):
model = self.app.model
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
self.session.flush()
self.assertEqual(len(user.roles), 0)
view.setup_enhance_admin_user(user)
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
view = self.make_view()
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)
self.assertIn('full_name', grid.linked_columns)
self.assertIn("full_name", grid.linked_columns)
def test_configure_form(self):
model = self.app.model
view = self.make_view()
# creating
with patch.object(view, 'creating', new=True):
with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.LocalCustomer)
view.configure_form(form)
self.assertNotIn('external_id', form)
self.assertNotIn('full_name', form)
self.assertNotIn('orders', form)
self.assertNotIn('new_order_batches', form)
self.assertNotIn("external_id", form)
self.assertNotIn("full_name", form)
self.assertNotIn("orders", form)
self.assertNotIn("new_order_batches", form)
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.LocalCustomer()
self.session.add(customer)
self.session.commit()
# viewing
with patch.object(view, 'viewing', new=True):
with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=customer)
view.configure_form(form)
self.assertIn('external_id', form)
self.assertIn('full_name', form)
self.assertIn('orders', form)
self.assertIn('new_order_batches', form)
self.assertIn("external_id", form)
self.assertIn("full_name", form)
self.assertIn("orders", form)
self.assertIn("new_order_batches", form)
def test_make_orders_grid(self):
model = self.app.model
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.LocalCustomer()
self.session.add(customer)
@ -74,21 +74,23 @@ class TestLocalCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
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):
model = self.app.model
handler = NewOrderBatchHandler(self.config)
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.LocalCustomer()
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.commit()
@ -97,31 +99,36 @@ class TestLocalCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view')
self.assertEqual(grid.actions[0].key, "view")
def test_objectify(self):
model = self.app.model
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
self.session.commit()
with patch.object(view, 'creating', new=True):
with patch.object(self.request, 'user', new=user):
with patch.object(view, "creating", new=True):
with patch.object(self.request, "user", new=user):
form = view.make_model_form()
with patch.object(form, 'validated', create=True, new={
'first_name': 'Chuck',
'last_name': 'Norris',
}):
with patch.object(
form,
"validated",
create=True,
new={
"first_name": "Chuck",
"last_name": "Norris",
},
):
customer = view.objectify(form)
self.assertIsInstance(customer, model.LocalCustomer)
self.assertEqual(customer.first_name, 'Chuck')
self.assertEqual(customer.last_name, 'Norris')
self.assertEqual(customer.full_name, 'Chuck Norris')
self.assertEqual(customer.first_name, "Chuck")
self.assertEqual(customer.last_name, "Norris")
self.assertEqual(customer.full_name, "Chuck Norris")
class TestPendingCustomerView(WebTestCase):
@ -135,7 +142,7 @@ class TestPendingCustomerView(WebTestCase):
# nb. mostly just getting coverage here
grid = view.make_grid(model_class=model.PendingCustomer)
view.configure_grid(grid)
self.assertIn('full_name', grid.linked_columns)
self.assertIn("full_name", grid.linked_columns)
def test_configure_form(self):
model = self.app.model
@ -143,41 +150,43 @@ class TestPendingCustomerView(WebTestCase):
view = self.make_view()
# creating
with patch.object(view, 'creating', new=True):
with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.PendingCustomer)
view.configure_form(form)
self.assertNotIn('status', form)
self.assertNotIn('created', form)
self.assertNotIn('created_by', form)
self.assertNotIn('orders', form)
self.assertNotIn('new_order_batches', form)
self.assertNotIn("status", form)
self.assertNotIn("created", form)
self.assertNotIn("created_by", form)
self.assertNotIn("orders", form)
self.assertNotIn("new_order_batches", form)
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer)
self.session.commit()
# viewing
with patch.object(view, 'viewing', new=True):
with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=customer)
view.configure_form(form)
self.assertIn('status', form)
self.assertIn('created', form)
self.assertIn('created_by', form)
self.assertIn('orders', form)
self.assertIn('new_order_batches', form)
self.assertIn("status", form)
self.assertIn("created", form)
self.assertIn("created_by", form)
self.assertIn("orders", form)
self.assertIn("new_order_batches", form)
def test_make_orders_grid(self):
model = self.app.model
enum = self.app.enum
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer)
order = model.Order(order_id=42, pending_customer=customer, created_by=user)
self.session.add(order)
@ -188,10 +197,10 @@ class TestPendingCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
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):
model = self.app.model
@ -199,12 +208,15 @@ class TestPendingCustomerView(WebTestCase):
handler = NewOrderBatchHandler(self.config)
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
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.commit()
@ -213,44 +225,54 @@ class TestPendingCustomerView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view')
self.assertEqual(grid.actions[0].key, "view")
def test_objectify(self):
model = self.app.model
enum = self.app.enum
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
self.session.commit()
with patch.object(view, 'creating', new=True):
with patch.object(self.request, 'user', new=user):
with patch.object(view, "creating", new=True):
with patch.object(self.request, "user", new=user):
form = view.make_model_form()
with patch.object(form, 'validated', create=True, new={
'full_name': "Fred Flinstone",
}):
with patch.object(
form,
"validated",
create=True,
new={
"full_name": "Fred Flinstone",
},
):
customer = view.objectify(form)
self.assertIsInstance(customer, model.PendingCustomer)
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):
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
enum = self.app.enum
handler = NewOrderBatchHandler(self.config)
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
# 1st customer is standalone, will be deleted
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer)
self.session.flush()
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)
# 2nd customer is attached to new order batch, will not be deleted
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
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.flush()
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)
# 3rd customer is attached to order, will not be deleted
customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING,
created_by=user)
customer = model.PendingCustomer(
status=enum.PendingCustomerStatus.PENDING, created_by=user
)
self.session.add(customer)
order = model.Order(order_id=42, created_by=user, pending_customer=customer)
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
view = self.make_view()
grid = view.make_grid(model_class=model.LocalProduct)
self.assertNotIn('scancode', grid.linked_columns)
self.assertNotIn('brand_name', grid.linked_columns)
self.assertNotIn('description', grid.linked_columns)
self.assertNotIn("scancode", grid.linked_columns)
self.assertNotIn("brand_name", grid.linked_columns)
self.assertNotIn("description", grid.linked_columns)
view.configure_grid(grid)
self.assertIn('scancode', grid.linked_columns)
self.assertIn('brand_name', grid.linked_columns)
self.assertIn('description', grid.linked_columns)
self.assertIn("scancode", grid.linked_columns)
self.assertIn("brand_name", grid.linked_columns)
self.assertIn("description", grid.linked_columns)
def test_configure_form(self):
model = self.app.model
view = self.make_view()
# creating
with patch.object(view, 'creating', new=True):
with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.LocalProduct)
self.assertIn('external_id', form)
self.assertIn("external_id", 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)
product = model.LocalProduct()
self.session.add(product)
self.session.commit()
# viewing
with patch.object(view, 'viewing', new=True):
with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=product)
self.assertNotIn('external_id', form.readonly_fields)
self.assertNotIn('local_products.view.orders', form.grid_vue_context)
self.assertNotIn("external_id", form.readonly_fields)
self.assertNotIn("local_products.view.orders", form.grid_vue_context)
view.configure_form(form)
self.assertIn('external_id', form.readonly_fields)
self.assertIn('local_products.view.orders', form.grid_vue_context)
self.assertIn("external_id", form.readonly_fields)
self.assertIn("local_products.view.orders", form.grid_vue_context)
def test_make_orders_grid(self):
model = self.app.model
enum = self.app.enum
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
order = model.Order(order_id=42, customer_id=42, created_by=user)
product = model.LocalProduct()
self.session.add(product)
item = model.OrderItem(local_product=product,
order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_INITIATED)
item = model.OrderItem(
local_product=product,
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_INITIATED,
)
order.items.append(item)
self.session.add(order)
self.session.commit()
@ -81,10 +84,10 @@ class TestLocalProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
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):
model = self.app.model
@ -92,14 +95,15 @@ class TestLocalProductView(WebTestCase):
handler = NewOrderBatchHandler(self.config)
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch)
product = model.LocalProduct()
self.session.add(product)
row = handler.make_row(local_product=product,
order_qty=1, order_uom=enum.ORDER_UOM_UNIT)
row = handler.make_row(
local_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT
)
handler.add_row(batch, row)
self.session.commit()
@ -108,10 +112,10 @@ class TestLocalProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
self.assertEqual(len(grid.actions), 1)
self.assertEqual(grid.actions[0].key, 'view')
self.assertEqual(grid.actions[0].key, "view")
class TestPendingProductView(WebTestCase):
@ -124,13 +128,13 @@ class TestPendingProductView(WebTestCase):
view = self.make_view()
# nb. mostly just getting coverage here
grid = view.make_grid(model_class=model.PendingProduct)
self.assertNotIn('scancode', grid.linked_columns)
self.assertNotIn('brand_name', grid.linked_columns)
self.assertNotIn('description', grid.linked_columns)
self.assertNotIn("scancode", grid.linked_columns)
self.assertNotIn("brand_name", grid.linked_columns)
self.assertNotIn("description", grid.linked_columns)
view.configure_grid(grid)
self.assertIn('scancode', grid.linked_columns)
self.assertIn('brand_name', grid.linked_columns)
self.assertIn('description', grid.linked_columns)
self.assertIn("scancode", grid.linked_columns)
self.assertIn("brand_name", grid.linked_columns)
self.assertIn("description", grid.linked_columns)
def test_grid_row_class(self):
enum = self.app.enum
@ -143,7 +147,7 @@ class TestPendingProductView(WebTestCase):
# warning for 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):
model = self.app.model
@ -151,41 +155,46 @@ class TestPendingProductView(WebTestCase):
view = self.make_view()
# creating
with patch.object(view, 'creating', new=True):
with patch.object(view, "creating", new=True):
form = view.make_form(model_class=model.PendingProduct)
view.configure_form(form)
self.assertNotIn('created', form)
self.assertNotIn('created_by', form)
self.assertNotIn("created", form)
self.assertNotIn("created_by", form)
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
self.session.commit()
# viewing
with patch.object(view, 'viewing', new=True):
with patch.object(view, "viewing", new=True):
form = view.make_form(model_instance=product)
view.configure_form(form)
self.assertIn('status', form)
self.assertIn('created', form)
self.assertIn('created_by', form)
self.assertIn("status", form)
self.assertIn("created", form)
self.assertIn("created_by", form)
def test_make_orders_grid(self):
model = self.app.model
enum = self.app.enum
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
order = model.Order(order_id=42, customer_id=42, created_by=user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
item = model.OrderItem(pending_product=product,
order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_INITIATED)
item = model.OrderItem(
pending_product=product,
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_INITIATED,
)
order.items.append(item)
self.session.add(order)
self.session.commit()
@ -195,10 +204,10 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
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):
model = self.app.model
@ -206,15 +215,17 @@ class TestPendingProductView(WebTestCase):
handler = NewOrderBatchHandler(self.config)
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
row = handler.make_row(pending_product=product,
order_qty=1, order_uom=enum.ORDER_UOM_UNIT)
row = handler.make_row(
pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT
)
handler.add_row(batch, row)
self.session.commit()
@ -223,57 +234,60 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(len(grid.actions), 0)
# 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)
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):
enum = self.app.enum
model = self.app.model
view = self.make_view()
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING)
orig_context = {'instance': product}
orig_context = {"instance": product}
# local setting omitted by default
context = view.get_template_context(orig_context)
self.assertNotIn('use_local_products', context)
self.assertNotIn("use_local_products", context)
# 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)
self.assertNotIn('use_local_products', context)
self.assertNotIn("use_local_products", context)
# still omitted even though correct status
product.status = enum.PendingProductStatus.READY
context = view.get_template_context(orig_context)
self.assertNotIn('use_local_products', context)
self.assertNotIn("use_local_products", context)
# 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)
self.assertIn('use_local_products', context)
self.assertIn("use_local_products", context)
# nb. true by default
self.assertTrue(context['use_local_products'])
self.assertTrue(context["use_local_products"])
# 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)
self.assertFalse(context['use_local_products'])
self.assertFalse(context["use_local_products"])
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
enum = self.app.enum
handler = NewOrderBatchHandler(self.config)
view = self.make_view()
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
# 1st product is standalone, will be deleted
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
self.session.flush()
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
batch = handler.make_batch(self.session, created_by=user)
self.session.add(batch)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
row = handler.make_row(pending_product=product,
order_qty=1, order_uom=enum.ORDER_UOM_UNIT)
row = handler.make_row(
pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT
)
handler.add_row(batch, row)
self.session.flush()
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)
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
enum = self.app.enum
view = self.make_view()
# sample data
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
self.session.flush()
info = {
'product_id': '07430500132',
'scancode': '07430500132',
'brand_name': "Bragg's",
'description': "Apple Cider Vinegar",
'size': "32oz",
'weighed': False,
'department_id': None,
'department_name': None,
'special_order': False,
'vendor_name': None,
'vendor_item_code': None,
'case_size': 12,
'unit_cost': 2.99,
'unit_price_reg': 5.99,
"product_id": "07430500132",
"scancode": "07430500132",
"brand_name": "Bragg's",
"description": "Apple Cider Vinegar",
"size": "32oz",
"weighed": False,
"department_id": None,
"department_name": None,
"special_order": False,
"vendor_name": None,
"vendor_item_code": None,
"case_size": 12,
"unit_cost": 2.99,
"unit_price_reg": 5.99,
}
with patch.object(view, 'Session', return_value=self.session):
with patch.object(self.request, 'user', new=user):
with patch.object(self.request, 'matchdict', new={'uuid': product.uuid}):
with patch.object(view, "Session", return_value=self.session):
with patch.object(self.request, "user", new=user):
with patch.object(
self.request, "matchdict", new={"uuid": product.uuid}
):
# flash error if wrong status
result = view.resolve()
self.assertIsInstance(result, HTTPFound)
self.assertTrue(self.request.session.peek_flash('error'))
self.assertEqual(self.request.session.pop_flash('error'),
["pending product does not have 'ready' status!"])
self.assertTrue(self.request.session.peek_flash("error"))
self.assertEqual(
self.request.session.pop_flash("error"),
["pending product does not have 'ready' status!"],
)
# flash error if product_id not specified
product.status = enum.PendingProductStatus.READY
result = view.resolve()
self.assertIsInstance(result, HTTPFound)
self.assertTrue(self.request.session.peek_flash('error'))
self.assertEqual(self.request.session.pop_flash('error'),
["must specify valid product_id"])
self.assertTrue(self.request.session.peek_flash("error"))
self.assertEqual(
self.request.session.pop_flash("error"),
["must specify valid product_id"],
)
# more sample data
order = model.Order(order_id=100, created_by=user,
customer_name="Fred Flintstone")
item = model.OrderItem(pending_product=product,
order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_READY)
order = model.Order(
order_id=100, created_by=user, customer_name="Fred Flintstone"
)
item = model.OrderItem(
pending_product=product,
order_qty=1,
order_uom=enum.ORDER_UOM_UNIT,
status_code=enum.ORDER_ITEM_STATUS_READY,
)
order.items.append(item)
self.session.add(order)
@ -369,45 +398,58 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(product.status, enum.PendingProductStatus.READY)
self.assertIsNone(item.product_id)
batch_handler = NewOrderBatchHandler(self.config)
with patch.object(batch_handler, 'get_product_info_external',
return_value=info):
with patch.object(self.app, 'get_batch_handler',
return_value=batch_handler):
with patch.object(self.request, 'POST',
new={'product_id': '07430500132'}):
with patch.object(batch_handler, 'get_product_info_external',
return_value=info):
with patch.object(
batch_handler, "get_product_info_external", return_value=info
):
with patch.object(
self.app, "get_batch_handler", return_value=batch_handler
):
with patch.object(
self.request, "POST", new={"product_id": "07430500132"}
):
with patch.object(
batch_handler,
"get_product_info_external",
return_value=info,
):
result = view.resolve()
self.assertIsInstance(result, HTTPFound)
self.assertFalse(self.request.session.peek_flash('error'))
self.assertEqual(product.product_id, '07430500132')
self.assertFalse(self.request.session.peek_flash("error"))
self.assertEqual(product.product_id, "07430500132")
self.assertEqual(product.status, enum.PendingProductStatus.RESOLVED)
self.assertEqual(item.product_id, '07430500132')
self.assertEqual(item.product_id, "07430500132")
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
enum = self.app.enum
view = self.make_view()
# sample data
user = model.User(username='barney')
user = model.User(username="barney")
self.session.add(user)
product = model.PendingProduct(status=enum.PendingProductStatus.PENDING,
created_by=user)
product = model.PendingProduct(
status=enum.PendingProductStatus.PENDING, created_by=user
)
self.session.add(product)
self.session.flush()
with patch.object(view, 'Session', return_value=self.session):
with patch.object(self.request, 'user', new=user):
with patch.object(self.request, 'matchdict', new={'uuid': product.uuid}):
with patch.object(view, "Session", return_value=self.session):
with patch.object(self.request, "user", new=user):
with patch.object(
self.request, "matchdict", new={"uuid": product.uuid}
):
# flash error if wrong status
result = view.ignore()
self.assertIsInstance(result, HTTPFound)
self.assertTrue(self.request.session.peek_flash('error'))
self.assertEqual(self.request.session.pop_flash('error'),
["pending product does not have 'ready' status!"])
self.assertTrue(self.request.session.peek_flash("error"))
self.assertEqual(
self.request.session.pop_flash("error"),
["pending product does not have 'ready' status!"],
)
# product updated
product.status = enum.PendingProductStatus.READY
@ -415,6 +457,6 @@ class TestPendingProductView(WebTestCase):
self.assertEqual(product.status, enum.PendingProductStatus.READY)
result = view.ignore()
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.assertEqual(product.status, enum.PendingProductStatus.IGNORED)

View file

@ -23,11 +23,11 @@ class TestStoreView(WebTestCase):
model = self.app.model
view = self.make_view()
grid = view.make_grid(model_class=model.Store)
self.assertNotIn('store_id', grid.linked_columns)
self.assertNotIn('name', grid.linked_columns)
self.assertNotIn("store_id", grid.linked_columns)
self.assertNotIn("name", grid.linked_columns)
view.configure_grid(grid)
self.assertIn('store_id', grid.linked_columns)
self.assertIn('name', grid.linked_columns)
self.assertIn("store_id", grid.linked_columns)
self.assertIn("name", grid.linked_columns)
def test_grid_row_class(self):
model = self.app.model
@ -39,7 +39,7 @@ class TestStoreView(WebTestCase):
store = model.Store(archived=True)
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):
model = self.app.model
@ -47,48 +47,48 @@ class TestStoreView(WebTestCase):
# unique validators are set
form = view.make_form(model_class=model.Store)
self.assertNotIn('store_id', form.validators)
self.assertNotIn('name', form.validators)
self.assertNotIn("store_id", form.validators)
self.assertNotIn("name", form.validators)
view.configure_form(form)
self.assertIn('store_id', form.validators)
self.assertIn('name', form.validators)
self.assertIn("store_id", form.validators)
self.assertIn("name", form.validators)
def test_unique_store_id(self):
model = self.app.model
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.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
node = colander.SchemaNode(colander.String(), name='store_id')
self.assertRaises(colander.Invalid, view.unique_store_id, node, '001')
node = colander.SchemaNode(colander.String(), name="store_id")
self.assertRaises(colander.Invalid, view.unique_store_id, node, "001")
# but not if store_id belongs to current store
with patch.object(self.request, 'matchdict', new={'uuid': store.uuid}):
with patch.object(view, 'editing', new=True):
node = colander.SchemaNode(colander.String(), name='store_id')
self.assertIsNone(view.unique_store_id(node, '001'))
with patch.object(self.request, "matchdict", new={"uuid": store.uuid}):
with patch.object(view, "editing", new=True):
node = colander.SchemaNode(colander.String(), name="store_id")
self.assertIsNone(view.unique_store_id(node, "001"))
def test_unique_name(self):
model = self.app.model
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.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
node = colander.SchemaNode(colander.String(), name='name')
self.assertRaises(colander.Invalid, view.unique_name, node, 'Acme Goods')
node = colander.SchemaNode(colander.String(), name="name")
self.assertRaises(colander.Invalid, view.unique_name, node, "Acme Goods")
# but not if name belongs to current store
with patch.object(self.request, 'matchdict', new={'uuid': store.uuid}):
with patch.object(view, 'editing', new=True):
node = colander.SchemaNode(colander.String(), name='name')
self.assertIsNone(view.unique_name(node, 'Acme Goods'))
with patch.object(self.request, "matchdict", new={"uuid": store.uuid}):
with patch.object(view, "editing", new=True):
node = colander.SchemaNode(colander.String(), name="name")
self.assertIsNone(view.unique_name(node, "Acme Goods"))