Add (restore?) basic support for mobile receiving from PO
This commit is contained in:
parent
87ba8026e5
commit
34bdd2ac84
|
@ -57,7 +57,7 @@ function setfocus() {
|
|||
var queries = [
|
||||
'#username',
|
||||
'#new-purchasing-batch-vendor-text',
|
||||
// '.receiving-upc-search',
|
||||
'#new-receiving-batch-vendor-text',
|
||||
];
|
||||
$.each(queries, function(i, query) {
|
||||
el = $(query);
|
||||
|
@ -92,16 +92,6 @@ $(document).on('click', 'form[name="new-purchasing-batch"] input[type="submit"]'
|
|||
}
|
||||
});
|
||||
|
||||
// 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() {
|
||||
|
|
|
@ -8,29 +8,31 @@
|
|||
************************************************************/
|
||||
|
||||
|
||||
// TODO: this is really just for receiving; should change form name?
|
||||
$(document).on('autocompleteitemselected', 'form[name="new-purchasing-batch"] .vendor', function(event, uuid) {
|
||||
// toggle visibility of "Receive" type buttons based on whether vendor is set
|
||||
$(document).on('autocompleteitemselected', 'form[name="new-receiving-batch"] .vendor', function(event, uuid) {
|
||||
$('#new-receiving-types').show();
|
||||
});
|
||||
|
||||
|
||||
// TODO: this is really just for receiving; should change form name?
|
||||
$(document).on('autocompleteitemcleared', 'form[name="new-purchasing-batch"] .vendor', function(event) {
|
||||
$(document).on('autocompleteitemcleared', 'form[name="new-receiving-batch"] .vendor', function(event) {
|
||||
$('#new-receiving-types').hide();
|
||||
});
|
||||
|
||||
|
||||
$(document).on('click', 'form[name="new-purchasing-batch"] #receive-truck-dump', function() {
|
||||
// submit new receiving batch form when user clicks "Receive" type button
|
||||
$(document).on('click', 'form[name="new-receiving-batch"] .start-receiving', function() {
|
||||
var form = $(this).parents('form');
|
||||
form.find('input[name="workflow"]').val('truck_dump');
|
||||
form.find('input[name="workflow"]').val($(this).data('workflow'));
|
||||
form.submit();
|
||||
});
|
||||
|
||||
|
||||
$(document).on('click', 'form[name="new-purchasing-batch"] #receive-from-scratch', function() {
|
||||
// submit new receiving batch form when user clicks Purchase Order option
|
||||
$(document).on('click', 'form[name="new-receiving-batch"] [data-role="listview"] a', function() {
|
||||
var form = $(this).parents('form');
|
||||
form.find('input[name="workflow"]').val('from_scratch');
|
||||
var key = $(this).parents('li').data('key');
|
||||
form.find('[name="workflow"]').val('from_po');
|
||||
form.find('.purchase-order-field').val(key);
|
||||
form.submit();
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -5,16 +5,16 @@
|
|||
|
||||
<%def name="page_title()">${h.link_to("Receiving", url('mobile.receiving'))} » New Batch</%def>
|
||||
|
||||
${h.form(request.current_route_url(), class_='ui-filterable', name='new-purchasing-batch')}
|
||||
${h.form(form.action_url, class_='ui-filterable', name='new-receiving-batch')}
|
||||
${h.csrf_token(request)}
|
||||
|
||||
% if vendor is Undefined:
|
||||
% if phase == 1:
|
||||
|
||||
<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>
|
||||
${h.text('new-receiving-batch-vendor-text', placeholder="Vendor name", autocomplete='off', **{'data-type': 'search'})}
|
||||
<ul data-role="listview" data-inset="true" data-filter="true" data-input="#new-receiving-batch-vendor-text"></ul>
|
||||
<button type="button" style="display: none;">Change Vendor</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -24,25 +24,29 @@ ${h.csrf_token(request)}
|
|||
<div id="new-receiving-types" style="display: none;">
|
||||
|
||||
${h.hidden('workflow')}
|
||||
${h.hidden('phase', value='1')}
|
||||
|
||||
% if master.allow_from_po:
|
||||
## ${h.submit('submit', "Find purchase orders")}
|
||||
<button type="button">Receive from PO</button>
|
||||
<button type="button" class="start-receiving" data-workflow="from_po">Receive from PO</button>
|
||||
% endif
|
||||
|
||||
% if master.allow_from_scratch:
|
||||
<button type="button" id="receive-from-scratch">Receive from Scratch</button>
|
||||
<button type="button" class="start-receiving" data-workflow="from_scratch">Receive from Scratch</button>
|
||||
% endif
|
||||
|
||||
% if master.allow_truck_dump:
|
||||
<button type="button" id="receive-truck-dump">Receive Truck Dump</button>
|
||||
<button type="button" class="start-receiving" data-workflow="truck_dump">Receive Truck Dump</button>
|
||||
% endif
|
||||
|
||||
</div>
|
||||
|
||||
% else: ## vendor is known
|
||||
% else: ## phase 2
|
||||
|
||||
${h.hidden('workflow')}
|
||||
${h.hidden('phase', value='2')}
|
||||
|
||||
<div class="field-wrapper vendor">
|
||||
<label>Vendor</label>
|
||||
<div class="field">
|
||||
${h.hidden('vendor', value=vendor.uuid)}
|
||||
${vendor}
|
||||
|
@ -50,17 +54,22 @@ ${h.csrf_token(request)}
|
|||
</div>
|
||||
|
||||
% if purchases:
|
||||
${h.hidden('purchase')}
|
||||
${h.hidden(purchase_order_fieldname, class_='purchase-order-field')}
|
||||
<p>Please choose a Purchase Order to receive:</p>
|
||||
<ul data-role="listview" data-inset="true">
|
||||
% for uuid, purchase in purchases:
|
||||
<li data-uuid="${uuid}">${h.link_to(purchase, '#')}</li>
|
||||
% for key, purchase in purchases:
|
||||
<li data-key="${key}">${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')}
|
||||
% if master.allow_from_scratch:
|
||||
<button type="button" class="start-receiving" data-workflow="from_scratch">Receive from Scratch</button>
|
||||
% endif
|
||||
|
||||
${h.link_to("Cancel", url('mobile.{}'.format(route_prefix)), class_='ui-btn ui-corner-all')}
|
||||
|
||||
% endif
|
||||
|
||||
|
|
|
@ -560,9 +560,11 @@ class PurchasingBatchView(BatchMasterView):
|
|||
|
||||
if self.batch_mode in (self.enum.PURCHASE_BATCH_MODE_RECEIVING,
|
||||
self.enum.PURCHASE_BATCH_MODE_COSTING):
|
||||
if batch.purchase_uuid:
|
||||
purchase = batch.purchase
|
||||
if not purchase and batch.purchase_uuid:
|
||||
purchase = self.Session.query(model.Purchase).get(batch.purchase_uuid)
|
||||
assert purchase
|
||||
if purchase:
|
||||
kwargs['purchase'] = purchase
|
||||
kwargs['buyer'] = purchase.buyer
|
||||
kwargs['buyer_uuid'] = purchase.buyer_uuid
|
||||
|
|
|
@ -66,8 +66,19 @@ class MobileItemStatusFilter(grids.filters.MobileFilter):
|
|||
|
||||
# TODO: is this accurate (enough) ?
|
||||
if value == 'incomplete':
|
||||
return query.filter(sa.or_(model.PurchaseBatchRow.cases_ordered != 0, model.PurchaseBatchRow.units_ordered != 0))\
|
||||
.filter(model.PurchaseBatchRow.status_code != model.PurchaseBatchRow.STATUS_OK)
|
||||
return query.filter(sa.or_(model.PurchaseBatchRow.cases_ordered != 0,
|
||||
model.PurchaseBatchRow.units_ordered != 0))\
|
||||
.filter(~model.PurchaseBatchRow.status_code.in_((
|
||||
model.PurchaseBatchRow.STATUS_OK,
|
||||
model.PurchaseBatchRow.STATUS_PRODUCT_NOT_FOUND)))
|
||||
|
||||
if value == 'invalid':
|
||||
return query.filter(model.PurchaseBatchRow.status_code.in_((
|
||||
model.PurchaseBatchRow.STATUS_PRODUCT_NOT_FOUND,
|
||||
model.PurchaseBatchRow.STATUS_COST_NOT_FOUND,
|
||||
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_UNKNOWN,
|
||||
model.PurchaseBatchRow.STATUS_CASE_QUANTITY_DIFFERS,
|
||||
)))
|
||||
|
||||
if value == 'unexpected':
|
||||
return query.filter(sa.and_(
|
||||
|
@ -118,6 +129,8 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
|
||||
default_uom_is_case = True
|
||||
|
||||
purchase_order_fieldname = 'purchase'
|
||||
|
||||
labels = {
|
||||
'truck_dump_batch': "Truck Dump Parent",
|
||||
'invoice_parser_key': "Invoice Parser",
|
||||
|
@ -363,18 +376,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
|
||||
def get_batch_kwargs(self, batch, mobile=False):
|
||||
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
||||
|
||||
if mobile:
|
||||
if 'purchase' in self.request.POST:
|
||||
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
|
||||
if not mobile:
|
||||
batch_type = self.request.POST['batch_type']
|
||||
if batch_type == 'from_scratch':
|
||||
kwargs.pop('truck_dump_batch', None)
|
||||
|
@ -516,10 +518,10 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
|
||||
# visible filter options will depend on whether batch came from purchase
|
||||
if batch.order_quantities_known:
|
||||
value_choices = ['incomplete', 'unexpected', 'damaged', 'expired', 'all']
|
||||
value_choices = ['incomplete', 'unexpected', 'damaged', 'expired', 'invalid', 'all']
|
||||
default_status = 'incomplete'
|
||||
else:
|
||||
value_choices = ['received', 'damaged', 'expired', 'all']
|
||||
value_choices = ['received', 'damaged', 'expired', 'invalid', 'all']
|
||||
default_status = 'all'
|
||||
|
||||
# remove 'expired' filter option if not relevant
|
||||
|
@ -540,10 +542,12 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
"""
|
||||
mode = self.batch_mode
|
||||
data = {'mode': mode}
|
||||
phase = 1
|
||||
|
||||
schema = MobileNewReceivingBatch().bind(session=self.Session())
|
||||
form = forms.Form(schema=schema, request=self.request)
|
||||
if form.validate(newstyle=True):
|
||||
phase = form.validated['phase']
|
||||
|
||||
if form.validated['workflow'] == 'from_scratch':
|
||||
if not self.allow_from_scratch:
|
||||
|
@ -556,7 +560,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
batch.date_received = localtime(self.rattail_config).date()
|
||||
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
||||
batch = self.handler.make_batch(self.Session(), **kwargs)
|
||||
return self.redirect(self.request.route_url('mobile.receiving.view', uuid=batch.uuid))
|
||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||
|
||||
elif form.validated['workflow'] == 'truck_dump':
|
||||
if not self.allow_truck_dump:
|
||||
|
@ -565,44 +569,85 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
batch.store = self.rattail_config.get_store(self.Session())
|
||||
batch.mode = mode
|
||||
batch.truck_dump = True
|
||||
batch.vendor = self.Session.merge(form.validated['vendor'])
|
||||
batch.vendor = self.Session.query(model.Vendor).get(form.validated['vendor'])
|
||||
batch.created_by = self.request.user
|
||||
batch.date_received = localtime(self.rattail_config).date()
|
||||
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
||||
batch = self.handler.make_batch(self.Session(), **kwargs)
|
||||
return self.redirect(self.request.route_url('mobile.receiving.view', uuid=batch.uuid))
|
||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||
|
||||
else:
|
||||
raise NotImplementedError("Requested workflow not supported: {}".format(form.validated['workflow']))
|
||||
elif form.validated['workflow'] == 'from_po':
|
||||
if not self.allow_from_po:
|
||||
raise NotImplementedError("Requested workflow not supported: from_po")
|
||||
|
||||
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:
|
||||
vendor = self.Session.query(model.Vendor).get(form.validated['vendor'])
|
||||
data['vendor'] = vendor
|
||||
|
||||
if self.request.POST.get('purchase'):
|
||||
purchase = self.get_purchase(self.request.POST['purchase'])
|
||||
if purchase:
|
||||
|
||||
schema = self.make_mobile_receiving_from_po_schema()
|
||||
po_form = forms.Form(schema=schema, request=self.request)
|
||||
if phase == 2:
|
||||
if po_form.validate(newstyle=True):
|
||||
batch = self.model_class()
|
||||
batch.store = self.rattail_config.get_store(self.Session())
|
||||
batch.mode = mode
|
||||
batch.vendor = vendor
|
||||
batch.store = self.rattail_config.get_store(self.Session())
|
||||
batch.buyer = self.request.user.employee
|
||||
batch.created_by = self.request.user
|
||||
batch.date_received = localtime(self.rattail_config).date()
|
||||
self.assign_purchase_order(batch, po_form)
|
||||
kwargs = self.get_batch_kwargs(batch, mobile=True)
|
||||
batch = self.handler.make_batch(self.Session(), **kwargs)
|
||||
if self.handler.should_populate(batch):
|
||||
self.handler.populate(batch)
|
||||
return self.redirect(self.request.route_url('mobile.receiving.view', uuid=batch.uuid))
|
||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||
|
||||
else:
|
||||
phase = 2
|
||||
|
||||
else:
|
||||
raise NotImplementedError("Requested workflow not supported: {}".format(form.validated['workflow']))
|
||||
|
||||
data['form'] = form
|
||||
data['dform'] = form.make_deform_form()
|
||||
data['mode_title'] = self.enum.PURCHASE_BATCH_MODE[mode].capitalize()
|
||||
if vendor:
|
||||
data['phase'] = phase
|
||||
if phase == 2:
|
||||
purchases = self.eligible_purchases(vendor.uuid, mode=mode)
|
||||
data['purchases'] = [(p['key'], p['display']) for p in purchases['purchases']]
|
||||
data['purchase_order_fieldname'] = self.purchase_order_fieldname
|
||||
return self.render_to_response('create', data, mobile=True)
|
||||
|
||||
def make_mobile_receiving_from_po_schema(self):
|
||||
schema = colander.MappingSchema()
|
||||
schema.add(colander.SchemaNode(colander.String(),
|
||||
name=self.purchase_order_fieldname,
|
||||
validator=self.validate_purchase))
|
||||
return schema.bind(session=self.Session())
|
||||
|
||||
@staticmethod
|
||||
@colander.deferred
|
||||
def validate_purchase(node, kw):
|
||||
session = kw['session']
|
||||
def validate(node, value):
|
||||
purchase = session.query(model.Purchase).get(value)
|
||||
if not purchase:
|
||||
raise colander.Invalid(node, "Purchase not found")
|
||||
return purchase.uuid
|
||||
return validate
|
||||
|
||||
def assign_purchase_order(self, batch, po_form):
|
||||
"""
|
||||
Assign the original purchase order to the given batch. Default
|
||||
behavior assumes a Rattail Purchase object is what we're after.
|
||||
"""
|
||||
purchase = self.get_purchase(po_form.validated[self.purchase_order_fieldname])
|
||||
if isinstance(purchase, model.Purchase):
|
||||
batch.purchase = purchase
|
||||
|
||||
department = self.department_for_purchase(purchase)
|
||||
if department:
|
||||
batch.department = department
|
||||
|
||||
def configure_mobile_form(self, f):
|
||||
super(ReceivingBatchView, self).configure_mobile_form(f)
|
||||
batch = f.model_instance
|
||||
|
@ -950,6 +995,13 @@ class MobileNewReceivingBatch(colander.MappingSchema):
|
|||
'truck_dump',
|
||||
]))
|
||||
|
||||
phase = colander.SchemaNode(colander.Int())
|
||||
|
||||
|
||||
class MobileNewReceivingFromPO(colander.MappingSchema):
|
||||
|
||||
purchase = colander.SchemaNode(colander.String())
|
||||
|
||||
|
||||
# TODO: this is a stopgap measure to fix an obvious bug, which exists when the
|
||||
# session is not provided by the view at runtime (i.e. when it was instead
|
||||
|
|
Loading…
Reference in a new issue