Add beginnings of mobile receiving views

Very incomplete, not much is supported yet, but this is a start..
This commit is contained in:
Lance Edgar 2017-02-18 13:34:31 -06:00
parent 6ed752d477
commit 3930ed9a16
5 changed files with 235 additions and 17 deletions

View file

@ -0,0 +1,78 @@
/******************************************
* jQuery Mobile plugins for Tailbone
*****************************************/
/******************************************
* mobile autocomplete
*****************************************/
(function($) {
$.widget('tailbone.mobileautocomplete', {
_create: function() {
var that = this;
// snag some element references
this.search = this.element.find('.ui-input-search');
this.hidden_field = this.element.find('input[type="hidden"]');
this.text_field = this.element.find('input[type="text"]');
this.ul = this.element.find('ul');
this.button = this.element.find('button');
// establish our autocomplete URL
this.url = this.options.url || this.element.data('url');
// NOTE: much of this code was copied from the jquery mobile demo site
// https://demos.jquerymobile.com/1.4.5/listview-autocomplete-remote/
this.ul.on('filterablebeforefilter', function(e, data) {
var $input = $( data.input ),
value = $input.val(),
html = "";
that.ul.html( "" );
if ( value && value.length > 2 ) {
that.ul.html( "<li><div class='ui-loader'><span class='ui-icon ui-icon-loading'></span></div></li>" );
that.ul.listview( "refresh" );
$.ajax({
url: that.url,
data: {
term: $input.val()
}
})
.then( function ( response ) {
$.each( response, function ( i, val ) {
html += '<li data-uuid="' + val.value + '">' + val.label + "</li>";
});
that.ul.html( html );
that.ul.listview( "refresh" );
that.ul.trigger( "updatelayout");
});
}
});
// when user clicks autocomplete result, hide search etc.
this.ul.on('click', 'li', function() {
var $li = $(this);
that.search.hide();
that.hidden_field.val($li.data('uuid'));
that.button.text($li.text()).show();
that.ul.hide();
});
// when user clicks "change" button, show search etc.
this.button.click(function() {
that.button.hide();
that.ul.empty().show();
that.hidden_field.val('');
that.search.show();
that.text_field.focus();
});
}
});
})( jQuery );

View file

