Add beginnings of mobile receiving views
Very incomplete, not much is supported yet, but this is a start..
This commit is contained in:
parent
6ed752d477
commit
3930ed9a16
78
tailbone/static/js/jquery.ui.tailbone.mobile.js
Normal file
78
tailbone/static/js/jquery.ui.tailbone.mobile.js
Normal 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 );
|
|
@ -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() {
|
$(document).on('pageshow', function() {
|
||||||
|
|
||||||
// on login page, auto-focus username
|
setfocus();
|
||||||
el = $('#username');
|
|
||||||
if (el.is(':visible')) {
|
|
||||||
el.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: seems like this should be better somehow...
|
// TODO: seems like this should be better somehow...
|
||||||
// remove all flash messages after 2.5 seconds
|
// 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');
|
$(this).button('disable');
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,16 +5,22 @@
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||||
<title>${self.global_title()} » ${self.title()}</title>
|
<title>${self.global_title()} » ${self.title()}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<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/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.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'))}
|
${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'))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/mobile.css'))}
|
||||||
% if not request.rattail_config.production():
|
% if not request.rattail_config.production():
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.ui-page-theme-a { background-image: url(${request.static_url('tailbone:static/img/testing.png')}); }
|
.ui-page-theme-a { background-image: url(${request.static_url('tailbone:static/img/testing.png')}); }
|
||||||
</style>
|
</style>
|
||||||
% endif
|
% endif
|
||||||
|
${self.extra_styles()}
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
${self.mobile_body()}
|
${self.mobile_body()}
|
||||||
</html>
|
</html>
|
||||||
|
@ -47,6 +53,10 @@
|
||||||
|
|
||||||
<%def name="page_title()">${self.title()}</%def>
|
<%def name="page_title()">${self.title()}</%def>
|
||||||
|
|
||||||
|
<%def name="extra_javascript()"></%def>
|
||||||
|
|
||||||
|
<%def name="extra_styles()"></%def>
|
||||||
|
|
||||||
<%def name="mobile_header()">
|
<%def name="mobile_header()">
|
||||||
<div data-role="header">
|
<div data-role="header">
|
||||||
${self.mobile_header_link()}
|
${self.mobile_header_link()}
|
||||||
|
|
49
tailbone/templates/purchases/batches/mobile_create.mako
Normal file
49
tailbone/templates/purchases/batches/mobile_create.mako
Normal 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()}
|
|
@ -240,14 +240,16 @@ class PurchaseBatchView(BatchMasterView):
|
||||||
fs.department.set(readonly=True)
|
fs.department.set(readonly=True)
|
||||||
fs.purchase.set(readonly=True)
|
fs.purchase.set(readonly=True)
|
||||||
|
|
||||||
def eligible_purchases(self):
|
def eligible_purchases(self, vendor_uuid=None, mode=None):
|
||||||
uuid = self.request.GET.get('vendor_uuid')
|
if not vendor_uuid:
|
||||||
vendor = Session.query(model.Vendor).get(uuid) if uuid else None
|
vendor_uuid = self.request.GET.get('vendor_uuid')
|
||||||
|
vendor = Session.query(model.Vendor).get(vendor_uuid) if vendor_uuid else None
|
||||||
if not vendor:
|
if not vendor:
|
||||||
return {'error': "Must specify a vendor."}
|
return {'error': "Must specify a vendor."}
|
||||||
|
|
||||||
mode = self.request.GET.get('mode')
|
if mode is None:
|
||||||
mode = int(mode) if mode and mode.isdigit() else 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:
|
if not mode or mode not in self.enum.PURCHASE_BATCH_MODE:
|
||||||
return {'error': "Unknown mode: {}".format(mode)}
|
return {'error': "Unknown mode: {}".format(mode)}
|
||||||
|
|
||||||
|
@ -888,6 +890,37 @@ class PurchaseBatchView(BatchMasterView):
|
||||||
self.mobile = True
|
self.mobile = True
|
||||||
return self.render_to_response('mobile_index', {})
|
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
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
route_prefix = cls.get_route_prefix()
|
route_prefix = cls.get_route_prefix()
|
||||||
|
@ -896,11 +929,16 @@ class PurchaseBatchView(BatchMasterView):
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
model_title = cls.get_model_title()
|
model_title = cls.get_model_title()
|
||||||
|
|
||||||
# mobile
|
# mobile index
|
||||||
config.add_route('{}.mobile'.format(route_prefix), '/mobile{}'.format(url_prefix))
|
config.add_route('{}.mobile'.format(route_prefix), '/mobile{}'.format(url_prefix))
|
||||||
config.add_view(cls, attr='mobile_index', route_name='{}.mobile'.format(route_prefix),
|
config.add_view(cls, attr='mobile_index', route_name='{}.mobile'.format(route_prefix),
|
||||||
permission='{}.list'.format(permission_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)
|
# eligible purchases (AJAX)
|
||||||
config.add_route('{}.eligible_purchases'.format(route_prefix), '{}/eligible-purchases'.format(url_prefix))
|
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),
|
config.add_view(cls, attr='eligible_purchases', route_name='{}.eligible_purchases'.format(route_prefix),
|
||||||
|
|
Loading…
Reference in a new issue