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):
@ -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.
@ -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.
@ -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
@ -906,17 +935,17 @@ class NewOrderBatchHandler(BatchHandler):
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):
"""
@ -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

@ -38,8 +38,10 @@ def install(
"""
config = ctx.parent.wutta_config
app = config.get_app()
install = app.get_install_handler(pkg_name='sideshow',
install = app.get_install_handler(
pkg_name="sideshow",
app_title="Sideshow",
pypi_name='Sideshow',
egg_name='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 = {
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,14 +165,19 @@ 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
@ -164,52 +200,61 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
"""
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 = 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,10 +102,11 @@ 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"
########################################
@ -206,7 +210,8 @@ ORDER_ITEM_STATUS_INACTIVE = 950
Indicates the order item has become inactive.
"""
ORDER_ITEM_STATUS = OrderedDict([
ORDER_ITEM_STATUS = OrderedDict(
[
(ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"),
(ORDER_ITEM_STATUS_INITIATED, "initiated"),
(ORDER_ITEM_STATUS_PAID_BEFORE, "paid"),
@ -223,7 +228,8 @@ ORDER_ITEM_STATUS = OrderedDict([
(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.
@ -366,7 +372,8 @@ 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 = OrderedDict(
[
(ORDER_ITEM_EVENT_INITIATED, "initiated"),
(ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"),
(ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"),
@ -388,7 +395,8 @@ ORDER_ITEM_EVENT = OrderedDict([
(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,
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'
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)\
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)\
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',
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',
"order_id",
"total_price",
"created",
"created_by",
],
labels={
'order_id': "Order ID",
})
grid.set_renderer('total_price', grid.render_currency)
"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',
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',
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
'id': "Batch ID",
"id": "Batch ID",
},
renderers={
'id': 'batch_id',
'total_price': 'currency',
})
"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',
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',
"order_id",
"total_price",
"created",
"created_by",
],
labels={
'order_id': "Order ID",
})
grid.set_renderer('total_price', grid.render_currency)
"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',
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',
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
'id': "Batch ID",
"id": "Batch ID",
},
renderers={
'id': 'batch_id',
'total_price': 'currency',
})
"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',
grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order,
data=orders,
columns=[
'order_id',
'total_price',
'created',
'created_by',
"order_id",
"total_price",
"created",
"created_by",
],
labels={
'order_id': "Order ID",
"order_id": "Order ID",
},
renderers={
'total_price': 'currency',
})
"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',
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',
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
'id': "Batch ID",
'status_code': "Status",
"id": "Batch ID",
"status_code": "Status",
},
renderers={
'id': 'batch_id',
})
"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',
grid = self.make_grid(
key=f"{route_prefix}.view.orders",
model_class=model.Order,
data=orders,
columns=[
'order_id',
'total_price',
'created',
'created_by',
"order_id",
"total_price",
"created",
"created_by",
],
labels={
'order_id': "Order ID",
"order_id": "Order ID",
},
renderers={
'total_price': 'currency',
})
"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',
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',
"id",
"total_price",
"created",
"created_by",
"executed",
],
labels={
'id': "Batch ID",
'status_code': "Status",
"id": "Batch ID",
"status_code": "Status",
},
renderers={
'id': 'batch_id',
})
"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,
pending = model.PendingProduct(
description="vinegar",
unit_price_reg=5.99,
status=enum.PendingProductStatus.PENDING,
created_by=user)
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,
pending2 = model.PendingProduct(
description="vinegar",
unit_price_reg=5.99,
status=enum.PendingProductStatus.READY,
created_by=user)
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"))