@ -29,13 +29,36 @@ $(document).on('pagecontainerchange', function(event, ui) {
});
$(document).on('pagecreate', function() {
// setup any autocomplete fields
$('.field.autocomplete').mobileautocomplete();
});
/**
* Automatically set focus to certain fields, on various pages
*/
function setfocus() {
var el = null;
var queries = [
'#username',
'#new-purchasing-batch-vendor-text',
];
$.each(queries, function(i, query) {
el = $(query);
if (el.is(':visible')) {
el.focus();
return false;
}
});
}
$(document).on('pageshow', function() {
// on login page, auto-focus username
el = $('#username');
if (el.is(':visible')) {
el.focus();
}
setfocus();
// TODO: seems like this should be better somehow...
// remove all flash messages after 2.5 seconds
@ -44,8 +67,28 @@ $(document).on('pageshow', function() {
});
$(document).on('click', '#datasync-restart', function() {
// vendor validation for new purchasing batch
$(document).on('click', 'form[name="new-purchasing-batch"] input[type="submit"]', function() {
var $form = $(this).parents('form');
if (! $form.find('[name="vendor"]').val()) {
alert("Please select a vendor");
$form.find('[name="new-purchasing-batch-vendor-text"]').focus();
return false;
}
});
// disable datasync restart button when clicked
// submit new purchasing batch form on Purchase click
$(document).on('click', 'form[name="new-purchasing-batch"] [data-role="listview"] a', function() {
var $form = $(this).parents('form');
var $field = $form.find('[name="purchase"]');
var uuid = $(this).parents('li').data('uuid');
$field.val(uuid);
$form.submit();
return false;
});
// disable datasync restart button when clicked
$(document).on('click', '#datasync-restart', function() {
$(this).button('disable');
});

View file

@ -5,16 +5,22 @@
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>${self.global_title()} &raquo; ${self.title()}</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
${h.javascript_link('https://code.jquery.com/jquery-1.12.4.min.js')}
${h.javascript_link('https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js')}
${h.stylesheet_link('https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css')}
${h.javascript_link(request.static_url('tailbone:static/js/jquery.ui.tailbone.mobile.js'))}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.mobile.js'))}
${self.extra_javascript()}
${h.stylesheet_link('https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css')}
${h.stylesheet_link(request.static_url('tailbone:static/css/mobile.css'))}
% if not request.rattail_config.production():
<style type="text/css">
.ui-page-theme-a { background-image: url(${request.static_url('tailbone:static/img/testing.png')}); }
</style>
<style type="text/css">
.ui-page-theme-a { background-image: url(${request.static_url('tailbone:static/img/testing.png')}); }
</style>
% endif
${self.extra_styles()}
</head>
${self.mobile_body()}
</html>
@ -47,6 +53,10 @@
<%def name="page_title()">${self.title()}</%def>
<%def name="extra_javascript()"></%def>
<%def name="extra_styles()"></%def>
<%def name="mobile_header()">
<div data-role="header">
${self.mobile_header_link()}

View file

@ -0,0 +1,49 @@
## -*- coding: utf-8 -*-
<%inherit file="/mobile/base.mako" />
<%def name="title()">New ${mode_title} Batch</%def>
${h.form(request.current_route_url(), class_='ui-filterable', name='new-purchasing-batch')}
${h.csrf_token(request)}
% if vendor is Undefined:
<div class="field-wrapper vendor">
<div class="field autocomplete" data-url="${url('vendors.autocomplete')}">
${h.hidden('vendor')}
${h.text('new-purchasing-batch-vendor-text', placeholder="Vendor name", autocomplete='off', **{'data-type': 'search'})}
<ul data-role="listview" data-inset="true" data-filter="true" data-input="#new-purchasing-batch-vendor-text"></ul>
<button type="button" style="display: none;">Change Vendor</button>
</div>
</div>
<br />
${h.submit('submit', "Find purchase orders")}
## <button type="button">New receiving from scratch</button>
% else: ## vendor is known
<div class="field-wrapper vendor">
<div class="field">
${h.hidden('vendor', value=vendor.uuid)}
${vendor}
</div>
</div>
% if purchases:
${h.hidden('purchase')}
<ul data-role="listview" data-inset="true">
% for uuid, purchase in purchases:
<li data-uuid="${uuid}">${h.link_to(purchase, '#')}</li>
% endfor
</ul>
% else:
<p>(no eligible purchases found)</p>
% endif
## ${h.link_to("Receive from scratch for {}".format(vendor), '#', class_='ui-btn ui-corner-all')}
${h.link_to("Start over", url('purchases.batch.mobile_create'), class_='ui-btn ui-corner-all')}
% endif
${h.end_form()}

View file

@ -240,14 +240,16 @@ class PurchaseBatchView(BatchMasterView):
fs.department.set(readonly=True)
fs.purchase.set(readonly=True)
def eligible_purchases(self):
uuid = self.request.GET.get('vendor_uuid')
vendor = Session.query(model.Vendor).get(uuid) if uuid else None
def eligible_purchases(self, vendor_uuid=None, mode=None):
if not vendor_uuid:
vendor_uuid = self.request.GET.get('vendor_uuid')
vendor = Session.query(model.Vendor).get(vendor_uuid) if vendor_uuid else None
if not vendor:
return {'error': "Must specify a vendor."}
mode = self.request.GET.get('mode')
mode = int(mode) if mode and mode.isdigit() else None
if mode is None:
mode = self.request.GET.get('mode')
mode = int(mode) if mode and mode.isdigit() else None
if not mode or mode not in self.enum.PURCHASE_BATCH_MODE:
return {'error': "Unknown mode: {}".format(mode)}
@ -888,6 +890,37 @@ class PurchaseBatchView(BatchMasterView):
self.mobile = True
return self.render_to_response('mobile_index', {})
def mobile_create(self):
"""
View for creating a new purchasing batch via mobile
"""
# TODO: make this dynamic somehow, support other modes
mode = self.enum.PURCHASE_BATCH_MODE_RECEIVING
data = {'mode': mode}
vendor = None
if self.request.method == 'POST' and self.request.POST.get('vendor'):
vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor'])
if vendor:
data['vendor'] = vendor
if self.request.POST.get('purchase'):
purchase = self.get_purchase(self.request.POST['purchase'])
if purchase:
# TODO: these kwargs need help!
batch = self.handler.make_batch(self.Session(),
mode=mode, vendor=vendor,
store=self.rattail_config.get_store(self.Session()),
buyer=self.request.user.employee,
created_by=self.request.user)
self.request.session.flash("Created new purchasing batch: {}".format(batch))
return self.redirect(self.request.route_url('purchases.batch.mobile_create'))
data['mode_title'] = self.enum.PURCHASE_BATCH_MODE[mode].capitalize()
if vendor:
purchases = self.eligible_purchases(vendor.uuid, mode=mode)
data['purchases'] = [(p['key'], p['display']) for p in purchases['purchases']]
return self.render_to_response('mobile_create', data)
@classmethod
def defaults(cls, config):
route_prefix = cls.get_route_prefix()
@ -896,11 +929,16 @@ class PurchaseBatchView(BatchMasterView):
model_key = cls.get_model_key()
model_title = cls.get_model_title()
# mobile
# mobile index
config.add_route('{}.mobile'.format(route_prefix), '/mobile{}'.format(url_prefix))
config.add_view(cls, attr='mobile_index', route_name='{}.mobile'.format(route_prefix),
permission='{}.list'.format(permission_prefix))
# mobile create
config.add_route('{}.mobile_create'.format(route_prefix), '/mobile{}/new'.format(url_prefix))
config.add_view(cls, attr='mobile_create', route_name='{}.mobile_create'.format(route_prefix),
permission='{}.create'.format(permission_prefix))
# eligible purchases (AJAX)
config.add_route('{}.eligible_purchases'.format(route_prefix), '{}/eligible-purchases'.format(url_prefix))
config.add_view(cls, attr='eligible_purchases', route_name='{}.eligible_purchases'.format(route_prefix),