Add "most of" support for truck dump receiving

still not complete, but conceptually it sort of is...
This commit is contained in:
Lance Edgar 2018-05-18 15:51:47 -05:00
parent 805a1afa3f
commit cd7922f204
8 changed files with 368 additions and 76 deletions

View file

@ -42,6 +42,7 @@ import deform
from colanderalchemy import SQLAlchemySchemaNode from colanderalchemy import SQLAlchemySchemaNode
from colanderalchemy.schema import _creation_order from colanderalchemy.schema import _creation_order
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid_deform import SessionFileUploadTempStore
from pyramid.renderers import render from pyramid.renderers import render
from webhelpers2.html import tags, HTML from webhelpers2.html import tags, HTML
@ -585,6 +586,11 @@ class Form(object):
self.set_widget(key, dfwidget.TextAreaWidget(cols=80, rows=8)) self.set_widget(key, dfwidget.TextAreaWidget(cols=80, rows=8))
elif type_ == 'text': elif type_ == 'text':
self.set_widget(key, dfwidget.TextAreaWidget(cols=80, rows=8)) self.set_widget(key, dfwidget.TextAreaWidget(cols=80, rows=8))
elif type_ == 'file':
tmpstore = SessionFileUploadTempStore(self.request)
self.set_node(key, colander.SchemaNode(deform.FileData(),
widget=dfwidget.FileUploadWidget(tmpstore),
title=self.get_label(key)))
else: else:
raise ValueError("unknown type for '{}' field: {}".format(key, type_)) raise ValueError("unknown type for '{}' field: {}".format(key, type_))

View file

@ -61,7 +61,13 @@
<%def name="execute_button()"> <%def name="execute_button()">
% if not batch.executed and request.has_perm('{}.execute'.format(permission_prefix)): % if not batch.executed and request.has_perm('{}.execute'.format(permission_prefix)):
<button type="button" id="execute-batch"${'' if execute_enabled else ' disabled="disabled"'}>${execute_title}</button> % if execute_enabled:
<button type="button" id="execute-batch">${execute_title}</button>
% elif why_not_execute:
<button type="button" id="execute-batch" disabled="disabled" title="${why_not_execute}">${execute_title}</button>
% else:
<button type="button" id="execute-batch" disabled="disabled">${execute_title}</button>
% endif
% endif % endif
</%def> </%def>

View file

@ -0,0 +1,67 @@
## -*- coding: utf-8; -*-
<%inherit file="/batch/create.mako" />
<%def name="extra_javascript()">
${parent.extra_javascript()}
${self.func_show_batch_type()}
<script type="text/javascript">
% if master.allow_truck_dump:
var batch_vendor_map = ${json.dumps(batch_vendor_map)|n};
% endif
$(function() {
$('.batch_type select').on('selectmenuchange', function(event, ui) {
show_batch_type(ui.item.value);
});
$('.truck_dump_batch_uuid select').on('selectmenuchange', function(event, ui) {
var form = $(this).parents('form');
var uuid = ui.item.value ? batch_vendor_map[ui.item.value] : '';
form.find('input[name="vendor_uuid"]').val(uuid);
});
show_batch_type();
});
</script>
</%def>
<%def name="func_show_batch_type()">
<script type="text/javascript">
function show_batch_type(batch_type) {
if (batch_type === undefined) {
batch_type = $('.field-wrapper.batch_type select').val();
}
if (batch_type == 'from_scratch') {
$('.field-wrapper.truck_dump_batch_uuid').hide();
$('.field-wrapper.invoice_file').hide();
$('.field-wrapper.invoice_parser_key').hide();
$('.field-wrapper.vendor_uuid').show();
$('.field-wrapper.date_ordered').show();
$('.field-wrapper.date_received').show();
$('.field-wrapper.po_number').show();
$('.field-wrapper.invoice_date').show();
$('.field-wrapper.invoice_number').show();
} else if (batch_type == 'truck_dump') {
$('.field-wrapper.truck_dump_batch_uuid').show();
$('.field-wrapper.invoice_file').show();
$('.field-wrapper.invoice_parser_key').show();
$('.field-wrapper.vendor_uuid').hide();
$('.field-wrapper.date_ordered').hide();
$('.field-wrapper.date_received').hide();
$('.field-wrapper.po_number').hide();
$('.field-wrapper.invoice_date').hide();
$('.field-wrapper.invoice_number').hide();
}
}
</script>
</%def>
${parent.body()}

