Add custom grid filter for phone number fields

and use it in various grid views
This commit is contained in:
Lance Edgar 2019-04-10 14:20:36 -05:00
parent 2bdcc4fe47
commit ec70d85638
6 changed files with 63 additions and 13 deletions

View file

@ -26,6 +26,7 @@ Grid Filters
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import re
import datetime import datetime
import logging import logging
@ -765,6 +766,45 @@ class AlchemyGPCFilter(AlchemyGridFilter):
return query return query
class AlchemyPhoneNumberFilter(AlchemyStringFilter):
"""
Special string filter, with logic to deal with phone numbers.
"""
def parse_value(self, value):
newvalue = None
# first we try to split according to typical 7- or 10-digit number
digits = re.sub(r'\D', '', value or '')
if len(digits) == 7:
newvalue = "{} {}".format(digits[:3], digits[3:])
elif len(digits) == 10:
newvalue = "{} {} {}".format(digits[:3], digits[3:6], digits[6:])
# if that didn't work, we can also try to split by grouped digits
if not newvalue and value:
parts = re.split(r'\D+', value)
newvalue = ' '.join(parts)
return newvalue or value
def filter_contains(self, query, value):
"""
Try to parse the value into "parts" of a phone number, then do a normal
'ILIKE' query with those parts.
"""
value = self.parse_value(value)
return super(AlchemyPhoneNumberFilter, self).filter_contains(query, value)
def filter_does_not_contain(self, query, value):
"""
Try to parse the value into "parts" of a phone number, then do a normal
'NOT ILIKE' query with those parts.
"""
value = self.parse_value(value)
return super(AlchemyPhoneNumberFilter, self).filter_does_not_contain(query, value)
class GridFilterSet(OrderedDict): class GridFilterSet(OrderedDict):
""" """
Collection class for :class:`GridFilter` instances. Collection class for :class:`GridFilter` instances.

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar # Copyright © 2010-2019 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -116,7 +116,9 @@ class CustomersView(MasterView):
model.CustomerPhoneNumber.parent_uuid == model.Customer.uuid, model.CustomerPhoneNumber.parent_uuid == model.Customer.uuid,
model.CustomerPhoneNumber.preference == 1))) model.CustomerPhoneNumber.preference == 1)))
g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.CustomerPhoneNumber.number, d)()) g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.CustomerPhoneNumber.number, d)())
g.set_filter('phone', model.CustomerPhoneNumber.number)#, label="Phone Number") g.set_filter('phone', model.CustomerPhoneNumber.number,
# label="Phone Number",
factory=grids.filters.AlchemyPhoneNumberFilter)
g.set_label('phone', "Phone Number") g.set_label('phone', "Phone Number")
# email # email

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar # Copyright © 2010-2019 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -35,6 +35,7 @@ import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from webhelpers2.html import tags, HTML from webhelpers2.html import tags, HTML
from tailbone import grids
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView, AutocompleteView from tailbone.views import MasterView, AutocompleteView
@ -90,8 +91,9 @@ class EmployeesView(MasterView):
g.filters['email'] = g.make_filter('email', model.EmployeeEmailAddress.address, g.filters['email'] = g.make_filter('email', model.EmployeeEmailAddress.address,
label="Email Address") label="Email Address")
g.filters['phone'] = g.make_filter('phone', model.EmployeePhoneNumber.number, g.set_filter('phone', model.EmployeePhoneNumber.number,
label="Phone Number") label="Phone Number",
factory=grids.filters.AlchemyPhoneNumberFilter)
# id # id
if self.request.has_perm('{}.edit'.format(route_prefix)): if self.request.has_perm('{}.edit'.format(route_prefix)):

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar # Copyright © 2010-2019 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -29,12 +29,13 @@ from __future__ import unicode_literals, absolute_import
import six import six
import sqlalchemy as sa import sqlalchemy as sa
from rattail.db import model
from deform import widget as dfwidget from deform import widget as dfwidget
from tailbone import grids
from tailbone.views import MasterView from tailbone.views import MasterView
from rattail.db import model
class MemberView(MasterView): class MemberView(MasterView):
""" """
@ -92,7 +93,8 @@ class MemberView(MasterView):
model.MemberPhoneNumber.parent_uuid == model.Member.uuid, model.MemberPhoneNumber.parent_uuid == model.Member.uuid,
model.MemberPhoneNumber.preference == 1))) model.MemberPhoneNumber.preference == 1)))
g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.MemberPhoneNumber.number, d)()) g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.MemberPhoneNumber.number, d)())
g.set_filter('phone', model.MemberPhoneNumber.number) g.set_filter('phone', model.MemberPhoneNumber.number,
factory=grids.filters.AlchemyPhoneNumberFilter)
g.set_label('phone', "Phone Number") g.set_label('phone', "Phone Number")
# email # email

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar # Copyright © 2010-2019 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -34,6 +34,7 @@ from rattail.db import model, api
from pyramid.httpexceptions import HTTPFound, HTTPNotFound from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from tailbone import grids
from tailbone.views import MasterView, AutocompleteView from tailbone.views import MasterView, AutocompleteView
@ -92,7 +93,8 @@ class PeopleView(MasterView):
model.PersonPhoneNumber.preference == 1)) model.PersonPhoneNumber.preference == 1))
g.filters['email'] = g.make_filter('email', model.PersonEmailAddress.address) g.filters['email'] = g.make_filter('email', model.PersonEmailAddress.address)
g.filters['phone'] = g.make_filter('phone', model.PersonPhoneNumber.number) g.set_filter('phone', model.PersonPhoneNumber.number,
factory=grids.filters.AlchemyPhoneNumberFilter)
g.joiners['customer_id'] = lambda q: q.outerjoin(model.CustomerPerson).outerjoin(model.Customer) g.joiners['customer_id'] = lambda q: q.outerjoin(model.CustomerPerson).outerjoin(model.Customer)
g.filters['customer_id'] = g.make_filter('customer_id', model.Customer.id) g.filters['customer_id'] = g.make_filter('customer_id', model.Customer.id)

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar # Copyright © 2010-2019 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -32,6 +32,7 @@ from rattail.db import model
import colander import colander
from tailbone import grids
from tailbone.views import MasterView from tailbone.views import MasterView
@ -65,7 +66,8 @@ class StoresView(MasterView):
model.StorePhoneNumber.parent_uuid == model.Store.uuid, model.StorePhoneNumber.parent_uuid == model.Store.uuid,
model.StorePhoneNumber.preference == 1))) model.StorePhoneNumber.preference == 1)))
g.filters['phone'] = g.make_filter('phone', model.StorePhoneNumber.number) g.set_filter('phone', model.StorePhoneNumber.number,
factory=grids.filters.AlchemyPhoneNumberFilter)
g.filters['email'] = g.make_filter('email', model.StoreEmailAddress.address) g.filters['email'] = g.make_filter('email', model.StoreEmailAddress.address)
g.filters['name'].default_active = True g.filters['name'].default_active = True
g.filters['name'].default_verb = 'contains' g.filters['name'].default_verb = 'contains'