fix: customize "view order item" page w/ panels
more to come soon..
This commit is contained in:
parent
13d576295e
commit
c79b0262f3
6
docs/api/sideshow.orders.rst
Normal file
6
docs/api/sideshow.orders.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``sideshow.orders``
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. automodule:: sideshow.orders
|
||||||
|
:members:
|
|
@ -45,12 +45,27 @@ Glossary
|
||||||
"submits" the order, the batch is executed which creates a true
|
"submits" the order, the batch is executed which creates a true
|
||||||
:term:`order`.
|
:term:`order`.
|
||||||
|
|
||||||
|
The batch handler is responsible for business logic for the order
|
||||||
|
creation step; the :term:`order handler` is responsible for
|
||||||
|
everything thereafter.
|
||||||
|
|
||||||
|
:class:`~sideshow.batch.neworder.NewOrderBatchHandler` is the
|
||||||
|
default handler for this.
|
||||||
|
|
||||||
order
|
order
|
||||||
This is the central focus of the app; it refers to a customer
|
This is the central focus of the app; it refers to a customer
|
||||||
case/special order which is tracked over time, from placement to
|
case/special order which is tracked over time, from placement to
|
||||||
fulfillment. Each order may have one or more :term:`order items
|
fulfillment. Each order may have one or more :term:`order items
|
||||||
<order item>`.
|
<order item>`.
|
||||||
|
|
||||||
|
order handler
|
||||||
|
The :term:`handler` responsible for business logic surrounding
|
||||||
|
:term:`order` workflows *after* initial creation. (Whereas the
|
||||||
|
:term:`new order batch` handler is responsible for creation.)
|
||||||
|
|
||||||
|
:class:`~sideshow.orders.OrderHandler` is the default handler for
|
||||||
|
this.
|
||||||
|
|
||||||
order item
|
order item
|
||||||
This is effectively a "line item" within an :term:`order`. It
|
This is effectively a "line item" within an :term:`order`. It
|
||||||
represents a particular product, with quantity and pricing
|
represents a particular product, with quantity and pricing
|
||||||
|
|
|
@ -39,6 +39,7 @@ the narrative docs are pretty scant. That will eventually change.
|
||||||
api/sideshow.db.model.orders
|
api/sideshow.db.model.orders
|
||||||
api/sideshow.db.model.products
|
api/sideshow.db.model.products
|
||||||
api/sideshow.enum
|
api/sideshow.enum
|
||||||
|
api/sideshow.orders
|
||||||
api/sideshow.web
|
api/sideshow.web
|
||||||
api/sideshow.web.app
|
api/sideshow.web.app
|
||||||
api/sideshow.web.forms
|
api/sideshow.web.forms
|
||||||
|
|
|
@ -44,6 +44,9 @@ class NewOrderBatchHandler(BatchHandler):
|
||||||
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch` tracks
|
:class:`~sideshow.db.model.batch.neworder.NewOrderBatch` tracks
|
||||||
all user input until they "submit" (execute) at which point an
|
all user input until they "submit" (execute) at which point an
|
||||||
:class:`~sideshow.db.model.orders.Order` is created.
|
:class:`~sideshow.db.model.orders.Order` is created.
|
||||||
|
|
||||||
|
After the batch has executed the :term:`order handler` takes over
|
||||||
|
responsibility for the rest of the order lifecycle.
|
||||||
"""
|
"""
|
||||||
model_class = NewOrderBatch
|
model_class = NewOrderBatch
|
||||||
|
|
||||||
|
|
77
src/sideshow/orders.py
Normal file
77
src/sideshow/orders.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Sideshow -- Case/Special Order Tracker
|
||||||
|
# Copyright © 2024-2025 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Sideshow.
|
||||||
|
#
|
||||||
|
# Sideshow is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Sideshow is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Sideshow. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Sideshow Order Handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
from wuttjamaican.app import GenericHandler
|
||||||
|
|
||||||
|
|
||||||
|
class OrderHandler(GenericHandler):
|
||||||
|
"""
|
||||||
|
Base class and default implementation for the :term:`order
|
||||||
|
handler`.
|
||||||
|
|
||||||
|
This is responsible for business logic involving customer orders
|
||||||
|
after they have been first created. (The :term:`new order batch`
|
||||||
|
handler is responsible for creation logic.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_order_qty_uom_text(self, order_qty, order_uom, case_size=None, html=False):
|
||||||
|
"""
|
||||||
|
Return the display text for a given order quantity.
|
||||||
|
|
||||||
|
Default logic will return something like ``"3 Cases (x 6 = 18
|
||||||
|
Units)"``.
|
||||||
|
|
||||||
|
:param order_qty: Numeric quantity.
|
||||||
|
|
||||||
|
:param order_uom: An order UOM constant; should be something
|
||||||
|
from :data:`~sideshow.enum.ORDER_UOM`.
|
||||||
|
|
||||||
|
:param case_size: Case size for the product, if known.
|
||||||
|
|
||||||
|
:param html: Whether the return value should include any HTML.
|
||||||
|
If false (the default), it will be plain text only. If
|
||||||
|
true, will replace the ``x`` character with ``×``.
|
||||||
|
|
||||||
|
:returns: Display text.
|
||||||
|
"""
|
||||||
|
enum = self.app.enum
|
||||||
|
|
||||||
|
if order_uom == enum.ORDER_UOM_CASE:
|
||||||
|
if case_size is None:
|
||||||
|
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 = '×' if html else 'x'
|
||||||
|
return (f"{order_qty} {CS} ({times} {case_qty} = {unit_qty} {EA})")
|
||||||
|
|
||||||
|
# units
|
||||||
|
unit_qty = self.app.render_quantity(order_qty)
|
||||||
|
EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT]
|
||||||
|
return f"{unit_qty} {EA}"
|
154
src/sideshow/web/templates/order-items/view.mako
Normal file
154
src/sideshow/web/templates/order-items/view.mako
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/view.mako" />
|
||||||
|
|
||||||
|
<%def name="extra_styles()">
|
||||||
|
${parent.extra_styles()}
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.field .field-label .label {
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
<div style="padding: 2rem; display: flex; justify-content: space-evenly; gap: 2rem;">
|
||||||
|
<div style="flex-grow: 1;">
|
||||||
|
|
||||||
|
<nav class="panel" style="width: 100%;">
|
||||||
|
<p class="panel-heading">Order Item</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<b-field horizontal label="ID">
|
||||||
|
<span>Order ID ${order.order_id} — Item #${item.sequence}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Order Qty">
|
||||||
|
<span>${order_qty_uom_text|n}</span>
|
||||||
|
</b-field>
|
||||||
|
% if item.discount_percent:
|
||||||
|
<b-field horizontal label="Discount">
|
||||||
|
<span>${app.render_percent(item.discount_percent)}</span>
|
||||||
|
</b-field>
|
||||||
|
% endif
|
||||||
|
<b-field horizontal label="Total Due">
|
||||||
|
<span>${app.render_currency(item.total_price)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Total Paid">
|
||||||
|
<span>${app.render_currency(item.paid_amount)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Status">
|
||||||
|
<span>${app.enum.ORDER_ITEM_STATUS[item.status_code]}</span>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<nav class="panel" style="width: 100%;">
|
||||||
|
<p class="panel-heading">Pricing</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<b-field horizontal label="Unit Cost">
|
||||||
|
<span>${app.render_currency(item.unit_cost, scale=4)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Unit Price Reg.">
|
||||||
|
<span>${app.render_currency(item.unit_price_reg)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Unit Price Sale">
|
||||||
|
<span>${app.render_currency(item.unit_price_sale)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Sale Ends">
|
||||||
|
<span>${app.render_datetime(item.sale_ends)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Unit Price Quoted">
|
||||||
|
<span>${app.render_currency(item.unit_price_quoted)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Case Size">
|
||||||
|
<span>${app.render_quantity(item.case_size)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Case Price Quoted">
|
||||||
|
<span>${app.render_currency(item.case_price_quoted)}</span>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div style="flex-grow: 1;">
|
||||||
|
|
||||||
|
<nav class="panel" style="width: 100%;">
|
||||||
|
<p class="panel-heading">Customer</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<b-field horizontal label="Customer ID">
|
||||||
|
<span>${order.customer_id}</span>
|
||||||
|
</b-field>
|
||||||
|
% if not order.customer_id and order.local_customer:
|
||||||
|
<b-field horizontal label="Local Customer">
|
||||||
|
<span>${h.link_to(order.local_customer, url('local_customers.view', uuid=order.local_customer.uuid))}</span>
|
||||||
|
</b-field>
|
||||||
|
% endif
|
||||||
|
% if not order.customer_id and order.pending_customer:
|
||||||
|
<b-field horizontal label="Pending Customer">
|
||||||
|
<span>${h.link_to(order.pending_customer, url('pending_customers.view', uuid=order.pending_customer.uuid))}</span>
|
||||||
|
</b-field>
|
||||||
|
% endif
|
||||||
|
<b-field horizontal label="Customer Name">
|
||||||
|
<span>${order.customer_name}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Phone Number">
|
||||||
|
<span>${order.phone_number}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Email Address">
|
||||||
|
<span>${order.email_address}</span>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<nav class="panel" style="width: 100%;">
|
||||||
|
<p class="panel-heading">Product</p>
|
||||||
|
<div class="panel-block">
|
||||||
|
<div style="width: 100%;">
|
||||||
|
<b-field horizontal label="Product ID">
|
||||||
|
<span>${item.product_id}</span>
|
||||||
|
</b-field>
|
||||||
|
% if not item.product_id and item.local_product:
|
||||||
|
<b-field horizontal label="Local Product">
|
||||||
|
<span>${h.link_to(item.local_product, url('local_products.view', uuid=order.local_product.uuid))}</span>
|
||||||
|
</b-field>
|
||||||
|
% endif
|
||||||
|
% if not item.product_id and item.pending_product:
|
||||||
|
<b-field horizontal label="Pending Product">
|
||||||
|
<span>${h.link_to(item.pending_product, url('pending_products.view', uuid=order.pending_product.uuid))}</span>
|
||||||
|
</b-field>
|
||||||
|
% endif
|
||||||
|
<b-field horizontal label="Scancode">
|
||||||
|
<span>${item.product_scancode}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Brand">
|
||||||
|
<span>${item.product_brand}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Description">
|
||||||
|
<span>${item.product_description}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Size">
|
||||||
|
<span>${item.product_size}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Sold by Weight">
|
||||||
|
<span>${app.render_boolean(item.product_weighed)}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Department">
|
||||||
|
<span>${item.department_name}</span>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Special Order">
|
||||||
|
<span>${app.render_boolean(item.special_order)}</span>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</%def>
|
|
@ -34,6 +34,7 @@ from wuttaweb.views import MasterView
|
||||||
from wuttaweb.forms.schema import UserRef, WuttaMoney, WuttaQuantity, WuttaEnum, WuttaDictEnum
|
from wuttaweb.forms.schema import UserRef, WuttaMoney, WuttaQuantity, WuttaEnum, WuttaDictEnum
|
||||||
|
|
||||||
from sideshow.db.model import Order, OrderItem
|
from sideshow.db.model import Order, OrderItem
|
||||||
|
from sideshow.orders import OrderHandler
|
||||||
from sideshow.batch.neworder import NewOrderBatchHandler
|
from sideshow.batch.neworder import NewOrderBatchHandler
|
||||||
from sideshow.web.forms.schema import (OrderRef,
|
from sideshow.web.forms.schema import (OrderRef,
|
||||||
LocalCustomerRef, LocalProductRef,
|
LocalCustomerRef, LocalProductRef,
|
||||||
|
@ -58,10 +59,16 @@ class OrderView(MasterView):
|
||||||
Note that the "edit" view is not exposed here; user must perform
|
Note that the "edit" view is not exposed here; user must perform
|
||||||
various other workflow actions to modify the order.
|
various other workflow actions to modify the order.
|
||||||
|
|
||||||
|
.. attribute:: order_handler
|
||||||
|
|
||||||
|
Reference to the :term:`order handler` as returned by
|
||||||
|
:meth:`get_order_handler()`. This gets set in the constructor.
|
||||||
|
|
||||||
.. attribute:: batch_handler
|
.. attribute:: batch_handler
|
||||||
|
|
||||||
Reference to the new order batch handler, as returned by
|
Reference to the :term:`new order batch` handler, as returned
|
||||||
:meth:`get_batch_handler()`. This gets set in the constructor.
|
by :meth:`get_batch_handler()`. This gets set in the
|
||||||
|
constructor.
|
||||||
"""
|
"""
|
||||||
model_class = Order
|
model_class = Order
|
||||||
editable = False
|
editable = False
|
||||||
|
@ -142,21 +149,22 @@ class OrderView(MasterView):
|
||||||
'unit_price_reg',
|
'unit_price_reg',
|
||||||
]
|
]
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def __init__(self, request, context=None):
|
||||||
""" """
|
super().__init__(request, context=context)
|
||||||
super().configure_grid(g)
|
self.order_handler = self.get_order_handler()
|
||||||
|
|
||||||
# order_id
|
def get_order_handler(self):
|
||||||
g.set_link('order_id')
|
"""
|
||||||
|
Returns the configured :term:`order handler`.
|
||||||
|
|
||||||
# customer_id
|
You normally would not need to call this, and can use
|
||||||
g.set_link('customer_id')
|
:attr:`order_handler` instead.
|
||||||
|
|
||||||
# customer_name
|
:rtype: :class:`~sideshow.orders.OrderHandler`
|
||||||
g.set_link('customer_name')
|
"""
|
||||||
|
if hasattr(self, 'order_handler'):
|
||||||
# total_price
|
return self.order_handler
|
||||||
g.set_renderer('total_price', g.render_currency)
|
return OrderHandler(self.config)
|
||||||
|
|
||||||
def get_batch_handler(self):
|
def get_batch_handler(self):
|
||||||
"""
|
"""
|
||||||
|
@ -174,6 +182,22 @@ class OrderView(MasterView):
|
||||||
return self.batch_handler
|
return self.batch_handler
|
||||||
return self.app.get_batch_handler('neworder')
|
return self.app.get_batch_handler('neworder')
|
||||||
|
|
||||||
|
def configure_grid(self, g):
|
||||||
|
""" """
|
||||||
|
super().configure_grid(g)
|
||||||
|
|
||||||
|
# order_id
|
||||||
|
g.set_link('order_id')
|
||||||
|
|
||||||
|
# customer_id
|
||||||
|
g.set_link('customer_id')
|
||||||
|
|
||||||
|
# customer_name
|
||||||
|
g.set_link('customer_name')
|
||||||
|
|
||||||
|
# total_price
|
||||||
|
g.set_renderer('total_price', g.render_currency)
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
"""
|
"""
|
||||||
Instead of the typical "create" view, this displays a "wizard"
|
Instead of the typical "create" view, this displays a "wizard"
|
||||||
|
@ -670,8 +694,6 @@ class OrderView(MasterView):
|
||||||
|
|
||||||
def normalize_row(self, row):
|
def normalize_row(self, row):
|
||||||
""" """
|
""" """
|
||||||
enum = self.app.enum
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'uuid': row.uuid.hex,
|
'uuid': row.uuid.hex,
|
||||||
'sequence': row.sequence,
|
'sequence': row.sequence,
|
||||||
|
@ -750,21 +772,8 @@ class OrderView(MasterView):
|
||||||
}
|
}
|
||||||
|
|
||||||
# display text for order qty/uom
|
# display text for order qty/uom
|
||||||
if row.order_uom == enum.ORDER_UOM_CASE:
|
data['order_qty_display'] = self.order_handler.get_order_qty_uom_text(
|
||||||
order_qty = self.app.render_quantity(row.order_qty)
|
row.order_qty, row.order_uom, case_size=row.case_size, html=True)
|
||||||
if row.case_size is None:
|
|
||||||
case_qty = unit_qty = '??'
|
|
||||||
else:
|
|
||||||
case_qty = self.app.render_quantity(row.case_size)
|
|
||||||
unit_qty = self.app.render_quantity(row.order_qty * row.case_size)
|
|
||||||
CS = enum.ORDER_UOM[enum.ORDER_UOM_CASE]
|
|
||||||
EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT]
|
|
||||||
data['order_qty_display'] = (f"{order_qty} {CS} "
|
|
||||||
f"(× {case_qty} = {unit_qty} {EA})")
|
|
||||||
else:
|
|
||||||
unit_qty = self.app.render_quantity(row.order_qty)
|
|
||||||
EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT]
|
|
||||||
data['order_qty_display'] = f"{unit_qty} {EA}"
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
@ -823,7 +832,7 @@ class OrderView(MasterView):
|
||||||
def configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
""" """
|
""" """
|
||||||
super().configure_row_grid(g)
|
super().configure_row_grid(g)
|
||||||
enum = self.app.enum
|
# enum = self.app.enum
|
||||||
|
|
||||||
# sequence
|
# sequence
|
||||||
g.set_label('sequence', "Seq.", column_only=True)
|
g.set_label('sequence', "Seq.", column_only=True)
|
||||||
|
@ -959,6 +968,11 @@ class OrderItemView(MasterView):
|
||||||
|
|
||||||
Note that this does not expose create, edit or delete. The user
|
Note that this does not expose create, edit or delete. The user
|
||||||
must perform various other workflow actions to modify the item.
|
must perform various other workflow actions to modify the item.
|
||||||
|
|
||||||
|
.. attribute:: order_handler
|
||||||
|
|
||||||
|
Reference to the :term:`order handler` as returned by
|
||||||
|
:meth:`get_order_handler()`.
|
||||||
"""
|
"""
|
||||||
model_class = OrderItem
|
model_class = OrderItem
|
||||||
model_title = "Order Item"
|
model_title = "Order Item"
|
||||||
|
@ -1030,6 +1044,23 @@ class OrderItemView(MasterView):
|
||||||
'payment_transaction_number',
|
'payment_transaction_number',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, request, context=None):
|
||||||
|
super().__init__(request, context=context)
|
||||||
|
self.order_handler = self.get_order_handler()
|
||||||
|
|
||||||
|
def get_order_handler(self):
|
||||||
|
"""
|
||||||
|
Returns the configured :term:`order handler`.
|
||||||
|
|
||||||
|
You normally would not need to call this, and can use
|
||||||
|
:attr:`order_handler` instead.
|
||||||
|
|
||||||
|
:rtype: :class:`~sideshow.orders.OrderHandler`
|
||||||
|
"""
|
||||||
|
if hasattr(self, 'order_handler'):
|
||||||
|
return self.order_handler
|
||||||
|
return OrderHandler(self.config)
|
||||||
|
|
||||||
def get_query(self, session=None):
|
def get_query(self, session=None):
|
||||||
""" """
|
""" """
|
||||||
query = super().get_query(session=session)
|
query = super().get_query(session=session)
|
||||||
|
@ -1139,6 +1170,17 @@ class OrderItemView(MasterView):
|
||||||
# paid_amount
|
# paid_amount
|
||||||
f.set_node('paid_amount', WuttaMoney(self.request))
|
f.set_node('paid_amount', WuttaMoney(self.request))
|
||||||
|
|
||||||
|
def get_template_context(self, context):
|
||||||
|
""" """
|
||||||
|
if self.viewing:
|
||||||
|
item = context['instance']
|
||||||
|
context['item'] = item
|
||||||
|
context['order'] = item.order
|
||||||
|
context['order_qty_uom_text'] = self.order_handler.get_order_qty_uom_text(
|
||||||
|
item.order_qty, item.order_uom, case_size=item.case_size, html=True)
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
def get_xref_buttons(self, item):
|
def get_xref_buttons(self, item):
|
||||||
""" """
|
""" """
|
||||||
buttons = super().get_xref_buttons(item)
|
buttons = super().get_xref_buttons(item)
|
||||||
|
|
40
tests/test_orders.py
Normal file
40
tests/test_orders.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from wuttjamaican.testing import DataTestCase
|
||||||
|
|
||||||
|
from sideshow import orders as mod
|
||||||
|
|
||||||
|
|
||||||
|
class TestOrderHandler(DataTestCase):
|
||||||
|
|
||||||
|
def make_config(self, **kwargs):
|
||||||
|
config = super().make_config(**kwargs)
|
||||||
|
config.setdefault('wutta.enum_spec', 'sideshow.enum')
|
||||||
|
return config
|
||||||
|
|
||||||
|
def make_handler(self):
|
||||||
|
return mod.OrderHandler(self.config)
|
||||||
|
|
||||||
|
def test_get_order_qty_uom_text(self):
|
||||||
|
enum = self.app.enum
|
||||||
|
handler = self.make_handler()
|
||||||
|
|
||||||
|
# typical, plain text
|
||||||
|
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_CASE, case_size=12)
|
||||||
|
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)
|
||||||
|
self.assertEqual(text, "2 Cases (× 12 = 24 Units)")
|
||||||
|
|
||||||
|
# unknown case size
|
||||||
|
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_CASE)
|
||||||
|
self.assertEqual(text, "2 Cases (x ?? = ?? Units)")
|
||||||
|
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_CASE, html=True)
|
||||||
|
self.assertEqual(text, "2 Cases (× ?? = ?? Units)")
|
||||||
|
|
||||||
|
# units only
|
||||||
|
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_UNIT)
|
||||||
|
self.assertEqual(text, "2 Units")
|
||||||
|
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_UNIT, html=True)
|
||||||
|
self.assertEqual(text, "2 Units")
|
|
@ -11,6 +11,7 @@ from pyramid.response import Response
|
||||||
from wuttaweb.forms.schema import WuttaMoney
|
from wuttaweb.forms.schema import WuttaMoney
|
||||||
|
|
||||||
from sideshow.batch.neworder import NewOrderBatchHandler
|
from sideshow.batch.neworder import NewOrderBatchHandler
|
||||||
|
from sideshow.orders import OrderHandler
|
||||||
from sideshow.testing import WebTestCase
|
from sideshow.testing import WebTestCase
|
||||||
from sideshow.web.views import orders as mod
|
from sideshow.web.views import orders as mod
|
||||||
from sideshow.web.forms.schema import OrderRef, PendingProductRef
|
from sideshow.web.forms.schema import OrderRef, PendingProductRef
|
||||||
|
@ -30,6 +31,13 @@ class TestOrderView(WebTestCase):
|
||||||
def make_handler(self):
|
def make_handler(self):
|
||||||
return NewOrderBatchHandler(self.config)
|
return NewOrderBatchHandler(self.config)
|
||||||
|
|
||||||
|
def test_order_handler(self):
|
||||||
|
view = self.make_view()
|
||||||
|
handler = view.order_handler
|
||||||
|
self.assertIsInstance(handler, OrderHandler)
|
||||||
|
handler2 = view.get_order_handler()
|
||||||
|
self.assertIs(handler2, handler)
|
||||||
|
|
||||||
def test_configure_grid(self):
|
def test_configure_grid(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
|
@ -1249,6 +1257,13 @@ class TestOrderItemView(WebTestCase):
|
||||||
def make_view(self):
|
def make_view(self):
|
||||||
return mod.OrderItemView(self.request)
|
return mod.OrderItemView(self.request)
|
||||||
|
|
||||||
|
def test_order_handler(self):
|
||||||
|
view = self.make_view()
|
||||||
|
handler = view.order_handler
|
||||||
|
self.assertIsInstance(handler, OrderHandler)
|
||||||
|
handler2 = view.get_order_handler()
|
||||||
|
self.assertIs(handler2, handler)
|
||||||
|
|
||||||
def test_get_query(self):
|
def test_get_query(self):
|
||||||
view = self.make_view()
|
view = self.make_view()
|
||||||
query = view.get_query(session=self.session)
|
query = view.get_query(session=self.session)
|
||||||
|
@ -1314,6 +1329,24 @@ class TestOrderItemView(WebTestCase):
|
||||||
self.assertIsInstance(schema['order'].typ, OrderRef)
|
self.assertIsInstance(schema['order'].typ, OrderRef)
|
||||||
self.assertNotIn('pending_product', form)
|
self.assertNotIn('pending_product', form)
|
||||||
|
|
||||||
|
def test_get_template_context(self):
|
||||||
|
model = self.app.model
|
||||||
|
enum = self.app.enum
|
||||||
|
view = self.make_view()
|
||||||
|
|
||||||
|
order = model.Order()
|
||||||
|
item = model.OrderItem(order_qty=2, order_uom=enum.ORDER_UOM_CASE, case_size=8)
|
||||||
|
order.items.append(item)
|
||||||
|
|
||||||
|
with patch.object(view, 'viewing', new=True):
|
||||||
|
context = view.get_template_context({'instance': item})
|
||||||
|
self.assertIn('item', context)
|
||||||
|
self.assertIs(context['item'], item)
|
||||||
|
self.assertIn('order', context)
|
||||||
|
self.assertIs(context['order'], order)
|
||||||
|
self.assertIn('order_qty_uom_text', context)
|
||||||
|
self.assertEqual(context['order_qty_uom_text'], "2 Cases (× 8 = 16 Units)")
|
||||||
|
|
||||||
def test_get_xref_buttons(self):
|
def test_get_xref_buttons(self):
|
||||||
self.pyramid_config.add_route('orders.view', '/orders/{uuid}')
|
self.pyramid_config.add_route('orders.view', '/orders/{uuid}')
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
Loading…
Reference in a new issue