diff --git a/tailbone/newgrids/core.py b/tailbone/newgrids/core.py
index 2ba93f17..36cf5b8b 100644
--- a/tailbone/newgrids/core.py
+++ b/tailbone/newgrids/core.py
@@ -26,6 +26,8 @@ Core Grid Classes
from __future__ import unicode_literals, absolute_import
+import six
+
from rattail.db.api import get_setting, save_setting
from rattail.util import prettify
@@ -106,7 +108,7 @@ class Grid(object):
"""
Iterate over all filters available to the grid.
"""
- return self.filters.itervalues()
+ return six.itervalues(self.filters)
def iter_active_filters(self):
"""
diff --git a/tailbone/newgrids/filters.py b/tailbone/newgrids/filters.py
index baaf1e1a..f0857059 100644
--- a/tailbone/newgrids/filters.py
+++ b/tailbone/newgrids/filters.py
@@ -149,7 +149,7 @@ class GridFilter(object):
setattr(self, key, value)
def __repr__(self):
- return "GridFilter({0})".format(repr(self.key))
+ return "{}({})".format(self.__class__.__name__, repr(self.key))
def get_default_verbs(self):
"""
@@ -206,6 +206,18 @@ class GridFilter(object):
return self.value_renderer.render(value=value, **kwargs)
+class MobileFilter(GridFilter):
+ """
+ Base class for mobile grid filters.
+ """
+ default_verbs = ['equal']
+
+ def __init__(self, key, **kwargs):
+ kwargs.setdefault('default_active', True)
+ kwargs.setdefault('default_verb', 'equal')
+ super(MobileFilter, self).__init__(key, **kwargs)
+
+
class AlchemyGridFilter(GridFilter):
"""
Base class for SQLAlchemy grid filters.
diff --git a/tailbone/newgrids/mobile.py b/tailbone/newgrids/mobile.py
index ab002e7c..c08453a3 100644
--- a/tailbone/newgrids/mobile.py
+++ b/tailbone/newgrids/mobile.py
@@ -26,16 +26,24 @@ Mobile Grids
from __future__ import unicode_literals, absolute_import
+from pyramid.renderers import render
from webhelpers2.html import HTML
-from tailbone.newgrids import AlchemyGrid
+from tailbone.newgrids import filters, AlchemyGrid
class MobileGrid(AlchemyGrid):
"""
Base class for all mobile grids
"""
+ default_filters = filters.GridFilterSet()
def column_header(self, column):
kwargs = {'c': column.label}
return HTML.tag('th', **kwargs)
+
+ def render_filters(self, template='/mobile/filters_simple.mako', **kwargs):
+ context = kwargs
+ context['request'] = self.request
+ context['grid'] = self
+ return render(template, context)
diff --git a/tailbone/static/css/mobile.css b/tailbone/static/css/mobile.css
index 7dd238f9..9ed2ed93 100644
--- a/tailbone/static/css/mobile.css
+++ b/tailbone/static/css/mobile.css
@@ -34,3 +34,8 @@
font-weight: bold;
margin-top: 1em;
}
+
+/* make sure space comes between simple filter and "grid" list */
+.simple-filter {
+ margin-bottom: 1.5em;
+}
diff --git a/tailbone/static/js/tailbone.mobile.js b/tailbone/static/js/tailbone.mobile.js
index af34191a..3e8db76b 100644
--- a/tailbone/static/js/tailbone.mobile.js
+++ b/tailbone/static/js/tailbone.mobile.js
@@ -69,6 +69,12 @@ $(document).on('pageshow', function() {
});
+// handle radio button value change for "simple" grid filter
+$(document).on('change', '.simple-filter .ui-radio', function() {
+ $(this).parents('form:first').submit();
+});
+
+
// vendor validation for new purchasing batch
$(document).on('click', 'form[name="new-purchasing-batch"] input[type="submit"]', function() {
var $form = $(this).parents('form');
diff --git a/tailbone/templates/mobile/filters_simple.mako b/tailbone/templates/mobile/filters_simple.mako
new file mode 100644
index 00000000..d0644e5f
--- /dev/null
+++ b/tailbone/templates/mobile/filters_simple.mako
@@ -0,0 +1,16 @@
+## -*- coding: utf-8; -*-
+
+ ${h.form(request.current_route_url(), method='get')}
+ ${h.csrf_token(request)}
+
+ % for filtr in grid.iter_filters():
+ ${h.hidden('{}.verb'.format(filtr.key), value=filtr.verb)}
+
+ % endfor
+
+ ${h.end_form()}
+
diff --git a/tailbone/templates/mobile/master/index.mako b/tailbone/templates/mobile/master/index.mako
index b212c3cd..74b5d7f7 100644
--- a/tailbone/templates/mobile/master/index.mako
+++ b/tailbone/templates/mobile/master/index.mako
@@ -9,6 +9,10 @@
<%def name="title()">${model_title_plural}%def>
+% if grid.filterable:
+ ${grid.render_filters()|n}
+% endif
+
% for obj in grid.iter_rows():
- ${grid.listitem.render_readonly()}
diff --git a/tailbone/templates/mobile/receiving/index.mako b/tailbone/templates/mobile/receiving/index.mako
index 098a0078..10941783 100644
--- a/tailbone/templates/mobile/receiving/index.mako
+++ b/tailbone/templates/mobile/receiving/index.mako
@@ -7,12 +7,4 @@
${h.link_to("New Receiving Batch", url('mobile.receiving.create'), class_='ui-btn ui-corner-all')}
% endif
-
-
-
${parent.body()}
diff --git a/tailbone/views/master.py b/tailbone/views/master.py
index 274eaf8d..604d1456 100644
--- a/tailbone/views/master.py
+++ b/tailbone/views/master.py
@@ -65,6 +65,7 @@ class MasterView(View):
supports_mobile = False
mobile_creatable = False
+ mobile_filterable = False
listing = False
creating = False
@@ -197,11 +198,19 @@ class MasterView(View):
defaults = {
'route_prefix': self.get_route_prefix(),
'pageable': self.pageable,
- 'sortable': True,
+ 'sortable': False,
+ 'filterable': self.mobile_filterable,
}
+ if self.mobile_filterable:
+ defaults['filters'] = self.make_mobile_filters()
defaults.update(kwargs)
return defaults
+ def make_mobile_filters(self):
+ """
+ Returns a set of filters for the mobile grid, if applicable.
+ """
+
def preconfigure_mobile_grid(self, grid):
"""
Optionally perform pre-configuration for the mobile grid, to establish
diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py
index 81a1c343..dbdcbbe7 100644
--- a/tailbone/views/purchasing/receiving.py
+++ b/tailbone/views/purchasing/receiving.py
@@ -33,16 +33,42 @@ import sqlalchemy as sa
from rattail import pod
from rattail.db import model
from rattail.gpc import GPC
-from rattail.util import pretty_quantity
+from rattail.util import pretty_quantity, prettify
import formalchemy as fa
import formencode as fe
from webhelpers2.html import tags
-from tailbone import forms
+from tailbone import forms, newgrids as grids
from tailbone.views.purchasing import PurchasingBatchView
+class MobileBatchStatusFilter(grids.filters.MobileFilter):
+
+ value_choices = ['pending', 'complete', 'executed', 'all']
+
+ def filter_equal(self, query, value):
+
+ if value == 'pending':
+ return query.filter(model.PurchaseBatch.executed == None)\
+ .filter(sa.or_(
+ model.PurchaseBatch.complete == None,
+ model.PurchaseBatch.complete == False))
+
+ if value == 'complete':
+ return query.filter(model.PurchaseBatch.executed == None)\
+ .filter(model.PurchaseBatch.complete == True)
+
+ if value == 'executed':
+ return query.filter(model.PurchaseBatch.executed != None)
+
+ return query
+
+ def iter_choices(self):
+ for value in self.value_choices:
+ yield value, prettify(value)
+
+
class ReceivingBatchView(PurchasingBatchView):
"""
Master view for receiving batches
@@ -55,12 +81,21 @@ class ReceivingBatchView(PurchasingBatchView):
rows_deletable = False
supports_mobile = True
mobile_creatable = True
+ mobile_filterable = True
mobile_rows_viewable = True
@property
def batch_mode(self):
return self.enum.PURCHASE_BATCH_MODE_RECEIVING
+ def make_mobile_filters(self):
+ """
+ Returns a set of filters for the mobile grid.
+ """
+ filters = grids.filters.GridFilterSet()
+ filters['status'] = MobileBatchStatusFilter('status', default_value='pending')
+ return filters
+
def mobile_create(self):
"""
Mobile view for creating a new receiving batch
@@ -115,12 +150,8 @@ class ReceivingBatchView(PurchasingBatchView):
return kwargs
def get_mobile_data(self, session=None):
- # TODO: this hard-codes list view to show Pending only
+ # TODO: this hard-codes the sort by batch ID desc
return super(ReceivingBatchView, self).get_mobile_data(session=session)\
- .filter(model.PurchaseBatch.executed == None)\
- .filter(sa.or_(
- model.PurchaseBatch.complete == None,
- model.PurchaseBatch.complete == False))\
.order_by(model.PurchaseBatch.id.desc())
def configure_mobile_grid(self, g):