Refactor batch views / templates per rattail framework overhaul
This commit is contained in:
parent
a5184e416a
commit
203f0242fb
|
@ -50,11 +50,12 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="form-wrapper">
|
<div class="form-wrapper">
|
||||||
% if master.edit_with_rows:
|
## TODO: clean this up or fix etc..?
|
||||||
${form.render(buttons=capture(buttons))|n}
|
## % if master.edit_with_rows:
|
||||||
% else:
|
## ${form.render(buttons=capture(buttons))|n}
|
||||||
|
## % else:
|
||||||
${form.render()|n}
|
${form.render()|n}
|
||||||
% endif
|
## % endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
% if master.edit_with_rows:
|
% if master.edit_with_rows:
|
||||||
|
|
|
@ -43,19 +43,11 @@
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="leading_buttons()">
|
<%def name="leading_buttons()"></%def>
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="refresh_button()">
|
<%def name="refresh_button()">
|
||||||
## TODO: the refreshable thing still seems confusing...
|
% if master.viewing and master.batch_refreshable(batch):
|
||||||
% if master.refreshable:
|
<button type="button" id="refresh-data">Refresh data</button>
|
||||||
% if form.readonly:
|
|
||||||
% if not batch.executed:
|
|
||||||
<button type="button" id="refresh-data">Refresh Data</button>
|
|
||||||
% endif
|
|
||||||
% elif batch.refreshable:
|
|
||||||
${h.submit('save-refresh', "Save & Refresh Data")}
|
|
||||||
% endif
|
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -75,12 +67,14 @@
|
||||||
|
|
||||||
${rows_grid|n}
|
${rows_grid|n}
|
||||||
|
|
||||||
<div id="execution-options-dialog" style="display: none;">
|
% if not batch.executed:
|
||||||
|
<div id="execution-options-dialog" style="display: none;">
|
||||||
|
|
||||||
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}
|
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}
|
||||||
% if master.has_execution_options:
|
% if master.has_execution_options:
|
||||||
${rendered_execution_options|n}
|
${rendered_execution_options|n}
|
||||||
% endif
|
% endif
|
||||||
${h.end_form()}
|
${h.end_form()}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
% endif
|
||||||
|
|
|
@ -63,12 +63,13 @@ class BatchMasterView(MasterView):
|
||||||
"""
|
"""
|
||||||
Base class for all "batch master" views.
|
Base class for all "batch master" views.
|
||||||
"""
|
"""
|
||||||
|
default_handler_spec = None
|
||||||
has_rows = True
|
has_rows = True
|
||||||
rows_deletable = True
|
rows_deletable = True
|
||||||
rows_downloadable = True
|
rows_downloadable = True
|
||||||
refreshable = True
|
refreshable = True
|
||||||
refresh_after_create = False
|
refresh_after_create = False
|
||||||
edit_with_rows = True
|
edit_with_rows = False
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
super(BatchMasterView, self).__init__(request)
|
super(BatchMasterView, self).__init__(request)
|
||||||
|
@ -92,7 +93,8 @@ class BatchMasterView(MasterView):
|
||||||
``batch_key`` attribute of the main batch model class.
|
``batch_key`` attribute of the main batch model class.
|
||||||
"""
|
"""
|
||||||
key = self.model_class.batch_key
|
key = self.model_class.batch_key
|
||||||
spec = self.rattail_config.get('rattail.batch', '{}.handler'.format(key))
|
spec = self.rattail_config.get('rattail.batch', '{}.handler'.format(key),
|
||||||
|
default=self.default_handler_spec)
|
||||||
if spec:
|
if spec:
|
||||||
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)
|
||||||
|
@ -233,41 +235,34 @@ class BatchMasterView(MasterView):
|
||||||
delattr(fs, field)
|
delattr(fs, field)
|
||||||
|
|
||||||
def save_create_form(self, form):
|
def save_create_form(self, form):
|
||||||
"""
|
|
||||||
Save the uploaded data file if necessary, etc. If batch initialization
|
|
||||||
fails, don't persist the batch at all; the user will be sent back to
|
|
||||||
the "create batch" page in that case.
|
|
||||||
"""
|
|
||||||
self.before_create(form)
|
self.before_create(form)
|
||||||
|
|
||||||
# Transfer form data to batch instance.
|
# transfer form data to batch instance
|
||||||
form.fieldset.sync()
|
form.fieldset.sync()
|
||||||
batch = form.fieldset.model
|
batch = form.fieldset.model
|
||||||
|
|
||||||
# Assign current user as creator.
|
# current user is batch creator
|
||||||
with Session.no_autoflush:
|
batch.created_by = self.request.user or self.late_login_user()
|
||||||
batch.created_by = self.request.user or self.late_login_user()
|
|
||||||
|
|
||||||
# TODO: Wouldn't this be handled sufficiently by `no_autoflush` ?
|
# destroy initial batch and re-make using handler
|
||||||
# Expunge batch from session to prevent it from being flushed
|
kwargs = self.get_batch_kwargs(batch)
|
||||||
# during init. This is done as a convenience to views which
|
|
||||||
# provide an init method. Some batches may have required fields
|
|
||||||
# which aren't filled in yet, but the view may need to query the
|
|
||||||
# database to obtain the values. This will cause a session flush,
|
|
||||||
# and the missing fields will trigger data integrity errors.
|
|
||||||
Session.expunge(batch)
|
Session.expunge(batch)
|
||||||
|
# TODO: is no_autoflush necessary?
|
||||||
|
with Session.no_autoflush:
|
||||||
|
batch = self.handler.make_batch(Session(), **kwargs)
|
||||||
|
|
||||||
self.batch_inited = self.init_batch(batch)
|
Session.flush()
|
||||||
if self.batch_inited:
|
|
||||||
Session.add(batch)
|
|
||||||
Session.flush()
|
|
||||||
|
|
||||||
else: # batch init failed
|
# TODO: this needs work yet surely...
|
||||||
|
# if batch has input data file, let handler properly establish that
|
||||||
|
filename = getattr(batch, 'filename', None)
|
||||||
|
if filename:
|
||||||
|
path = os.path.join(self.upload_dir, filename)
|
||||||
|
self.handler.set_input_file(batch, path)
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
# Here we assume that the :meth:`init_batch()` method responsible
|
# return this object to replace the original
|
||||||
# for indicating the failure will have set a flash message for the
|
return batch
|
||||||
# user with more info.
|
|
||||||
raise self.redirect(self.request.current_route_url())
|
|
||||||
|
|
||||||
def init_batch(self, batch):
|
def init_batch(self, batch):
|
||||||
"""
|
"""
|
||||||
|
@ -283,9 +278,12 @@ class BatchMasterView(MasterView):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def redirect_after_create(self, batch):
|
def redirect_after_create(self, batch):
|
||||||
if self.refresh_after_create:
|
if self.handler.requires_prefill(batch):
|
||||||
|
return self.redirect(self.get_action_url('prefill', batch))
|
||||||
|
elif self.refresh_after_create:
|
||||||
return self.redirect(self.get_action_url('refresh', batch))
|
return self.redirect(self.get_action_url('refresh', batch))
|
||||||
return super(BatchMasterView, self).redirect_after_create(batch)
|
else:
|
||||||
|
return self.redirect(self.get_action_url('view', batch))
|
||||||
|
|
||||||
# TODO: some of this at least can go to master now right?
|
# TODO: some of this at least can go to master now right?
|
||||||
def edit(self):
|
def edit(self):
|
||||||
|
@ -398,6 +396,22 @@ class BatchMasterView(MasterView):
|
||||||
def executable(self, batch):
|
def executable(self, batch):
|
||||||
return self.handler.executable(batch)
|
return self.handler.executable(batch)
|
||||||
|
|
||||||
|
def batch_refreshable(self, batch):
|
||||||
|
"""
|
||||||
|
Return a boolean indicating whether the given batch should allow a
|
||||||
|
refresh operation.
|
||||||
|
"""
|
||||||
|
# TODO: deprecate/remove this?
|
||||||
|
if not self.refreshable:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# (this is how it should be done i think..)
|
||||||
|
if callable(self.handler.refreshable):
|
||||||
|
return self.handler.refreshable(batch)
|
||||||
|
|
||||||
|
# TODO: deprecate/remove this
|
||||||
|
return self.handler.refreshable and not batch.executed
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_execution_options(self):
|
def has_execution_options(self):
|
||||||
return bool(self.execution_options_schema)
|
return bool(self.execution_options_schema)
|
||||||
|
@ -419,7 +433,68 @@ class BatchMasterView(MasterView):
|
||||||
defaults=defaults or None)
|
defaults=defaults or None)
|
||||||
|
|
||||||
def get_execute_title(self, batch):
|
def get_execute_title(self, batch):
|
||||||
return self.handler.get_execute_title(batch)
|
if hasattr(self.handler, 'get_execute_title'):
|
||||||
|
return self.handler.get_execute_title(batch)
|
||||||
|
return "Execute this batch"
|
||||||
|
|
||||||
|
def prefill(self):
|
||||||
|
"""
|
||||||
|
View which will attempt to prefill all data for the batch. What
|
||||||
|
exactly this means will depend on the type of batch etc.
|
||||||
|
"""
|
||||||
|
batch = self.get_instance()
|
||||||
|
route_prefix = self.get_route_prefix()
|
||||||
|
permission_prefix = self.get_permission_prefix()
|
||||||
|
|
||||||
|
# showing progress requires a separate thread; start that first
|
||||||
|
progress_key = '{}.prefill'.format(route_prefix)
|
||||||
|
progress = SessionProgress(self.request, progress_key)
|
||||||
|
thread = Thread(target=self.prefill_thread, args=(batch.uuid, progress))
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
# Send user to progress page.
|
||||||
|
kwargs = {
|
||||||
|
'key': progress_key,
|
||||||
|
'cancel_url': self.get_action_url('view', batch),
|
||||||
|
'cancel_msg': "Batch prefill was canceled.",
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: This seems hacky...it exists for (only) one specific scenario.
|
||||||
|
if not self.request.has_perm('{}.view'.format(permission_prefix)):
|
||||||
|
kwargs['cancel_url'] = self.request.route_url('{}.create'.format(route_prefix))
|
||||||
|
|
||||||
|
return self.render_progress(kwargs)
|
||||||
|
|
||||||
|
def prefill_thread(self, batch_uuid, progress):
|
||||||
|
"""
|
||||||
|
Thread target for prefilling batch data with progress indicator.
|
||||||
|
"""
|
||||||
|
# mustn't use tailbone web session here
|
||||||
|
session = RattailSession()
|
||||||
|
batch = session.query(self.model_class).get(batch_uuid)
|
||||||
|
try:
|
||||||
|
self.handler.make_initial_rows(batch, progress=progress)
|
||||||
|
except Exception as error:
|
||||||
|
session.rollback()
|
||||||
|
log.warning("batch pre-fill failed: {}".format(batch), exc_info=True)
|
||||||
|
session.close()
|
||||||
|
if progress:
|
||||||
|
progress.session.load()
|
||||||
|
progress.session['error'] = True
|
||||||
|
progress.session['error_msg'] = "Batch pre-fill failed: {} {}".format(error.__class__.__name__, error)
|
||||||
|
progress.session.save()
|
||||||
|
return
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
session.refresh(batch)
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
# finalize progress
|
||||||
|
if progress:
|
||||||
|
progress.session.load()
|
||||||
|
progress.session['complete'] = True
|
||||||
|
progress.session['success_url'] = self.get_action_url('view', batch)
|
||||||
|
progress.session.save()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
"""
|
"""
|
||||||
|
@ -430,14 +505,16 @@ class BatchMasterView(MasterView):
|
||||||
route_prefix = self.get_route_prefix()
|
route_prefix = self.get_route_prefix()
|
||||||
permission_prefix = self.get_permission_prefix()
|
permission_prefix = self.get_permission_prefix()
|
||||||
|
|
||||||
|
# TODO: deprecate / remove this
|
||||||
cognizer = self.request.user
|
cognizer = self.request.user
|
||||||
if not cognizer:
|
if not cognizer:
|
||||||
uuid = self.request.session.pop('late_login_user', None)
|
uuid = self.request.session.pop('late_login_user', None)
|
||||||
cognizer = Session.query(model.User).get(uuid) if uuid else None
|
cognizer = Session.query(model.User).get(uuid) if uuid else None
|
||||||
|
|
||||||
|
# TODO: refresh should probably always imply/use progress
|
||||||
# If handler doesn't declare the need for progress indicator, things
|
# If handler doesn't declare the need for progress indicator, things
|
||||||
# are nice and simple.
|
# are nice and simple.
|
||||||
if not self.handler.show_progress:
|
if not getattr(self.handler, 'show_progress', True):
|
||||||
self.refresh_data(Session, batch, cognizer=cognizer)
|
self.refresh_data(Session, batch, cognizer=cognizer)
|
||||||
self.request.session.flash("Batch data has been refreshed.")
|
self.request.session.flash("Batch data has been refreshed.")
|
||||||
|
|
||||||
|
@ -479,9 +556,14 @@ class BatchMasterView(MasterView):
|
||||||
"""
|
"""
|
||||||
Instruct the batch handler to refresh all data for the batch.
|
Instruct the batch handler to refresh all data for the batch.
|
||||||
"""
|
"""
|
||||||
self.handler.refresh_data(session, batch, progress=progress)
|
# TODO: deprecate/remove this
|
||||||
batch.cognized = datetime.datetime.utcnow()
|
if hasattr(self.handler, 'refresh_data'):
|
||||||
batch.cognized_by = cognizer or session.merge(self.request.user)
|
self.handler.refresh_data(session, batch, progress=progress)
|
||||||
|
batch.cognized = datetime.datetime.utcnow()
|
||||||
|
batch.cognized_by = cognizer or session.merge(self.request.user)
|
||||||
|
|
||||||
|
else: # the future
|
||||||
|
self.handler.refresh(batch, progress=progress)
|
||||||
|
|
||||||
def refresh_thread(self, batch_uuid, progress=None, cognizer_uuid=None, success_url=None):
|
def refresh_thread(self, batch_uuid, progress=None, cognizer_uuid=None, success_url=None):
|
||||||
"""
|
"""
|
||||||
|
@ -744,6 +826,11 @@ class BatchMasterView(MasterView):
|
||||||
# else the perm group label will not display correctly...
|
# else the perm group label will not display correctly...
|
||||||
config.add_tailbone_permission_group(permission_prefix, model_title_plural, overwrite=False)
|
config.add_tailbone_permission_group(permission_prefix, model_title_plural, overwrite=False)
|
||||||
|
|
||||||
|
# prefill row data
|
||||||
|
config.add_route('{}.prefill'.format(route_prefix), '{}/{{uuid}}/prefill'.format(url_prefix))
|
||||||
|
config.add_view(cls, attr='prefill', route_name='{}.prefill'.format(route_prefix),
|
||||||
|
permission='{}.create'.format(permission_prefix))
|
||||||
|
|
||||||
# refresh rows data
|
# refresh rows data
|
||||||
config.add_route('{}.refresh'.format(route_prefix), '{}/{{uuid}}/refresh'.format(url_prefix))
|
config.add_route('{}.refresh'.format(route_prefix), '{}/{{uuid}}/refresh'.format(url_prefix))
|
||||||
config.add_view(cls, attr='refresh', route_name='{}.refresh'.format(route_prefix),
|
config.add_view(cls, attr='refresh', route_name='{}.refresh'.format(route_prefix),
|
||||||
|
@ -835,46 +922,15 @@ class FileBatchMasterView(BatchMasterView):
|
||||||
fs.filename,
|
fs.filename,
|
||||||
])
|
])
|
||||||
|
|
||||||
def save_create_form(self, form):
|
def get_batch_kwargs(self, batch):
|
||||||
self.before_create(form)
|
"""
|
||||||
|
Return a kwargs dict for use with ``self.handler.make_batch()``, using
|
||||||
# Transfer form data to batch instance.
|
the given batch as a template.
|
||||||
form.fieldset.sync()
|
"""
|
||||||
batch = form.fieldset.model
|
kwargs = {'created_by': batch.created_by}
|
||||||
|
if hasattr(batch, 'filename'):
|
||||||
# Assign current user as creator.
|
kwargs['filename'] = batch.filename
|
||||||
with Session.no_autoflush:
|
return kwargs
|
||||||
batch.created_by = self.request.user or self.late_login_user()
|
|
||||||
|
|
||||||
# TODO: Wouldn't this be handled sufficiently by `no_autoflush` ?
|
|
||||||
# Expunge batch from session to prevent it from being flushed
|
|
||||||
# during init. This is done as a convenience to views which
|
|
||||||
# provide an init method. Some batches may have required fields
|
|
||||||
# which aren't filled in yet, but the view may need to query the
|
|
||||||
# database to obtain the values. This will cause a session flush,
|
|
||||||
# and the missing fields will trigger data integrity errors.
|
|
||||||
Session.expunge(batch)
|
|
||||||
|
|
||||||
self.batch_inited = self.init_batch(batch)
|
|
||||||
|
|
||||||
if self.batch_inited:
|
|
||||||
Session.add(batch)
|
|
||||||
Session.flush()
|
|
||||||
|
|
||||||
# Handler saves a copy of the file and updates the batch filename.
|
|
||||||
path = os.path.join(self.upload_dir, batch.filename)
|
|
||||||
self.handler.set_data_file(batch, path)
|
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
else: # batch init failed
|
|
||||||
|
|
||||||
# Here we assume that the :meth:`init_batch()` method responsible
|
|
||||||
# for indicating the failure will have set a flash message for the
|
|
||||||
# user with more info.
|
|
||||||
raise self.redirect(self.request.current_route_url())
|
|
||||||
|
|
||||||
def redirect_after_create(self, batch):
|
|
||||||
return self.redirect(self.get_action_url('refresh', batch))
|
|
||||||
|
|
||||||
def download(self):
|
def download(self):
|
||||||
"""
|
"""
|
||||||
|
@ -902,6 +958,10 @@ class FileBatchMasterView(BatchMasterView):
|
||||||
url_prefix = cls.get_url_prefix()
|
url_prefix = cls.get_url_prefix()
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
model_title = cls.get_model_title()
|
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
|
# download batch data file
|
||||||
config.add_route('{}.download'.format(route_prefix), '{}/{{uuid}}/download'.format(url_prefix))
|
config.add_route('{}.download'.format(route_prefix), '{}/{{uuid}}/download'.format(url_prefix))
|
||||||
|
|
|
@ -41,6 +41,7 @@ class View(object):
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
self.enum = self.rattail_config.get_enum()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rattail_config(self):
|
def rattail_config(self):
|
||||||
|
|
|
@ -26,9 +26,10 @@ Views for handheld batches
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from rattail import enum
|
from rattail import enum
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.db.batch.handheld.handler import HandheldBatchHandler
|
|
||||||
from rattail.util import OrderedDict
|
from rattail.util import OrderedDict
|
||||||
|
|
||||||
import formalchemy as fa
|
import formalchemy as fa
|
||||||
|
@ -36,6 +37,7 @@ import formencode as fe
|
||||||
from webhelpers.html import tags
|
from webhelpers.html import tags
|
||||||
|
|
||||||
from tailbone import forms
|
from tailbone import forms
|
||||||
|
from tailbone.db import Session
|
||||||
from tailbone.views.batch import FileBatchMasterView
|
from tailbone.views.batch import FileBatchMasterView
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,47 +73,63 @@ class HandheldBatchView(FileBatchMasterView):
|
||||||
Master view for handheld batches.
|
Master view for handheld batches.
|
||||||
"""
|
"""
|
||||||
model_class = model.HandheldBatch
|
model_class = model.HandheldBatch
|
||||||
|
default_handler_spec = 'rattail.batch.handheld:HandheldBatchHandler'
|
||||||
model_title_plural = "Handheld Batches"
|
model_title_plural = "Handheld Batches"
|
||||||
batch_handler_class = HandheldBatchHandler
|
|
||||||
route_prefix = 'batch.handheld'
|
route_prefix = 'batch.handheld'
|
||||||
url_prefix = '/batch/handheld'
|
url_prefix = '/batch/handheld'
|
||||||
execution_options_schema = ExecutionOptions
|
execution_options_schema = ExecutionOptions
|
||||||
editable = False
|
editable = False
|
||||||
refreshable = False
|
|
||||||
|
|
||||||
model_row_class = model.HandheldBatchRow
|
model_row_class = model.HandheldBatchRow
|
||||||
rows_creatable = False
|
rows_creatable = False
|
||||||
rows_editable = True
|
rows_editable = True
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
|
enum = self.rattail_config.get_enum()
|
||||||
g.configure(
|
g.configure(
|
||||||
include=[
|
include=[
|
||||||
g.id,
|
g.id,
|
||||||
|
g.device_type.with_renderer(forms.renderers.EnumFieldRenderer(enum.HANDHELD_DEVICE_TYPE)),
|
||||||
|
g.device_name,
|
||||||
g.created,
|
g.created,
|
||||||
g.created_by,
|
g.created_by,
|
||||||
g.device_name,
|
|
||||||
g.executed,
|
g.executed,
|
||||||
g.executed_by,
|
|
||||||
],
|
],
|
||||||
readonly=True)
|
readonly=True)
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
fs.configure(
|
fs.device_type.set(renderer=forms.renderers.EnumFieldRenderer(enum.HANDHELD_DEVICE_TYPE))
|
||||||
include=[
|
|
||||||
fs.id,
|
|
||||||
fs.created,
|
|
||||||
fs.created_by,
|
|
||||||
fs.filename,
|
|
||||||
fs.device_type.with_renderer(forms.renderers.EnumFieldRenderer(enum.HANDHELD_DEVICE_TYPE)),
|
|
||||||
fs.device_name,
|
|
||||||
fs.executed,
|
|
||||||
fs.executed_by,
|
|
||||||
])
|
|
||||||
if self.creating:
|
if self.creating:
|
||||||
del fs.id
|
fs.configure(
|
||||||
elif self.viewing and fs.model.inventory_batch:
|
include=[
|
||||||
|
fs.filename,
|
||||||
|
fs.device_type,
|
||||||
|
fs.device_name,
|
||||||
|
])
|
||||||
|
|
||||||
|
else:
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.id,
|
||||||
|
fs.device_type,
|
||||||
|
fs.device_name,
|
||||||
|
fs.filename,
|
||||||
|
fs.created,
|
||||||
|
fs.created_by,
|
||||||
|
fs.executed,
|
||||||
|
fs.executed_by,
|
||||||
|
])
|
||||||
|
|
||||||
|
if self.viewing and fs.model.inventory_batch:
|
||||||
fs.append(fa.Field('inventory_batch', value=fs.model.inventory_batch, renderer=InventoryBatchFieldRenderer))
|
fs.append(fa.Field('inventory_batch', value=fs.model.inventory_batch, renderer=InventoryBatchFieldRenderer))
|
||||||
|
|
||||||
|
def get_batch_kwargs(self, batch):
|
||||||
|
kwargs = super(HandheldBatchView, self).get_batch_kwargs(batch)
|
||||||
|
kwargs['device_type'] = batch.device_type
|
||||||
|
kwargs['device_name'] = batch.device_name
|
||||||
|
return kwargs
|
||||||
|
|
||||||
def configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
g.configure(
|
g.configure(
|
||||||
include=[
|
include=[
|
||||||
|
@ -167,16 +185,6 @@ class HandheldBatchView(FileBatchMasterView):
|
||||||
return self.request.route_url('labels.batch.view', uuid=result.uuid)
|
return self.request.route_url('labels.batch.view', uuid=result.uuid)
|
||||||
return super(HandheldBatchView, self).get_execute_success_url(batch)
|
return super(HandheldBatchView, self).get_execute_success_url(batch)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def defaults(cls, config):
|
|
||||||
|
|
||||||
# fix permission group title
|
|
||||||
config.add_tailbone_permission_group('batch.handheld', "Handheld Batches")
|
|
||||||
|
|
||||||
cls._filebatch_defaults(config)
|
|
||||||
cls._batch_defaults(config)
|
|
||||||
cls._defaults(config)
|
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
HandheldBatchView.defaults(config)
|
HandheldBatchView.defaults(config)
|
||||||
|
|
|
@ -26,9 +26,7 @@ Views for inventory batches
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from rattail import enum
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.db.batch.inventory.handler import InventoryBatchHandler
|
|
||||||
|
|
||||||
from tailbone import forms
|
from tailbone import forms
|
||||||
from tailbone.views.batch import BatchMasterView
|
from tailbone.views.batch import BatchMasterView
|
||||||
|
@ -40,19 +38,18 @@ class InventoryBatchView(BatchMasterView):
|
||||||
"""
|
"""
|
||||||
model_class = model.InventoryBatch
|
model_class = model.InventoryBatch
|
||||||
model_title_plural = "Inventory Batches"
|
model_title_plural = "Inventory Batches"
|
||||||
batch_handler_class = InventoryBatchHandler
|
default_handler_spec = 'rattail.batch.inventory:InventoryBatchHandler'
|
||||||
route_prefix = 'batch.inventory'
|
route_prefix = 'batch.inventory'
|
||||||
url_prefix = '/batch/inventory'
|
url_prefix = '/batch/inventory'
|
||||||
creatable = False
|
creatable = False
|
||||||
editable = False
|
editable = False
|
||||||
refreshable = False
|
|
||||||
|
|
||||||
model_row_class = model.InventoryBatchRow
|
model_row_class = model.InventoryBatchRow
|
||||||
rows_editable = True
|
rows_editable = True
|
||||||
|
|
||||||
def _preconfigure_grid(self, g):
|
def _preconfigure_grid(self, g):
|
||||||
super(InventoryBatchView, self)._preconfigure_grid(g)
|
super(InventoryBatchView, self)._preconfigure_grid(g)
|
||||||
g.mode.set(renderer=forms.renderers.EnumFieldRenderer(enum.INVENTORY_MODE),
|
g.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE),
|
||||||
label="Count Mode")
|
label="Count Mode")
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
|
@ -62,7 +59,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
def _preconfigure_fieldset(self, fs):
|
def _preconfigure_fieldset(self, fs):
|
||||||
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
||||||
fs.handheld_batch.set(renderer=forms.renderers.HandheldBatchFieldRenderer, readonly=True)
|
fs.handheld_batch.set(renderer=forms.renderers.HandheldBatchFieldRenderer, readonly=True)
|
||||||
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(enum.INVENTORY_MODE),
|
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE),
|
||||||
label="Count Mode")
|
label="Count Mode")
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
|
@ -124,15 +121,6 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.units,
|
fs.units,
|
||||||
])
|
])
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def defaults(cls, config):
|
|
||||||
|
|
||||||
# fix permission group title
|
|
||||||
config.add_tailbone_permission_group('batch.inventory', "Inventory Batches")
|
|
||||||
|
|
||||||
cls._batch_defaults(config)
|
|
||||||
cls._defaults(config)
|
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
InventoryBatchView.defaults(config)
|
InventoryBatchView.defaults(config)
|
||||||
|
|
|
@ -27,7 +27,6 @@ Views for label batches
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.db.batch.labels.handler import LabelBatchHandler
|
|
||||||
|
|
||||||
from tailbone import forms
|
from tailbone import forms
|
||||||
from tailbone.views.batch import BatchMasterView
|
from tailbone.views.batch import BatchMasterView
|
||||||
|
@ -39,7 +38,7 @@ class LabelBatchView(BatchMasterView):
|
||||||
"""
|
"""
|
||||||
model_class = model.LabelBatch
|
model_class = model.LabelBatch
|
||||||
model_row_class = model.LabelBatchRow
|
model_row_class = model.LabelBatchRow
|
||||||
batch_handler_class = LabelBatchHandler
|
default_handler_spec = 'rattail.batch.labels:LabelBatchHandler'
|
||||||
model_title_plural = "Label Batches"
|
model_title_plural = "Label Batches"
|
||||||
route_prefix = 'labels.batch'
|
route_prefix = 'labels.batch'
|
||||||
url_prefix = '/labels/batches'
|
url_prefix = '/labels/batches'
|
||||||
|
@ -61,6 +60,9 @@ class LabelBatchView(BatchMasterView):
|
||||||
fs.executed,
|
fs.executed,
|
||||||
fs.executed_by,
|
fs.executed_by,
|
||||||
])
|
])
|
||||||
|
batch = fs.model
|
||||||
|
if self.viewing and not batch.handheld_batch:
|
||||||
|
del fs.handheld_batch
|
||||||
|
|
||||||
def _preconfigure_row_grid(self, g):
|
def _preconfigure_row_grid(self, g):
|
||||||
super(LabelBatchView, self)._preconfigure_row_grid(g)
|
super(LabelBatchView, self)._preconfigure_row_grid(g)
|
||||||
|
|
|
@ -128,12 +128,12 @@ class MasterView(View):
|
||||||
form = self.make_form(self.get_model_class())
|
form = self.make_form(self.get_model_class())
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
if form.validate():
|
if form.validate():
|
||||||
self.save_create_form(form)
|
# let save_create_form() return alternate object if necessary
|
||||||
instance = form.fieldset.model
|
obj = self.save_create_form(form) or form.fieldset.model
|
||||||
self.after_create(instance)
|
self.after_create(obj)
|
||||||
self.request.session.flash("{} has been created: {}".format(
|
self.request.session.flash("{} has been created: {}".format(
|
||||||
self.get_model_title(), self.get_instance_title(instance)))
|
self.get_model_title(), self.get_instance_title(obj)))
|
||||||
return self.redirect_after_create(instance)
|
return self.redirect_after_create(obj)
|
||||||
return self.render_to_response('create', {'form': form})
|
return self.render_to_response('create', {'form': form})
|
||||||
|
|
||||||
def save_create_form(self, form):
|
def save_create_form(self, form):
|
||||||
|
|
|
@ -38,6 +38,7 @@ from rattail.gpc import GPC
|
||||||
from rattail.threads import Thread
|
from rattail.threads import Thread
|
||||||
from rattail.exceptions import LabelPrintingError
|
from rattail.exceptions import LabelPrintingError
|
||||||
from rattail.util import load_object
|
from rattail.util import load_object
|
||||||
|
from rattail.batch import get_batch_handler
|
||||||
|
|
||||||
import formalchemy as fa
|
import formalchemy as fa
|
||||||
from pyramid import httpexceptions
|
from pyramid import httpexceptions
|
||||||
|
@ -349,15 +350,14 @@ class ProductsView(MasterView):
|
||||||
# okay then, new-style it is
|
# okay then, new-style it is
|
||||||
# TODO: make this more configurable surely..?
|
# TODO: make this more configurable surely..?
|
||||||
supported = {
|
supported = {
|
||||||
'labels': 'rattail.db.batch.labels.handler:LabelBatchHandler',
|
'labels': 'rattail.batch.labels:LabelBatchHandler',
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
batch_key = self.request.POST.get('batch_type')
|
batch_key = self.request.POST.get('batch_type')
|
||||||
if batch_key and batch_key in supported:
|
if batch_key and batch_key in supported:
|
||||||
handler_spec = self.rattail_config.get('rattail.batch', '{}.handler'.format(batch_key),
|
handler = get_batch_handler(self.rattail_config, batch_key,
|
||||||
default=supported[batch_key])
|
default=supported[batch_key])
|
||||||
handler = load_object(handler_spec)(self.rattail_config)
|
|
||||||
|
|
||||||
progress = SessionProgress(self.request, 'products.batch')
|
progress = SessionProgress(self.request, 'products.batch')
|
||||||
thread = Thread(target=self.make_batch_thread,
|
thread = Thread(target=self.make_batch_thread,
|
||||||
|
@ -372,7 +372,7 @@ class ProductsView(MasterView):
|
||||||
batch_types = []
|
batch_types = []
|
||||||
for key, spec in supported.iteritems():
|
for key, spec in supported.iteritems():
|
||||||
handler = load_object(spec)(self.rattail_config)
|
handler = load_object(spec)(self.rattail_config)
|
||||||
batch_types.append((key, handler.model_title))
|
batch_types.append((key, handler.get_model_title()))
|
||||||
|
|
||||||
return {'supported_batches': batch_types}
|
return {'supported_batches': batch_types}
|
||||||
|
|
||||||
|
@ -384,11 +384,9 @@ class ProductsView(MasterView):
|
||||||
user = session.query(model.User).get(user_uuid)
|
user = session.query(model.User).get(user_uuid)
|
||||||
assert user
|
assert user
|
||||||
products = self.get_effective_query(session)
|
products = self.get_effective_query(session)
|
||||||
batch = handler.make_batch(session, created_by=user, products=products, progress=progress)
|
batch = handler.make_batch(session, created_by=user)
|
||||||
if not batch:
|
batch.products = products.all()
|
||||||
session.rollback()
|
handler.make_initial_rows(batch, progress=progress)
|
||||||
session.close()
|
|
||||||
return
|
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(batch)
|
session.refresh(batch)
|
||||||
|
|
|
@ -31,7 +31,6 @@ from sqlalchemy import orm
|
||||||
from rattail import enum
|
from rattail import enum
|
||||||
from rattail.db import model, api
|
from rattail.db import model, api
|
||||||
from rattail.gpc import GPC
|
from rattail.gpc import GPC
|
||||||
from rattail.db.batch.purchase.handler import PurchaseBatchHandler
|
|
||||||
from rattail.time import localtime
|
from rattail.time import localtime
|
||||||
from rattail.core import Object
|
from rattail.core import Object
|
||||||
from rattail.util import OrderedDict
|
from rattail.util import OrderedDict
|
||||||
|
@ -50,7 +49,7 @@ class PurchaseBatchView(BatchMasterView):
|
||||||
model_class = model.PurchaseBatch
|
model_class = model.PurchaseBatch
|
||||||
model_title_plural = "Purchase Batches"
|
model_title_plural = "Purchase Batches"
|
||||||
model_row_class = model.PurchaseBatchRow
|
model_row_class = model.PurchaseBatchRow
|
||||||
batch_handler_class = PurchaseBatchHandler
|
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
|
||||||
route_prefix = 'purchases.batch'
|
route_prefix = 'purchases.batch'
|
||||||
url_prefix = '/purchases/batches'
|
url_prefix = '/purchases/batches'
|
||||||
rows_creatable = True
|
rows_creatable = True
|
||||||
|
|
22
tailbone/views/vendors/catalogs.py
vendored
22
tailbone/views/vendors/catalogs.py
vendored
|
@ -29,7 +29,6 @@ from __future__ import unicode_literals, absolute_import
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from rattail.db import model, api
|
from rattail.db import model, api
|
||||||
from rattail.db.batch.vendorcatalog.handler import VendorCatalogHandler
|
|
||||||
from rattail.vendors.catalogs import iter_catalog_parsers
|
from rattail.vendors.catalogs import iter_catalog_parsers
|
||||||
|
|
||||||
import formalchemy
|
import formalchemy
|
||||||
|
@ -48,7 +47,7 @@ class VendorCatalogsView(FileBatchMasterView):
|
||||||
"""
|
"""
|
||||||
model_class = model.VendorCatalog
|
model_class = model.VendorCatalog
|
||||||
model_row_class = model.VendorCatalogRow
|
model_row_class = model.VendorCatalogRow
|
||||||
batch_handler_class = VendorCatalogHandler
|
default_handler_spec = 'rattail.batch.vendorcatalog:VendorCatalogHandler'
|
||||||
url_prefix = '/vendors/catalogs'
|
url_prefix = '/vendors/catalogs'
|
||||||
editable = False
|
editable = False
|
||||||
|
|
||||||
|
@ -105,6 +104,15 @@ class VendorCatalogsView(FileBatchMasterView):
|
||||||
fs.executed_by,
|
fs.executed_by,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def get_batch_kwargs(self, batch):
|
||||||
|
kwargs = super(VendorCatalogsView, self).get_batch_kwargs(batch)
|
||||||
|
kwargs['parser_key'] = batch.parser_key
|
||||||
|
if batch.vendor:
|
||||||
|
kwargs['vendor'] = batch.vendor
|
||||||
|
elif batch.vendor_uuid:
|
||||||
|
kwargs['vendor_uuid'] = batch.vendor_uuid
|
||||||
|
return kwargs
|
||||||
|
|
||||||
def configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
g.configure(
|
g.configure(
|
||||||
include=[
|
include=[
|
||||||
|
@ -146,16 +154,6 @@ class VendorCatalogsView(FileBatchMasterView):
|
||||||
kwargs['parsers'] = parsers
|
kwargs['parsers'] = parsers
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def defaults(cls, config):
|
|
||||||
|
|
||||||
# fix permission group title
|
|
||||||
config.add_tailbone_permission_group('vendorcatalogs', "Vendor Catalogs")
|
|
||||||
|
|
||||||
cls._filebatch_defaults(config)
|
|
||||||
cls._batch_defaults(config)
|
|
||||||
cls._defaults(config)
|
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
VendorCatalogsView.defaults(config)
|
VendorCatalogsView.defaults(config)
|
||||||
|
|
18
tailbone/views/vendors/invoices.py
vendored
18
tailbone/views/vendors/invoices.py
vendored
|
@ -27,7 +27,6 @@ Views for maintaining vendor invoices
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from rattail.db import model, api
|
from rattail.db import model, api
|
||||||
from rattail.db.batch.vendorinvoice.handler import VendorInvoiceHandler
|
|
||||||
from rattail.vendors.invoices import iter_invoice_parsers, require_invoice_parser
|
from rattail.vendors.invoices import iter_invoice_parsers, require_invoice_parser
|
||||||
|
|
||||||
import formalchemy
|
import formalchemy
|
||||||
|
@ -42,7 +41,7 @@ class VendorInvoicesView(FileBatchMasterView):
|
||||||
"""
|
"""
|
||||||
model_class = model.VendorInvoice
|
model_class = model.VendorInvoice
|
||||||
model_row_class = model.VendorInvoiceRow
|
model_row_class = model.VendorInvoiceRow
|
||||||
batch_handler_class = VendorInvoiceHandler
|
default_handler_spec = 'rattail.batch.vendorinvoice:VendorInvoiceHandler'
|
||||||
url_prefix = '/vendors/invoices'
|
url_prefix = '/vendors/invoices'
|
||||||
|
|
||||||
def get_instance_title(self, batch):
|
def get_instance_title(self, batch):
|
||||||
|
@ -109,6 +108,11 @@ class VendorInvoicesView(FileBatchMasterView):
|
||||||
except ValueError as error:
|
except ValueError as error:
|
||||||
raise formalchemy.ValidationError(unicode(error))
|
raise formalchemy.ValidationError(unicode(error))
|
||||||
|
|
||||||
|
def get_batch_kwargs(self, batch):
|
||||||
|
kwargs = super(VendorInvoicesView, self).get_batch_kwargs(batch)
|
||||||
|
kwargs['parser_key'] = batch.parser_key
|
||||||
|
return kwargs
|
||||||
|
|
||||||
def init_batch(self, batch):
|
def init_batch(self, batch):
|
||||||
parser = require_invoice_parser(batch.parser_key)
|
parser = require_invoice_parser(batch.parser_key)
|
||||||
vendor = api.get_vendor(Session(), parser.vendor_key)
|
vendor = api.get_vendor(Session(), parser.vendor_key)
|
||||||
|
@ -148,16 +152,6 @@ class VendorInvoicesView(FileBatchMasterView):
|
||||||
attrs['class_'] = 'warning'
|
attrs['class_'] = 'warning'
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def defaults(cls, config):
|
|
||||||
|
|
||||||
# fix permission group title
|
|
||||||
config.add_tailbone_permission_group('vendorinvoices', "Vendor Invoices")
|
|
||||||
|
|
||||||
cls._filebatch_defaults(config)
|
|
||||||
cls._batch_defaults(config)
|
|
||||||
cls._defaults(config)
|
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
VendorInvoicesView.defaults(config)
|
VendorInvoicesView.defaults(config)
|
||||||
|
|
Loading…
Reference in a new issue