View file

@ -42,11 +42,9 @@ from rattail.util import load_object, prettify
import colander import colander
import deform import deform
from deform import widget as dfwidget
from pyramid import httpexceptions from pyramid import httpexceptions
from pyramid.renderers import render_to_response from pyramid.renderers import render_to_response
from pyramid.response import FileResponse from pyramid.response import FileResponse
from pyramid_deform import SessionFileUploadTempStore
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from tailbone import forms, grids from tailbone import forms, grids
@ -131,6 +129,9 @@ class BatchMasterView(MasterView):
return load_object(spec)(self.rattail_config) return load_object(spec)(self.rattail_config)
return self.batch_handler_class(self.rattail_config) return self.batch_handler_class(self.rattail_config)
def download_path(self, batch, filename):
return self.rattail_config.batch_filepath(batch.batch_key, batch.uuid, filename)
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
batch = kwargs['instance'] batch = kwargs['instance']
kwargs['batch'] = batch kwargs['batch'] = batch
@ -140,6 +141,8 @@ class BatchMasterView(MasterView):
if kwargs['execute_enabled']: if kwargs['execute_enabled']:
url = self.get_action_url('execute', batch) url = self.get_action_url('execute', batch)
kwargs['execute_form'] = self.make_execute_form(batch, action_url=url) kwargs['execute_form'] = self.make_execute_form(batch, action_url=url)
else:
kwargs['why_not_execute'] = self.handler.why_not_execute(batch)
return kwargs return kwargs
def allow_worksheet(self, batch): def allow_worksheet(self, batch):
@ -278,9 +281,6 @@ class BatchMasterView(MasterView):
return status_code_text return status_code_text
return render_status return render_status
def download_path(self, batch, filename):
return self.rattail_config.batch_filepath(batch.batch_key, batch.uuid, filename)
def render_user(self, batch, field): def render_user(self, batch, field):
user = getattr(batch, field) user = getattr(batch, field)
if not user: if not user:
@ -312,6 +312,7 @@ class BatchMasterView(MasterView):
f.remove_field('complete') f.remove_field('complete')
def save_create_form(self, form): def save_create_form(self, form):
uploads = self.normalize_uploads(form, skip=['filename'])
self.before_create(form) self.before_create(form)
session = self.Session() session = self.Session()
@ -346,17 +347,15 @@ class BatchMasterView(MasterView):
batch = self.handler.make_batch(session, **kwargs) batch = self.handler.make_batch(session, **kwargs)
self.Session.flush() self.Session.flush()
self.process_uploads(batch, form, uploads)
# TODO: this needs work yet surely...
# if batch has input data file, let handler properly establish that
if 'filename' in form.schema:
if filedict:
self.handler.set_input_file(batch, filepath)
os.remove(filepath)
os.rmdir(tempdir)
return batch return batch
def process_uploads(self, batch, form, uploads):
for key, upload in six.iteritems(uploads):
self.handler.set_input_file(batch, upload['temp_path'], attr=key)
os.remove(upload['temp_path'])
os.rmdir(upload['tempdir'])
def save_mobile_create_form(self, form): def save_mobile_create_form(self, form):
self.before_create(form) self.before_create(form)
session = self.Session() session = self.Session()
@ -536,6 +535,39 @@ class BatchMasterView(MasterView):
url = self.request.route_url('{}.delete_rows'.format(self.get_route_prefix()), uuid=batch.uuid) url = self.request.route_url('{}.delete_rows'.format(self.get_route_prefix()), uuid=batch.uuid)
return HTML.tag('p', c=[tags.link_to("Delete all rows matching current search", url)]) return HTML.tag('p', c=[tags.link_to("Delete all rows matching current search", url)])
def make_row_grid_kwargs(self, **kwargs):
"""
Whether or not rows may be edited or deleted will depend partially on
whether the parent batch has been executed.
"""
batch = self.get_instance()
# TODO: most of this logic is copied from MasterView, should refactor/merge somehow...
if 'main_actions' not in kwargs:
actions = []
# view action
if self.rows_viewable:
view = lambda r, i: self.get_row_action_url('view', r)
actions.append(grids.GridAction('view', icon='zoomin', url=view))
# edit and delete are NOT allowed after execution, or if batch is "complete"
if not batch.executed and not batch.complete:
# edit action
if self.rows_editable:
actions.append(grids.GridAction('edit', icon='pencil', url=self.row_edit_action_url))
# delete action
permission_prefix = self.get_permission_prefix()
if self.rows_deletable and self.request.has_perm('{}.delete_row'.format(permission_prefix)):
actions.append(grids.GridAction('delete', icon='trash', url=self.row_delete_action_url))
kwargs.setdefault('delete_speedbump', self.rows_deletable_speedbump)
kwargs['main_actions'] = actions
return super(BatchMasterView, self).make_row_grid_kwargs(**kwargs)
def make_row_grid_tools(self, batch): def make_row_grid_tools(self, batch):
return (self.make_default_row_grid_tools(batch) or '') + (self.make_batch_row_grid_tools(batch) or '') return (self.make_default_row_grid_tools(batch) or '') + (self.make_batch_row_grid_tools(batch) or '')
@ -555,10 +587,7 @@ class BatchMasterView(MasterView):
""" """
Delete all data (files etc.) for the batch. Delete all data (files etc.) for the batch.
""" """
if hasattr(batch, 'delete_data'): self.handler.delete(batch)
batch.delete_data(self.rattail_config)
if hasattr(batch, 'data_rows'):
del batch.data_rows[:]
super(BatchMasterView, self).delete_instance(batch) super(BatchMasterView, self).delete_instance(batch)
def get_fallback_templates(self, template, mobile=False): def get_fallback_templates(self, template, mobile=False):
@ -1153,6 +1182,7 @@ class FileBatchMasterView(BatchMasterView):
""" """
Base class for all file-based "batch master" views. Base class for all file-based "batch master" views.
""" """
downloadable = True
@property @property
def upload_dir(self): def upload_dir(self):
@ -1171,62 +1201,26 @@ class FileBatchMasterView(BatchMasterView):
def configure_form(self, f): def configure_form(self, f):
super(FileBatchMasterView, self).configure_form(f) super(FileBatchMasterView, self).configure_form(f)
batch = f.model_instance
# filename # filename
f.set_renderer('filename', self.render_filename)
f.set_label('filename', "Data File")
if self.editing:
f.set_readonly('filename')
if self.creating: if self.creating:
if 'filename' not in f.fields: # TODO: what's up with this re-insertion again..?
f.fields.insert(0, 'filename') # if 'filename' not in f.fields:
tmpstore = SessionFileUploadTempStore(self.request) # f.fields.insert(0, 'filename')
f.set_node('filename', colander.SchemaNode(deform.FileData(), widget=dfwidget.FileUploadWidget(tmpstore))) f.set_type('filename', 'file')
else:
f.set_readonly('filename')
f.set_renderer('filename', self.render_filename)
def render_filename(self, batch, field): def render_filename(self, batch, field):
path = batch.filepath(self.rattail_config, filename=batch.filename) filename = getattr(batch, field)
if not filename:
return ""
path = batch.filepath(self.rattail_config, filename=filename)
url = self.get_action_url('download', batch) url = self.get_action_url('download', batch)
return self.render_file_field(path, url) return self.render_file_field(path, url)
def download(self):
"""
View for downloading the data file associated with a batch.
"""
batch = self.get_instance()
if not batch:
raise httpexceptions.HTTPNotFound()
path = batch.filepath(self.rattail_config)
response = FileResponse(path, request=self.request)
response.headers[b'Content-Length'] = six.binary_type(os.path.getsize(path))
filename = os.path.basename(batch.filename).encode('ascii', 'replace')
response.headers[b'Content-Disposition'] = b'attachment; filename="{}"'.format(filename)
return response
@classmethod
def defaults(cls, config):
cls._filebatch_defaults(config)
cls._batch_defaults(config)
cls._defaults(config)
@classmethod
def _filebatch_defaults(cls, config):
route_prefix = cls.get_route_prefix()
url_prefix = cls.get_url_prefix()
permission_prefix = cls.get_permission_prefix()
model_title = cls.get_model_title()
model_title_plural = cls.get_model_title_plural()
# fix permission group title
config.add_tailbone_permission_group(permission_prefix, model_title_plural)
# download batch data file
config.add_route('{}.download'.format(route_prefix), '{}/{{uuid}}/download'.format(url_prefix))
config.add_view(cls, attr='download', route_name='{}.download'.format(route_prefix),
permission='{}.download'.format(permission_prefix))
config.add_tailbone_permission(permission_prefix, '{}.download'.format(permission_prefix),
"Download existing {} data file".format(model_title))
class MobileBatchStatusFilter(grids.filters.MobileFilter): class MobileBatchStatusFilter(grids.filters.MobileFilter):

