Refactor mobile receiving to use "quick row" feature
plus some other random things thrown in there, for good measure..
This commit is contained in:
parent
3cc8adba86
commit
a34a42d2b2
|
@ -113,7 +113,6 @@ $(document).on('click', '#datasync-restart', function() {
|
||||||
// handle global keypress on product batch "row" page, for sake of scanner wedge
|
// handle global keypress on product batch "row" page, for sake of scanner wedge
|
||||||
var product_batch_routes = [
|
var product_batch_routes = [
|
||||||
'mobile.batch.inventory.view',
|
'mobile.batch.inventory.view',
|
||||||
'mobile.receiving.view',
|
|
||||||
];
|
];
|
||||||
$(document).on('keypress', function(event) {
|
$(document).on('keypress', function(event) {
|
||||||
var current_route = $('.ui-page-active [role="main"]').data('route');
|
var current_route = $('.ui-page-active [role="main"]').data('route');
|
||||||
|
@ -146,10 +145,12 @@ $(document).on('keypress', function(event) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// handle ENTER press for quick_row forms
|
// handle various keypress events for quick row forms
|
||||||
$(document).on('keypress', function(event) {
|
$(document).on('keypress', function(event) {
|
||||||
var quick_row = $('.ui-page-active #quick_row_entry');
|
var quick_row = $('.ui-page-active #quick_row_entry');
|
||||||
if (quick_row.length) {
|
if (quick_row.length) {
|
||||||
|
|
||||||
|
// if user hits enter with quick row input focused, submit form
|
||||||
if (quick_row.is(':focus')) {
|
if (quick_row.is(':focus')) {
|
||||||
if (event.which == 13) { // ENTER
|
if (event.which == 13) { // ENTER
|
||||||
if (quick_row.val()) {
|
if (quick_row.val()) {
|
||||||
|
@ -158,6 +159,31 @@ $(document).on('keypress', function(event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else { // quick row input not focused
|
||||||
|
|
||||||
|
// mimic keyboard wedge if we're so instructed
|
||||||
|
if (quick_row.data('wedge')) {
|
||||||
|
|
||||||
|
if (event.which >= 48 && event.which <= 57) { // numeric (qwerty)
|
||||||
|
if (!event.altKey && !event.ctrlKey && !event.metaKey) {
|
||||||
|
quick_row.val(quick_row.val() + event.key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these codes are correct for 'keydown' but apparently not 'keypress' ?
|
||||||
|
// } else if (event.which >= 96 && event.which <= 105) { // numeric (10-key)
|
||||||
|
// upc.val(upc.val() + event.key);
|
||||||
|
|
||||||
|
} else if (event.which == 13) { // ENTER
|
||||||
|
// submit form when ENTER is received via keyboard "wedge"
|
||||||
|
if (quick_row.val()) {
|
||||||
|
var form = quick_row.parents('form:first');
|
||||||
|
form.submit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,21 +18,23 @@ ${form.render()|n}
|
||||||
% if master.mobile_rows_creatable and master.rows_creatable_for(instance):
|
% if master.mobile_rows_creatable and master.rows_creatable_for(instance):
|
||||||
## TODO: this seems like a poor choice of names? what are we really testing for here?
|
## TODO: this seems like a poor choice of names? what are we really testing for here?
|
||||||
% if master.mobile_rows_creatable_via_browse:
|
% if master.mobile_rows_creatable_via_browse:
|
||||||
${h.link_to(add_item_title, url('mobile.{}.create_row'.format(route_prefix), uuid=instance.uuid), class_='ui-btn ui-corner-all')}
|
<% add_title = "Add Record" if add_item_title is Undefined else add_item_title %>
|
||||||
|
${h.link_to(add_title, url('mobile.{}.create_row'.format(route_prefix), uuid=instance.uuid), class_='ui-btn ui-corner-all')}
|
||||||
% endif
|
% endif
|
||||||
% endif
|
% endif
|
||||||
% if master.mobile_rows_quickable and master.rows_quickable_for(instance):
|
% if master.mobile_rows_quickable and master.rows_quickable_for(instance):
|
||||||
|
<% placeholder = '' if quick_row_entry_placeholder is Undefined else quick_row_entry_placeholder %>
|
||||||
${h.form(url('mobile.{}.quick_row'.format(route_prefix), uuid=instance.uuid))}
|
${h.form(url('mobile.{}.quick_row'.format(route_prefix), uuid=instance.uuid))}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
% if quick_row_autocomplete:
|
% if quick_row_autocomplete:
|
||||||
<div class="field autocomplete quick-row" data-url="${quick_row_autocomplete_url}">
|
<div class="field autocomplete quick-row" data-url="${quick_row_autocomplete_url}">
|
||||||
${h.hidden('quick_row_entry')}
|
${h.hidden('quick_row_entry')}
|
||||||
${h.text('quick_row_autocomplete_text', placeholder=quick_row_entry_placeholder, autocomplete='off', data_type='search')}
|
${h.text('quick_row_autocomplete_text', placeholder=placeholder, autocomplete='off', data_type='search')}
|
||||||
<ul data-role="listview" data-inset="true" data-filter="true" data-input="#quick_row_autocomplete_text"></ul>
|
<ul data-role="listview" data-inset="true" data-filter="true" data-input="#quick_row_autocomplete_text"></ul>
|
||||||
<button type="button" style="display: none;">Change</button>
|
<button type="button" style="display: none;">Change</button>
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
${h.text('quick_row_entry', placeholder=quick_row_entry_placeholder, autocomplete='off', **{'data-type': 'search', 'data-url': url('mobile.{}.quick_row'.format(route_prefix), uuid=instance.uuid)})}
|
${h.text('quick_row_entry', placeholder=placeholder, autocomplete='off', **{'data-type': 'search', 'data-url': url('mobile.{}.quick_row'.format(route_prefix), uuid=instance.uuid), 'data-wedge': quick_row_keyboard_wedge})}
|
||||||
% endif
|
% endif
|
||||||
${h.end_form()}
|
${h.end_form()}
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
## -*- coding: utf-8; -*-
|
|
||||||
<%inherit file="/mobile/master/view.mako" />
|
|
||||||
|
|
||||||
<%def name="title()">Receiving » ${instance.id_str}</%def>
|
|
||||||
|
|
||||||
<%def name="page_title()">${h.link_to("Receiving", url('mobile.receiving'))} » ${instance.id_str}</%def>
|
|
||||||
|
|
||||||
${form.render()|n}
|
|
||||||
<br />
|
|
||||||
|
|
||||||
% if not instance.executed and not instance.complete:
|
|
||||||
${h.text('upc-search', class_='receiving-upc-search', placeholder="Enter UPC", autocomplete='off', **{'data-type': 'search', 'data-url': url('mobile.receiving.lookup', uuid=batch.uuid)})}
|
|
||||||
<br />
|
|
||||||
% endif
|
|
||||||
|
|
||||||
${grid.render_complete()|n}
|
|
||||||
|
|
||||||
% if not instance.executed and not instance.complete:
|
|
||||||
<br /><br />
|
|
||||||
${h.form(request.route_url('mobile.receiving.mark_complete', uuid=instance.uuid))}
|
|
||||||
${h.csrf_token(request)}
|
|
||||||
${h.hidden('mark-complete', value='true')}
|
|
||||||
<button type="submit">Mark Batch as Complete</button>
|
|
||||||
% endif
|
|
|
@ -2,9 +2,9 @@
|
||||||
<%inherit file="/mobile/master/view_row.mako" />
|
<%inherit file="/mobile/master/view_row.mako" />
|
||||||
<%namespace file="/mobile/keypad.mako" import="keypad" />
|
<%namespace file="/mobile/keypad.mako" import="keypad" />
|
||||||
|
|
||||||
<%def name="title()">Receiving » ${batch.id_str} » ${row.upc.pretty()}</%def>
|
<%def name="title()">Receiving » ${batch.id_str} » ${master.render_product_key_value(row)}</%def>
|
||||||
|
|
||||||
<%def name="page_title()">${h.link_to("Receiving", url('mobile.receiving'))} » ${h.link_to(batch.id_str, url('mobile.receiving.view', uuid=batch.uuid))} » ${row.upc.pretty()}</%def>
|
<%def name="page_title()">${h.link_to("Receiving", url('mobile.receiving'))} » ${h.link_to(batch.id_str, url('mobile.receiving.view', uuid=batch.uuid))} » ${master.render_product_key_value(row)}</%def>
|
||||||
|
|
||||||
|
|
||||||
<div class="ui-grid-a">
|
<div class="ui-grid-a">
|
||||||
|
|
|
@ -172,10 +172,8 @@ class CustomersView(MasterView):
|
||||||
|
|
||||||
raise HTTPNotFound
|
raise HTTPNotFound
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_common_form(self, f):
|
||||||
if not self.mobile:
|
super(CustomersView, self).configure_common_form(f)
|
||||||
super(CustomersView, self).configure_form(f)
|
|
||||||
|
|
||||||
customer = f.model_instance
|
customer = f.model_instance
|
||||||
permission_prefix = self.get_permission_prefix()
|
permission_prefix = self.get_permission_prefix()
|
||||||
|
|
||||||
|
@ -222,10 +220,6 @@ class CustomersView(MasterView):
|
||||||
f.set_renderer('groups', self.render_groups)
|
f.set_renderer('groups', self.render_groups)
|
||||||
f.set_readonly('groups')
|
f.set_readonly('groups')
|
||||||
|
|
||||||
def configure_mobile_form(self, f):
|
|
||||||
super(CustomersView, self).configure_mobile_form(f)
|
|
||||||
self.configure_form(f)
|
|
||||||
|
|
||||||
def objectify(self, form, data):
|
def objectify(self, form, data):
|
||||||
customer = super(CustomersView, self).objectify(form, data)
|
customer = super(CustomersView, self).objectify(form, data)
|
||||||
customer = self.objectify_contact(customer, data)
|
customer = self.objectify_contact(customer, data)
|
||||||
|
|
|
@ -717,6 +717,15 @@ class MasterView(View):
|
||||||
def process_uploads(self, obj, form, uploads):
|
def process_uploads(self, obj, form, uploads):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def render_product_key_value(self, obj):
|
||||||
|
"""
|
||||||
|
Render the "canonical" product key value for the given object.
|
||||||
|
"""
|
||||||
|
product_key = self.rattail_config.product_key()
|
||||||
|
if product_key == 'upc':
|
||||||
|
return obj.upc.pretty() if obj.upc else ''
|
||||||
|
return getattr(obj, product_key)
|
||||||
|
|
||||||
def before_create_flush(self, obj, form):
|
def before_create_flush(self, obj, form):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1060,24 +1069,36 @@ class MasterView(View):
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
def configure_mobile_form(self, form):
|
def configure_common_form(self, form):
|
||||||
"""
|
"""
|
||||||
Configure the primary mobile form.
|
Configure the form in whatever way is deemed "common" - i.e. where
|
||||||
|
configuration should be done the same for desktop and mobile.
|
||||||
|
|
||||||
|
By default this removes the 'uuid' field (if present), sets any primary
|
||||||
|
key fields to be readonly (if we have a :attr:`model_class` and are in
|
||||||
|
edit mode), and sets labels as defined by the master class hierarchy.
|
||||||
"""
|
"""
|
||||||
# TODO: is any of this stuff from configure_form() needed?
|
form.remove_field('uuid')
|
||||||
# if self.editing:
|
|
||||||
# model_class = self.get_model_class(error=False)
|
if self.editing:
|
||||||
# if model_class:
|
model_class = self.get_model_class(error=False)
|
||||||
# mapper = orm.class_mapper(model_class)
|
if model_class:
|
||||||
# for key in mapper.primary_key:
|
# set readonly for all primary key fields
|
||||||
# for field in form.fields:
|
mapper = orm.class_mapper(model_class)
|
||||||
# if field == key.name:
|
for key in mapper.primary_key:
|
||||||
# form.set_readonly(field)
|
for field in form.fields:
|
||||||
# break
|
if field == key.name:
|
||||||
# form.remove_field('uuid')
|
form.set_readonly(field)
|
||||||
|
break
|
||||||
|
|
||||||
self.set_labels(form)
|
self.set_labels(form)
|
||||||
|
|
||||||
|
def configure_mobile_form(self, form):
|
||||||
|
"""
|
||||||
|
Configure the main "mobile" form for the view's data model.
|
||||||
|
"""
|
||||||
|
self.configure_common_form(form)
|
||||||
|
|
||||||
def validate_mobile_form(self, form):
|
def validate_mobile_form(self, form):
|
||||||
controls = self.request.POST.items()
|
controls = self.request.POST.items()
|
||||||
try:
|
try:
|
||||||
|
@ -1832,17 +1853,14 @@ class MasterView(View):
|
||||||
context['row_model_title_plural'] = self.get_row_model_title_plural()
|
context['row_model_title_plural'] = self.get_row_model_title_plural()
|
||||||
context['row_action_url'] = self.get_row_action_url
|
context['row_action_url'] = self.get_row_action_url
|
||||||
|
|
||||||
if mobile:
|
if mobile and self.viewing and self.mobile_rows_quickable:
|
||||||
|
|
||||||
if self.mobile_rows_creatable:
|
# quick row does *not* mimic keyboard wedge by default, but can
|
||||||
context['add_item_title'] = "Add Record"
|
context['quick_row_keyboard_wedge'] = False
|
||||||
|
|
||||||
if self.mobile_rows_quickable:
|
# quick row does *not* use autocomplete by default, but can
|
||||||
context['quick_row_entry_placeholder'] = "Enter search text"
|
context['quick_row_autocomplete'] = False
|
||||||
|
context['quick_row_autocomplete_url'] = '#'
|
||||||
# quick row does *not* use autocomplete by default
|
|
||||||
context['quick_row_autocomplete'] = False
|
|
||||||
context['quick_row_autocomplete_url'] = '#'
|
|
||||||
|
|
||||||
context.update(data)
|
context.update(data)
|
||||||
context.update(self.template_kwargs(**context))
|
context.update(self.template_kwargs(**context))
|
||||||
|
@ -2366,22 +2384,9 @@ class MasterView(View):
|
||||||
|
|
||||||
def configure_form(self, form):
|
def configure_form(self, form):
|
||||||
"""
|
"""
|
||||||
Configure the primary form. By default this just sets any primary key
|
Configure the main "desktop" form for the view's data model.
|
||||||
fields to be readonly (if we have a :attr:`model_class`).
|
|
||||||
"""
|
"""
|
||||||
if self.editing:
|
self.configure_common_form(form)
|
||||||
model_class = self.get_model_class(error=False)
|
|
||||||
if model_class:
|
|
||||||
mapper = orm.class_mapper(model_class)
|
|
||||||
for key in mapper.primary_key:
|
|
||||||
for field in form.fields:
|
|
||||||
if field == key.name:
|
|
||||||
form.set_readonly(field)
|
|
||||||
break
|
|
||||||
|
|
||||||
form.remove_field('uuid')
|
|
||||||
|
|
||||||
self.set_labels(form)
|
|
||||||
|
|
||||||
def validate_form(self, form):
|
def validate_form(self, form):
|
||||||
if form.validate(newstyle=True):
|
if form.validate(newstyle=True):
|
||||||
|
|
|
@ -139,9 +139,8 @@ class PeopleView(MasterView):
|
||||||
return not bool(person.user and person.user.username == 'chuck')
|
return not bool(person.user and person.user.username == 'chuck')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_common_form(self, f):
|
||||||
if not self.mobile:
|
super(PeopleView, self).configure_common_form(f)
|
||||||
super(PeopleView, self).configure_form(f)
|
|
||||||
|
|
||||||
f.set_label('display_name', "Full Name")
|
f.set_label('display_name', "Full Name")
|
||||||
|
|
||||||
|
@ -163,10 +162,6 @@ class PeopleView(MasterView):
|
||||||
f.set_readonly('users')
|
f.set_readonly('users')
|
||||||
f.set_renderer('users', self.render_users)
|
f.set_renderer('users', self.render_users)
|
||||||
|
|
||||||
def configure_mobile_form(self, f):
|
|
||||||
super(PeopleView, self).configure_mobile_form(f)
|
|
||||||
self.configure_form(f)
|
|
||||||
|
|
||||||
def render_employee(self, person, field):
|
def render_employee(self, person, field):
|
||||||
employee = person.employee
|
employee = person.employee
|
||||||
if not employee:
|
if not employee:
|
||||||
|
|
|
@ -50,6 +50,10 @@ class PurchasingBatchView(BatchMasterView):
|
||||||
supports_new_product = False
|
supports_new_product = False
|
||||||
cloneable = True
|
cloneable = True
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
'po_total': "PO Total",
|
||||||
|
}
|
||||||
|
|
||||||
grid_columns = [
|
grid_columns = [
|
||||||
'id',
|
'id',
|
||||||
'vendor',
|
'vendor',
|
||||||
|
@ -214,6 +218,16 @@ class PurchasingBatchView(BatchMasterView):
|
||||||
# form = super(PurchasingBatchView, self).make_form(batch, **kwargs)
|
# form = super(PurchasingBatchView, self).make_form(batch, **kwargs)
|
||||||
# return form
|
# return form
|
||||||
|
|
||||||
|
def configure_common_form(self, f):
|
||||||
|
super(PurchasingBatchView, self).configure_common_form(f)
|
||||||
|
|
||||||
|
# po_total
|
||||||
|
if self.creating:
|
||||||
|
f.remove_field('po_total')
|
||||||
|
else:
|
||||||
|
f.set_readonly('po_total')
|
||||||
|
f.set_type('po_total', 'currency')
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_form(self, f):
|
||||||
super(PurchasingBatchView, self).configure_form(f)
|
super(PurchasingBatchView, self).configure_form(f)
|
||||||
batch = f.model_instance
|
batch = f.model_instance
|
||||||
|
@ -329,11 +343,6 @@ class PurchasingBatchView(BatchMasterView):
|
||||||
# po_number
|
# po_number
|
||||||
f.set_label('po_number', "PO Number")
|
f.set_label('po_number', "PO Number")
|
||||||
|
|
||||||
# po_total
|
|
||||||
f.set_readonly('po_total')
|
|
||||||
f.set_type('po_total', 'currency')
|
|
||||||
f.set_label('po_total', "PO Total")
|
|
||||||
|
|
||||||
# invoice_total
|
# invoice_total
|
||||||
f.set_readonly('invoice_total')
|
f.set_readonly('invoice_total')
|
||||||
f.set_type('invoice_total', 'currency')
|
f.set_type('invoice_total', 'currency')
|
||||||
|
@ -364,12 +373,6 @@ class PurchasingBatchView(BatchMasterView):
|
||||||
'vendor_contact',
|
'vendor_contact',
|
||||||
'status_code')
|
'status_code')
|
||||||
|
|
||||||
def configure_mobile_form(self, f):
|
|
||||||
super(PurchasingBatchView, self).configure_mobile_form(f)
|
|
||||||
|
|
||||||
# currency fields
|
|
||||||
f.set_type('po_total', 'currency')
|
|
||||||
|
|
||||||
def render_store(self, batch, field):
|
def render_store(self, batch, field):
|
||||||
store = batch.store
|
store = batch.store
|
||||||
if not store:
|
if not store:
|
||||||
|
|
|
@ -104,6 +104,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
mobile_creatable = True
|
mobile_creatable = True
|
||||||
mobile_rows_filterable = True
|
mobile_rows_filterable = True
|
||||||
mobile_rows_creatable = True
|
mobile_rows_creatable = True
|
||||||
|
mobile_rows_quickable = True
|
||||||
|
|
||||||
allow_from_po = False
|
allow_from_po = False
|
||||||
allow_from_scratch = True
|
allow_from_scratch = True
|
||||||
|
@ -351,7 +352,17 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
|
|
||||||
def get_batch_kwargs(self, batch, mobile=False):
|
def get_batch_kwargs(self, batch, mobile=False):
|
||||||
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
||||||
if not mobile:
|
|
||||||
|
if mobile:
|
||||||
|
purchase = self.get_purchase(self.request.POST['purchase'])
|
||||||
|
if isinstance(purchase, model.Purchase):
|
||||||
|
kwargs['purchase'] = purchase
|
||||||
|
|
||||||
|
department = self.department_for_purchase(purchase)
|
||||||
|
if department:
|
||||||
|
kwargs['department'] = department
|
||||||
|
|
||||||
|
else: # not mobile
|
||||||
batch_type = self.request.POST['batch_type']
|
batch_type = self.request.POST['batch_type']
|
||||||
if batch_type == 'from_scratch':
|
if batch_type == 'from_scratch':
|
||||||
kwargs.pop('truck_dump_batch', None)
|
kwargs.pop('truck_dump_batch', None)
|
||||||
|
@ -365,6 +376,9 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
def department_for_purchase(self, purchase):
|
||||||
|
pass
|
||||||
|
|
||||||
def delete_instance(self, batch):
|
def delete_instance(self, batch):
|
||||||
"""
|
"""
|
||||||
Delete all data (files etc.) for the batch.
|
Delete all data (files etc.) for the batch.
|
||||||
|
@ -498,6 +512,9 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
default_value=default_status)
|
default_value=default_status)
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
|
def get_purchase(self, uuid):
|
||||||
|
return self.Session.query(model.Purchase).get(uuid)
|
||||||
|
|
||||||
def mobile_create(self):
|
def mobile_create(self):
|
||||||
"""
|
"""
|
||||||
Mobile view for creating a new receiving batch
|
Mobile view for creating a new receiving batch
|
||||||
|
@ -580,8 +597,9 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
f.set_readonly('invoice_total')
|
f.set_readonly('invoice_total')
|
||||||
|
|
||||||
def render_mobile_row_listitem(self, row, i):
|
def render_mobile_row_listitem(self, row, i):
|
||||||
|
key = self.render_product_key_value(row)
|
||||||
description = row.product.full_description if row.product else row.description
|
description = row.product.full_description if row.product else row.description
|
||||||
return "({}) {}".format(row.upc.pretty(), description)
|
return "({}) {}".format(key, description)
|
||||||
|
|
||||||
def should_aggregate_products(self, batch):
|
def should_aggregate_products(self, batch):
|
||||||
"""
|
"""
|
||||||
|
@ -590,72 +608,79 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# TODO: this view can create new rows, with only a GET query. that should
|
def quick_locate_rows(self, batch, entry):
|
||||||
# probably be changed to require POST; for now we just require the "create
|
rows = []
|
||||||
# batch row" perm and call it good..
|
|
||||||
def mobile_lookup(self):
|
|
||||||
"""
|
|
||||||
Locate and/or create a row within the batch, according to the given
|
|
||||||
product UPC, then redirect to the row view page.
|
|
||||||
"""
|
|
||||||
batch = self.get_instance()
|
|
||||||
row = None
|
|
||||||
upc = self.request.GET.get('upc', '').strip()
|
|
||||||
upc = re.sub(r'\D', '', upc)
|
|
||||||
if not upc:
|
|
||||||
self.request.session.flash("Invalid UPC: {}".format(self.request.GET.get('upc')), 'error')
|
|
||||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
|
||||||
|
|
||||||
# first try to locate existing batch row by UPC match
|
|
||||||
provided = GPC(upc, calc_check_digit=False)
|
|
||||||
checked = GPC(upc, calc_check_digit='upc')
|
|
||||||
rows = self.Session.query(model.PurchaseBatchRow)\
|
|
||||||
.filter(model.PurchaseBatchRow.batch == batch)\
|
|
||||||
.filter(model.PurchaseBatchRow.upc.in_((provided, checked)))\
|
|
||||||
.filter(model.PurchaseBatchRow.removed == False)\
|
|
||||||
.all()
|
|
||||||
|
|
||||||
|
# we prefer "exact" matches, i.e. those which assumed the entry already
|
||||||
|
# contained the check digit.
|
||||||
|
provided = GPC(entry, calc_check_digit=False)
|
||||||
|
for row in batch.active_rows():
|
||||||
|
if row.upc == provided:
|
||||||
|
rows.append(row)
|
||||||
if rows:
|
if rows:
|
||||||
if self.should_aggregate_products(batch):
|
return rows
|
||||||
if len(rows) > 1:
|
|
||||||
log.warning("found multiple UPC matches for {} in batch {}: {}".format(
|
|
||||||
upc, batch.id_str, batch))
|
|
||||||
row = rows[0]
|
|
||||||
|
|
||||||
else:
|
# if no "exact" matches, we'll settle for those which assume the entry
|
||||||
|
# lacked a check digit.
|
||||||
|
checked = GPC(entry, calc_check_digit='upc')
|
||||||
|
for row in batch.active_rows():
|
||||||
|
if row.upc == checked:
|
||||||
|
rows.append(row)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def save_quick_row_form(self, form):
|
||||||
|
batch = self.get_instance()
|
||||||
|
entry = form.validated['quick_row_entry']
|
||||||
|
|
||||||
|
# maybe try to locate existing row first
|
||||||
|
rows = self.quick_locate_rows(batch, entry)
|
||||||
|
if rows:
|
||||||
|
|
||||||
|
# if aggregating, just re-use matching row
|
||||||
|
prefer_existing = self.should_aggregate_products(batch)
|
||||||
|
if prefer_existing:
|
||||||
|
if len(rows) > 1:
|
||||||
|
log.warning("found multiple row matches for '%s' in batch %s: %s",
|
||||||
|
entry, batch.id_str, batch)
|
||||||
|
return rows[0]
|
||||||
|
|
||||||
|
else: # borrow product from matching row, but make new row
|
||||||
other_row = rows[0]
|
other_row = rows[0]
|
||||||
row = model.PurchaseBatchRow()
|
row = model.PurchaseBatchRow()
|
||||||
row.product = other_row.product
|
row.product = other_row.product
|
||||||
self.handler.add_row(batch, row)
|
self.handler.add_row(batch, row)
|
||||||
# TODO: is this necessary here? is so, then what about further below?
|
self.Session.flush()
|
||||||
# self.handler.refresh_batch_status(batch)
|
return row
|
||||||
|
|
||||||
else:
|
# try to locate product by upc
|
||||||
|
provided = GPC(entry, calc_check_digit=False)
|
||||||
|
checked = GPC(entry, calc_check_digit='upc')
|
||||||
|
product = api.get_product_by_upc(self.Session(), provided)
|
||||||
|
if not product:
|
||||||
|
product = api.get_product_by_upc(self.Session(), checked)
|
||||||
|
if product:
|
||||||
|
row = model.PurchaseBatchRow()
|
||||||
|
row.product = product
|
||||||
|
self.handler.add_row(batch, row)
|
||||||
|
self.Session.flush()
|
||||||
|
return row
|
||||||
|
|
||||||
# try to locate general product by UPC; add to batch if found
|
# check for "bad" upc
|
||||||
product = api.get_product_by_upc(self.Session(), provided)
|
if len(entry) > 14:
|
||||||
if not product:
|
return
|
||||||
product = api.get_product_by_upc(self.Session(), checked)
|
|
||||||
if product:
|
|
||||||
row = model.PurchaseBatchRow()
|
|
||||||
row.product = product
|
|
||||||
self.handler.add_row(batch, row)
|
|
||||||
|
|
||||||
# check for "bad" upc
|
|
||||||
elif len(upc) > 14:
|
|
||||||
self.request.session.flash("Invalid UPC: {}".format(upc), 'error')
|
|
||||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
|
||||||
|
|
||||||
# product in system, but sane upc, so add to batch anyway
|
|
||||||
else:
|
|
||||||
row = model.PurchaseBatchRow()
|
|
||||||
row.upc = provided # TODO: why not checked? how to know?
|
|
||||||
row.description = "(unknown product)"
|
|
||||||
self.handler.add_row(batch, row)
|
|
||||||
self.handler.refresh_batch_status(batch)
|
|
||||||
|
|
||||||
|
# product not in system, but presumably sane upc, so add to batch anyway
|
||||||
|
row = model.PurchaseBatchRow()
|
||||||
|
row.upc = provided # TODO: why not checked? how to know?
|
||||||
|
row.description = "(unknown product)"
|
||||||
|
self.handler.add_row(batch, row)
|
||||||
self.Session.flush()
|
self.Session.flush()
|
||||||
return self.redirect(self.mobile_row_route_url('view', uuid=row.batch_uuid, row_uuid=row.uuid))
|
return row
|
||||||
|
|
||||||
|
def redirect_after_quick_row(self, row, mobile=False):
|
||||||
|
if mobile:
|
||||||
|
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
|
||||||
|
return super(ReceivingBatchView, self).redirect_after_quick_row(row, mobile=mobile)
|
||||||
|
|
||||||
def mobile_view_row(self):
|
def mobile_view_row(self):
|
||||||
"""
|
"""
|
||||||
|
@ -806,11 +831,6 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
|
||||||
# mobile lookup (note perm; this view can create new rows)
|
|
||||||
config.add_route('mobile.{}.lookup'.format(route_prefix), '/mobile{}/{{{}}}/lookup'.format(url_prefix, model_key))
|
|
||||||
config.add_view(cls, attr='mobile_lookup', route_name='mobile.{}.lookup'.format(route_prefix),
|
|
||||||
renderer='json', permission='{}.create_row'.format(permission_prefix))
|
|
||||||
|
|
||||||
if cls.allow_truck_dump:
|
if cls.allow_truck_dump:
|
||||||
config.add_route('{}.add_child_from_invoice'.format(route_prefix), '{}/{{{}}}/add-child-from-invoice'.format(url_prefix, model_key))
|
config.add_route('{}.add_child_from_invoice'.format(route_prefix), '{}/{{{}}}/add-child-from-invoice'.format(url_prefix, model_key))
|
||||||
config.add_view(cls, attr='add_child_from_invoice', route_name='{}.add_child_from_invoice'.format(route_prefix),
|
config.add_view(cls, attr='add_child_from_invoice', route_name='{}.add_child_from_invoice'.format(route_prefix),
|
||||||
|
|
Loading…
Reference in a new issue