feat: add initial support for order item events
so far just attaching events on creation, but then can view them
This commit is contained in:
parent
c79b0262f3
commit
b4deea76e0
|
@ -994,9 +994,35 @@ class NewOrderBatchHandler(BatchHandler):
|
|||
order.items.append(item)
|
||||
|
||||
# set item status
|
||||
item.status_code = enum.ORDER_ITEM_STATUS_INITIATED
|
||||
self.set_initial_item_status(item, user)
|
||||
|
||||
self.app.progress_loop(convert, rows, progress,
|
||||
message="Converting batch rows to order items")
|
||||
session.flush()
|
||||
return order
|
||||
|
||||
def set_initial_item_status(self, item, user, **kwargs):
|
||||
"""
|
||||
Set the initial status and attach event(s) for the given item.
|
||||
|
||||
This is called from :meth:`make_new_order()` for each item
|
||||
after it is added to the order.
|
||||
|
||||
Default logic will set status to
|
||||
:data:`~sideshow.enum.ORDER_ITEM_STATUS_READY` and attach 2
|
||||
events:
|
||||
|
||||
* :data:`~sideshow.enum.ORDER_ITEM_EVENT_INITIATED`
|
||||
* :data:`~sideshow.enum.ORDER_ITEM_EVENT_READY`
|
||||
|
||||
:param item: :class:`~sideshow.db.model.orders.OrderItem`
|
||||
being added to the new order.
|
||||
|
||||
:param user:
|
||||
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
|
||||
is performing the action.
|
||||
"""
|
||||
enum = self.app.enum
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_INITIATED, user)
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_READY, user)
|
||||
item.status_code = enum.ORDER_ITEM_STATUS_READY
|
||||
|
|
|
@ -156,6 +156,19 @@ def upgrade() -> None:
|
|||
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'))
|
||||
)
|
||||
|
||||
# sideshow_batch_neworder
|
||||
op.create_table('sideshow_batch_neworder',
|
||||
sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False),
|
||||
|
@ -227,6 +240,9 @@ def downgrade() -> None:
|
|||
op.drop_table('sideshow_batch_neworder_row')
|
||||
op.drop_table('sideshow_batch_neworder')
|
||||
|
||||
# sideshow_order_item_event
|
||||
op.drop_table('sideshow_order_item_event')
|
||||
|
||||
# sideshow_order_item
|
||||
op.drop_table('sideshow_order_item')
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ Primary :term:`data models <data model>`:
|
|||
|
||||
* :class:`~sideshow.db.model.orders.Order`
|
||||
* :class:`~sideshow.db.model.orders.OrderItem`
|
||||
* :class:`~sideshow.db.model.orders.OrderItemEvent`
|
||||
* :class:`~sideshow.db.model.customers.LocalCustomer`
|
||||
* :class:`~sideshow.db.model.products.LocalProduct`
|
||||
* :class:`~sideshow.db.model.customers.PendingCustomer`
|
||||
|
@ -49,7 +50,7 @@ from wuttjamaican.db.model import *
|
|||
# sideshow models
|
||||
from .customers import LocalCustomer, PendingCustomer
|
||||
from .products import LocalProduct, PendingProduct
|
||||
from .orders import Order, OrderItem
|
||||
from .orders import Order, OrderItem, OrderItemEvent
|
||||
|
||||
# batch models
|
||||
from .batch.neworder import NewOrderBatch, NewOrderBatchRow
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Sideshow -- Case/Special Order Tracker
|
||||
# Copyright © 2024 Lance Edgar
|
||||
# Copyright © 2024-2025 Lance Edgar
|
||||
#
|
||||
# This file is part of Sideshow.
|
||||
#
|
||||
|
@ -332,6 +332,16 @@ class OrderItem(model.Base):
|
|||
applicable/known.
|
||||
""")
|
||||
|
||||
events = orm.relationship(
|
||||
'OrderItemEvent',
|
||||
order_by='OrderItemEvent.occurred, OrderItemEvent.uuid',
|
||||
cascade='all, delete-orphan',
|
||||
cascade_backrefs=False,
|
||||
back_populates='item',
|
||||
doc="""
|
||||
List of :class:`OrderItemEvent` records for the item.
|
||||
""")
|
||||
|
||||
@property
|
||||
def full_description(self):
|
||||
""" """
|
||||
|
@ -344,3 +354,52 @@ class OrderItem(model.Base):
|
|||
|
||||
def __str__(self):
|
||||
return self.full_description
|
||||
|
||||
def add_event(self, type_code, user, **kwargs):
|
||||
"""
|
||||
Convenience method to add a new :class:`OrderItemEvent` for
|
||||
the item.
|
||||
"""
|
||||
kwargs['type_code'] = type_code
|
||||
kwargs['actor'] = user
|
||||
self.events.append(OrderItemEvent(**kwargs))
|
||||
|
||||
|
||||
class OrderItemEvent(model.Base):
|
||||
"""
|
||||
An event in the life of an :term:`order item`.
|
||||
"""
|
||||
__tablename__ = 'sideshow_order_item_event'
|
||||
|
||||
uuid = model.uuid_column()
|
||||
|
||||
item_uuid = model.uuid_fk_column('sideshow_order_item.uuid', nullable=False)
|
||||
item = orm.relationship(
|
||||
OrderItem,
|
||||
cascade_backrefs=False,
|
||||
back_populates='events',
|
||||
doc="""
|
||||
Reference to the :class:`OrderItem` to which the event
|
||||
pertains.
|
||||
""")
|
||||
|
||||
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="""
|
||||
Date and time when the event occurred.
|
||||
""")
|
||||
|
||||
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="""
|
||||
Optional note recorded for the event.
|
||||
""")
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Sideshow -- Case/Special Order Tracker
|
||||
# Copyright © 2024 Lance Edgar
|
||||
# Copyright © 2024-2025 Lance Edgar
|
||||
#
|
||||
# This file is part of Sideshow.
|
||||
#
|
||||
|
@ -108,23 +108,101 @@ class PendingProductStatus(Enum):
|
|||
########################################
|
||||
|
||||
ORDER_ITEM_STATUS_UNINITIATED = 1
|
||||
"""
|
||||
Indicates the item is "not yet initiated" - this probably is not
|
||||
useful but exists as a possibility just in case.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_INITIATED = 10
|
||||
"""
|
||||
Indicates the item is "initiated" (aka. created) but not yet "ready"
|
||||
for buyer/PO. This may imply the price needs confirmation etc.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_PAID_BEFORE = 50
|
||||
"""
|
||||
Indicates the customer has fully paid for the item, up-front before
|
||||
the buyer places PO etc. It implies the item is not yet "ready" for
|
||||
some reason.
|
||||
"""
|
||||
|
||||
# TODO: deprecate / remove this one
|
||||
ORDER_ITEM_STATUS_PAID = ORDER_ITEM_STATUS_PAID_BEFORE
|
||||
|
||||
ORDER_ITEM_STATUS_READY = 100
|
||||
"""
|
||||
Indicates the item is "ready" for buyer to include it on a vendor
|
||||
purchase order.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_PLACED = 200
|
||||
"""
|
||||
Indicates the buyer has placed a vendor purchase order which includes
|
||||
this item. The item is thereby "on order" until the truck arrives.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_RECEIVED = 300
|
||||
"""
|
||||
Indicates the item has been received as part of a vendor delivery.
|
||||
The item is thereby "on hand" until customer comes in for pickup.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_CONTACTED = 350
|
||||
"""
|
||||
Indicates the customer has been notified that the item is "on hand"
|
||||
and awaiting their pickup.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_CONTACT_FAILED = 375
|
||||
"""
|
||||
Indicates the attempt(s) to notify customer have failed. The item is
|
||||
on hand but the customer does not know to pickup.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_DELIVERED = 500
|
||||
"""
|
||||
Indicates the customer has picked up the item.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_PAID_AFTER = 550
|
||||
"""
|
||||
Indicates the customer has fully paid for the item, as part of their
|
||||
pickup. This completes the cycle for orders which require payment on
|
||||
the tail end.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_CANCELED = 900
|
||||
"""
|
||||
Indicates the order item has been canceled.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_REFUND_PENDING = 910
|
||||
"""
|
||||
Indicates the order item has been canceled, and the customer is due a
|
||||
(pending) refund.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_REFUNDED = 920
|
||||
"""
|
||||
Indicates the order item has been canceled, and the customer has been
|
||||
given a refund.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_RESTOCKED = 930
|
||||
"""
|
||||
Indicates the product has been restocked, e.g. after the order item
|
||||
was canceled.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_EXPIRED = 940
|
||||
"""
|
||||
Indicates the order item and/or product has expired.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS_INACTIVE = 950
|
||||
"""
|
||||
Indicates the order item has become inactive.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_STATUS = OrderedDict([
|
||||
(ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"),
|
||||
|
@ -144,3 +222,169 @@ ORDER_ITEM_STATUS = OrderedDict([
|
|||
(ORDER_ITEM_STATUS_EXPIRED, "expired"),
|
||||
(ORDER_ITEM_STATUS_INACTIVE, "inactive"),
|
||||
])
|
||||
"""
|
||||
Dict of possible code -> label options for :term:`order item` status.
|
||||
|
||||
These codes are referenced by:
|
||||
|
||||
* :attr:`sideshow.db.model.orders.OrderItem.status_code`
|
||||
"""
|
||||
|
||||
|
||||
########################################
|
||||
# Order Item Event Type
|
||||
########################################
|
||||
|
||||
ORDER_ITEM_EVENT_INITIATED = 10
|
||||
"""
|
||||
Indicates the item was "initiated" - this occurs when the
|
||||
:term:`order` is first created.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_PRICE_CONFIRMED = 20
|
||||
"""
|
||||
Indicates the item's price was confirmed by a user who is authorized
|
||||
to do that.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_PAYMENT_RECEIVED = 50
|
||||
"""
|
||||
Indicates payment was received for the item. This may occur toward
|
||||
the beginning, or toward the end, of the item's life cycle depending
|
||||
on app configuration etc.
|
||||
"""
|
||||
|
||||
# TODO: deprecate / remove this
|
||||
ORDER_ITEM_EVENT_PAID = ORDER_ITEM_EVENT_PAYMENT_RECEIVED
|
||||
|
||||
ORDER_ITEM_EVENT_READY = 100
|
||||
"""
|
||||
Indicates the item has become "ready" for buyer placement on a new
|
||||
vendor purchase order. Often this will occur when the :term:`order`
|
||||
is first created, if the data is suitable. However this may be
|
||||
delayed if e.g. the price needs confirmation.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_CUSTOMER_RESOLVED = 120
|
||||
"""
|
||||
Indicates the customer for the :term:`order` has been assigned to a
|
||||
"proper" (existing) account. This may happen (after the fact) if the
|
||||
order was first created with a new/unknown customer.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_PRODUCT_RESOLVED = 140
|
||||
"""
|
||||
Indicates the product for the :term:`order item` has been assigned to
|
||||
a "proper" (existing) product record. This may happen (after the
|
||||
fact) if the order was first created with a new/unknown product.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_PLACED = 200
|
||||
"""
|
||||
Indicates the buyer has placed a vendor purchase order which includes
|
||||
this item. So the item is "on order" until the truck arrives.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_RECEIVED = 300
|
||||
"""
|
||||
Indicates the receiver has found the item while receiving a vendor
|
||||
delivery. The item is set aside and is "on hand" until customer comes
|
||||
in to pick it up.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_CONTACTED = 350
|
||||
"""
|
||||
Indicates the customer has been contacted, to notify them of the item
|
||||
being on hand and ready for pickup.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_CONTACT_FAILED = 375
|
||||
"""
|
||||
Indicates an attempt was made to contact the customer, to notify them
|
||||
of item being on hand, but the attempt failed, e.g. due to bad phone
|
||||
or email on file.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_DELIVERED = 500
|
||||
"""
|
||||
Indicates the customer has picked up the item.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_STATUS_CHANGE = 700
|
||||
"""
|
||||
Indicates a manual status change was made. Such an event should
|
||||
ideally contain a note with further explanation.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_NOTE_ADDED = 750
|
||||
"""
|
||||
Indicates an arbitrary note was added.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_CANCELED = 900
|
||||
"""
|
||||
Indicates the :term:`order item` was canceled.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_REFUND_PENDING = 910
|
||||
"""
|
||||
Indicates the customer is due a (pending) refund for the item.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_REFUNDED = 920
|
||||
"""
|
||||
Indicates the customer has been refunded for the item.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_RESTOCKED = 930
|
||||
"""
|
||||
Indicates the product has been restocked, e.g. due to the order item
|
||||
being canceled.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_EXPIRED = 940
|
||||
"""
|
||||
Indicates the order item (or its product) has expired.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_INACTIVE = 950
|
||||
"""
|
||||
Indicates the order item has become inactive.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT_OTHER = 999
|
||||
"""
|
||||
Arbitrary event type which does not signify anything in particular.
|
||||
If used, the event should be given an explanatory note.
|
||||
"""
|
||||
|
||||
ORDER_ITEM_EVENT = OrderedDict([
|
||||
(ORDER_ITEM_EVENT_INITIATED, "initiated"),
|
||||
(ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"),
|
||||
(ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"),
|
||||
(ORDER_ITEM_EVENT_READY, "ready to proceed"),
|
||||
(ORDER_ITEM_EVENT_CUSTOMER_RESOLVED, "customer resolved"),
|
||||
(ORDER_ITEM_EVENT_PRODUCT_RESOLVED, "product resolved"),
|
||||
(ORDER_ITEM_EVENT_PLACED, "placed with vendor"),
|
||||
(ORDER_ITEM_EVENT_RECEIVED, "received from vendor"),
|
||||
(ORDER_ITEM_EVENT_CONTACTED, "customer contacted"),
|
||||
(ORDER_ITEM_EVENT_CONTACT_FAILED, "contact failed"),
|
||||
(ORDER_ITEM_EVENT_DELIVERED, "delivered"),
|
||||
(ORDER_ITEM_EVENT_STATUS_CHANGE, "status change"),
|
||||
(ORDER_ITEM_EVENT_NOTE_ADDED, "note added"),
|
||||
(ORDER_ITEM_EVENT_CANCELED, "canceled"),
|
||||
(ORDER_ITEM_EVENT_REFUND_PENDING, "refund pending"),
|
||||
(ORDER_ITEM_EVENT_REFUNDED, "refunded"),
|
||||
(ORDER_ITEM_EVENT_RESTOCKED, "restocked"),
|
||||
(ORDER_ITEM_EVENT_EXPIRED, "expired"),
|
||||
(ORDER_ITEM_EVENT_INACTIVE, "inactive"),
|
||||
(ORDER_ITEM_EVENT_OTHER, "other"),
|
||||
])
|
||||
"""
|
||||
Dict of possible code -> label options for :term:`order item` event
|
||||
types.
|
||||
|
||||
These codes are referenced by:
|
||||
|
||||
* :attr:`sideshow.db.model.orders.OrderItemEvent.type_code`
|
||||
"""
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
<b-field horizontal label="ID">
|
||||
<span>Order ID ${order.order_id} — Item #${item.sequence}</span>
|
||||
<span>${h.link_to(f"Order ID {order.order_id}", url('orders.view', uuid=order.uuid))} — Item #${item.sequence}</span>
|
||||
</b-field>
|
||||
<b-field horizontal label="Order Qty">
|
||||
<span>${order_qty_uom_text|n}</span>
|
||||
|
@ -151,4 +151,40 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 0 2rem;">
|
||||
<nav class="panel" style="width: 100%;">
|
||||
<p class="panel-heading">Events</p>
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
${events_grid.render_table_element()}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="render_vue_templates()">
|
||||
${parent.render_vue_templates()}
|
||||
${events_grid.render_vue_template()}
|
||||
</%def>
|
||||
|
||||
<%def name="modify_vue_vars()">
|
||||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
## TODO: ugh the hackiness
|
||||
ThisPageData.gridContext = {
|
||||
% for key, data in form.grid_vue_context.items():
|
||||
'${key}': ${json.dumps(data)|n},
|
||||
% endfor
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_vue_components()">
|
||||
${parent.make_vue_components()}
|
||||
${events_grid.render_vue_finalize()}
|
||||
</%def>
|
||||
|
|
|
@ -30,6 +30,8 @@ import logging
|
|||
import colander
|
||||
from sqlalchemy import orm
|
||||
|
||||
from webhelpers2.html import tags
|
||||
|
||||
from wuttaweb.views import MasterView
|
||||
from wuttaweb.forms.schema import UserRef, WuttaMoney, WuttaQuantity, WuttaEnum, WuttaDictEnum
|
||||
|
||||
|
@ -1173,12 +1175,38 @@ class OrderItemView(MasterView):
|
|||
def get_template_context(self, context):
|
||||
""" """
|
||||
if self.viewing:
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
route_prefix = self.get_route_prefix()
|
||||
item = context['instance']
|
||||
form = context['form']
|
||||
|
||||
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)
|
||||
|
||||
grid = self.make_grid(key=f'{route_prefix}.view.events',
|
||||
model_class=model.OrderItemEvent,
|
||||
data=item.events,
|
||||
columns=[
|
||||
'occurred',
|
||||
'actor',
|
||||
'type_code',
|
||||
'note',
|
||||
],
|
||||
labels={
|
||||
'occurred': "Date/Time",
|
||||
'actor': "User",
|
||||
'type_code': "Event Type",
|
||||
})
|
||||
grid.set_renderer('type_code', lambda e, k, v: enum.ORDER_ITEM_EVENT[v])
|
||||
if self.request.has_perm('users.view'):
|
||||
grid.set_renderer('actor', lambda e, k, v: tags.link_to(
|
||||
e.actor, self.request.route_url('users.view', uuid=e.actor.uuid)))
|
||||
form.add_grid_vue_context(grid)
|
||||
context['events_grid'] = grid
|
||||
|
||||
return context
|
||||
|
||||
def get_xref_buttons(self, item):
|
||||
|
|
|
@ -1109,6 +1109,20 @@ class TestNewOrderBatchHandler(DataTestCase):
|
|||
self.assertEqual(item.unit_cost, decimal.Decimal('3.99'))
|
||||
self.assertEqual(item.unit_price_reg, decimal.Decimal('5.99'))
|
||||
|
||||
def test_set_initial_item_status(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
handler = self.make_handler()
|
||||
user = model.User(username='barney')
|
||||
item = model.OrderItem()
|
||||
self.assertIsNone(item.status_code)
|
||||
self.assertEqual(len(item.events), 0)
|
||||
handler.set_initial_item_status(item, user)
|
||||
self.assertEqual(item.status_code, enum.ORDER_ITEM_STATUS_READY)
|
||||
self.assertEqual(len(item.events), 2)
|
||||
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_INITIATED)
|
||||
self.assertEqual(item.events[1].type_code, enum.ORDER_ITEM_EVENT_READY)
|
||||
|
||||
def test_execute(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
|
|
|
@ -19,6 +19,11 @@ class TestOrder(DataTestCase):
|
|||
|
||||
class TestOrderItem(DataTestCase):
|
||||
|
||||
def make_config(self, **kw):
|
||||
config = super().make_config(**kw)
|
||||
config.setdefault('wutta.enum_spec', 'sideshow.enum')
|
||||
return config
|
||||
|
||||
def test_full_description(self):
|
||||
|
||||
item = mod.OrderItem()
|
||||
|
@ -44,3 +49,15 @@ class TestOrderItem(DataTestCase):
|
|||
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')
|
||||
item = mod.OrderItem()
|
||||
self.assertEqual(item.events, [])
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_INITIATED, user)
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_READY, user)
|
||||
self.assertEqual(len(item.events), 2)
|
||||
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_INITIATED)
|
||||
self.assertEqual(item.events[1].type_code, enum.ORDER_ITEM_EVENT_READY)
|
||||
|
|
|
@ -1338,14 +1338,16 @@ class TestOrderItemView(WebTestCase):
|
|||
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)")
|
||||
with patch.object(self.request, 'is_root', new=True):
|
||||
with patch.object(view, 'viewing', new=True):
|
||||
form = view.make_model_form(model_instance=item)
|
||||
context = view.get_template_context({'instance': item, 'form': form})
|
||||
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):
|
||||
self.pyramid_config.add_route('orders.view', '/orders/{uuid}')
|
||||
|
|
Loading…
Reference in a new issue