Various changes to support a certain new app

improve inventory support, plus "hiding" person data but still using it
This commit is contained in:
Lance Edgar 2017-08-09 21:41:42 -05:00
parent fcffe0f79d
commit 18f4b4ff5c
7 changed files with 64 additions and 18 deletions

View file

@ -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

View file

@ -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")

View file

@ -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.default_phone,
# fs.phone.label("Phone Number").readonly(), fs.default_email,
fs.default_phone, fs.email_preference,
# fs.email.label("Email Address").readonly(), fs.active_in_pos,
fs.default_email, fs.active_in_pos_sticky,
fs.email_preference, ]
if not self.creating:
include.extend([
fs.people, fs.people,
fs.active_in_pos,
fs.active_in_pos_sticky,
]) ])
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.

View file

@ -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,

View file

@ -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):
""" """

View file

@ -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):

View file

@ -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,7 +343,8 @@ class UserEventsView(MasterView):
return event.user.username return event.user.username
def render_person(self, event, column): def render_person(self, event, column):
return event.user.person.display_name if event.user.person:
return event.user.person.display_name
def includeme(config): def includeme(config):