diff --git a/tailbone/static/css/mobile.css b/tailbone/static/css/mobile.css
index 9ed2ed93..a3493f12 100644
--- a/tailbone/static/css/mobile.css
+++ b/tailbone/static/css/mobile.css
@@ -26,6 +26,11 @@
margin-bottom: 1em;
}
+/* receiving warning flash messages */
+.receiving-warning {
+ color: red;
+}
+
.replacement-header {
display: none;
}
diff --git a/tailbone/templates/mobile/receiving/view_row.mako b/tailbone/templates/mobile/receiving/view_row.mako
index fc9dbf5a..6ff114d5 100644
--- a/tailbone/templates/mobile/receiving/view_row.mako
+++ b/tailbone/templates/mobile/receiving/view_row.mako
@@ -4,13 +4,17 @@
## TODO: this is broken for actual page (header) title
<%def name="title()">${h.link_to("Receiving", url('mobile.receiving'))} » ${h.link_to(instance.batch.id_str, url('mobile.receiving.view', uuid=instance.batch_uuid))} » ${row.upc.pretty()}%def>
-<% unit_uom = 'LB' if row.product.weighed else 'EA' %>
+<% unit_uom = 'LB' if row.product and row.product.weighed else 'EA' %>
-
${instance.brand_name}
- ${instance.description} ${instance.size}
- ${h.pretty_quantity(row.case_quantity)} ${unit_uom} per CS
+ % if instance.product:
+ ${instance.brand_name or ""}
+ ${instance.description} ${instance.size}
+ ${h.pretty_quantity(row.case_quantity)} ${unit_uom} per CS
+ % else:
+ ${instance.description}
+ % endif
${h.image(product_image_url, "product image")}
@@ -38,6 +42,12 @@
+% if request.session.peek_flash('receiving-warning'):
+ % for error in request.session.pop_flash('receiving-warning'):
+
${error}
+ % endfor
+% endif
+
diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py
index 56ef0a6c..ed78a5bd 100644
--- a/tailbone/views/purchasing/receiving.py
+++ b/tailbone/views/purchasing/receiving.py
@@ -31,7 +31,7 @@ import re
import sqlalchemy as sa
from rattail import pod
-from rattail.db import model
+from rattail.db import model, api
from rattail.gpc import GPC
from rattail.util import pretty_quantity, prettify
@@ -71,13 +71,23 @@ class MobileBatchStatusFilter(grids.filters.MobileFilter):
class MobileItemStatusFilter(grids.filters.MobileFilter):
- value_choices = ['incomplete', 'damaged', 'expired', 'all']
+ value_choices = ['incomplete', 'unexpected', 'damaged', 'expired', 'all']
def filter_equal(self, query, value):
# TODO: is this accurate (enough) ?
if value == 'incomplete':
- return query.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 != model.PurchaseBatchRow.STATUS_OK)
+
+ if value == 'unexpected':
+ return query.filter(sa.and_(
+ sa.or_(
+ model.PurchaseBatchRow.cases_ordered == None,
+ model.PurchaseBatchRow.cases_ordered == 0),
+ sa.or_(
+ model.PurchaseBatchRow.units_ordered == None,
+ model.PurchaseBatchRow.units_ordered == 0)))
if value == 'damaged':
return query.filter(sa.or_(
@@ -207,16 +217,18 @@ class ReceivingBatchView(PurchasingBatchView):
def render_mobile_row_listitem(self, row, **kwargs):
if row is None:
return ''
- title = "({}) {}".format(row.upc.pretty(), row.product.full_description)
+ description = row.product.full_description if row.product else row.description
+ title = "({}) {}".format(row.upc.pretty(), description)
url = self.request.route_url('mobile.receiving.rows.view', uuid=row.uuid)
return tags.link_to(title, url)
def mobile_lookup(self):
"""
- Try to locate a product by UPC, and validate it in the context of
- current batch, returning some data for client JS.
+ 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 upc:
@@ -234,10 +246,27 @@ class ReceivingBatchView(PurchasingBatchView):
log.warning("found multiple UPC matches for {} in batch {}: {}".format(
upc, batch.id_str, batch))
row = rows[0]
- return self.redirect(self.request.route_url('mobile.{}.view'.format(self.get_row_route_prefix()), uuid=row.uuid))
- # TODO: how to handle product not found in system / purchase ?
- raise NotImplementedError
+ # try to locate general product by UPC; add to batch if found
+ 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
+ batch.add_row(row)
+ self.handler.refresh_row(row)
+
+ # if product not even in system, add to batch anyway..
+ if not row:
+ row = model.PurchaseBatchRow()
+ row.upc = provided # TODO: why not checked? how to know?
+ row.description = "(unknown product)"
+ batch.add_row(row)
+ self.handler.refresh_row(row)
+
+ self.Session.flush()
+ return self.redirect(self.request.route_url('mobile.{}.view'.format(self.get_row_route_prefix()), uuid=row.uuid))
def mobile_view_row(self):
"""
@@ -285,6 +314,8 @@ class ReceivingBatchView(PurchasingBatchView):
return self.redirect(self.request.route_url('mobile.{}.view'.format(self.get_route_prefix()), uuid=row.batch_uuid))
+ if not row.cases_ordered and not row.units_ordered:
+ self.request.session.flash("This item was NOT on the original purchase order.", 'receiving-warning')
return self.render_to_response('view_row', context, mobile=True)
def attach_credit(self, row, credit_type, cases, units, expiration_date=None, discarded=None, mispick_product=None):