Show events instead of notes, in field subgrid for custorder item
This commit is contained in:
parent
e930199f83
commit
1cad8b2481
|
@ -280,7 +280,7 @@
|
||||||
:disabled="addNoteSaveDisabled"
|
:disabled="addNoteSaveDisabled"
|
||||||
icon-pack="fas"
|
icon-pack="fas"
|
||||||
icon-left="save">
|
icon-left="save">
|
||||||
{{ addNoteSubmitText }}
|
{{ addNoteSubmitting ? "Working, please wait..." : "Save Note" }}
|
||||||
</b-button>
|
</b-button>
|
||||||
<b-button @click="showAddNoteDialog = false">
|
<b-button @click="showAddNoteDialog = false">
|
||||||
Cancel
|
Cancel
|
||||||
|
@ -295,7 +295,7 @@
|
||||||
${parent.modify_this_page_vars()}
|
${parent.modify_this_page_vars()}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
${form.component_studly}Data.notesData = ${json.dumps(notes_data)|n}
|
${form.component_studly}Data.eventsData = ${json.dumps(events_data)|n}
|
||||||
|
|
||||||
% if master.has_perm('confirm_price'):
|
% if master.has_perm('confirm_price'):
|
||||||
|
|
||||||
|
@ -406,7 +406,6 @@
|
||||||
ThisPageData.newNoteText = null
|
ThisPageData.newNoteText = null
|
||||||
ThisPageData.newNoteApplyAll = false
|
ThisPageData.newNoteApplyAll = false
|
||||||
ThisPageData.addNoteSubmitting = false
|
ThisPageData.addNoteSubmitting = false
|
||||||
ThisPageData.addNoteSubmitText = "Save Note"
|
|
||||||
|
|
||||||
ThisPage.computed.addNoteSaveDisabled = function() {
|
ThisPage.computed.addNoteSaveDisabled = function() {
|
||||||
if (!this.newNoteText) {
|
if (!this.newNoteText) {
|
||||||
|
@ -429,43 +428,19 @@
|
||||||
|
|
||||||
ThisPage.methods.addNoteSave = function() {
|
ThisPage.methods.addNoteSave = function() {
|
||||||
this.addNoteSubmitting = true
|
this.addNoteSubmitting = true
|
||||||
this.addNoteSubmitText = "Working, please wait..."
|
|
||||||
|
|
||||||
let url = '${url('{}.add_note'.format(route_prefix), uuid=instance.uuid)}'
|
let url = '${url('{}.add_note'.format(route_prefix), uuid=instance.uuid)}'
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
note: this.newNoteText,
|
note: this.newNoteText,
|
||||||
apply_all: this.newNoteApplyAll,
|
apply_all: this.newNoteApplyAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
let headers = {
|
this.simplePOST(url, params, response => {
|
||||||
## TODO: should find a better way to handle CSRF token
|
this.$refs.mainForm.eventsData = response.data.events
|
||||||
'X-CSRF-TOKEN': this.csrftoken,
|
this.showAddNoteDialog = false
|
||||||
}
|
|
||||||
|
|
||||||
## TODO: should find a better way to handle CSRF token
|
|
||||||
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
|
|
||||||
if (data.success) {
|
|
||||||
this.$refs.mainForm.notesData = data.notes
|
|
||||||
this.showAddNoteDialog = false
|
|
||||||
} else {
|
|
||||||
this.$buefy.toast.open({
|
|
||||||
message: "Save failed: " + (data.error || "(unknown error)"),
|
|
||||||
type: 'is-danger',
|
|
||||||
duration: 4000, // 4 seconds
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.addNoteSubmitting = false
|
this.addNoteSubmitting = false
|
||||||
this.addNoteSubmitText = "Save Note"
|
}, response => {
|
||||||
}).catch((error) => {
|
|
||||||
// TODO: should handle this better somehow..?
|
|
||||||
this.$buefy.toast.open({
|
|
||||||
message: "Save failed: (unknown error)",
|
|
||||||
type: 'is-danger',
|
|
||||||
duration: 4000, // 4 seconds
|
|
||||||
})
|
|
||||||
this.addNoteSubmitting = false
|
this.addNoteSubmitting = false
|
||||||
this.addNoteSubmitText = "Save Note"
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,7 @@ import datetime
|
||||||
|
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
from rattail.db import model
|
from rattail.db.model import CustomerOrderItem
|
||||||
from rattail.time import localtime
|
|
||||||
|
|
||||||
from webhelpers2.html import HTML, tags
|
from webhelpers2.html import HTML, tags
|
||||||
|
|
||||||
|
@ -41,7 +40,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
"""
|
"""
|
||||||
Master view for customer order items
|
Master view for customer order items
|
||||||
"""
|
"""
|
||||||
model_class = model.CustomerOrderItem
|
model_class = CustomerOrderItem
|
||||||
route_prefix = 'custorders.items'
|
route_prefix = 'custorders.items'
|
||||||
url_prefix = '/custorders/items'
|
url_prefix = '/custorders/items'
|
||||||
creatable = False
|
creatable = False
|
||||||
|
@ -72,21 +71,6 @@ class CustomerOrderItemView(MasterView):
|
||||||
'flagged',
|
'flagged',
|
||||||
]
|
]
|
||||||
|
|
||||||
has_rows = True
|
|
||||||
model_row_class = model.CustomerOrderItemEvent
|
|
||||||
rows_title = "Event History"
|
|
||||||
rows_filterable = False
|
|
||||||
rows_sortable = False
|
|
||||||
rows_pageable = False
|
|
||||||
rows_viewable = False
|
|
||||||
|
|
||||||
row_grid_columns = [
|
|
||||||
'occurred',
|
|
||||||
'type_code',
|
|
||||||
'user',
|
|
||||||
'note',
|
|
||||||
]
|
|
||||||
|
|
||||||
form_fields = [
|
form_fields = [
|
||||||
'order',
|
'order',
|
||||||
'customer',
|
'customer',
|
||||||
|
@ -98,20 +82,32 @@ class CustomerOrderItemView(MasterView):
|
||||||
'product_brand',
|
'product_brand',
|
||||||
'product_description',
|
'product_description',
|
||||||
'product_size',
|
'product_size',
|
||||||
|
'case_quantity',
|
||||||
'order_quantity',
|
'order_quantity',
|
||||||
'order_uom',
|
'order_uom',
|
||||||
'case_quantity',
|
|
||||||
'unit_price',
|
'unit_price',
|
||||||
'total_price',
|
'total_price',
|
||||||
'special_order',
|
'special_order',
|
||||||
'price_needs_confirmation',
|
'price_needs_confirmation',
|
||||||
'paid_amount',
|
'paid_amount',
|
||||||
|
'payment_transaction_number',
|
||||||
'status_code',
|
'status_code',
|
||||||
'flagged',
|
'flagged',
|
||||||
'notes',
|
'contact_attempts',
|
||||||
|
'last_contacted',
|
||||||
|
'events',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
super().__init__(request)
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
self.custorder_handler = app.get_custorder_handler()
|
||||||
|
self.batch_handler = app.get_batch_handler(
|
||||||
|
'custorder',
|
||||||
|
default='rattail.batch.custorder:CustomerOrderBatchHandler')
|
||||||
|
|
||||||
def query(self, session):
|
def query(self, session):
|
||||||
|
model = self.model
|
||||||
return session.query(model.CustomerOrderItem)\
|
return session.query(model.CustomerOrderItem)\
|
||||||
.join(model.CustomerOrder)\
|
.join(model.CustomerOrder)\
|
||||||
.options(orm.joinedload(model.CustomerOrderItem.order)\
|
.options(orm.joinedload(model.CustomerOrderItem.order)\
|
||||||
|
@ -119,7 +115,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
super().configure_grid(g)
|
super().configure_grid(g)
|
||||||
batch_handler = self.get_batch_handler()
|
model = self.model
|
||||||
|
|
||||||
# order_id
|
# order_id
|
||||||
g.set_renderer('order_id', self.render_order_id)
|
g.set_renderer('order_id', self.render_order_id)
|
||||||
|
@ -155,7 +151,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
|
|
||||||
# order_uom
|
# order_uom
|
||||||
# nb. this is not relevant if "case orders only"
|
# nb. this is not relevant if "case orders only"
|
||||||
if not batch_handler.allow_unit_orders():
|
if not self.batch_handler.allow_unit_orders():
|
||||||
g.remove('order_uom')
|
g.remove('order_uom')
|
||||||
else:
|
else:
|
||||||
g.set_enum('order_uom', self.enum.UNIT_OF_MEASURE)
|
g.set_enum('order_uom', self.enum.UNIT_OF_MEASURE)
|
||||||
|
@ -168,6 +164,19 @@ class CustomerOrderItemView(MasterView):
|
||||||
# status_code
|
# status_code
|
||||||
g.set_renderer('status_code', self.render_status_code_column)
|
g.set_renderer('status_code', self.render_status_code_column)
|
||||||
|
|
||||||
|
# abbreviate some labels, only in grid header
|
||||||
|
g.set_label('case_quantity', "Case Qty")
|
||||||
|
g.filters['case_quantity'].label = "Case Quantity"
|
||||||
|
g.set_label('order_quantity', "Order Qty")
|
||||||
|
g.filters['order_quantity'].label = "Order Quantity"
|
||||||
|
g.set_label('department_name', "Department")
|
||||||
|
g.filters['department_name'].label = "Department Name"
|
||||||
|
g.set_label('total_price', "Total")
|
||||||
|
g.filters['total_price'].label = "Total Price"
|
||||||
|
g.set_label('order_created', "Ordered")
|
||||||
|
if 'order_created' in g.filters:
|
||||||
|
g.filters['order_created'].label = "Order Created"
|
||||||
|
|
||||||
def render_order_id(self, item, field):
|
def render_order_id(self, item, field):
|
||||||
return item.order.id
|
return item.order.id
|
||||||
|
|
||||||
|
@ -178,7 +187,8 @@ class CustomerOrderItemView(MasterView):
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def render_order_created(self, item, column):
|
def render_order_created(self, item, column):
|
||||||
value = localtime(self.rattail_config, item.order.created, from_utc=True)
|
app = self.get_rattail_app()
|
||||||
|
value = app.localtime(item.order.created, from_utc=True)
|
||||||
return raw_datetime(self.rattail_config, value)
|
return raw_datetime(self.rattail_config, value)
|
||||||
|
|
||||||
def render_status_code_column(self, item, field):
|
def render_status_code_column(self, item, field):
|
||||||
|
@ -188,12 +198,6 @@ class CustomerOrderItemView(MasterView):
|
||||||
return HTML.tag('span', title=item.status_text, c=[text])
|
return HTML.tag('span', title=item.status_text, c=[text])
|
||||||
return text
|
return text
|
||||||
|
|
||||||
def get_batch_handler(self):
|
|
||||||
app = self.get_rattail_app()
|
|
||||||
return app.get_batch_handler(
|
|
||||||
'custorder',
|
|
||||||
default='rattail.batch.custorder:CustomerOrderBatchHandler')
|
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_form(self, f):
|
||||||
super().configure_form(f)
|
super().configure_form(f)
|
||||||
item = f.model_instance
|
item = f.model_instance
|
||||||
|
@ -202,8 +206,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
f.set_renderer('order', self.render_order)
|
f.set_renderer('order', self.render_order)
|
||||||
|
|
||||||
# contact
|
# contact
|
||||||
batch_handler = self.get_batch_handler()
|
if self.batch_handler.new_order_requires_customer():
|
||||||
if batch_handler.new_order_requires_customer():
|
|
||||||
f.remove('person')
|
f.remove('person')
|
||||||
else:
|
else:
|
||||||
f.remove('customer')
|
f.remove('customer')
|
||||||
|
@ -221,7 +224,9 @@ class CustomerOrderItemView(MasterView):
|
||||||
elif item.pending_product and not item.product:
|
elif item.pending_product and not item.product:
|
||||||
f.remove('product')
|
f.remove('product')
|
||||||
|
|
||||||
# product uom
|
# product*
|
||||||
|
if not self.creating and item.product:
|
||||||
|
f.remove('product_brand', 'product_description')
|
||||||
f.set_enum('product_unit_of_measure', self.enum.UNIT_OF_MEASURE)
|
f.set_enum('product_unit_of_measure', self.enum.UNIT_OF_MEASURE)
|
||||||
|
|
||||||
# highlight pending fields
|
# highlight pending fields
|
||||||
|
@ -251,8 +256,8 @@ class CustomerOrderItemView(MasterView):
|
||||||
# flagged
|
# flagged
|
||||||
f.set_renderer('flagged', self.render_flagged)
|
f.set_renderer('flagged', self.render_flagged)
|
||||||
|
|
||||||
# notes
|
# events
|
||||||
f.set_renderer('notes', self.render_notes)
|
f.set_renderer('events', self.render_events)
|
||||||
|
|
||||||
def render_flagged(self, item, field):
|
def render_flagged(self, item, field):
|
||||||
text = "Yes" if item.flagged else "No"
|
text = "Yes" if item.flagged else "No"
|
||||||
|
@ -375,27 +380,28 @@ class CustomerOrderItemView(MasterView):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def render_notes(self, item, field):
|
def render_events(self, item, field):
|
||||||
route_prefix = self.get_route_prefix()
|
route_prefix = self.get_route_prefix()
|
||||||
|
|
||||||
factory = self.get_grid_factory()
|
factory = self.get_grid_factory()
|
||||||
g = factory(
|
g = factory(
|
||||||
key=f'{route_prefix}.notes',
|
key=f'{route_prefix}.events',
|
||||||
data=[],
|
data=[],
|
||||||
columns=[
|
columns=[
|
||||||
'created',
|
'occurred',
|
||||||
'created_by',
|
'type_code',
|
||||||
'text',
|
'user',
|
||||||
|
'note',
|
||||||
],
|
],
|
||||||
labels={
|
labels={
|
||||||
'created': "Date/Time",
|
'occurred': "When",
|
||||||
'created_by': "Added by",
|
'type_code': "What",
|
||||||
'text': "Note",
|
'user': "Who",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
table = HTML.literal(
|
table = HTML.literal(
|
||||||
g.render_buefy_table_element(data_prop='notesData'))
|
g.render_buefy_table_element(data_prop='eventsData'))
|
||||||
elements = [table]
|
elements = [table]
|
||||||
|
|
||||||
if self.has_perm('add_note'):
|
if self.has_perm('add_note'):
|
||||||
|
@ -412,12 +418,13 @@ class CustomerOrderItemView(MasterView):
|
||||||
c=elements)
|
c=elements)
|
||||||
|
|
||||||
def template_kwargs_view(self, **kwargs):
|
def template_kwargs_view(self, **kwargs):
|
||||||
kwargs = super(CustomerOrderItemView, self).template_kwargs_view(**kwargs)
|
kwargs = super().template_kwargs_view(**kwargs)
|
||||||
|
model = self.model
|
||||||
app = self.get_rattail_app()
|
app = self.get_rattail_app()
|
||||||
item = kwargs['instance']
|
item = kwargs['instance']
|
||||||
|
|
||||||
# fetch notes for current item
|
# fetch events for current item
|
||||||
kwargs['notes_data'] = self.get_context_notes(item)
|
kwargs['events_data'] = self.get_context_events(item)
|
||||||
|
|
||||||
# fetch "other" order items, siblings of current one
|
# fetch "other" order items, siblings of current one
|
||||||
order = item.order
|
order = item.order
|
||||||
|
@ -431,7 +438,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
|
|
||||||
order_date = None
|
order_date = None
|
||||||
if order.created:
|
if order.created:
|
||||||
order_date = localtime(self.rattail_config, order.created, from_utc=True).date()
|
order_date = app.localtime(order.created, from_utc=True).date()
|
||||||
|
|
||||||
other_data.append({
|
other_data.append({
|
||||||
'uuid': other.uuid,
|
'uuid': other.uuid,
|
||||||
|
@ -454,16 +461,18 @@ class CustomerOrderItemView(MasterView):
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_context_notes(self, item):
|
def get_context_events(self, item):
|
||||||
notes = []
|
app = self.get_rattail_app()
|
||||||
for note in reversed(item.notes):
|
events = []
|
||||||
created = localtime(self.rattail_config, note.created, from_utc=True)
|
for event in item.events:
|
||||||
notes.append({
|
occurred = app.localtime(event.occurred, from_utc=True)
|
||||||
'created': raw_datetime(self.rattail_config, created),
|
events.append({
|
||||||
'created_by': note.created_by.display_name,
|
'occurred': raw_datetime(self.rattail_config, occurred),
|
||||||
'text': note.text,
|
'type_code': self.enum.CUSTORDER_ITEM_EVENT.get(event.type_code, event.type_code),
|
||||||
|
'user': str(event.user),
|
||||||
|
'note': event.note,
|
||||||
})
|
})
|
||||||
return notes
|
return events
|
||||||
|
|
||||||
def confirm_price(self):
|
def confirm_price(self):
|
||||||
"""
|
"""
|
||||||
|
@ -517,6 +526,7 @@ class CustomerOrderItemView(MasterView):
|
||||||
"""
|
"""
|
||||||
View for changing status of one or more order items.
|
View for changing status of one or more order items.
|
||||||
"""
|
"""
|
||||||
|
model = self.model
|
||||||
order_item = self.get_instance()
|
order_item = self.get_instance()
|
||||||
redirect = self.redirect(self.get_action_url('view', order_item))
|
redirect = self.redirect(self.get_action_url('view', order_item))
|
||||||
|
|
||||||
|
@ -570,30 +580,15 @@ class CustomerOrderItemView(MasterView):
|
||||||
View for adding a new note to current order item, optinally
|
View for adding a new note to current order item, optinally
|
||||||
also adding it to all other items under the parent order.
|
also adding it to all other items under the parent order.
|
||||||
"""
|
"""
|
||||||
order_item = self.get_instance()
|
item = self.get_instance()
|
||||||
data = self.request.json_body
|
data = self.request.json_body
|
||||||
new_note = data['note']
|
|
||||||
apply_all = data['apply_all'] == True
|
|
||||||
user = self.request.user
|
|
||||||
|
|
||||||
if apply_all:
|
self.custorder_handler.add_note(item, data['note'], self.request.user,
|
||||||
order_items = order_item.order.items
|
apply_all=data['apply_all'] == True)
|
||||||
else:
|
|
||||||
order_items = [order_item]
|
|
||||||
|
|
||||||
for item in order_items:
|
|
||||||
item.notes.append(model.CustomerOrderItemNote(
|
|
||||||
created_by=user, text=new_note))
|
|
||||||
|
|
||||||
# # attach event
|
|
||||||
# item.events.append(model.CustomerOrderItemEvent(
|
|
||||||
# type_code=self.enum.CUSTORDER_ITEM_EVENT_ADDED_NOTE,
|
|
||||||
# user=user, note=new_note))
|
|
||||||
|
|
||||||
self.Session.flush()
|
self.Session.flush()
|
||||||
self.Session.refresh(order_item)
|
self.Session.refresh(item)
|
||||||
return {'success': True,
|
return {'events': self.get_context_events(item)}
|
||||||
'notes': self.get_context_notes(order_item)}
|
|
||||||
|
|
||||||
def render_order(self, item, field):
|
def render_order(self, item, field):
|
||||||
order = item.order
|
order = item.order
|
||||||
|
@ -610,22 +605,6 @@ class CustomerOrderItemView(MasterView):
|
||||||
url = self.request.route_url('people.view', uuid=person.uuid)
|
url = self.request.route_url('people.view', uuid=person.uuid)
|
||||||
return tags.link_to(text, url)
|
return tags.link_to(text, url)
|
||||||
|
|
||||||
def get_row_data(self, item):
|
|
||||||
return self.Session.query(model.CustomerOrderItemEvent)\
|
|
||||||
.filter(model.CustomerOrderItemEvent.item == item)\
|
|
||||||
.order_by(model.CustomerOrderItemEvent.occurred,
|
|
||||||
model.CustomerOrderItemEvent.type_code)
|
|
||||||
|
|
||||||
def configure_row_grid(self, g):
|
|
||||||
super(CustomerOrderItemView, self).configure_row_grid(g)
|
|
||||||
|
|
||||||
g.set_enum('type_code', self.enum.CUSTORDER_ITEM_EVENT)
|
|
||||||
|
|
||||||
g.set_label('occurred', "When")
|
|
||||||
g.set_label('type_code', "What") # TODO: enum renderer
|
|
||||||
g.set_label('user', "Who")
|
|
||||||
g.set_label('note', "Notes")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
cls._order_item_defaults(config)
|
cls._order_item_defaults(config)
|
||||||
|
|
Loading…
Reference in a new issue