View file

@ -27,6 +27,7 @@ Model Master View
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import os import os
import tempfile
import logging import logging
import six import six
@ -633,14 +634,39 @@ class MasterView(View):
return self.render_to_response('create', {'form': form}, mobile=True) return self.render_to_response('create', {'form': form}, mobile=True)
def save_create_form(self, form): def save_create_form(self, form):
uploads = self.normalize_uploads(form)
self.before_create(form) self.before_create(form)
with self.Session().no_autoflush: with self.Session().no_autoflush:
obj = self.objectify(form, self.form_deserialized) obj = self.objectify(form, self.form_deserialized)
self.before_create_flush(obj, form) self.before_create_flush(obj, form)
self.Session.add(obj) self.Session.add(obj)
self.Session.flush() self.Session.flush()
self.process_uploads(obj, form, uploads)
return obj return obj
def normalize_uploads(self, form, skip=None):
uploads = {}
for node in form.schema:
if isinstance(node.typ, deform.FileData):
if skip and node.name in skip:
continue
filedict = self.form_deserialized.get(node.name)
if filedict:
tempdir = tempfile.mkdtemp()
filepath = os.path.join(tempdir, filedict['filename'])
tmpinfo = form.deform_form[node.name].widget.tmpstore.get(filedict['uid'])
tmpdata = tmpinfo['fp'].read()
with open(filepath, 'wb') as f:
f.write(tmpdata)
uploads[node.name] = {
'tempdir': tempdir,
'temp_path': filepath,
}
return uploads
def process_uploads(self, obj, form, uploads):
pass
def before_create_flush(self, obj, form): def before_create_flush(self, obj, form):
pass pass
@ -1230,6 +1256,8 @@ class MasterView(View):
""" """
obj = self.get_instance() obj = self.get_instance()
filename = self.request.GET.get('filename', None) filename = self.request.GET.get('filename', None)
if not filename:
raise self.notfound()
path = self.download_path(obj, filename) path = self.download_path(obj, filename)
response = FileResponse(path, request=self.request) response = FileResponse(path, request=self.request)
response.content_length = os.path.getsize(path) response.content_length = os.path.getsize(path)
@ -2124,6 +2152,14 @@ class MasterView(View):
""" """
return getattr(cls, 'mobile_row_form_factory', forms.Form) return getattr(cls, 'mobile_row_form_factory', forms.Form)
def render_downloadable_file(self, obj, field):
filename = getattr(obj, field)
if not filename:
return ""
path = self.download_path(obj, filename)
url = self.get_action_url('download', obj, _query={'filename': filename})
return self.render_file_field(path, url)
def render_file_field(self, path, url=None, filename=None): def render_file_field(self, path, url=None, filename=None):
""" """
Convenience for rendering a file with optional download link Convenience for rendering a file with optional download link

