Various changes to support a certain new app
improve inventory support, plus "hiding" person data but still using it
This commit is contained in:
parent
fcffe0f79d
commit
18f4b4ff5c
|
@ -274,6 +274,7 @@ class BatchMasterView(MasterView):
|
||||||
kwargs['created_by'] = batch.created_by
|
kwargs['created_by'] = batch.created_by
|
||||||
elif batch.created_by_uuid:
|
elif batch.created_by_uuid:
|
||||||
kwargs['created_by_uuid'] = batch.created_by_uuid
|
kwargs['created_by_uuid'] = batch.created_by_uuid
|
||||||
|
kwargs['description'] = batch.description
|
||||||
kwargs['notes'] = batch.notes
|
kwargs['notes'] = batch.notes
|
||||||
if hasattr(batch, 'filename'):
|
if hasattr(batch, 'filename'):
|
||||||
kwargs['filename'] = batch.filename
|
kwargs['filename'] = batch.filename
|
||||||
|
|
|
@ -80,6 +80,9 @@ class BatchMasterView2(MasterView2, BatchMasterView):
|
||||||
g.set_renderer('id', self.render_batch_id)
|
g.set_renderer('id', self.render_batch_id)
|
||||||
|
|
||||||
g.set_link('id')
|
g.set_link('id')
|
||||||
|
g.set_link('description')
|
||||||
|
g.set_link('created')
|
||||||
|
g.set_link('executed')
|
||||||
|
|
||||||
g.set_label('id', "Batch ID")
|
g.set_label('id', "Batch ID")
|
||||||
g.set_label('created_by', "Created by")
|
g.set_label('created_by', "Created by")
|
||||||
|
|
|
@ -31,6 +31,7 @@ import re
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
|
import formalchemy as fa
|
||||||
from pyramid.httpexceptions import HTTPNotFound
|
from pyramid.httpexceptions import HTTPNotFound
|
||||||
|
|
||||||
from tailbone import forms
|
from tailbone import forms
|
||||||
|
@ -127,26 +128,29 @@ class CustomersView(MasterView):
|
||||||
fs.id.set(label="ID")
|
fs.id.set(label="ID")
|
||||||
fs.append(forms.fields.DefaultPhoneField('default_phone', label="Phone Number"))
|
fs.append(forms.fields.DefaultPhoneField('default_phone', label="Phone Number"))
|
||||||
fs.append(forms.fields.DefaultEmailField('default_email', label="Email Address"))
|
fs.append(forms.fields.DefaultEmailField('default_email', label="Email Address"))
|
||||||
fs.email_preference.set(renderer=forms.EnumFieldRenderer(self.enum.EMAIL_PREFERENCE))
|
fs.email_preference.set(renderer=forms.EnumFieldRenderer(self.enum.EMAIL_PREFERENCE),
|
||||||
|
attrs={'auto-enhance': 'true'})
|
||||||
|
fs.email_preference._null_option = ("(no preference)", '')
|
||||||
fs.append(forms.AssociationProxyField('people', renderer=forms.renderers.PeopleFieldRenderer,
|
fs.append(forms.AssociationProxyField('people', renderer=forms.renderers.PeopleFieldRenderer,
|
||||||
readonly=True))
|
readonly=True))
|
||||||
fs.active_in_pos.set(label="Active in POS")
|
fs.active_in_pos.set(label="Active in POS")
|
||||||
fs.active_in_pos_sticky.set(label="Always Active in POS")
|
fs.active_in_pos_sticky.set(label="Always Active in POS")
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
fs.configure(
|
include = [
|
||||||
include=[
|
|
||||||
fs.id,
|
fs.id,
|
||||||
fs.name,
|
fs.name,
|
||||||
# fs.phone.label("Phone Number").readonly(),
|
|
||||||
fs.default_phone,
|
fs.default_phone,
|
||||||
# fs.email.label("Email Address").readonly(),
|
|
||||||
fs.default_email,
|
fs.default_email,
|
||||||
fs.email_preference,
|
fs.email_preference,
|
||||||
fs.people,
|
|
||||||
fs.active_in_pos,
|
fs.active_in_pos,
|
||||||
fs.active_in_pos_sticky,
|
fs.active_in_pos_sticky,
|
||||||
|
]
|
||||||
|
if not self.creating:
|
||||||
|
include.extend([
|
||||||
|
fs.people,
|
||||||
])
|
])
|
||||||
|
fs.configure(include=include)
|
||||||
|
|
||||||
def configure_mobile_fieldset(self, fs):
|
def configure_mobile_fieldset(self, fs):
|
||||||
fs.configure(
|
fs.configure(
|
||||||
|
@ -164,6 +168,15 @@ class CustomersView(MasterView):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def unique_id(value, field):
|
||||||
|
customer = field.parent.model
|
||||||
|
query = Session.query(model.Customer).filter(model.Customer.id == value)
|
||||||
|
if customer.uuid:
|
||||||
|
query = query.filter(model.Customer.uuid != customer.uuid)
|
||||||
|
if query.count():
|
||||||
|
raise fa.ValidationError("Customer ID must be unique")
|
||||||
|
|
||||||
|
|
||||||
class CustomerNameAutocomplete(AutocompleteView):
|
class CustomerNameAutocomplete(AutocompleteView):
|
||||||
"""
|
"""
|
||||||
Autocomplete view which operates on customer name.
|
Autocomplete view which operates on customer name.
|
||||||
|
|
|
@ -59,6 +59,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
'id',
|
'id',
|
||||||
'created',
|
'created',
|
||||||
'created_by',
|
'created_by',
|
||||||
|
'description',
|
||||||
'mode',
|
'mode',
|
||||||
'rowcount',
|
'rowcount',
|
||||||
'total_cost',
|
'total_cost',
|
||||||
|
@ -76,6 +77,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
'brand_name',
|
'brand_name',
|
||||||
'description',
|
'description',
|
||||||
'size',
|
'size',
|
||||||
|
'previous_units_on_hand',
|
||||||
'cases',
|
'cases',
|
||||||
'units',
|
'units',
|
||||||
'unit_cost',
|
'unit_cost',
|
||||||
|
@ -99,7 +101,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
def _preconfigure_fieldset(self, fs):
|
def _preconfigure_fieldset(self, fs):
|
||||||
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
||||||
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE),
|
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE),
|
||||||
label="Count Mode")
|
label="Count Mode", required=True, attrs={'auto-enhance': 'true'})
|
||||||
fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
||||||
fs.append(fa.Field('handheld_batches', renderer=forms.renderers.HandheldBatchesFieldRenderer, readonly=True,
|
fs.append(fa.Field('handheld_batches', renderer=forms.renderers.HandheldBatchesFieldRenderer, readonly=True,
|
||||||
value=lambda b: b._handhelds))
|
value=lambda b: b._handhelds))
|
||||||
|
@ -108,6 +110,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.configure(
|
fs.configure(
|
||||||
include=[
|
include=[
|
||||||
fs.id,
|
fs.id,
|
||||||
|
fs.description,
|
||||||
fs.created,
|
fs.created,
|
||||||
fs.created_by,
|
fs.created_by,
|
||||||
fs.handheld_batches,
|
fs.handheld_batches,
|
||||||
|
@ -118,6 +121,8 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.executed,
|
fs.executed,
|
||||||
fs.executed_by,
|
fs.executed_by,
|
||||||
])
|
])
|
||||||
|
if not self.creating:
|
||||||
|
fs.mode.set(readonly=True)
|
||||||
|
|
||||||
def save_edit_row_form(self, form):
|
def save_edit_row_form(self, form):
|
||||||
row = form.fieldset.model
|
row = form.fieldset.model
|
||||||
|
@ -241,6 +246,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
def configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
super(InventoryBatchView, self).configure_row_grid(g)
|
super(InventoryBatchView, self).configure_row_grid(g)
|
||||||
|
|
||||||
|
g.set_type('previous_units_on_hand', 'quantity')
|
||||||
g.set_type('cases', 'quantity')
|
g.set_type('cases', 'quantity')
|
||||||
g.set_type('units', 'quantity')
|
g.set_type('units', 'quantity')
|
||||||
g.set_type('unit_cost', 'currency')
|
g.set_type('unit_cost', 'currency')
|
||||||
|
@ -254,6 +260,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
g.set_label('upc', "UPC")
|
g.set_label('upc', "UPC")
|
||||||
g.set_label('brand_name', "Brand")
|
g.set_label('brand_name', "Brand")
|
||||||
g.set_label('status_code', "Status")
|
g.set_label('status_code', "Status")
|
||||||
|
g.set_label('previous_units_on_hand', "Prev. On Hand")
|
||||||
|
|
||||||
def row_grid_extra_class(self, row, i):
|
def row_grid_extra_class(self, row, i):
|
||||||
if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
|
if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
|
||||||
|
@ -273,6 +280,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.brand_name.set(readonly=True)
|
fs.brand_name.set(readonly=True)
|
||||||
fs.description.set(readonly=True)
|
fs.description.set(readonly=True)
|
||||||
fs.size.set(readonly=True)
|
fs.size.set(readonly=True)
|
||||||
|
fs.previous_units_on_hand.set(label="Prev. On Hand")
|
||||||
fs.cases.set(renderer=forms.renderers.QuantityFieldRenderer)
|
fs.cases.set(renderer=forms.renderers.QuantityFieldRenderer)
|
||||||
fs.units.set(renderer=forms.renderers.QuantityFieldRenderer)
|
fs.units.set(renderer=forms.renderers.QuantityFieldRenderer)
|
||||||
fs.unit_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
fs.unit_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
||||||
|
@ -287,6 +295,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.description,
|
fs.description,
|
||||||
fs.size,
|
fs.size,
|
||||||
fs.status_code,
|
fs.status_code,
|
||||||
|
fs.previous_units_on_hand,
|
||||||
fs.cases,
|
fs.cases,
|
||||||
fs.units,
|
fs.units,
|
||||||
fs.unit_cost,
|
fs.unit_cost,
|
||||||
|
|
|
@ -1650,7 +1650,7 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def redirect_after_edit_row(self, row, mobile=False):
|
def redirect_after_edit_row(self, row, mobile=False):
|
||||||
return self.redirect(self.get_row_action_url('view', row, mobile=True))
|
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
|
||||||
|
|
||||||
def row_deletable(self, row):
|
def row_deletable(self, row):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -256,6 +256,8 @@ class ProductsView(MasterView):
|
||||||
cost = product.cost
|
cost = product.cost
|
||||||
if not cost:
|
if not cost:
|
||||||
return ""
|
return ""
|
||||||
|
if cost.unit_cost is None:
|
||||||
|
return ""
|
||||||
return "${:0.2f}".format(cost.unit_cost)
|
return "${:0.2f}".format(cost.unit_cost)
|
||||||
|
|
||||||
def render_on_hand(self, product, column):
|
def render_on_hand(self, product, column):
|
||||||
|
|
|
@ -159,6 +159,7 @@ class UsersView(PrincipalMasterView):
|
||||||
|
|
||||||
def query(self, session):
|
def query(self, session):
|
||||||
return session.query(model.User)\
|
return session.query(model.User)\
|
||||||
|
.outerjoin(model.Person)\
|
||||||
.options(orm.joinedload(model.User.person))
|
.options(orm.joinedload(model.User.person))
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
|
@ -177,28 +178,44 @@ class UsersView(PrincipalMasterView):
|
||||||
g.filters['password'] = g.make_filter('password', model.User.password,
|
g.filters['password'] = g.make_filter('password', model.User.password,
|
||||||
verbs=['is_null', 'is_not_null'])
|
verbs=['is_null', 'is_not_null'])
|
||||||
|
|
||||||
g.sorters['person'] = lambda q, d: q.order_by(getattr(model.Person.display_name, d)())
|
g.set_sorter('person', model.Person.display_name)
|
||||||
|
g.set_sorter('first_name', model.Person.first_name)
|
||||||
|
g.set_sorter('last_name', model.Person.last_name)
|
||||||
|
g.set_sorter('display_name', model.Person.display_name)
|
||||||
g.default_sortkey = 'username'
|
g.default_sortkey = 'username'
|
||||||
|
|
||||||
g.set_label('person', "Person's Name")
|
g.set_label('person', "Person's Name")
|
||||||
|
|
||||||
g.set_link('username')
|
g.set_link('username')
|
||||||
g.set_link('person')
|
g.set_link('person')
|
||||||
|
g.set_link('first_name')
|
||||||
|
g.set_link('last_name')
|
||||||
|
g.set_link('display_name')
|
||||||
|
|
||||||
def _preconfigure_fieldset(self, fs):
|
def _preconfigure_fieldset(self, fs):
|
||||||
fs.username.set(renderer=forms.renderers.StrippedTextFieldRenderer, validate=unique_username)
|
fs.username.set(renderer=forms.renderers.StrippedTextFieldRenderer, validate=unique_username)
|
||||||
fs.person.set(renderer=forms.renderers.PersonFieldRenderer, options=[])
|
fs.person.set(renderer=forms.renderers.PersonFieldRenderer, options=[])
|
||||||
fs.append(PasswordField('password', label="Set Password"))
|
fs.append(PasswordField('password', label="Set Password"))
|
||||||
fs.password.attrs(autocomplete='off')
|
|
||||||
fs.append(formalchemy.Field('confirm_password', renderer=PasswordFieldRenderer))
|
fs.append(formalchemy.Field('confirm_password', renderer=PasswordFieldRenderer))
|
||||||
fs.confirm_password.attrs(autocomplete='off')
|
|
||||||
fs.append(RolesField('roles', renderer=RolesFieldRenderer(self.request), size=10))
|
fs.append(RolesField('roles', renderer=RolesFieldRenderer(self.request), size=10))
|
||||||
|
fs.append(forms.AssociationProxyField('first_name'))
|
||||||
|
fs.append(forms.AssociationProxyField('last_name'))
|
||||||
|
fs.append(forms.AssociationProxyField('display_name', label="Full Name"))
|
||||||
|
|
||||||
|
# hm this should work according to MDN but doesn't seem to...
|
||||||
|
# https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||||
|
fs.username.attrs(autocomplete='new-password')
|
||||||
|
fs.password.attrs(autocomplete='new-password')
|
||||||
|
fs.confirm_password.attrs(autocomplete='new-password')
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
fs.configure(
|
fs.configure(
|
||||||
include=[
|
include=[
|
||||||
fs.username,
|
fs.username,
|
||||||
fs.person,
|
fs.person,
|
||||||
|
fs.first_name,
|
||||||
|
fs.last_name,
|
||||||
|
fs.display_name,
|
||||||
fs.active,
|
fs.active,
|
||||||
fs.active_sticky,
|
fs.active_sticky,
|
||||||
fs.password,
|
fs.password,
|
||||||
|
@ -326,6 +343,7 @@ class UserEventsView(MasterView):
|
||||||
return event.user.username
|
return event.user.username
|
||||||
|
|
||||||
def render_person(self, event, column):
|
def render_person(self, event, column):
|
||||||
|
if event.user.person:
|
||||||
return event.user.person.display_name
|
return event.user.person.display_name
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue