Refactor batch views / templates per rattail framework overhaul

This commit is contained in:
Lance Edgar 2016-11-19 18:09:14 -06:00
parent a5184e416a
commit 203f0242fb
12 changed files with 225 additions and 182 deletions

View file

@ -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:

View file

@ -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,6 +67,7 @@
${rows_grid|n} ${rows_grid|n}
% if not batch.executed:
<div id="execution-options-dialog" style="display: none;"> <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')}
@ -84,3 +77,4 @@ ${rows_grid|n}
${h.end_form()} ${h.end_form()}
</div> </div>
% endif

View file

@ -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)
if self.batch_inited:
Session.add(batch)
Session.flush() 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):
if hasattr(self.handler, 'get_execute_title'):
return self.handler.get_execute_title(batch) 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,10 +556,15 @@ 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.
""" """
# TODO: deprecate/remove this
if hasattr(self.handler, 'refresh_data'):
self.handler.refresh_data(session, batch, progress=progress) self.handler.refresh_data(session, batch, progress=progress)
batch.cognized = datetime.datetime.utcnow() batch.cognized = datetime.datetime.utcnow()
batch.cognized_by = cognizer or session.merge(self.request.user) 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):
""" """
Thread target for refreshing batch data with progress indicator. Thread target for refreshing batch data with progress indicator.
@ -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))

View file

@ -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):

View file

@ -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.device_type.set(renderer=forms.renderers.EnumFieldRenderer(enum.HANDHELD_DEVICE_TYPE))
if self.creating:
fs.configure(
include=[
fs.filename,
fs.device_type,
fs.device_name,
])
else:
fs.configure( fs.configure(
include=[ include=[
fs.id, fs.id,
fs.device_type,
fs.device_name,
fs.filename,
fs.created, fs.created,
fs.created_by, fs.created_by,
fs.filename,
fs.device_type.with_renderer(forms.renderers.EnumFieldRenderer(enum.HANDHELD_DEVICE_TYPE)),
fs.device_name,
fs.executed, fs.executed,
fs.executed_by, fs.executed_by,
]) ])
if self.creating:
del fs.id if self.viewing and fs.model.inventory_batch:
elif 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)

View file

@ -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)

View file

@ -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)

View file

@ -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):

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)