View file

@ -26,6 +26,8 @@ Views for "true" purchase credits
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import six
from rattail.db import model from rattail.db import model
from webhelpers2.html import tags from webhelpers2.html import tags
@ -70,12 +72,13 @@ class PurchaseCreditView(MasterView):
g.set_sort_defaults('date_received', 'desc') g.set_sort_defaults('date_received', 'desc')
g.set_enum('status', self.enum.PURCHASE_CREDIT_STATUS)
g.filters['status'].set_value_renderer(grids.filters.EnumValueRenderer(self.enum.PURCHASE_CREDIT_STATUS)) g.filters['status'].set_value_renderer(grids.filters.EnumValueRenderer(self.enum.PURCHASE_CREDIT_STATUS))
g.filters['status'].default_active = True g.filters['status'].default_active = True
g.filters['status'].default_verb = 'not_equal' g.filters['status'].default_verb = 'not_equal'
g.filters['status'].default_value = self.enum.PURCHASE_CREDIT_STATUS_SATISFIED # TODO: should not have to convert value to string!
g.filters['status'].default_value = six.text_type(self.enum.PURCHASE_CREDIT_STATUS_SATISFIED)
g.set_enum('status', self.enum.PURCHASE_CREDIT_STATUS)
# g.set_type('upc', 'gpc') # g.set_type('upc', 'gpc')
g.set_type('cases_shorted', 'quantity') g.set_type('cases_shorted', 'quantity')
g.set_type('units_shorted', 'quantity') g.set_type('units_shorted', 'quantity')

