Add way to prevent "case" entries for inventory adjustment batch
This commit is contained in:
parent
57c2a7981f
commit
54bfafdbfe
|
@ -9,14 +9,20 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function assert_quantity() {
|
function assert_quantity() {
|
||||||
|
% if allow_cases:
|
||||||
if ($('#cases').val() && parseFloat($('#cases').val())) {
|
if ($('#cases').val() && parseFloat($('#cases').val())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
% endif
|
||||||
if ($('#units').val() && parseFloat($('#units').val())) {
|
if ($('#units').val() && parseFloat($('#units').val())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
alert("Please provide case and/or unit quantity");
|
alert("Please provide case and/or unit quantity");
|
||||||
|
% if allow_cases:
|
||||||
$('#cases').select().focus();
|
$('#cases').select().focus();
|
||||||
|
% else:
|
||||||
|
$('#units').select().focus();
|
||||||
|
% endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +30,9 @@
|
||||||
$('#product-info p').text(msg);
|
$('#product-info p').text(msg);
|
||||||
$('#product-info img').hide();
|
$('#product-info img').hide();
|
||||||
$('#upc').focus().select();
|
$('#upc').focus().select();
|
||||||
|
% if allow_cases:
|
||||||
$('.field-wrapper.cases input').prop('disabled', true);
|
$('.field-wrapper.cases input').prop('disabled', true);
|
||||||
|
% endif
|
||||||
$('.field-wrapper.units input').prop('disabled', true);
|
$('.field-wrapper.units input').prop('disabled', true);
|
||||||
$('.buttons button').button('disable');
|
$('.buttons button').button('disable');
|
||||||
}
|
}
|
||||||
|
@ -65,7 +73,9 @@
|
||||||
$('#product-info .warning').hide();
|
$('#product-info .warning').hide();
|
||||||
$('.product-fields').hide();
|
$('.product-fields').hide();
|
||||||
// $('.receiving-fields').hide();
|
// $('.receiving-fields').hide();
|
||||||
|
% if allow_cases:
|
||||||
$('.field-wrapper.cases input').prop('disabled', true);
|
$('.field-wrapper.cases input').prop('disabled', true);
|
||||||
|
% endif
|
||||||
$('.field-wrapper.units input').prop('disabled', true);
|
$('.field-wrapper.units input').prop('disabled', true);
|
||||||
$('.buttons button').button('disable');
|
$('.buttons button').button('disable');
|
||||||
return true;
|
return true;
|
||||||
|
@ -102,13 +112,19 @@
|
||||||
$('.product-fields').show();
|
$('.product-fields').show();
|
||||||
}
|
}
|
||||||
$('#product-info .warning.notordered').show();
|
$('#product-info .warning.notordered').show();
|
||||||
|
% if allow_cases:
|
||||||
$('.field-wrapper.cases input').prop('disabled', false);
|
$('.field-wrapper.cases input').prop('disabled', false);
|
||||||
|
% endif
|
||||||
$('.field-wrapper.units input').prop('disabled', false);
|
$('.field-wrapper.units input').prop('disabled', false);
|
||||||
$('.buttons button').button('enable');
|
$('.buttons button').button('enable');
|
||||||
if (data.product.type2) {
|
if (data.product.type2) {
|
||||||
$('#units').focus().select();
|
$('#units').focus().select();
|
||||||
} else {
|
} else {
|
||||||
|
% if allow_cases:
|
||||||
$('#cases').focus().select();
|
$('#cases').focus().select();
|
||||||
|
% else:
|
||||||
|
$('#units').focus().select();
|
||||||
|
% endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is maybe useful if "new products" may be added via inventory batch
|
// TODO: this is maybe useful if "new products" may be added via inventory batch
|
||||||
|
@ -147,7 +163,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#upc').focus();
|
$('#upc').focus();
|
||||||
|
% if allow_cases:
|
||||||
$('.field-wrapper.cases input').prop('disabled', true);
|
$('.field-wrapper.cases input').prop('disabled', true);
|
||||||
|
% endif
|
||||||
$('.field-wrapper.units input').prop('disabled', true);
|
$('.field-wrapper.units input').prop('disabled', true);
|
||||||
$('.buttons button').button('disable');
|
$('.buttons button').button('disable');
|
||||||
|
|
||||||
|
@ -232,10 +250,12 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field-wrapper cases">
|
% if allow_cases:
|
||||||
<label for="cases">Cases</label>
|
<div class="field-wrapper cases">
|
||||||
<div class="field">${h.text('cases', autocomplete='off')}</div>
|
<label for="cases">Cases</label>
|
||||||
</div>
|
<div class="field">${h.text('cases', autocomplete='off')}</div>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
|
||||||
<div class="field-wrapper units">
|
<div class="field-wrapper units">
|
||||||
<label for="units">Units</label>
|
<label for="units">Units</label>
|
||||||
|
|
|
@ -3,22 +3,19 @@
|
||||||
<%namespace file="/mobile/keypad.mako" import="keypad" />
|
<%namespace file="/mobile/keypad.mako" import="keypad" />
|
||||||
|
|
||||||
## TODO: this is broken for actual page (header) title
|
## TODO: this is broken for actual page (header) title
|
||||||
<%def name="title()">${h.link_to("Inventory", url('mobile.batch.inventory'))} » ${h.link_to(instance.batch.id_str, url('mobile.batch.inventory.view', uuid=instance.batch_uuid))} » ${row.upc.pretty()}</%def>
|
<%def name="title()">${h.link_to("Inventory", url('mobile.batch.inventory'))} » ${h.link_to(batch.id_str, url('mobile.batch.inventory.view', uuid=batch.uuid))} » ${row.upc.pretty()}</%def>
|
||||||
|
|
||||||
<%
|
<%
|
||||||
unit_uom = 'LB' if row.product and row.product.weighed else 'EA'
|
unit_uom = 'LB' if row.product and row.product.weighed else 'EA'
|
||||||
|
|
||||||
if row.cases:
|
if row.cases and allow_cases:
|
||||||
uom = 'CS'
|
uom = 'CS'
|
||||||
elif row.units:
|
elif row.units:
|
||||||
if row.product and row.product.weighed:
|
uom = unit_uom
|
||||||
uom = 'LB'
|
elif row.case_quantity and allow_cases:
|
||||||
else:
|
|
||||||
uom = 'EA'
|
|
||||||
elif row.case_quantity:
|
|
||||||
uom = 'CS'
|
uom = 'CS'
|
||||||
else:
|
else:
|
||||||
uom = 'EA'
|
uom = unit_uom
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<div class="ui-grid-a">
|
<div class="ui-grid-a">
|
||||||
|
@ -46,20 +43,22 @@
|
||||||
${uom}
|
${uom}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
% if not row.batch.executed and not row.batch.complete:
|
% if not batch.executed and not batch.complete:
|
||||||
|
|
||||||
${h.form(request.current_route_url())}
|
${h.form(request.current_route_url())}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
${h.hidden('row', value=row.uuid)}
|
${h.hidden('row', value=row.uuid)}
|
||||||
|
% if allow_cases:
|
||||||
${h.hidden('cases')}
|
${h.hidden('cases')}
|
||||||
|
% endif
|
||||||
${h.hidden('units')}
|
${h.hidden('units')}
|
||||||
|
|
||||||
${keypad(unit_uom, uom, quantity=row.cases or row.units or 1)}
|
${keypad(unit_uom, uom, quantity=(row.cases or row.units or 1) if allow_cases else (row.units or 1), allow_cases=allow_cases)}
|
||||||
|
|
||||||
<fieldset data-role="controlgroup" data-type="horizontal" class="inventory-actions">
|
<fieldset data-role="controlgroup" data-type="horizontal" class="inventory-actions">
|
||||||
<button type="button" class="ui-btn-inline ui-corner-all save">Save</button>
|
<button type="button" class="ui-btn-inline ui-corner-all save">Save</button>
|
||||||
<button type="button" class="ui-btn-inline ui-corner-all delete" disabled="disabled">Delete</button>
|
<button type="button" class="ui-btn-inline ui-corner-all delete" disabled="disabled">Delete</button>
|
||||||
${h.link_to("Cancel", url('mobile.batch.inventory.view', uuid=row.batch.uuid), class_='ui-btn ui-btn-inline ui-corner-all')}
|
${h.link_to("Cancel", url('mobile.batch.inventory.view', uuid=batch.uuid), class_='ui-btn ui-btn-inline ui-corner-all')}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
${h.end_form()}
|
${h.end_form()}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
<%def name="keypad(unit_uom, selected_uom, quantity=1)">
|
<%def name="keypad(unit_uom, selected_uom, quantity=1, allow_cases=True)">
|
||||||
<div class="quantity-keypad-thingy" data-changed="false">
|
<div class="quantity-keypad-thingy" data-changed="false">
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
@ -31,7 +31,9 @@
|
||||||
<fieldset data-role="controlgroup" data-type="horizontal">
|
<fieldset data-role="controlgroup" data-type="horizontal">
|
||||||
<button type="button" class="ui-btn-active keypad-quantity">${h.pretty_quantity(quantity or 1)}</button>
|
<button type="button" class="ui-btn-active keypad-quantity">${h.pretty_quantity(quantity or 1)}</button>
|
||||||
<button type="button" disabled="disabled"> </button>
|
<button type="button" disabled="disabled"> </button>
|
||||||
|
% if allow_cases:
|
||||||
${h.radio('keypad-uom', value='CS', checked=selected_uom == 'CS', label="CS")}
|
${h.radio('keypad-uom', value='CS', checked=selected_uom == 'CS', label="CS")}
|
||||||
|
% endif
|
||||||
${h.radio('keypad-uom', value=unit_uom, checked=selected_uom == unit_uom, label=unit_uom)}
|
${h.radio('keypad-uom', value=unit_uom, checked=selected_uom == unit_uom, label=unit_uom)}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,10 @@ class InventoryBatchView(BatchMasterView):
|
||||||
# set to False to disable "zero all" batch count mode
|
# set to False to disable "zero all" batch count mode
|
||||||
allow_zero_all = True
|
allow_zero_all = True
|
||||||
|
|
||||||
|
# set to False to prevent exposing case fields for user input,
|
||||||
|
# when the batch count mode is "adjust only"
|
||||||
|
allow_adjustment_cases = True
|
||||||
|
|
||||||
labels = {
|
labels = {
|
||||||
'mode': "Count Mode",
|
'mode': "Count Mode",
|
||||||
}
|
}
|
||||||
|
@ -310,8 +314,16 @@ class InventoryBatchView(BatchMasterView):
|
||||||
'index_url': self.get_action_url('view', batch),
|
'index_url': self.get_action_url('view', batch),
|
||||||
'form': form,
|
'form': form,
|
||||||
'dform': form.make_deform_form(),
|
'dform': form.make_deform_form(),
|
||||||
|
'allow_cases': self.allow_cases(batch),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def allow_cases(self, batch):
|
||||||
|
if batch.mode == self.enum.INVENTORY_MODE_ADJUST:
|
||||||
|
if self.allow_adjustment_cases:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def desktop_lookup(self):
|
def desktop_lookup(self):
|
||||||
"""
|
"""
|
||||||
Try to locate a product by UPC, and validate it in the context of
|
Try to locate a product by UPC, and validate it in the context of
|
||||||
|
@ -463,23 +475,26 @@ class InventoryBatchView(BatchMasterView):
|
||||||
"""
|
"""
|
||||||
self.viewing = True
|
self.viewing = True
|
||||||
row = self.get_row_instance()
|
row = self.get_row_instance()
|
||||||
parent = self.get_parent(row)
|
batch = self.get_parent(row)
|
||||||
form = self.make_mobile_row_form(row)
|
form = self.make_mobile_row_form(row)
|
||||||
context = {
|
context = {
|
||||||
'row': row,
|
'row': row,
|
||||||
|
'batch': batch,
|
||||||
'instance': row,
|
'instance': row,
|
||||||
'instance_title': self.get_row_instance_title(row),
|
'instance_title': self.get_row_instance_title(row),
|
||||||
'parent_model_title': self.get_model_title(),
|
'parent_model_title': self.get_model_title(),
|
||||||
'parent_title': self.get_instance_title(parent),
|
'parent_title': self.get_instance_title(batch),
|
||||||
'parent_url': self.get_action_url('view', parent, mobile=True),
|
'parent_url': self.get_action_url('view', batch, mobile=True),
|
||||||
'product_image_url': pod.get_image_url(self.rattail_config, row.upc),
|
'product_image_url': pod.get_image_url(self.rattail_config, row.upc),
|
||||||
'form': form,
|
'form': form,
|
||||||
|
'allow_cases': self.allow_cases(batch),
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.request.has_perm('{}.edit_row'.format(self.get_permission_prefix())):
|
if self.request.has_perm('{}.edit_row'.format(self.get_permission_prefix())):
|
||||||
update_form = forms.Form(schema=InventoryForm(), request=self.request)
|
schema = InventoryForm().bind(session=self.Session())
|
||||||
|
update_form = forms.Form(schema=schema, request=self.request)
|
||||||
if update_form.validate(newstyle=True):
|
if update_form.validate(newstyle=True):
|
||||||
row = update_form.validated['row']
|
row = self.Session.query(model.InventoryBatchRow).get(update_form.validated['row'])
|
||||||
cases = update_form.validated['cases']
|
cases = update_form.validated['cases']
|
||||||
units = update_form.validated['units']
|
units = update_form.validated['units']
|
||||||
if cases:
|
if cases:
|
||||||
|
@ -489,7 +504,8 @@ class InventoryBatchView(BatchMasterView):
|
||||||
row.cases = None
|
row.cases = None
|
||||||
row.units = units
|
row.units = units
|
||||||
self.handler.refresh_row(row)
|
self.handler.refresh_row(row)
|
||||||
return self.redirect(self.request.route_url('mobile.{}.view'.format(self.get_route_prefix()), uuid=row.batch_uuid))
|
route_prefix = self.get_route_prefix()
|
||||||
|
return self.redirect(self.request.route_url('mobile.{}.view'.format(route_prefix), uuid=batch.uuid))
|
||||||
|
|
||||||
return self.render_to_response('view_row', context, mobile=True)
|
return self.render_to_response('view_row', context, mobile=True)
|
||||||
|
|
||||||
|
@ -533,6 +549,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
|
|
||||||
def configure_row_form(self, f):
|
def configure_row_form(self, f):
|
||||||
super(InventoryBatchView, self).configure_row_form(f)
|
super(InventoryBatchView, self).configure_row_form(f)
|
||||||
|
row = f.model_instance
|
||||||
|
|
||||||
# readonly fields
|
# readonly fields
|
||||||
f.set_readonly('upc')
|
f.set_readonly('upc')
|
||||||
|
@ -557,6 +574,11 @@ class InventoryBatchView(BatchMasterView):
|
||||||
# upc
|
# upc
|
||||||
f.set_renderer('upc', self.render_upc)
|
f.set_renderer('upc', self.render_upc)
|
||||||
|
|
||||||
|
# cases
|
||||||
|
if self.editing:
|
||||||
|
if not self.allow_cases(row.batch):
|
||||||
|
f.set_readonly('cases')
|
||||||
|
|
||||||
def render_upc(self, row, field):
|
def render_upc(self, row, field):
|
||||||
upc = row.upc
|
upc = row.upc
|
||||||
if not upc:
|
if not upc:
|
||||||
|
@ -599,19 +621,26 @@ class InventoryBatchView(BatchMasterView):
|
||||||
permission='{}.create_row'.format(permission_prefix))
|
permission='{}.create_row'.format(permission_prefix))
|
||||||
|
|
||||||
|
|
||||||
class InventoryBatchRowType(forms.types.ObjectType):
|
# TODO: this is a stopgap measure to fix an obvious bug, which exists when the
|
||||||
model_class = model.InventoryBatchRow
|
# session is not provided by the view at runtime (i.e. when it was instead
|
||||||
|
# being provided by the type instance, which was created upon app startup).
|
||||||
def deserialize(self, node, cstruct):
|
@colander.deferred
|
||||||
row = super(InventoryBatchRowType, self).deserialize(node, cstruct)
|
def valid_inventory_batch_row(node, kw):
|
||||||
if row and row.batch.executed:
|
session = kw['session']
|
||||||
|
def validate(node, value):
|
||||||
|
row = session.query(model.InventoryBatchRow).get(value)
|
||||||
|
if not row:
|
||||||
|
raise colander.Invalid(node, "Batch row not found")
|
||||||
|
if row.batch.executed:
|
||||||
raise colander.Invalid(node, "Batch has already been executed")
|
raise colander.Invalid(node, "Batch has already been executed")
|
||||||
return row
|
return row.uuid
|
||||||
|
return validate
|
||||||
|
|
||||||
|
|
||||||
class InventoryForm(colander.MappingSchema):
|
class InventoryForm(colander.MappingSchema):
|
||||||
|
|
||||||
row = colander.SchemaNode(InventoryBatchRowType())
|
row = colander.SchemaNode(colander.String(),
|
||||||
|
validator=valid_inventory_batch_row)
|
||||||
|
|
||||||
cases = colander.SchemaNode(colander.Decimal(), missing=colander.null)
|
cases = colander.SchemaNode(colander.Decimal(), missing=colander.null)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue