diff --git a/tailbone/templates/custorders/view.mako b/tailbone/templates/custorders/view.mako new file mode 100644 index 00000000..e2af7bf4 --- /dev/null +++ b/tailbone/templates/custorders/view.mako @@ -0,0 +1,3 @@ +## -*- coding: utf-8; -*- +<%inherit file="/master/view.mako" /> +${parent.body()} diff --git a/tailbone/views/custorders/items.py b/tailbone/views/custorders/items.py index 5dc61e4d..5d4f6049 100644 --- a/tailbone/views/custorders/items.py +++ b/tailbone/views/custorders/items.py @@ -85,8 +85,10 @@ class CustomerOrderItemView(MasterView): form_fields = [ 'order', - 'sequence', + 'customer', 'person', + 'sequence', + '_product_key_', 'product', 'pending_product', 'product_brand', @@ -97,9 +99,11 @@ class CustomerOrderItemView(MasterView): 'case_quantity', 'unit_price', 'total_price', + 'special_order', 'price_needs_confirmation', 'paid_amount', 'status_code', + 'flagged', 'notes', ] @@ -167,13 +171,30 @@ class CustomerOrderItemView(MasterView): return HTML.tag('span', title=item.status_text, c=[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): - super(CustomerOrderItemView, self).configure_form(f) + super().configure_form(f) item = f.model_instance # order f.set_renderer('order', self.render_order) + # contact + batch_handler = self.get_batch_handler() + if batch_handler.new_order_requires_customer(): + f.remove('person') + else: + f.remove('customer') + + # product key + key = self.get_product_key_field() + f.set_renderer(key, lambda item, field: getattr(item, f'product_{key}')) + # (pending) product f.set_renderer('product', self.render_product) f.set_renderer('pending_product', self.render_pending_product) @@ -192,13 +213,6 @@ class CustomerOrderItemView(MasterView): f.set_renderer('product_size', self.highlight_pending_field) f.set_renderer('case_quantity', self.highlight_pending_field_quantity) - 'unit_price', - 'total_price', - 'price_needs_confirmation', - 'paid_amount', - 'status_code', - 'notes', - # quantity fields f.set_type('cases_ordered', 'quantity') f.set_type('units_ordered', 'quantity') diff --git a/tailbone/views/custorders/orders.py b/tailbone/views/custorders/orders.py index cdf765a6..abbcf87c 100644 --- a/tailbone/views/custorders/orders.py +++ b/tailbone/views/custorders/orders.py @@ -52,7 +52,7 @@ class CustomerOrderView(MasterView): configurable = True labels = { - 'id': "ID", + 'id': "Order ID", 'status_code': "Status", } @@ -60,8 +60,9 @@ class CustomerOrderView(MasterView): 'id', 'customer', 'person', - 'created', 'status_code', + 'created', + 'created_by', ] form_fields = [ @@ -88,14 +89,17 @@ class CustomerOrderView(MasterView): row_grid_columns = [ 'sequence', + '_product_key_', 'product_brand', 'product_description', 'product_size', 'order_quantity', 'order_uom', 'case_quantity', + 'department_name', 'total_price', 'status_code', + 'flagged', ] def __init__(self, request): @@ -107,11 +111,19 @@ class CustomerOrderView(MasterView): .options(orm.joinedload(model.CustomerOrder.customer)) def configure_grid(self, g): - super(CustomerOrderView, self).configure_grid(g) + super().configure_grid(g) + + # id + g.set_link('id') + g.filters['id'].default_active = True + g.filters['id'].default_verb = 'equal' + + # import ipdb; ipdb.set_trace() # customer or person if self.batch_handler.new_order_requires_customer(): g.remove('person') + g.set_link('customer') g.set_joiner('customer', lambda q: q.outerjoin(model.Customer)) g.set_sorter('customer', model.Customer.name) g.filters['customer'] = g.make_filter('customer', model.Customer.name, @@ -120,6 +132,7 @@ class CustomerOrderView(MasterView): default_verb='contains') else: g.remove('customer') + g.set_link('person') g.set_joiner('person', lambda q: q.outerjoin(model.Person)) g.set_sorter('person', model.Person.display_name) g.filters['person'] = g.make_filter('person', model.Person.display_name, @@ -127,13 +140,14 @@ class CustomerOrderView(MasterView): default_active=True, default_verb='contains') + # status_code g.set_enum('status_code', self.enum.CUSTORDER_STATUS) + # created g.set_sort_defaults('created', 'desc') - g.set_link('id') - g.set_link('customer') - g.set_link('person') + def get_instance_title(self, order): + return f"#{order.id} for {order.customer or order.person}" def configure_form(self, f): super(CustomerOrderView, self).configure_form(f) @@ -232,6 +246,10 @@ class CustomerOrderView(MasterView): 'custorder', default='rattail.batch.custorder:CustomerOrderBatchHandler') + # product key + key = self.get_product_key_field() + g.set_renderer(key, lambda item, field: getattr(item, f'product_{key}')) + g.set_type('case_quantity', 'quantity') g.set_type('order_quantity', 'quantity') g.set_type('cases_ordered', 'quantity') @@ -962,6 +980,61 @@ class CustomerOrderView(MasterView): def execute_new_order_batch(self, batch, data): return self.batch_handler.do_execute(batch, self.request.user) + def fetch_order_data(self): + app = self.get_rattail_app() + model = self.model + + order = None + uuid = self.request.GET.get('uuid') + if uuid: + order = self.Session.get(model.CustomerOrder, uuid) + if not order: + # raise self.notfound() + return {'error': "Customer order not found"} + + address = None + if self.batch_handler.new_order_requires_customer(): + contact = order.customer + else: + contact = order.person + if contact and contact.address: + a = contact.address + address = { + 'street_1': a.street, + 'street_2': a.street2, + 'city': a.city, + 'state': a.state, + 'zip': a.zipcode, + } + + # gather all the order items + items = [] + grand_total = 0 + for item in order.items: + item_data = { + 'uuid': item.uuid, + 'special_order': False, # TODO + 'product_description': item.product_description, + 'order_quantity': app.render_quantity(item.order_quantity), + 'department': item.department_name, + 'price': app.render_currency(item.unit_price), + 'total': app.render_currency(item.total_price), + } + items.append(item_data) + grand_total += item.total_price + + return { + 'uuid': order.uuid, + 'id': order.id, + 'created_display': app.render_datetime(app.localtime(order.created, from_utc=True)), + 'contact_display': str(contact or ''), + 'address': address, + 'phone_display': str(contact.phone) if contact and contact.phone else "", + 'email_display': str(contact.email) if contact and contact.email else "", + 'items': items, + 'grand_total_display': app.render_currency(grand_total), + } + def configure_get_simple_settings(self): return [ @@ -1048,6 +1121,14 @@ class CustomerOrderView(MasterView): renderer='json', permission='products.list') + # fetch order data + config.add_route(f'{route_prefix}.fetch_order_data', + f'{url_prefix}/fetch-order-data') + config.add_view(cls, attr='fetch_order_data', + route_name=f'{route_prefix}.fetch_order_data', + renderer='json', + permission=f'{permission_prefix}.view') + # TODO: deprecate / remove this CustomerOrdersView = CustomerOrderView