Add initial support for receiving truck dump batch via mobile
i.e. just the initial truck dump, but secondary invoice batches are not yet supported. also this maybe breaks other things..we'll see
This commit is contained in:
parent
b515331e48
commit
9ed501a8cc
9 changed files with 214 additions and 62 deletions
|
@ -512,6 +512,7 @@ class PurchasingBatchView(BatchMasterView):
|
|||
def get_batch_kwargs(self, batch, mobile=False):
|
||||
kwargs = super(PurchasingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
||||
kwargs['mode'] = self.batch_mode
|
||||
kwargs['truck_dump'] = batch.truck_dump
|
||||
if batch.store:
|
||||
kwargs['store'] = batch.store
|
||||
elif batch.store_uuid:
|
||||
|
|
|
@ -36,6 +36,8 @@ from rattail.gpc import GPC
|
|||
from rattail.util import pretty_quantity, prettify
|
||||
|
||||
import colander
|
||||
from deform import widget as dfwidget
|
||||
from pyramid import httpexceptions
|
||||
from webhelpers2.html import tags
|
||||
|
||||
from tailbone import forms, grids
|
||||
|
@ -48,6 +50,12 @@ class MobileItemStatusFilter(grids.filters.MobileFilter):
|
|||
|
||||
def filter_equal(self, query, value):
|
||||
|
||||
# NOTE: this is only relevant for truck dump
|
||||
if value == 'received':
|
||||
return query.filter(sa.or_(
|
||||
model.PurchaseBatchRow.cases_received != 0,
|
||||
model.PurchaseBatchRow.units_received != 0))
|
||||
|
||||
# TODO: is this accurate (enough) ?
|
||||
if value == 'incomplete':
|
||||
return query.filter(sa.or_(model.PurchaseBatchRow.cases_ordered != 0, model.PurchaseBatchRow.units_ordered != 0))\
|
||||
|
@ -95,10 +103,29 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
mobile_rows_filterable = True
|
||||
mobile_rows_creatable = True
|
||||
|
||||
allow_from_po = False
|
||||
allow_from_scratch = True
|
||||
allow_truck_dump = False
|
||||
|
||||
grid_columns = [
|
||||
'id',
|
||||
'vendor',
|
||||
'truck_dump',
|
||||
'department',
|
||||
'buyer',
|
||||
'date_ordered',
|
||||
'created',
|
||||
'created_by',
|
||||
'rowcount',
|
||||
'status_code',
|
||||
'executed',
|
||||
]
|
||||
|
||||
form_fields = [
|
||||
'id',
|
||||
'store',
|
||||
'vendor',
|
||||
'truck_dump',
|
||||
'department',
|
||||
'purchase',
|
||||
'vendor_email',
|
||||
|
@ -123,6 +150,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
|
||||
mobile_form_fields = [
|
||||
'vendor',
|
||||
'truck_dump',
|
||||
'department',
|
||||
]
|
||||
|
||||
|
@ -175,6 +203,13 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
def batch_mode(self):
|
||||
return self.enum.PURCHASE_BATCH_MODE_RECEIVING
|
||||
|
||||
def configure_form(self, f):
|
||||
super(ReceivingBatchView, self).configure_form(f)
|
||||
|
||||
# truck_dump
|
||||
if self.editing:
|
||||
f.set_readonly('truck_dump')
|
||||
|
||||
def render_mobile_listitem(self, batch, i):
|
||||
title = "({}) {} for ${:0,.2f} - {}, {}".format(
|
||||
batch.id_str,
|
||||
|
@ -188,8 +223,17 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
"""
|
||||
Returns a set of filters for the mobile row grid.
|
||||
"""
|
||||
batch = self.get_instance()
|
||||
filters = grids.filters.GridFilterSet()
|
||||
filters['status'] = MobileItemStatusFilter('status', default_value='incomplete')
|
||||
if batch.truck_dump:
|
||||
value_choices = ['received', 'damaged', 'expired', 'all']
|
||||
default_status = 'all'
|
||||
else:
|
||||
value_choices = ['incomplete', 'unexpected', 'damaged', 'expired', 'all']
|
||||
default_status = 'incomplete'
|
||||
filters['status'] = MobileItemStatusFilter('status',
|
||||
value_choices=value_choices,
|
||||
default_value=default_status)
|
||||
return filters
|
||||
|
||||
def mobile_create(self):
|
||||
|
@ -199,6 +243,25 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
mode = self.batch_mode
|
||||
data = {'mode': mode}
|
||||
|
||||
form = forms.Form(schema=MobileNewReceivingBatch(), request=self.request)
|
||||
if form.validate(newstyle=True):
|
||||
|
||||
if form.validated['workflow'] == 'truck_dump':
|
||||
if not self.allow_truck_dump:
|
||||
raise NotImplementedError("Requested workflow not supported: truck_dump")
|
||||
batch = self.model_class()
|
||||
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.created_by = self.request.user
|
||||
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))
|
||||
|
||||
else:
|
||||
raise NotImplementedError("Requested workflow not supported: {}".format(form.validated['workflow']))
|
||||
|
||||
vendor = None
|
||||
if self.request.method == 'POST' and self.request.POST.get('vendor'):
|
||||
vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor'])
|
||||
|
@ -227,28 +290,19 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
data['purchases'] = [(p['key'], p['display']) for p in purchases['purchases']]
|
||||
return self.render_to_response('create', data, mobile=True)
|
||||
|
||||
def get_batch_kwargs(self, batch, mobile=False):
|
||||
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
|
||||
if mobile:
|
||||
|
||||
purchase = self.get_purchase(self.request.POST['purchase'])
|
||||
numbers = [d.F03 for d in purchase.details]
|
||||
if numbers:
|
||||
number = max(set(numbers), key=numbers.count)
|
||||
kwargs['department'] = self.Session.query(model.Department)\
|
||||
.filter(model.Department.number == number)\
|
||||
.one()
|
||||
|
||||
return kwargs
|
||||
|
||||
def configure_mobile_form(self, f):
|
||||
super(ReceivingBatchView, self).configure_mobile_form(f)
|
||||
batch = f.model_instance
|
||||
|
||||
# vendor
|
||||
# fs.vendor.with_renderer(fa.TextFieldRenderer),
|
||||
# truck_dump
|
||||
if not self.creating:
|
||||
if not batch.truck_dump:
|
||||
f.remove_field('truck_dump')
|
||||
|
||||
# department
|
||||
# fs.department.with_renderer(fa.TextFieldRenderer),
|
||||
if not self.creating:
|
||||
if batch.truck_dump:
|
||||
f.remove_field('department')
|
||||
|
||||
def configure_row_form(self, f):
|
||||
super(ReceivingBatchView, self).configure_row_form(f)
|
||||
|
@ -302,8 +356,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
if product:
|
||||
row = model.PurchaseBatchRow()
|
||||
row.product = product
|
||||
batch.add_row(row)
|
||||
self.handler.refresh_row(row)
|
||||
self.handler.add_row(batch, row)
|
||||
|
||||
# check for "bad" upc
|
||||
elif len(upc) > 14:
|
||||
|
@ -329,9 +382,12 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
"""
|
||||
self.viewing = True
|
||||
row = self.get_row_instance()
|
||||
batch = row.batch
|
||||
permission_prefix = self.get_permission_prefix()
|
||||
form = self.make_mobile_row_form(row)
|
||||
context = {
|
||||
'row': row,
|
||||
'batch': batch,
|
||||
'instance': row,
|
||||
'instance_title': self.get_row_instance_title(row),
|
||||
'parent_model_title': self.get_model_title(),
|
||||
|
@ -339,36 +395,45 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
'form': form,
|
||||
}
|
||||
|
||||
if self.request.has_perm('{}.create_row'.format(self.get_permission_prefix())):
|
||||
update_form = forms.Form(schema=ReceivingForm(), request=self.request)
|
||||
if self.request.has_perm('{}.create_row'.format(permission_prefix)):
|
||||
update_form = forms.Form(schema=MobileReceivingForm(), request=self.request)
|
||||
if update_form.validate(newstyle=True):
|
||||
row = self.Session.merge(update_form.validated['row'])
|
||||
mode = update_form.validated['mode']
|
||||
cases = update_form.validated['cases']
|
||||
units = update_form.validated['units']
|
||||
if cases:
|
||||
setattr(row, 'cases_{}'.format(mode),
|
||||
(getattr(row, 'cases_{}'.format(mode)) or 0) + cases)
|
||||
if units:
|
||||
setattr(row, 'units_{}'.format(mode),
|
||||
(getattr(row, 'units_{}'.format(mode)) or 0) + units)
|
||||
|
||||
# if mode in ('damaged', 'expired', 'mispick'):
|
||||
if mode in ('damaged', 'expired'):
|
||||
self.attach_credit(row, mode, cases, units,
|
||||
expiration_date=update_form.validated['expiration_date'],
|
||||
# discarded=update_form.data['trash'],
|
||||
# mispick_product=shipped_product)
|
||||
)
|
||||
# TODO: surely this (delete_row) should be split out to a separate view
|
||||
if update_form.validated['delete_row']:
|
||||
if not self.request.has_perm('{}.delete_row'.format(permission_prefix)):
|
||||
raise httpexceptions.HTTPForbidden()
|
||||
self.handler.remove_row(row)
|
||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||
|
||||
# first undo any totals previously in effect for the row, then refresh
|
||||
if row.invoice_total:
|
||||
row.batch.invoice_total -= row.invoice_total
|
||||
self.handler.refresh_row(row)
|
||||
else: # not delete_row
|
||||
mode = update_form.validated['mode']
|
||||
cases = update_form.validated['cases']
|
||||
units = update_form.validated['units']
|
||||
if cases:
|
||||
setattr(row, 'cases_{}'.format(mode),
|
||||
(getattr(row, 'cases_{}'.format(mode)) or 0) + cases)
|
||||
if units:
|
||||
setattr(row, 'units_{}'.format(mode),
|
||||
(getattr(row, 'units_{}'.format(mode)) or 0) + units)
|
||||
|
||||
return self.redirect(self.request.route_url('mobile.{}.view'.format(self.get_route_prefix()), uuid=row.batch_uuid))
|
||||
# if mode in ('damaged', 'expired', 'mispick'):
|
||||
if mode in ('damaged', 'expired'):
|
||||
self.attach_credit(row, mode, cases, units,
|
||||
expiration_date=update_form.validated['expiration_date'],
|
||||
# discarded=update_form.data['trash'],
|
||||
# mispick_product=shipped_product)
|
||||
)
|
||||
|
||||
if not row.cases_ordered and not row.units_ordered:
|
||||
# first undo any totals previously in effect for the row, then refresh
|
||||
if row.invoice_total:
|
||||
batch.invoice_total -= row.invoice_total
|
||||
self.handler.refresh_row(row)
|
||||
|
||||
return self.redirect(self.get_action_url('view', batch, mobile=True))
|
||||
|
||||
if not row.cases_ordered and not row.units_ordered and not batch.truck_dump:
|
||||
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)
|
||||
|
||||
|
@ -438,22 +503,39 @@ class PurchaseBatchRowType(forms.types.ObjectType):
|
|||
return row
|
||||
|
||||
|
||||
class ReceivingForm(colander.MappingSchema):
|
||||
class MobileNewReceivingBatch(colander.MappingSchema):
|
||||
|
||||
vendor = colander.SchemaNode(forms.types.VendorType())
|
||||
|
||||
workflow = colander.SchemaNode(colander.String(),
|
||||
validator=colander.OneOf([
|
||||
'from_po',
|
||||
'from_scratch',
|
||||
'truck_dump',
|
||||
]))
|
||||
|
||||
|
||||
class MobileReceivingForm(colander.MappingSchema):
|
||||
|
||||
row = colander.SchemaNode(PurchaseBatchRowType())
|
||||
|
||||
mode = colander.SchemaNode(colander.String(),
|
||||
validator=colander.OneOf(['received',
|
||||
'damaged',
|
||||
'expired',
|
||||
# 'mispick',
|
||||
validator=colander.OneOf([
|
||||
'received',
|
||||
'damaged',
|
||||
'expired',
|
||||
# 'mispick',
|
||||
]))
|
||||
|
||||
cases = colander.SchemaNode(colander.Decimal(), missing=None)
|
||||
|
||||
units = colander.SchemaNode(colander.Decimal(), missing=None)
|
||||
|
||||
expiration_date = colander.SchemaNode(colander.Date(), missing=colander.null)
|
||||
expiration_date = colander.SchemaNode(colander.Date(),
|
||||
widget=dfwidget.TextInputWidget(),
|
||||
missing=colander.null)
|
||||
|
||||
delete_row = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
|
||||
def includeme(config):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue