Refactor purchasing batch views to use master3

This commit is contained in:
Lance Edgar 2018-01-27 11:59:52 -06:00
parent d20601c359
commit eac59ba5c8
6 changed files with 233 additions and 154 deletions

View file

@ -54,18 +54,9 @@
</ul>
<div class="form-wrapper">
## TODO: clean this up or fix etc..?
## % if master.edit_with_rows:
## ${form.render(buttons=capture(buttons))|n}
## % else:
${form.render()|n}
## % endif
</div>
% if master.edit_with_rows:
${rows_grid.render_complete(allow_save_defaults=False, tools=capture(self.grid_tools))|n}
% endif
<div id="execution-options-dialog" style="display: none;">
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}

View file

@ -51,7 +51,6 @@
}
});
// show_mode(${form.fieldset.model.mode or enum.PURCHASE_BATCH_MODE_ORDERING});
show_mode(${enum.PURCHASE_BATCH_MODE_ORDERING});
});

View file

@ -65,7 +65,6 @@ class BatchMasterView(MasterView):
rows_downloadable_csv = True
refreshable = True
refresh_after_create = False
edit_with_rows = False
cloneable = False
executable = True
results_executable = False
@ -311,53 +310,14 @@ class BatchMasterView(MasterView):
else:
return self.redirect(self.get_action_url('view', batch, mobile=mobile))
# TODO: some of this at least can go to master now right?
def edit(self):
"""
Don't allow editing a batch which has already been executed.
"""
self.editing = True
batch = self.get_instance()
if not self.editable_instance(batch):
return self.redirect(self.get_action_url('view', batch))
if self.edit_with_rows:
grid = self.make_row_grid(batch=batch)
# If user just refreshed the page with a reset instruction, issue a
# redirect in order to clear out the query string.
if self.request.GET.get('reset-to-default-filters') == 'true':
return self.redirect(self.request.current_route_url(_query=None))
if self.request.params.get('partial'):
self.request.response.content_type = b'text/html'
self.request.response.text = grid.render_grid()
return self.request.response
form = self.make_form(batch)
if self.request.method == 'POST':
if self.validate_form(form):
self.save_edit_form(form)
self.request.session.flash("{} has been updated: {}".format(
self.get_model_title(), self.get_instance_title(batch)))
return self.redirect_after_edit(batch)
context = {
'instance': batch,
'instance_title': self.get_instance_title(batch),
'instance_deletable': self.deletable_instance(batch),
'form': form,
'batch': batch,
'execute_title': self.get_execute_title(batch),
'execute_enabled': self.instance_executable(batch),
}
if self.edit_with_rows:
context['rows_grid'] = grid
if context['execute_enabled'] and self.has_execution_options(batch):
context['rendered_execution_options'] = self.render_execution_options(batch)
return self.render_to_response('edit', context)
def template_kwargs_edit(self, **kwargs):
batch = kwargs['instance']
kwargs['batch'] = batch
kwargs['execute_title'] = self.get_execute_title(batch)
kwargs['execute_enabled'] = self.instance_executable(batch)
if kwargs['execute_enabled'] and self.has_execution_options(batch):
kwargs['rendered_execution_options'] = self.render_execution_options(batch)
return kwargs
def mobile_mark_complete(self):
batch = self.get_instance()
@ -429,8 +389,6 @@ class BatchMasterView(MasterView):
"""
if self.request.params.get('refresh') == 'true':
return self.redirect(self.get_action_url('refresh', batch))
if self.edit_with_rows:
return self.redirect(self.get_action_url('edit', batch))
return self.redirect(self.get_action_url('view', batch))
def delete_instance(self, batch):

View file

@ -76,6 +76,7 @@ class BatchMasterView3(MasterView3, BatchMasterView2):
f.set_label('rowcount', "Row Count")
# status_code
f.set_readonly('status_code')
f.set_renderer('status_code', self.make_status_renderer(self.model_class.STATUS))
f.set_label('status_code', "Status")
@ -141,7 +142,7 @@ class BatchMasterView3(MasterView3, BatchMasterView2):
return batch
def make_status_renderer(self, enum):
def render_status(self, batch, field):
def render_status(batch, field):
value = batch.status_code
if value is None:
return ""

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
@ -26,17 +26,18 @@ Base views for purchasing batches
from __future__ import unicode_literals, absolute_import
# from sqlalchemy import orm
import six
from rattail.db import model, api
# from rattail.gpc import GPC
from rattail.time import localtime
import formalchemy as fa
import colander
from deform import widget as dfwidget
from pyramid import httpexceptions
from webhelpers2.html import tags
from tailbone import forms
from tailbone.views.batch import BatchMasterView2 as BatchMasterView
from tailbone import forms, forms2
from tailbone.views.batch import BatchMasterView3 as BatchMasterView
class PurchasingBatchView(BatchMasterView):
@ -76,6 +77,33 @@ class PurchasingBatchView(BatchMasterView):
# 'status_code',
# ]
form_fields = [
'id',
'store',
'buyer',
'vendor',
'department',
'purchase',
'vendor_email',
'vendor_fax',
'vendor_contact',
'vendor_phone',
'date_ordered',
'date_received',
'po_number',
'po_total',
'invoice_date',
'invoice_number',
'invoice_total',
'notes',
'created',
'created_by',
'status_code',
'complete',
'executed',
'executed_by',
]
@property
def batch_mode(self):
raise NotImplementedError("Please define `batch_mode` for your purchasing batch view")
@ -118,29 +146,189 @@ class PurchasingBatchView(BatchMasterView):
# form = super(PurchasingBatchView, self).make_form(batch, **kwargs)
# return form
def _preconfigure_fieldset(self, fs):
super(PurchasingBatchView, self)._preconfigure_fieldset(fs)
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.PURCHASE_BATCH_MODE))
fs.store.set(renderer=forms.renderers.StoreFieldRenderer)
fs.purchase.set(renderer=forms.renderers.PurchaseFieldRenderer, options=[])
fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer,
attrs={'selected': 'vendor_selected',
'cleared': 'vendor_cleared'})
fs.department.set(renderer=forms.renderers.DepartmentFieldRenderer,
options=self.get_department_options())
fs.buyer.set(renderer=forms.renderers.EmployeeFieldRenderer)
fs.po_number.set(label="PO Number")
fs.po_total.set(label="PO Total", readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
fs.invoice_total.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
def configure_form(self, f):
super(PurchasingBatchView, self).configure_form(f)
batch = f.model_instance
today = localtime(self.rattail_config).date()
fs.append(fa.Field('vendor_email', readonly=True,
value=lambda b: b.vendor.email.address if b.vendor.email else None))
fs.append(fa.Field('vendor_fax', readonly=True,
value=self.get_vendor_fax_number))
fs.append(fa.Field('vendor_contact', readonly=True,
value=lambda b: b.vendor.contact or None))
fs.append(fa.Field('vendor_phone', readonly=True,
value=self.get_vendor_phone_number))
# mode
f.set_enum('mode', self.enum.PURCHASE_BATCH_MODE)
# TODO: this hardly seems complete...
# store
if not self.creating:
f.set_readonly('store')
f.set_renderer('store', self.render_store)
# purchase
f.set_renderer('purchase', self.render_purchase)
if self.editing:
f.set_readonly('purchase')
# vendor
# fs.vendor.set(renderer=forms.renderers.VendorFieldRenderer,
# attrs={'selected': 'vendor_selected',
# 'cleared': 'vendor_cleared'})
f.set_renderer('vendor', self.render_vendor)
if self.creating:
f.replace('vendor', 'vendor_uuid')
f.set_node('vendor_uuid', colander.String())
vendor_display = ""
if self.request.method == 'POST':
if self.request.POST.get('vendor_uuid'):
vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor_uuid'])
if vendor:
vendor_display = six.text_type(vendor)
vendors_url = self.request.route_url('vendors.autocomplete')
f.set_widget('vendor_uuid', forms2.widgets.JQueryAutocompleteWidget(
field_display=vendor_display, service_url=vendors_url))
f.set_label('vendor_uuid', "Vendor")
elif self.editing:
f.set_readonly('vendor')
# department
f.set_renderer('department', self.render_department)
if self.creating:
f.replace('department', 'department_uuid')
f.set_node('department_uuid', colander.String())
dept_options = self.get_department_options()
dept_values = [(v, k) for k, v in dept_options]
f.set_widget('department_uuid', dfwidget.SelectWidget(values=dept_values))
f.set_label('department_uuid', "Department")
else:
f.set_readonly('department')
# buyer
f.set_renderer('buyer', self.render_buyer)
if self.creating or self.editing:
f.replace('buyer', 'buyer_uuid')
f.set_node('buyer_uuid', colander.String(), missing=colander.null)
buyer_display = ""
if self.request.method == 'POST':
if self.request.POST.get('buyer_uuid'):
buyer = self.Session.query(model.Employee).get(self.request.POST['buyer_uuid'])
if buyer:
buyer_display = six.text_type(buyer)
elif self.creating:
buyer = self.request.user.employee
buyer_display = six.text_type(buyer)
f.set_default('buyer_uuid', buyer.uuid)
elif self.editing:
buyer_display = six.text_type(batch.buyer or '')
buyers_url = self.request.route_url('employees.autocomplete')
f.set_widget('buyer_uuid', forms2.widgets.JQueryAutocompleteWidget(
field_display=buyer_display, service_url=buyers_url))
f.set_label('buyer_uuid', "Buyer")
# date_ordered
f.set_type('date_ordered', 'date_jquery')
if self.creating:
f.set_default('date_ordered', today)
# date_received
f.set_type('date_received', 'date_jquery')
if self.creating:
f.set_default('date_received', today)
# invoice_date
f.set_type('invoice_date', 'date_jquery')
# po_number
f.set_label('po_number', "PO Number")
# po_total
f.set_readonly('po_total')
f.set_type('po_total', 'currency')
f.set_label('po_total', "PO Total")
# invoice_total
f.set_readonly('invoice_total')
f.set_type('invoice_total', 'currency')
# vendor_email
f.set_readonly('vendor_email')
f.set_renderer('vendor_email', self.render_vendor_email)
# vendor_fax
f.set_readonly('vendor_fax')
f.set_renderer('vendor_fax', self.render_vendor_fax)
# vendor_contact
f.set_readonly('vendor_contact')
f.set_renderer('vendor_contact', self.render_vendor_contact)
# vendor_phone
f.set_readonly('vendor_phone')
f.set_renderer('vendor_phone', self.render_vendor_phone)
if self.creating:
f.remove_fields('po_total',
'invoice_total',
'complete',
'vendor_email',
'vendor_fax',
'vendor_phone',
'vendor_contact',
'status_code')
def render_store(self, batch, field):
store = batch.store
if not store:
return ""
text = "({}) {}".format(store.id, store.name)
url = self.request.route_url('stores.view', uuid=store.uuid)
return tags.link_to(text, url)
def render_purchase(self, batch, field):
purchase = batch.purchase
if not purchase:
return ""
text = six.text_type(purchase)
url = self.request.route_url('purchases.view', uuid=purchase.uuid)
return tags.link_to(text, url)
def render_vendor(self, batch, field):
vendor = batch.vendor
if not vendor:
return ""
text = "({}) {}".format(vendor.id, vendor.name)
url = self.request.route_url('vendors.view', uuid=vendor.uuid)
return tags.link_to(text, url)
def render_vendor_email(self, batch, field):
if batch.vendor.email:
return batch.vendor.email.address
def render_vendor_fax(self, batch, field):
return self.get_vendor_fax_number(batch)
def render_vendor_contact(self, batch, field):
if batch.vendor.contact:
return six.text_type(batch.vendor.contact)
def render_vendor_phone(self, batch, field):
return self.get_vendor_phone_number(batch)
def render_department(self, batch, field):
department = batch.department
if not department:
return ""
if department.number:
text = "({}) {}".format(department.number, department.name)
else:
text = department.name
url = self.request.route_url('departments.view', uuid=department.uuid)
return tags.link_to(text, url)
def render_buyer(self, batch, field):
employee = batch.buyer
if not employee:
return ""
text = six.text_type(employee)
if self.request.has_perm('employees.view'):
url = self.request.route_url('employees.view', uuid=employee.uuid)
return tags.link_to(text, url)
return text
def get_department_options(self):
departments = self.Session.query(model.Department).order_by(model.Department.number)
@ -156,70 +344,6 @@ class PurchasingBatchView(BatchMasterView):
if phone.type == 'Fax':
return phone.number
def configure_fieldset(self, fs):
fs.configure(
include=[
fs.id,
fs.store,
fs.buyer,
fs.vendor,
fs.department,
fs.purchase,
fs.vendor_email,
fs.vendor_fax,
fs.vendor_contact,
fs.vendor_phone,
fs.date_ordered,
fs.date_received,
fs.po_number,
fs.po_total,
fs.invoice_date,
fs.invoice_number,
fs.invoice_total,
fs.notes,
fs.created,
fs.created_by,
fs.status_code,
fs.complete,
fs.executed,
fs.executed_by,
])
if self.creating:
del fs.po_total
del fs.invoice_total
del fs.complete
del fs.vendor_email
del fs.vendor_fax
del fs.vendor_phone
del fs.vendor_contact
del fs.status_code
# default store may be configured
store = self.rattail_config.get('rattail', 'store')
if store:
store = api.get_store(self.Session(), store)
if store:
fs.model.store = store
# default buyer is current user
if self.request.method != 'POST':
buyer = self.request.user.employee
if buyer:
fs.model.buyer = buyer
# TODO: something tells me this isn't quite safe..
# all dates have today as default
today = localtime(self.rattail_config).date()
fs.model.date_ordered = today
fs.model.date_received = today
elif self.editing:
fs.store.set(readonly=True)
fs.vendor.set(readonly=True)
fs.department.set(readonly=True)
fs.purchase.set(readonly=True)
def eligible_purchases(self, vendor_uuid=None, mode=None):
if not vendor_uuid:
vendor_uuid = self.request.GET.get('vendor_uuid')

View file

@ -86,6 +86,12 @@ class OrderingBatchView(PurchasingBatchView):
def batch_mode(self):
return self.enum.PURCHASE_BATCH_MODE_ORDERING
def configure_form(self, f):
super(OrderingBatchView, self).configure_form(f)
# purchase
f.remove_field('purchase')
def order_form(self):
"""
View for editing batch row data as an order form.