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() {
|
||||
|
||||
// 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');
|
||||
});
|
||||
|
|
|
@ -5,16 +5,22 @@
|
|||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||
<title>${self.global_title()} » ${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()}
|
||||
|
|
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.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),
|
||||
|
|
Loading…
Reference in a new issue