Split "new receiving batch" process into 2 steps: choose, create
so that the form used to create the batch can be made custom per-workflow, and it won't have to think about any other workflows since we just use one form at a time for that
This commit is contained in:
parent
ff2e39f67a
commit
a2b7f882bc
|
@ -93,7 +93,8 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
|
||||
form_fields = [
|
||||
'id',
|
||||
'batch_type',
|
||||
'batch_type', # TODO: ideally would get rid of this one
|
||||
'receiving_workflow',
|
||||
'store',
|
||||
'vendor',
|
||||
'description',
|
||||
|
@ -202,6 +203,79 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
def batch_mode(self):
|
||||
return self.enum.PURCHASE_BATCH_MODE_RECEIVING
|
||||
|
||||
def create(self, form=None, **kwargs):
|
||||
"""
|
||||
Custom view for creating a new receiving batch. We split the process
|
||||
into two steps, 1) choose and 2) create. This is because the specific
|
||||
form details for creating a batch will depend on which "type" of batch
|
||||
creation is to be done, and it's much easier to keep conditional logic
|
||||
for that in the server instead of client-side etc.
|
||||
"""
|
||||
route_prefix = self.get_route_prefix()
|
||||
workflows = self.handler.supported_receiving_workflows()
|
||||
valid_workflows = [workflow['workflow_key']
|
||||
for workflow in workflows]
|
||||
|
||||
# if user has already identified their desired workflow, then we can
|
||||
# just farm out to the default logic. we will of course configure our
|
||||
# form differently, based on workflow, but this create() method at
|
||||
# least will not need customization for that.
|
||||
if 'workflow_key' in self.request.matchdict:
|
||||
|
||||
# however we do have one more thing to check - the workflow
|
||||
# requested must of course be valid!
|
||||
workflow_key = self.request.matchdict['workflow_key']
|
||||
if workflow_key not in valid_workflows:
|
||||
self.request.session.flash(
|
||||
"Not a supported workflow: {}".format(workflow_key),
|
||||
'error')
|
||||
raise self.redirect(self.request.route_url('{}.create'.format(route_prefix)))
|
||||
|
||||
# okay now do the normal thing, per workflow
|
||||
return super(ReceivingBatchView, self).create(**kwargs)
|
||||
|
||||
# on the other hand, if caller provided a form, that means we are in
|
||||
# the middle of some other custom workflow, e.g. "add child to truck
|
||||
# dump parent" or some such. in which case we also defer to the normal
|
||||
# logic, so as to not interfere with that.
|
||||
if form:
|
||||
return super(ReceivingBatchView, self).create(form=form, **kwargs)
|
||||
|
||||
# okay, at this point we need the user to select a workflow...
|
||||
self.creating = True
|
||||
use_buefy = self.get_use_buefy()
|
||||
context = {}
|
||||
|
||||
# form to accept user choice of workflow
|
||||
schema = NewBatchType().bind(valid_workflows=valid_workflows)
|
||||
form = forms.Form(schema=schema, request=self.request,
|
||||
use_buefy=use_buefy)
|
||||
values = [(workflow['workflow_key'], workflow['display'])
|
||||
for workflow in workflows]
|
||||
if use_buefy:
|
||||
# if workflows:
|
||||
# form.set_default('workflow', workflows[0]['workflow_key'])
|
||||
form.set_widget('workflow',
|
||||
dfwidget.SelectWidget(values=values))
|
||||
else:
|
||||
form.set_widget('workflow',
|
||||
forms.widgets.JQuerySelectWidget(values=values))
|
||||
form.submit_label = "Continue"
|
||||
form.cancel_url = self.get_index_url()
|
||||
|
||||
# if form validates, that means user has chosen a creation type, so we
|
||||
# just redirect to the appropriate "new batch of type X" page
|
||||
if form.validate(newstyle=True):
|
||||
workflow_key = form.validated['workflow']
|
||||
url = self.request.route_url('{}.create_type'.format(route_prefix),
|
||||
workflow_key=workflow_key)
|
||||
raise self.redirect(url)
|
||||
|
||||
context['form'] = form
|
||||
if hasattr(form, 'make_deform_form'):
|
||||
context['dform'] = form.make_deform_form()
|
||||
return self.render_to_response('create', context)
|
||||
|
||||
def row_deletable(self, row):
|
||||
batch = row.batch
|
||||
|
||||
|
@ -243,18 +317,25 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
super(ReceivingBatchView, self).configure_form(f)
|
||||
batch = f.model_instance
|
||||
allow_truck_dump = self.handler.allow_truck_dump_receiving()
|
||||
workflow = self.request.matchdict.get('workflow_key')
|
||||
route_prefix = self.get_route_prefix()
|
||||
|
||||
# cancel should take us back to choosing a workflow, when creating
|
||||
if self.creating and workflow:
|
||||
f.cancel_url = self.request.route_url('{}.create'.format(route_prefix))
|
||||
|
||||
# receiving_workflow
|
||||
if self.creating and workflow:
|
||||
f.set_readonly('receiving_workflow')
|
||||
f.set_renderer('receiving_workflow', self.render_receiving_workflow)
|
||||
else:
|
||||
f.remove('receiving_workflow')
|
||||
|
||||
# batch_type
|
||||
if self.creating:
|
||||
batch_types = OrderedDict()
|
||||
if self.handler.allow_receiving_from_scratch():
|
||||
batch_types['from_scratch'] = "From Scratch"
|
||||
if self.handler.allow_receiving_from_purchase_order():
|
||||
batch_types['from_po'] = "From PO"
|
||||
if allow_truck_dump:
|
||||
batch_types['truck_dump_children_first'] = "Truck Dump (children FIRST)"
|
||||
batch_types['truck_dump_children_last'] = "Truck Dump (children LAST)"
|
||||
f.set_enum('batch_type', batch_types)
|
||||
f.set_widget('batch_type', dfwidget.HiddenWidget())
|
||||
f.set_default('batch_type', workflow)
|
||||
f.set_hidden('batch_type')
|
||||
else:
|
||||
f.remove_field('batch_type')
|
||||
|
||||
|
@ -361,6 +442,44 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
# invoice totals
|
||||
f.set_label('invoice_total', "Invoice Total (Orig.)")
|
||||
f.set_label('invoice_total_calculated', "Invoice Total (Calc.)")
|
||||
if self.creating:
|
||||
f.remove('invoice_total_calculated')
|
||||
|
||||
# receiving_complete
|
||||
if self.creating:
|
||||
f.remove('receiving_complete')
|
||||
|
||||
# now that all fields are setup, we get rid of some, based on workflow
|
||||
if self.creating:
|
||||
|
||||
if workflow == 'from_scratch':
|
||||
f.remove('truck_dump_batch_uuid',
|
||||
'invoice_file',
|
||||
'invoice_parser_key')
|
||||
|
||||
elif workflow == 'truck_dump_children_first':
|
||||
f.remove('truck_dump_batch_uuid',
|
||||
'invoice_file',
|
||||
'invoice_parser_key',
|
||||
'date_ordered',
|
||||
'po_number',
|
||||
'invoice_date',
|
||||
'invoice_number')
|
||||
|
||||
elif workflow == 'truck_dump_children_last':
|
||||
f.remove('truck_dump_batch_uuid',
|
||||
'invoice_file',
|
||||
'invoice_parser_key',
|
||||
'date_ordered',
|
||||
'po_number',
|
||||
'invoice_date',
|
||||
'invoice_number')
|
||||
|
||||
def render_receiving_workflow(self, batch, field):
|
||||
key = self.request.matchdict['workflow_key']
|
||||
info = self.handler.receiving_workflow_info(key)
|
||||
if info:
|
||||
return info['display']
|
||||
|
||||
def template_kwargs_create(self, **kwargs):
|
||||
kwargs = super(ReceivingBatchView, self).template_kwargs_create(**kwargs)
|
||||
|
@ -488,6 +607,9 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
|
||||
self.configure_form(f)
|
||||
|
||||
# cancel should go back to truck dump parent
|
||||
f.cancel_url = self.get_action_url('view', truck_dump)
|
||||
|
||||
f.set_fields([
|
||||
'batch_type',
|
||||
'truck_dump_parent',
|
||||
|
@ -502,6 +624,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
# batch_type
|
||||
f.set_widget('batch_type', forms.widgets.ReadonlyWidget())
|
||||
f.set_default('batch_type', 'truck_dump_child_from_invoice')
|
||||
f.set_hidden('batch_type', False)
|
||||
|
||||
# truck_dump_batch_uuid
|
||||
f.set_readonly('truck_dump_parent')
|
||||
|
@ -1272,6 +1395,13 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
progress.session['success_url'] = success_url
|
||||
progress.session.save()
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._receiving_defaults(config)
|
||||
cls._purchasing_defaults(config)
|
||||
cls._batch_defaults(config)
|
||||
cls._defaults(config)
|
||||
|
||||
@classmethod
|
||||
def _receiving_defaults(cls, config):
|
||||
rattail_config = config.registry.settings.get('rattail_config')
|
||||
|
@ -1281,13 +1411,18 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
model_key = cls.get_model_key()
|
||||
permission_prefix = cls.get_permission_prefix()
|
||||
|
||||
# new receiving batch using workflow X
|
||||
config.add_route('{}.create_type'.format(route_prefix), '{}/new/{{workflow_key}}'.format(url_prefix))
|
||||
config.add_view(cls, attr='create', route_name='{}.create_type'.format(route_prefix),
|
||||
permission='{}.create'.format(permission_prefix))
|
||||
|
||||
# row-level receiving
|
||||
config.add_route('{}.receive_row'.format(route_prefix), '{}/{{uuid}}/rows/{{row_uuid}}/receive'.format(url_prefix))
|
||||
config.add_route('{}.receive_row'.format(route_prefix), '{}/rows/{{row_uuid}}/receive'.format(instance_url_prefix))
|
||||
config.add_view(cls, attr='receive_row', route_name='{}.receive_row'.format(route_prefix),
|
||||
permission='{}.edit_row'.format(permission_prefix))
|
||||
|
||||
# declare credit for row
|
||||
config.add_route('{}.declare_credit'.format(route_prefix), '{}/{{uuid}}/rows/{{row_uuid}}/declare-credit'.format(url_prefix))
|
||||
config.add_route('{}.declare_credit'.format(route_prefix), '{}/rows/{{row_uuid}}/declare-credit'.format(instance_url_prefix))
|
||||
config.add_view(cls, attr='declare_credit', route_name='{}.declare_credit'.format(route_prefix),
|
||||
permission='{}.edit_row'.format(permission_prefix))
|
||||
|
||||
|
@ -1298,29 +1433,45 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
renderer='json')
|
||||
|
||||
# add TD child batch, from invoice file
|
||||
config.add_route('{}.add_child_from_invoice'.format(route_prefix), '{}/{{{}}}/add-child-from-invoice'.format(url_prefix, model_key))
|
||||
config.add_route('{}.add_child_from_invoice'.format(route_prefix), '{}/add-child-from-invoice'.format(instance_url_prefix))
|
||||
config.add_view(cls, attr='add_child_from_invoice', route_name='{}.add_child_from_invoice'.format(route_prefix),
|
||||
permission='{}.create'.format(permission_prefix))
|
||||
|
||||
# transform TD parent row from "pack" to "unit" item
|
||||
config.add_route('{}.transform_unit_row'.format(route_prefix), '{}/{{{}}}/transform-unit'.format(url_prefix, model_key))
|
||||
config.add_route('{}.transform_unit_row'.format(route_prefix), '{}/transform-unit'.format(instance_url_prefix))
|
||||
config.add_view(cls, attr='transform_unit_row', route_name='{}.transform_unit_row'.format(route_prefix),
|
||||
permission='{}.edit_row'.format(permission_prefix), renderer='json')
|
||||
|
||||
# auto-receive all items
|
||||
if not rattail_config.production():
|
||||
config.add_route('{}.auto_receive'.format(route_prefix), '{}/{{{}}}/auto-receive'.format(url_prefix, model_key),
|
||||
config.add_route('{}.auto_receive'.format(route_prefix), '{}/auto-receive'.format(instance_url_prefix),
|
||||
request_method='POST')
|
||||
config.add_view(cls, attr='auto_receive', route_name='{}.auto_receive'.format(route_prefix),
|
||||
permission='admin')
|
||||
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._receiving_defaults(config)
|
||||
cls._purchasing_defaults(config)
|
||||
cls._batch_defaults(config)
|
||||
cls._defaults(config)
|
||||
@colander.deferred
|
||||
def valid_workflow(node, kw):
|
||||
"""
|
||||
Deferred validator for ``workflow`` field, for new batches.
|
||||
"""
|
||||
valid_workflows = kw['valid_workflows']
|
||||
|
||||
def validate(node, value):
|
||||
# we just need to provide possible values, and let stock validator
|
||||
# handle the rest
|
||||
oneof = colander.OneOf(valid_workflows)
|
||||
return oneof(node, value)
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
class NewBatchType(colander.Schema):
|
||||
"""
|
||||
Schema for choosing which "type" of new receiving batch should be created.
|
||||
"""
|
||||
workflow = colander.SchemaNode(colander.String(),
|
||||
validator=valid_workflow)
|
||||
|
||||
|
||||
class ReceiveRowForm(colander.MappingSchema):
|
||||
|
|
Loading…
Reference in a new issue