View file

@ -48,6 +48,7 @@ class PurchasingBatchView(BatchMasterView):
model_row_class = model.PurchaseBatchRow model_row_class = model.PurchaseBatchRow
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler' default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
supports_new_product = False supports_new_product = False
cloneable = True
grid_columns = [ grid_columns = [
'id', 'id',
@ -513,22 +514,33 @@ class PurchasingBatchView(BatchMasterView):
kwargs = super(PurchasingBatchView, self).get_batch_kwargs(batch, mobile=mobile) kwargs = super(PurchasingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
kwargs['mode'] = self.batch_mode kwargs['mode'] = self.batch_mode
kwargs['truck_dump'] = batch.truck_dump kwargs['truck_dump'] = batch.truck_dump
kwargs['invoice_parser_key'] = batch.invoice_parser_key
if batch.store: if batch.store:
kwargs['store'] = batch.store kwargs['store'] = batch.store
elif batch.store_uuid: elif batch.store_uuid:
kwargs['store_uuid'] = batch.store_uuid kwargs['store_uuid'] = batch.store_uuid
if batch.truck_dump_batch:
kwargs['truck_dump_batch'] = batch.truck_dump_batch
elif batch.truck_dump_batch_uuid:
kwargs['truck_dump_batch_uuid'] = batch.truck_dump_batch_uuid
if batch.vendor: if batch.vendor:
kwargs['vendor'] = batch.vendor kwargs['vendor'] = batch.vendor
elif batch.vendor_uuid: elif batch.vendor_uuid:
kwargs['vendor_uuid'] = batch.vendor_uuid kwargs['vendor_uuid'] = batch.vendor_uuid
if batch.department: if batch.department:
kwargs['department'] = batch.department kwargs['department'] = batch.department
elif batch.department_uuid: elif batch.department_uuid:
kwargs['department_uuid'] = batch.department_uuid kwargs['department_uuid'] = batch.department_uuid
if batch.buyer: if batch.buyer:
kwargs['buyer'] = batch.buyer kwargs['buyer'] = batch.buyer
elif batch.buyer_uuid: elif batch.buyer_uuid:
kwargs['buyer_uuid'] = batch.buyer_uuid kwargs['buyer_uuid'] = batch.buyer_uuid
kwargs['po_number'] = batch.po_number kwargs['po_number'] = batch.po_number
kwargs['po_total'] = batch.po_total kwargs['po_total'] = batch.po_total
@ -600,7 +612,9 @@ class PurchasingBatchView(BatchMasterView):
def row_grid_extra_class(self, row, i): def row_grid_extra_class(self, row, i):
if row.status_code == row.STATUS_PRODUCT_NOT_FOUND: if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
return 'warning' return 'warning'
if row.status_code in (row.STATUS_INCOMPLETE, row.STATUS_ORDERED_RECEIVED_DIFFER): if row.status_code in (row.STATUS_INCOMPLETE,
row.STATUS_ORDERED_RECEIVED_DIFFER,
row.STATUS_TRUCKDUMP_UNCLAIMED):
return 'notice' return 'notice'
def configure_row_form(self, f): def configure_row_form(self, f):

View file

@ -28,17 +28,19 @@ from __future__ import unicode_literals, absolute_import
import re import re
import six
import sqlalchemy as sa import sqlalchemy as sa
from rattail import pod from rattail import pod
from rattail.db import model, api from rattail.db import model, api
from rattail.gpc import GPC from rattail.gpc import GPC
from rattail.util import pretty_quantity, prettify from rattail.util import pretty_quantity, prettify
from rattail.vendors.invoices import iter_invoice_parsers, require_invoice_parser
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid import httpexceptions from pyramid import httpexceptions
from webhelpers2.html import tags from webhelpers2.html import tags, HTML
from tailbone import forms, grids from tailbone import forms, grids
from tailbone.views.purchasing import PurchasingBatchView from tailbone.views.purchasing import PurchasingBatchView
@ -96,9 +98,8 @@ class ReceivingBatchView(PurchasingBatchView):
model_title = "Receiving Batch" model_title = "Receiving Batch"
model_title_plural = "Receiving Batches" model_title_plural = "Receiving Batches"
index_title = "Receiving" index_title = "Receiving"
creatable = False downloadable = True
rows_editable = True rows_editable = True
rows_deletable = False
mobile_creatable = True mobile_creatable = True
mobile_rows_filterable = True mobile_rows_filterable = True
mobile_rows_creatable = True mobile_rows_creatable = True
@ -107,6 +108,11 @@ class ReceivingBatchView(PurchasingBatchView):
allow_from_scratch = True allow_from_scratch = True
allow_truck_dump = False allow_truck_dump = False
labels = {
'truck_dump_batch': "Truck Dump Parent",
'invoice_parser_key': "Invoice Parser",
}
grid_columns = [ grid_columns = [
'id', 'id',
'vendor', 'vendor',
@ -123,9 +129,14 @@ class ReceivingBatchView(PurchasingBatchView):
form_fields = [ form_fields = [
'id', 'id',
'batch_type',
'store', 'store',
'vendor', 'vendor',
'truck_dump', 'truck_dump',
'truck_dump_children',
'truck_dump_batch',
'invoice_file',
'invoice_parser_key',
'department', 'department',
'purchase', 'purchase',
'vendor_email', 'vendor_email',
@ -143,6 +154,7 @@ class ReceivingBatchView(PurchasingBatchView):
'created', 'created',
'created_by', 'created_by',
'status_code', 'status_code',
'rowcount',
'complete', 'complete',
'executed', 'executed',
'executed_by', 'executed_by',
@ -203,13 +215,167 @@ class ReceivingBatchView(PurchasingBatchView):
def batch_mode(self): def batch_mode(self):
return self.enum.PURCHASE_BATCH_MODE_RECEIVING return self.enum.PURCHASE_BATCH_MODE_RECEIVING
def row_editable(self, row):
batch = row.batch
if batch.truck_dump_batch:
return False
return True
def row_deletable(self, row):
batch = row.batch
if batch.truck_dump:
return True
return False
def configure_form(self, f): def configure_form(self, f):
super(ReceivingBatchView, self).configure_form(f) super(ReceivingBatchView, self).configure_form(f)
batch = f.model_instance
# batch_type
if self.creating:
batch_type_values = [
('from_scratch', "New from Scratch"),
]
if self.allow_truck_dump:
batch_type_values.append(('truck_dump', "Invoice for Truck Dump"))
f.set_widget('batch_type', forms.widgets.JQuerySelectWidget(values=batch_type_values))
else:
f.remove_field('batch_type')
# truck_dump*
if self.allow_truck_dump:
# truck_dump # truck_dump
if self.editing: if self.creating:
f.remove_field('truck_dump')
elif batch.truck_dump_batch:
f.remove_field('truck_dump')
else:
f.set_readonly('truck_dump') f.set_readonly('truck_dump')
# truck_dump_children
if self.viewing:
if batch.truck_dump:
f.set_renderer('truck_dump_children', self.render_truck_dump_children)
else:
f.remove_field('truck_dump_children')
else:
f.remove_field('truck_dump_children')
# truck_dump_batch
if self.creating:
f.replace('truck_dump_batch', 'truck_dump_batch_uuid')
batches = self.Session.query(model.PurchaseBatch)\
.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING)\
.filter(model.PurchaseBatch.truck_dump == True)\
.filter(model.PurchaseBatch.complete == True)\
.filter(model.PurchaseBatch.executed == None)\
.order_by(model.PurchaseBatch.id)
batch_values = [(b.uuid, six.text_type(b)) for b in batches]
batch_values.insert(0, ('', "(please choose)"))
f.set_widget('truck_dump_batch_uuid', forms.widgets.JQuerySelectWidget(values=batch_values))
f.set_label('truck_dump_batch_uuid', "Truck Dump Parent")
elif batch.truck_dump:
f.remove_field('truck_dump_batch')
elif batch.truck_dump_batch:
f.set_readonly('truck_dump_batch')
f.set_renderer('truck_dump_batch', self.render_truck_dump_batch)
else:
f.remove_field('truck_dump_batch')
else:
f.remove_fields('truck_dump',
'truck_dump_children',
'truck_dump_batch')
# invoice_file
if self.creating:
f.set_type('invoice_file', 'file')
else:
f.set_readonly('invoice_file')
f.set_renderer('invoice_file', self.render_downloadable_file)
# invoice_parser_key
if self.creating:
parsers = sorted(iter_invoice_parsers(), key=lambda p: p.display)
parser_values = [(p.key, p.display) for p in parsers]
parser_values.insert(0, ('', "(please choose)"))
f.set_widget('invoice_parser_key', forms.widgets.JQuerySelectWidget(values=parser_values))
else:
f.remove_field('invoice_parser_key')
# store
if self.creating:
store = self.rattail_config.get_store(self.Session())
f.set_widget('store_uuid', forms.widgets.ReadonlyWidget())
f.set_default('store_uuid', store.uuid)
f.set_hidden('store_uuid')
# purchase
if self.creating:
f.remove_field('purchase')
# department
if self.creating:
f.remove_field('department_uuid')
def template_kwargs_create(self, **kwargs):
kwargs = super(ReceivingBatchView, self).template_kwargs_create(**kwargs)
if self.allow_truck_dump:
vmap = {}
batches = self.Session.query(model.PurchaseBatch)\
.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING)\
.filter(model.PurchaseBatch.truck_dump == True)\
.filter(model.PurchaseBatch.complete == True)
for batch in batches:
vmap[batch.uuid] = batch.vendor_uuid
kwargs['batch_vendor_map'] = vmap
return kwargs
def get_batch_kwargs(self, batch, mobile=False):
kwargs = super(ReceivingBatchView, self).get_batch_kwargs(batch, mobile=mobile)
if not mobile:
batch_type = self.request.POST['batch_type']
if batch_type == 'from_scratch':
kwargs.pop('truck_dump_batch', None)
kwargs.pop('truck_dump_batch_uuid', None)
elif batch_type == 'truck_dump':
pass
else:
raise NotImplementedError
return kwargs
def delete_instance(self, batch):
"""
Delete all data (files etc.) for the batch.
"""
truck_dump = batch.truck_dump_batch
if batch.truck_dump:
for child in batch.truck_dump_children:
self.delete_instance(child)
super(ReceivingBatchView, self).delete_instance(batch)
if truck_dump:
self.handler.refresh(truck_dump)
def render_truck_dump_batch(self, batch, field):
truck_dump = batch.truck_dump_batch
if not truck_dump:
return ""
text = six.text_type(truck_dump)
url = self.request.route_url('receiving.view', uuid=truck_dump.uuid)
return tags.link_to(text, url)
def render_truck_dump_children(self, batch, field):
children = batch.truck_dump_children
if not children:
return ""
items = []
for child in children:
text = six.text_type(child)
url = self.request.route_url('receiving.view', uuid=child.uuid)
items.append(HTML.tag('li', c=[tags.link_to(text, url)]))
return HTML.tag('ul', c=items)
def render_mobile_listitem(self, batch, i): def render_mobile_listitem(self, batch, i):
title = "({}) {} for ${:0,.2f} - {}, {}".format( title = "({}) {} for ${:0,.2f} - {}, {}".format(
batch.id_str, batch.id_str,