feat: add tools to change order item status; add notes
This commit is contained in:
parent
b4deea76e0
commit
9d378a0c5f
|
@ -370,8 +370,8 @@ ORDER_ITEM_EVENT = OrderedDict([
|
|||
(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_STATUS_CHANGE, "changed status"),
|
||||
(ORDER_ITEM_EVENT_NOTE_ADDED, "added note"),
|
||||
(ORDER_ITEM_EVENT_CANCELED, "canceled"),
|
||||
(ORDER_ITEM_EVENT_REFUND_PENDING, "refund pending"),
|
||||
(ORDER_ITEM_EVENT_REFUNDED, "refunded"),
|
||||
|
|
|
@ -75,3 +75,24 @@ class OrderHandler(GenericHandler):
|
|||
unit_qty = self.app.render_quantity(order_qty)
|
||||
EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT]
|
||||
return f"{unit_qty} {EA}"
|
||||
|
||||
def item_status_to_variant(self, status_code):
|
||||
"""
|
||||
Return a Buefy style variant for the given status code.
|
||||
|
||||
Default logic will return ``None`` for "normal" item status,
|
||||
but may return ``'warning'`` for some (e.g. canceled).
|
||||
|
||||
:param status_code: The status code for an order item.
|
||||
|
||||
:returns: Style variant string (e.g. ``'warning'``) or
|
||||
``None``.
|
||||
"""
|
||||
enum = self.app.enum
|
||||
if status_code in (enum.ORDER_ITEM_STATUS_CANCELED,
|
||||
enum.ORDER_ITEM_STATUS_REFUND_PENDING,
|
||||
enum.ORDER_ITEM_STATUS_REFUNDED,
|
||||
enum.ORDER_ITEM_STATUS_RESTOCKED,
|
||||
enum.ORDER_ITEM_STATUS_EXPIRED,
|
||||
enum.ORDER_ITEM_STATUS_INACTIVE):
|
||||
return 'warning'
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/view.mako" />
|
||||
|
||||
<%def name="content_title()">
|
||||
(${app.enum.ORDER_ITEM_STATUS[item.status_code]})
|
||||
${instance_title}
|
||||
</%def>
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
<style>
|
||||
|
||||
.field .field-label .label {
|
||||
nav .field .field-label .label {
|
||||
white-space: nowrap;
|
||||
width: 10rem;
|
||||
}
|
||||
|
@ -39,7 +44,86 @@
|
|||
<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>
|
||||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||||
<span
|
||||
% if item_status_variant:
|
||||
class="has-background-${item_status_variant}"
|
||||
% endif
|
||||
% if master.has_perm('change_status'):
|
||||
style="padding: 0.25rem;"
|
||||
% endif
|
||||
>
|
||||
${app.enum.ORDER_ITEM_STATUS[item.status_code]}
|
||||
</span>
|
||||
% if master.has_perm('change_status'):
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="edit"
|
||||
@click="changeStatusInit()">
|
||||
Change Status
|
||||
</b-button>
|
||||
<${b}-modal
|
||||
% if request.use_oruga:
|
||||
v-model:active="changeStatusShowDialog"
|
||||
% else:
|
||||
:active.sync="changeStatusShowDialog"
|
||||
% endif
|
||||
>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
|
||||
<h4 class="block is-size-4">Change Item Status</h4>
|
||||
|
||||
<b-field horizontal label="Current Status">
|
||||
<span>{{ changeStatusCodes[changeStatusOldCode] }}</span>
|
||||
</b-field>
|
||||
|
||||
<br />
|
||||
|
||||
<b-field horizontal label="New Status"
|
||||
:type="changeStatusNewCode ? null : 'is-danger'">
|
||||
<b-select v-model="changeStatusNewCode">
|
||||
<option v-for="status in changeStatusCodeOptions"
|
||||
:key="status.key"
|
||||
:value="status.key">
|
||||
{{ status.label }}
|
||||
</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Note">
|
||||
<b-input v-model="changeStatusNote"
|
||||
type="textarea" rows="4" />
|
||||
</b-field>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="buttons">
|
||||
<b-button type="is-primary"
|
||||
:disabled="changeStatusSaveDisabled"
|
||||
icon-pack="fas"
|
||||
icon-left="save"
|
||||
@click="changeStatusSave()">
|
||||
{{ changeStatusSubmitting ? "Working, please wait..." : "Update Status" }}
|
||||
</b-button>
|
||||
<b-button @click="changeStatusShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</${b}-modal>
|
||||
|
||||
${h.form(master.get_action_url('change_status', item), ref='changeStatusForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('new_status', **{'v-model': 'changeStatusNewCode'})}
|
||||
## ${h.hidden('uuids', **{':value': 'changeStatusCheckedRows.map((row) => {return row.uuid}).join()'})}
|
||||
${h.hidden('note', **{':value': 'changeStatusNote'})}
|
||||
${h.end_form()}
|
||||
|
||||
% endif
|
||||
</div>
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -154,7 +238,70 @@
|
|||
|
||||
<div style="padding: 0 2rem;">
|
||||
<nav class="panel" style="width: 100%;">
|
||||
<p class="panel-heading">Events</p>
|
||||
<p class="panel-heading"
|
||||
% if master.has_perm('add_note'):
|
||||
style="display: flex; gap: 2rem; align-items: center;"
|
||||
% endif
|
||||
>
|
||||
<span>Events</span>
|
||||
% if master.has_perm('add_note'):
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="plus"
|
||||
@click="addNoteInit()">
|
||||
Add Note
|
||||
</b-button>
|
||||
<${b}-modal has-modal-card
|
||||
% if request.use_oruga:
|
||||
v-model:active="addNoteShowDialog"
|
||||
% else:
|
||||
:active.sync="addNoteShowDialog"
|
||||
% endif
|
||||
>
|
||||
<div class="modal-card">
|
||||
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Add Note</p>
|
||||
</header>
|
||||
|
||||
<section class="modal-card-body">
|
||||
<b-field>
|
||||
<b-input type="textarea" rows="8"
|
||||
v-model="addNoteText"
|
||||
ref="addNoteText"
|
||||
expanded />
|
||||
</b-field>
|
||||
## <b-field>
|
||||
## <b-checkbox v-model="addNoteApplyAll">
|
||||
## Apply to all products on this order
|
||||
## </b-checkbox>
|
||||
## </b-field>
|
||||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<b-button type="is-primary"
|
||||
@click="addNoteSave()"
|
||||
:disabled="addNoteSaveDisabled"
|
||||
icon-pack="fas"
|
||||
icon-left="save">
|
||||
{{ addNoteSubmitting ? "Working, please wait..." : "Add Note" }}
|
||||
</b-button>
|
||||
<b-button @click="addNoteShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
</footer>
|
||||
</div>
|
||||
</${b}-modal>
|
||||
|
||||
${h.form(master.get_action_url('add_note', item), ref='addNoteForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('note', **{':value': 'addNoteText'})}
|
||||
## ${h.hidden('uuids', **{':value': 'changeStatusCheckedRows.map((row) => {return row.uuid}).join()'})}
|
||||
${h.end_form()}
|
||||
|
||||
% endif
|
||||
|
||||
</p>
|
||||
<div class="panel-block">
|
||||
<div style="width: 100%;">
|
||||
${events_grid.render_table_element()}
|
||||
|
@ -174,6 +321,80 @@
|
|||
${parent.modify_vue_vars()}
|
||||
<script>
|
||||
|
||||
% if master.has_perm('add_note'):
|
||||
|
||||
ThisPageData.addNoteShowDialog = false
|
||||
ThisPageData.addNoteText = null
|
||||
## ThisPageData.addNoteApplyAll = false
|
||||
ThisPageData.addNoteSubmitting = false
|
||||
|
||||
ThisPage.computed.addNoteSaveDisabled = function() {
|
||||
if (!this.addNoteText) {
|
||||
return true
|
||||
}
|
||||
if (this.addNoteSubmitting) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
ThisPage.methods.addNoteInit = function() {
|
||||
this.addNoteText = null
|
||||
## this.addNoteApplyAll = false
|
||||
this.addNoteShowDialog = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.addNoteText.focus()
|
||||
})
|
||||
}
|
||||
|
||||
ThisPage.methods.addNoteSave = function() {
|
||||
this.addNoteSubmitting = true
|
||||
this.$refs.addNoteForm.submit()
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
% if master.has_perm('change_status'):
|
||||
|
||||
ThisPageData.changeStatusCodes = ${json.dumps(app.enum.ORDER_ITEM_STATUS)|n}
|
||||
ThisPageData.changeStatusCodeOptions = ${json.dumps([dict(key=k, label=v) for k, v in app.enum.ORDER_ITEM_STATUS.items()])|n}
|
||||
|
||||
ThisPageData.changeStatusShowDialog = false
|
||||
ThisPageData.changeStatusOldCode = ${instance.status_code}
|
||||
ThisPageData.changeStatusNewCode = null
|
||||
ThisPageData.changeStatusNote = null
|
||||
ThisPageData.changeStatusSubmitting = false
|
||||
|
||||
ThisPage.computed.changeStatusSaveDisabled = function() {
|
||||
if (!this.changeStatusNewCode) {
|
||||
return true
|
||||
}
|
||||
if (this.changeStatusSubmitting) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
ThisPage.methods.changeStatusInit = function() {
|
||||
this.changeStatusNewCode = null
|
||||
// clear out any checked rows
|
||||
// this.changeStatusCheckedRows.length = 0
|
||||
this.changeStatusNote = null
|
||||
this.changeStatusShowDialog = true
|
||||
}
|
||||
|
||||
ThisPage.methods.changeStatusSave = function() {
|
||||
if (this.changeStatusNewCode == this.changeStatusOldCode) {
|
||||
alert("You chose the same status it already had...")
|
||||
return
|
||||
}
|
||||
|
||||
this.changeStatusSubmitting = true
|
||||
this.$refs.changeStatusForm.submit()
|
||||
}
|
||||
|
||||
% endif
|
||||
|
||||
## TODO: ugh the hackiness
|
||||
ThisPageData.gridContext = {
|
||||
% for key, data in form.grid_vue_context.items():
|
||||
|
|
|
@ -30,7 +30,7 @@ import logging
|
|||
import colander
|
||||
from sqlalchemy import orm
|
||||
|
||||
from webhelpers2.html import tags
|
||||
from webhelpers2.html import tags, HTML
|
||||
|
||||
from wuttaweb.views import MasterView
|
||||
from wuttaweb.forms.schema import UserRef, WuttaMoney, WuttaQuantity, WuttaEnum, WuttaDictEnum
|
||||
|
@ -862,6 +862,15 @@ class OrderView(MasterView):
|
|||
# status_code
|
||||
g.set_renderer('status_code', self.render_status_code)
|
||||
|
||||
# TODO: upstream should set this automatically
|
||||
g.row_class = self.row_grid_row_class
|
||||
|
||||
def row_grid_row_class(self, item, data, i):
|
||||
""" """
|
||||
variant = self.order_handler.item_status_to_variant(item.status_code)
|
||||
if variant:
|
||||
return f'has-background-{variant}'
|
||||
|
||||
def render_status_code(self, item, key, value):
|
||||
""" """
|
||||
enum = self.app.enum
|
||||
|
@ -1117,12 +1126,11 @@ class OrderItemView(MasterView):
|
|||
enum = self.app.enum
|
||||
return enum.ORDER_ITEM_STATUS[value]
|
||||
|
||||
def get_instance_title(self, item):
|
||||
def grid_row_class(self, item, data, i):
|
||||
""" """
|
||||
enum = self.app.enum
|
||||
title = str(item)
|
||||
status = enum.ORDER_ITEM_STATUS[item.status_code]
|
||||
return f"({status}) {title}"
|
||||
variant = self.order_handler.item_status_to_variant(item.status_code)
|
||||
if variant:
|
||||
return f'has-background-{variant}'
|
||||
|
||||
def configure_form(self, f):
|
||||
""" """
|
||||
|
@ -1185,6 +1193,7 @@ class OrderItemView(MasterView):
|
|||
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)
|
||||
context['item_status_variant'] = self.order_handler.item_status_to_variant(item.status_code)
|
||||
|
||||
grid = self.make_grid(key=f'{route_prefix}.view.events',
|
||||
model_class=model.OrderItemEvent,
|
||||
|
@ -1201,6 +1210,7 @@ class OrderItemView(MasterView):
|
|||
'type_code': "Event Type",
|
||||
})
|
||||
grid.set_renderer('type_code', lambda e, k, v: enum.ORDER_ITEM_EVENT[v])
|
||||
grid.set_renderer('note', self.render_event_note)
|
||||
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)))
|
||||
|
@ -1209,6 +1219,15 @@ class OrderItemView(MasterView):
|
|||
|
||||
return context
|
||||
|
||||
def render_event_note(self, event, key, value):
|
||||
""" """
|
||||
enum = self.app.enum
|
||||
if event.type_code == enum.ORDER_ITEM_EVENT_NOTE_ADDED:
|
||||
return HTML.tag('span', class_='has-background-info-light',
|
||||
style='padding: 0.25rem 0.5rem;',
|
||||
c=[value])
|
||||
return value
|
||||
|
||||
def get_xref_buttons(self, item):
|
||||
""" """
|
||||
buttons = super().get_xref_buttons(item)
|
||||
|
@ -1221,6 +1240,112 @@ class OrderItemView(MasterView):
|
|||
|
||||
return buttons
|
||||
|
||||
def add_note(self):
|
||||
"""
|
||||
View which adds a note to an order item. This is POST-only;
|
||||
will redirect back to the item view.
|
||||
"""
|
||||
enum = self.app.enum
|
||||
item = self.get_instance()
|
||||
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, self.request.user,
|
||||
note=self.request.POST['note'])
|
||||
|
||||
return self.redirect(self.get_action_url('view', item))
|
||||
|
||||
def change_status(self):
|
||||
"""
|
||||
View which changes status for an order item. This is
|
||||
POST-only; will redirect back to the item view.
|
||||
"""
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
main_item = self.get_instance()
|
||||
session = self.Session()
|
||||
redirect = self.redirect(self.get_action_url('view', main_item))
|
||||
|
||||
extra_note = self.request.POST.get('note')
|
||||
|
||||
# validate new status
|
||||
new_status_code = int(self.request.POST['new_status'])
|
||||
if new_status_code not in enum.ORDER_ITEM_STATUS:
|
||||
self.request.session.flash("Invalid status code", 'error')
|
||||
return redirect
|
||||
new_status_text = enum.ORDER_ITEM_STATUS[new_status_code]
|
||||
|
||||
# locate all items to which new status will be applied
|
||||
items = [main_item]
|
||||
# uuids = self.request.POST.get('uuids')
|
||||
# if uuids:
|
||||
# for uuid in uuids.split(','):
|
||||
# item = Session.get(model.OrderItem, uuid)
|
||||
# if item:
|
||||
# items.append(item)
|
||||
|
||||
# update item(s)
|
||||
for item in items:
|
||||
if item.status_code != new_status_code:
|
||||
|
||||
# event: change status
|
||||
note = 'status changed from "{}" to "{}"'.format(
|
||||
enum.ORDER_ITEM_STATUS[item.status_code],
|
||||
new_status_text)
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_STATUS_CHANGE,
|
||||
self.request.user, note=note)
|
||||
|
||||
# event: add note
|
||||
if extra_note:
|
||||
item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED,
|
||||
self.request.user, note=extra_note)
|
||||
|
||||
# new status
|
||||
item.status_code = new_status_code
|
||||
|
||||
self.request.session.flash(f"Status has been updated to: {new_status_text}")
|
||||
return redirect
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._order_item_defaults(config)
|
||||
cls._defaults(config)
|
||||
|
||||
@classmethod
|
||||
def _order_item_defaults(cls, config):
|
||||
route_prefix = cls.get_route_prefix()
|
||||
permission_prefix = cls.get_permission_prefix()
|
||||
instance_url_prefix = cls.get_instance_url_prefix()
|
||||
model_title = cls.get_model_title()
|
||||
model_title_plural = cls.get_model_title_plural()
|
||||
|
||||
# fix perm group
|
||||
config.add_wutta_permission_group(permission_prefix,
|
||||
model_title_plural,
|
||||
overwrite=False)
|
||||
|
||||
# add note
|
||||
config.add_route(f'{route_prefix}.add_note',
|
||||
f'{instance_url_prefix}/add_note',
|
||||
request_method='POST')
|
||||
config.add_view(cls, attr='add_note',
|
||||
route_name=f'{route_prefix}.add_note',
|
||||
renderer='json',
|
||||
permission=f'{permission_prefix}.add_note')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.add_note',
|
||||
f"Add note for {model_title}")
|
||||
|
||||
# change status
|
||||
config.add_route(f'{route_prefix}.change_status',
|
||||
f'{instance_url_prefix}/change-status',
|
||||
request_method='POST')
|
||||
config.add_view(cls, attr='change_status',
|
||||
route_name=f'{route_prefix}.change_status',
|
||||
renderer='json',
|
||||
permission=f'{permission_prefix}.change_status')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.change_status',
|
||||
f"Change status for {model_title}")
|
||||
|
||||
|
||||
def defaults(config, **kwargs):
|
||||
base = globals()
|
||||
|
|
|
@ -38,3 +38,23 @@ class TestOrderHandler(DataTestCase):
|
|||
self.assertEqual(text, "2 Units")
|
||||
text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_UNIT, html=True)
|
||||
self.assertEqual(text, "2 Units")
|
||||
|
||||
def test_item_status_to_variant(self):
|
||||
enum = self.app.enum
|
||||
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_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_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')
|
||||
|
|
|
@ -1183,6 +1183,19 @@ class TestOrderView(WebTestCase):
|
|||
view.configure_row_grid(grid)
|
||||
self.assertIn('product_scancode', grid.linked_columns)
|
||||
|
||||
def test_row_grid_row_class(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
# typical
|
||||
item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_READY)
|
||||
self.assertIsNone(view.row_grid_row_class(item, {}, 1))
|
||||
|
||||
# warning
|
||||
item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_CANCELED)
|
||||
self.assertEqual(view.row_grid_row_class(item, {}, 1), 'has-background-warning')
|
||||
|
||||
def test_render_status_code(self):
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
@ -1291,17 +1304,18 @@ class TestOrderItemView(WebTestCase):
|
|||
self.assertEqual(view.render_status_code(None, None, enum.ORDER_ITEM_STATUS_INITIATED),
|
||||
'initiated')
|
||||
|
||||
def test_get_instance_title(self):
|
||||
def test_grid_row_class(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
item = model.OrderItem(product_brand='Bragg',
|
||||
product_description='Vinegar',
|
||||
product_size='32oz',
|
||||
status_code=enum.ORDER_ITEM_STATUS_INITIATED)
|
||||
title = view.get_instance_title(item)
|
||||
self.assertEqual(title, "(initiated) Bragg Vinegar 32oz")
|
||||
# typical
|
||||
item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_READY)
|
||||
self.assertIsNone(view.grid_row_class(item, {}, 1))
|
||||
|
||||
# warning
|
||||
item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_CANCELED)
|
||||
self.assertEqual(view.grid_row_class(item, {}, 1), 'has-background-warning')
|
||||
|
||||
def test_configure_form(self):
|
||||
model = self.app.model
|
||||
|
@ -1349,6 +1363,24 @@ class TestOrderItemView(WebTestCase):
|
|||
self.assertIn('order_qty_uom_text', context)
|
||||
self.assertEqual(context['order_qty_uom_text'], "2 Cases (× 8 = 16 Units)")
|
||||
|
||||
def test_render_event_note(self):
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
# typical
|
||||
event = model.OrderItemEvent(type_code=enum.ORDER_ITEM_EVENT_READY, note='testing')
|
||||
result = view.render_event_note(event, 'note', 'testing')
|
||||
self.assertEqual(result, 'testing')
|
||||
|
||||
# user note
|
||||
event = model.OrderItemEvent(type_code=enum.ORDER_ITEM_EVENT_NOTE_ADDED, note='testing2')
|
||||
result = view.render_event_note(event, 'note', 'testing2')
|
||||
self.assertNotEqual(result, 'testing2')
|
||||
self.assertIn('<span', result)
|
||||
self.assertIn('class="has-background-info-light"', result)
|
||||
self.assertIn('testing2', result)
|
||||
|
||||
def test_get_xref_buttons(self):
|
||||
self.pyramid_config.add_route('orders.view', '/orders/{uuid}')
|
||||
model = self.app.model
|
||||
|
@ -1371,3 +1403,86 @@ class TestOrderItemView(WebTestCase):
|
|||
buttons = view.get_xref_buttons(item)
|
||||
self.assertEqual(len(buttons), 1)
|
||||
self.assertIn("View the Order", buttons[0])
|
||||
|
||||
def test_add_note(self):
|
||||
self.pyramid_config.add_route('order_items.view', '/order-items/{uuid}')
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
|
||||
order = model.Order(order_id=42, created_by=user)
|
||||
self.session.add(order)
|
||||
item = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
|
||||
status_code=enum.ORDER_ITEM_STATUS_INITIATED)
|
||||
order.items.append(item)
|
||||
self.session.flush()
|
||||
|
||||
with patch.object(view, 'Session', return_value=self.session):
|
||||
with patch.object(self.request, 'matchdict', new={'uuid': item.uuid}):
|
||||
with patch.object(self.request, 'POST', new={'note': 'testing'}):
|
||||
self.assertEqual(len(item.events), 0)
|
||||
result = view.add_note()
|
||||
self.assertEqual(len(item.events), 1)
|
||||
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED)
|
||||
self.assertEqual(item.events[0].note, 'testing')
|
||||
|
||||
def test_change_status(self):
|
||||
self.pyramid_config.add_route('order_items.view', '/order-items/{uuid}')
|
||||
model = self.app.model
|
||||
enum = self.app.enum
|
||||
view = self.make_view()
|
||||
|
||||
user = model.User(username='barney')
|
||||
self.session.add(user)
|
||||
|
||||
order = model.Order(order_id=42, created_by=user)
|
||||
self.session.add(order)
|
||||
item = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT,
|
||||
status_code=enum.ORDER_ITEM_STATUS_INITIATED)
|
||||
order.items.append(item)
|
||||
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': item.uuid}):
|
||||
|
||||
# just status change, no note
|
||||
with patch.object(self.request, 'POST', new={
|
||||
'new_status': enum.ORDER_ITEM_STATUS_PLACED}):
|
||||
self.assertEqual(len(item.events), 0)
|
||||
result = view.change_status()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertFalse(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(len(item.events), 1)
|
||||
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_STATUS_CHANGE)
|
||||
self.assertEqual(item.events[0].note,
|
||||
"status changed from 'initiated' to 'placed'")
|
||||
|
||||
# status change plus note
|
||||
with patch.object(self.request, 'POST', new={
|
||||
'new_status': enum.ORDER_ITEM_STATUS_RECEIVED,
|
||||
'note': 'check it out'}):
|
||||
self.assertEqual(len(item.events), 1)
|
||||
result = view.change_status()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertFalse(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(len(item.events), 3)
|
||||
self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_STATUS_CHANGE)
|
||||
self.assertEqual(item.events[0].note,
|
||||
"status changed from 'initiated' to 'placed'")
|
||||
self.assertEqual(item.events[1].type_code, enum.ORDER_ITEM_EVENT_STATUS_CHANGE)
|
||||
self.assertEqual(item.events[1].note,
|
||||
"status changed from 'placed' to 'received'")
|
||||
self.assertEqual(item.events[2].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED)
|
||||
self.assertEqual(item.events[2].note, "check it out")
|
||||
|
||||
# invalid status
|
||||
with patch.object(self.request, 'POST', new={'new_status': 23432143}):
|
||||
self.assertEqual(len(item.events), 3)
|
||||
result = view.change_status()
|
||||
self.assertIsInstance(result, HTTPFound)
|
||||
self.assertTrue(self.request.session.peek_flash('error'))
|
||||
self.assertEqual(len(item.events), 3)
|
||||
|
|
Loading…
Reference in a new issue