Add MasterView.has_rows
concept and related logic
Now the `BatchMasterView` no longer provides most of these goodies. Also tweak some custom batch views to reflect changes etc.
This commit is contained in:
parent
8a19b90efa
commit
901c2fc573
17
tailbone/templates/master/edit_row.mako
Normal file
17
tailbone/templates/master/edit_row.mako
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
## -*- coding: utf-8 -*-
|
||||||
|
<%inherit file="/master/edit.mako" />
|
||||||
|
|
||||||
|
<%def name="context_menu_items()">
|
||||||
|
<li>${h.link_to("Back to {}".format(model_title), index_url)}</li>
|
||||||
|
% if master.rows_viewable and request.has_perm('{}.view'.format(row_permission_prefix)):
|
||||||
|
<li>${h.link_to("View this {}".format(row_model_title), row_action_url('view', instance))}</li>
|
||||||
|
% endif
|
||||||
|
% if master.rows_deletable and instance_deletable and request.has_perm('{}.delete'.format(row_permission_prefix)):
|
||||||
|
<li>${h.link_to("Delete this {}".format(row_model_title), row_action_url('delete', instance))}</li>
|
||||||
|
% endif
|
||||||
|
% if master.rows_creatable and request.has_perm('{}.create'.format(row_permission_prefix)):
|
||||||
|
<li>${h.link_to("Create a new {}".format(row_model_title), url('{}.create'.format(row_route_prefix)))}</li>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -4,7 +4,7 @@
|
||||||
<%def name="title()">${model_title}</%def>
|
<%def name="title()">${model_title}</%def>
|
||||||
|
|
||||||
<%def name="context_menu_items()">
|
<%def name="context_menu_items()">
|
||||||
<li>${h.link_to("Back to {}".format(batch_model_title), index_url)}</li>
|
<li>${h.link_to("Back to {}".format(parent_model_title), index_url)}</li>
|
||||||
% if master.rows_editable and instance_editable and request.has_perm('{}.edit'.format(permission_prefix)):
|
% if master.rows_editable and instance_editable and request.has_perm('{}.edit'.format(permission_prefix)):
|
||||||
<li>${h.link_to("Edit this {}".format(model_title), action_url('edit', instance))}</li>
|
<li>${h.link_to("Edit this {}".format(model_title), action_url('edit', instance))}</li>
|
||||||
% endif
|
% endif
|
||||||
|
@ -16,4 +16,10 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
${parent.body()}
|
<ul id="context-menu">
|
||||||
|
${self.context_menu_items()}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="form-wrapper">
|
||||||
|
${form.render()|n}
|
||||||
|
</div><!-- form-wrapper -->
|
|
@ -42,7 +42,7 @@
|
||||||
% if master.listing:
|
% if master.listing:
|
||||||
<span class="global">${model_title_plural}</span>
|
<span class="global">${model_title_plural}</span>
|
||||||
% else:
|
% else:
|
||||||
${h.link_to(model_title_plural, index_url, class_='global')}
|
${h.link_to(index_title, index_url, class_='global')}
|
||||||
% if master.viewing and grid_index:
|
% if master.viewing and grid_index:
|
||||||
${grid_index_nav()}
|
${grid_index_nav()}
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -63,11 +63,9 @@ class BatchMasterView(MasterView):
|
||||||
"""
|
"""
|
||||||
Base class for all "batch master" views.
|
Base class for all "batch master" views.
|
||||||
"""
|
"""
|
||||||
refreshable = True
|
has_rows = True
|
||||||
rows_viewable = True
|
|
||||||
rows_creatable = False
|
|
||||||
rows_editable = False
|
|
||||||
rows_deletable = True
|
rows_deletable = True
|
||||||
|
refreshable = True
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
super(BatchMasterView, self).__init__(request)
|
super(BatchMasterView, self).__init__(request)
|
||||||
|
@ -96,45 +94,17 @@ 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 view(self):
|
def template_kwargs_view(self, **kwargs):
|
||||||
"""
|
batch = kwargs['instance']
|
||||||
View for viewing details of an existing model record.
|
kwargs['batch'] = batch
|
||||||
"""
|
kwargs['handler'] = self.handler
|
||||||
self.viewing = True
|
kwargs['execute_title'] = self.get_execute_title(batch)
|
||||||
batch = self.get_instance()
|
kwargs['execute_enabled'] = self.executable(batch)
|
||||||
form = self.make_form(batch)
|
if kwargs['execute_enabled'] and self.has_execution_options:
|
||||||
grid = self.make_row_grid(batch=batch)
|
kwargs['rendered_execution_options'] = self.render_execution_options(batch)
|
||||||
|
return kwargs
|
||||||
|
|
||||||
# If user just refreshed the page with a reset instruction, issue a
|
def render_execution_options(self, batch):
|
||||||
# redirect in order to clear out the query string.
|
|
||||||
if self.request.GET.get('reset-to-default-filters') == 'true':
|
|
||||||
return self.redirect(self.request.current_route_url(_query=None))
|
|
||||||
|
|
||||||
if self.request.params.get('partial'):
|
|
||||||
self.request.response.content_type = b'text/html'
|
|
||||||
self.request.response.text = grid.render_grid()
|
|
||||||
return self.request.response
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'handler': self.handler,
|
|
||||||
'instance': batch,
|
|
||||||
'instance_title': self.get_instance_title(batch),
|
|
||||||
'instance_editable': self.editable_instance(batch),
|
|
||||||
'instance_deletable': self.deletable_instance(batch),
|
|
||||||
'form': form,
|
|
||||||
'batch': batch,
|
|
||||||
'execute_title': self.get_execute_title(batch),
|
|
||||||
'execute_enabled': self.executable(batch),
|
|
||||||
'rows_grid': grid.render_complete(allow_save_defaults=False),
|
|
||||||
}
|
|
||||||
|
|
||||||
if context['execute_enabled'] and self.has_execution_options:
|
|
||||||
context['rendered_execution_options'] = self.render_execution_options()
|
|
||||||
|
|
||||||
return self.render_to_response('view', context)
|
|
||||||
|
|
||||||
def render_execution_options(self):
|
|
||||||
batch = self.get_instance()
|
|
||||||
form = self.make_execution_options_form(batch)
|
form = self.make_execution_options_form(batch)
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'batch': batch,
|
'batch': batch,
|
||||||
|
@ -302,6 +272,7 @@ class BatchMasterView(MasterView):
|
||||||
"""
|
"""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# TODO: some of this at least can go to master now right?
|
||||||
def edit(self):
|
def edit(self):
|
||||||
"""
|
"""
|
||||||
Don't allow editing a batch which has already been executed.
|
Don't allow editing a batch which has already been executed.
|
||||||
|
@ -343,7 +314,7 @@ class BatchMasterView(MasterView):
|
||||||
}
|
}
|
||||||
|
|
||||||
if context['execute_enabled'] and self.has_execution_options:
|
if context['execute_enabled'] and self.has_execution_options:
|
||||||
context['rendered_execution_options'] = self.render_execution_options()
|
context['rendered_execution_options'] = self.render_execution_options(batch)
|
||||||
|
|
||||||
return self.render_to_response('edit', context)
|
return self.render_to_response('edit', context)
|
||||||
|
|
||||||
|
@ -500,175 +471,80 @@ class BatchMasterView(MasterView):
|
||||||
# batch rows
|
# batch rows
|
||||||
########################################
|
########################################
|
||||||
|
|
||||||
def get_row_model_title(self):
|
def get_row_instance_title(self, row):
|
||||||
return "{} Row".format(self.get_model_title())
|
return "Row {}".format(row.sequence)
|
||||||
|
|
||||||
def get_row_model_title_plural(self):
|
|
||||||
return "{} Rows".format(self.get_model_title())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_row_permission_prefix(cls):
|
|
||||||
"""
|
|
||||||
Permission prefix specific to the row-level data for this batch type,
|
|
||||||
e.g. ``'vendorcatalogs.rows'``.
|
|
||||||
"""
|
|
||||||
return "{}.rows".format(cls.get_permission_prefix())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_row_route_prefix(cls):
|
|
||||||
"""
|
|
||||||
Route prefix specific to the row-level views for a batch, e.g.
|
|
||||||
``'vendorcatalogs.rows'``.
|
|
||||||
"""
|
|
||||||
return "{}.rows".format(cls.get_route_prefix())
|
|
||||||
|
|
||||||
def make_row_grid(self, **kwargs):
|
|
||||||
"""
|
|
||||||
Make and return a new (configured) batch rows grid instance.
|
|
||||||
"""
|
|
||||||
batch = kwargs.pop('batch', self.get_instance())
|
|
||||||
key = '{}.{}'.format(self.get_grid_key(), batch.uuid)
|
|
||||||
data = self.get_row_data(batch)
|
|
||||||
kwargs = self.make_row_grid_kwargs(**kwargs)
|
|
||||||
grid = grids.AlchemyGrid(key, self.request, data=data, model_class=self.batch_row_class, **kwargs)
|
|
||||||
self._preconfigure_row_grid(grid)
|
|
||||||
self.configure_row_grid(grid)
|
|
||||||
grid.load_settings()
|
|
||||||
return grid
|
|
||||||
|
|
||||||
def _preconfigure_row_grid(self, g):
|
def _preconfigure_row_grid(self, g):
|
||||||
|
|
||||||
g.filters['status_code'].label = "Status"
|
g.filters['status_code'].label = "Status"
|
||||||
g.filters['status_code'].set_value_renderer(grids.filters.EnumValueRenderer(self.batch_row_class.STATUS))
|
g.filters['status_code'].set_value_renderer(grids.filters.EnumValueRenderer(self.model_row_class.STATUS))
|
||||||
|
|
||||||
g.default_sortkey = 'sequence'
|
g.default_sortkey = 'sequence'
|
||||||
|
|
||||||
g.sequence.set(label="Seq.")
|
g.sequence.set(label="Seq.")
|
||||||
g.status_code.set(label="Status",
|
g.status_code.set(label="Status",
|
||||||
renderer=StatusRenderer(self.batch_row_class.STATUS))
|
renderer=StatusRenderer(self.model_row_class.STATUS))
|
||||||
|
|
||||||
def configure_row_grid(self, grid):
|
|
||||||
grid.configure()
|
|
||||||
|
|
||||||
def get_row_data(self, batch):
|
def get_row_data(self, batch):
|
||||||
"""
|
"""
|
||||||
Generate the base data set for a rows grid.
|
Generate the base data set for a rows grid.
|
||||||
"""
|
"""
|
||||||
session = orm.object_session(batch)
|
return self.Session.query(self.model_row_class)\
|
||||||
return session.query(self.batch_row_class)\
|
.filter(self.model_row_class.batch == batch)\
|
||||||
.filter(self.batch_row_class.batch == batch)\
|
.filter(self.model_row_class.removed == False)
|
||||||
.filter(self.batch_row_class.removed == False)
|
|
||||||
|
|
||||||
def make_row_grid_kwargs(self, **kwargs):
|
def row_editable(self, row):
|
||||||
"""
|
"""
|
||||||
Return a dict of kwargs to be used when constructing a new rows grid.
|
Batch rows are editable only until batch has been executed.
|
||||||
"""
|
"""
|
||||||
route_prefix = self.get_row_route_prefix()
|
return self.rows_editable and not row.batch.executed
|
||||||
permission_prefix = self.get_row_permission_prefix()
|
|
||||||
|
|
||||||
defaults = {
|
def row_edit_action_url(self, row, i):
|
||||||
'width': 'full',
|
if self.row_editable(row):
|
||||||
'filterable': True,
|
return self.get_row_action_url('edit', row)
|
||||||
'sortable': True,
|
|
||||||
'pageable': True,
|
|
||||||
'row_attrs': self.row_grid_row_attrs,
|
|
||||||
'model_title': self.get_row_model_title(),
|
|
||||||
'model_title_plural': self.get_row_model_title_plural(),
|
|
||||||
'permission_prefix': permission_prefix,
|
|
||||||
'route_prefix': route_prefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'main_actions' not in defaults:
|
def row_deletable(self, row):
|
||||||
actions = []
|
|
||||||
|
|
||||||
# view action
|
|
||||||
if self.rows_viewable:
|
|
||||||
view = lambda r, i: self.request.route_url('{}.view'.format(route_prefix),
|
|
||||||
uuid=r.uuid)
|
|
||||||
actions.append(grids.GridAction('view', icon='zoomin', url=view))
|
|
||||||
|
|
||||||
# delete action
|
|
||||||
batch = self.get_instance()
|
|
||||||
if self.editing and self.rows_deletable and not batch.executed:
|
|
||||||
delete = lambda r, i: self.request.route_url('{}.delete'.format(route_prefix),
|
|
||||||
uuid=r.uuid)
|
|
||||||
actions.append(grids.GridAction('delete', icon='trash', url=delete))
|
|
||||||
|
|
||||||
defaults['main_actions'] = actions
|
|
||||||
|
|
||||||
defaults.update(kwargs)
|
|
||||||
return defaults
|
|
||||||
|
|
||||||
def row_grid_row_attrs(self, row, i):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def view_row(self):
|
|
||||||
"""
|
"""
|
||||||
View for viewing details of a single batch row.
|
Batch rows are deletable only until batch has been executed.
|
||||||
"""
|
"""
|
||||||
self.viewing = True
|
return self.rows_deletable and not row.batch.executed
|
||||||
row = self.get_row_instance()
|
|
||||||
form = self.make_row_form(row)
|
|
||||||
return self.render_to_response('view_row', {
|
|
||||||
'instance': row,
|
|
||||||
'instance_title': self.get_row_instance_title(row),
|
|
||||||
'instance_editable': False,
|
|
||||||
'instance_deletable': False,
|
|
||||||
'model_title': self.get_row_model_title(),
|
|
||||||
'model_title_plural': self.get_row_model_title_plural(),
|
|
||||||
'batch_model_title': self.get_model_title(),
|
|
||||||
'index_url': self.get_action_url('view', row.batch),
|
|
||||||
'index_title': '{} {}'.format(
|
|
||||||
self.get_model_title(),
|
|
||||||
self.get_instance_title(row.batch)),
|
|
||||||
'form': form})
|
|
||||||
|
|
||||||
def get_row_instance(self):
|
def row_delete_action_url(self, row, i):
|
||||||
key = self.request.matchdict[self.get_model_key()]
|
if self.row_deletable(row):
|
||||||
instance = self.Session.query(self.batch_row_class).get(key)
|
return self.get_row_action_url('delete', row)
|
||||||
if not instance:
|
|
||||||
raise httpexceptions.HTTPNotFound()
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def get_row_instance_title(self, instance):
|
def _preconfigure_row_fieldset(self, fs):
|
||||||
return self.get_row_model_title()
|
fs.sequence.set(readonly=True)
|
||||||
|
fs.status_code.set(renderer=StatusRenderer(self.model_row_class.STATUS),
|
||||||
def make_row_form(self, instance, **kwargs):
|
label="Status", readonly=True)
|
||||||
"""
|
fs.status_text.set(readonly=True)
|
||||||
Make a FormAlchemy form for use with CRUD views for a batch *row*.
|
fs.removed.set(readonly=True)
|
||||||
"""
|
try:
|
||||||
# TODO: Some hacky stuff here, to accommodate old form cruft. Probably
|
fs.product.set(readonly=True)
|
||||||
# should refactor forms soon too, but trying to avoid it for the moment.
|
except AttributeError:
|
||||||
|
pass
|
||||||
kwargs.setdefault('creating', self.creating)
|
|
||||||
kwargs.setdefault('editing', self.editing)
|
|
||||||
|
|
||||||
fieldset = self.make_fieldset(instance)
|
|
||||||
self.configure_row_fieldset(fieldset)
|
|
||||||
|
|
||||||
kwargs.setdefault('action_url', self.request.current_route_url(_query=None))
|
|
||||||
if self.creating:
|
|
||||||
kwargs.setdefault('cancel_url', self.get_action_url('view', instance.batch))
|
|
||||||
else:
|
|
||||||
kwargs.setdefault('cancel_url', self.request.route_url('{}.view'.format(self.get_row_route_prefix()),
|
|
||||||
uuid=instance.uuid))
|
|
||||||
form = forms.AlchemyForm(self.request, fieldset, **kwargs)
|
|
||||||
form.readonly = self.viewing
|
|
||||||
return form
|
|
||||||
|
|
||||||
def configure_row_fieldset(self, fs):
|
def configure_row_fieldset(self, fs):
|
||||||
fs.configure()
|
fs.configure()
|
||||||
|
del fs.batch
|
||||||
|
|
||||||
|
def template_kwargs_view_row(self, **kwargs):
|
||||||
|
kwargs['batch_model_title'] = kwargs['parent_model_title']
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def get_parent(self, row):
|
||||||
|
return row.batch
|
||||||
|
|
||||||
def delete_row(self):
|
def delete_row(self):
|
||||||
"""
|
"""
|
||||||
"Delete" a row from the batch. This sets the ``removed`` flag on the
|
"Delete" a row from the batch. This sets the ``removed`` flag on the
|
||||||
row but does not truly delete it.
|
row but does not truly delete it.
|
||||||
"""
|
"""
|
||||||
row = self.Session.query(self.batch_row_class).get(self.request.matchdict['uuid'])
|
row = self.Session.query(self.model_row_class).get(self.request.matchdict['uuid'])
|
||||||
if not row:
|
if not row:
|
||||||
raise httpexceptions.HTTPNotFound()
|
raise httpexceptions.HTTPNotFound()
|
||||||
row.removed = True
|
row.removed = True
|
||||||
return self.redirect(self.get_action_url('edit', row.batch))
|
return self.redirect(self.get_action_url('view', self.get_parent(row)))
|
||||||
|
|
||||||
def bulk_delete_rows(self):
|
def bulk_delete_rows(self):
|
||||||
"""
|
"""
|
||||||
|
@ -679,17 +555,6 @@ class BatchMasterView(MasterView):
|
||||||
query.update({'removed': True}, synchronize_session=False)
|
query.update({'removed': True}, synchronize_session=False)
|
||||||
return self.redirect(self.get_action_url('view', self.get_instance()))
|
return self.redirect(self.get_action_url('view', self.get_instance()))
|
||||||
|
|
||||||
def get_effective_row_query(self):
|
|
||||||
"""
|
|
||||||
Convenience method which returns the "effective" query for the master
|
|
||||||
grid, filtered and sorted to match what would show on the UI, but not
|
|
||||||
paged etc.
|
|
||||||
"""
|
|
||||||
batch = self.get_instance()
|
|
||||||
grid = self.make_row_grid(batch=batch, sortable=False, pageable=False,
|
|
||||||
main_actions=[])
|
|
||||||
return grid._fa_grid.rows
|
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
"""
|
"""
|
||||||
Execute a batch. Starts a separate thread for the execution, and
|
Execute a batch. Starts a separate thread for the execution, and
|
||||||
|
@ -795,7 +660,7 @@ class BatchMasterView(MasterView):
|
||||||
Return the list of fields to be written to CSV download.
|
Return the list of fields to be written to CSV download.
|
||||||
"""
|
"""
|
||||||
fields = []
|
fields = []
|
||||||
mapper = orm.class_mapper(self.batch_row_class)
|
mapper = orm.class_mapper(self.model_row_class)
|
||||||
for prop in mapper.iterate_properties:
|
for prop in mapper.iterate_properties:
|
||||||
if isinstance(prop, orm.ColumnProperty):
|
if isinstance(prop, orm.ColumnProperty):
|
||||||
if prop.key != 'removed' and not prop.key.endswith('uuid'):
|
if prop.key != 'removed' and not prop.key.endswith('uuid'):
|
||||||
|
@ -824,24 +689,11 @@ class BatchMasterView(MasterView):
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
model_title = cls.get_model_title()
|
model_title = cls.get_model_title()
|
||||||
|
|
||||||
# view row
|
|
||||||
if cls.rows_viewable:
|
|
||||||
config.add_route('{}.rows.view'.format(route_prefix), '{}/rows/{{uuid}}'.format(url_prefix))
|
|
||||||
config.add_view(cls, attr='view_row', route_name='{}.rows.view'.format(route_prefix),
|
|
||||||
permission='{}.rows.view'.format(permission_prefix))
|
|
||||||
config.add_tailbone_permission(permission_prefix, '{}.rows.view'.format(permission_prefix),
|
|
||||||
"View {} Row Details".format(model_title))
|
|
||||||
|
|
||||||
# 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),
|
||||||
permission='{}.create'.format(permission_prefix))
|
permission='{}.create'.format(permission_prefix))
|
||||||
|
|
||||||
# delete row
|
|
||||||
config.add_route('{}.rows.delete'.format(route_prefix), '{}/delete-row/{{uuid}}'.format(url_prefix))
|
|
||||||
config.add_view(cls, attr='delete_row', route_name='{}.rows.delete'.format(route_prefix),
|
|
||||||
permission='{}.edit'.format(permission_prefix))
|
|
||||||
|
|
||||||
# bulk delete rows
|
# bulk delete rows
|
||||||
config.add_route('{}.rows.bulk_delete'.format(route_prefix), '{}/{{uuid}}/rows/delete'.format(url_prefix))
|
config.add_route('{}.rows.bulk_delete'.format(route_prefix), '{}/{{uuid}}/rows/delete'.format(url_prefix))
|
||||||
config.add_view(cls, attr='bulk_delete_rows', route_name='{}.rows.bulk_delete'.format(route_prefix),
|
config.add_view(cls, attr='bulk_delete_rows', route_name='{}.rows.bulk_delete'.format(route_prefix),
|
||||||
|
|
|
@ -73,11 +73,16 @@ class HandheldBatchView(FileBatchMasterView):
|
||||||
"""
|
"""
|
||||||
model_class = model.HandheldBatch
|
model_class = model.HandheldBatch
|
||||||
model_title_plural = "Handheld Batches"
|
model_title_plural = "Handheld Batches"
|
||||||
batch_row_class = model.HandheldBatchRow
|
|
||||||
batch_handler_class = HandheldBatchHandler
|
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
|
||||||
|
refreshable = False
|
||||||
|
|
||||||
|
model_row_class = model.HandheldBatchRow
|
||||||
|
rows_creatable = False
|
||||||
|
rows_editable = True
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
g.configure(
|
g.configure(
|
||||||
|
@ -105,7 +110,7 @@ class HandheldBatchView(FileBatchMasterView):
|
||||||
])
|
])
|
||||||
if self.creating:
|
if self.creating:
|
||||||
del fs.id
|
del fs.id
|
||||||
elif self.viewing:
|
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 configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
|
@ -128,6 +133,27 @@ class HandheldBatchView(FileBatchMasterView):
|
||||||
attrs['class_'] = 'warning'
|
attrs['class_'] = 'warning'
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def _preconfigure_row_fieldset(self, fs):
|
||||||
|
super(HandheldBatchView, self)._preconfigure_row_fieldset(fs)
|
||||||
|
fs.upc.set(readonly=True, label="UPC", renderer=forms.renderers.GPCFieldRenderer,
|
||||||
|
attrs={'link': lambda r: self.request.route_url('products.view', uuid=r.product_uuid)})
|
||||||
|
fs.brand_name.set(readonly=True)
|
||||||
|
fs.description.set(readonly=True)
|
||||||
|
fs.size.set(readonly=True)
|
||||||
|
|
||||||
|
def configure_row_fieldset(self, fs):
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.sequence,
|
||||||
|
fs.upc,
|
||||||
|
fs.brand_name,
|
||||||
|
fs.description,
|
||||||
|
fs.size,
|
||||||
|
fs.status_code,
|
||||||
|
fs.cases,
|
||||||
|
fs.units,
|
||||||
|
])
|
||||||
|
|
||||||
def get_exec_options_kwargs(self, **kwargs):
|
def get_exec_options_kwargs(self, **kwargs):
|
||||||
kwargs['ACTION_OPTIONS'] = list(ACTION_OPTIONS.iteritems())
|
kwargs['ACTION_OPTIONS'] = list(ACTION_OPTIONS.iteritems())
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
|
@ -26,6 +26,8 @@ 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.batch.inventory.handler import InventoryBatchHandler
|
from rattail.db.batch.inventory.handler import InventoryBatchHandler
|
||||||
|
|
||||||
import formalchemy as fa
|
import formalchemy as fa
|
||||||
|
@ -34,8 +36,6 @@ from webhelpers.html import tags
|
||||||
from tailbone import forms
|
from tailbone import forms
|
||||||
from tailbone.views.batch import BatchMasterView
|
from tailbone.views.batch import BatchMasterView
|
||||||
|
|
||||||
from dtail.db import model
|
|
||||||
|
|
||||||
|
|
||||||
class HandheldBatchFieldRenderer(fa.FieldRenderer):
|
class HandheldBatchFieldRenderer(fa.FieldRenderer):
|
||||||
"""
|
"""
|
||||||
|
@ -57,11 +57,30 @@ class InventoryBatchView(BatchMasterView):
|
||||||
"""
|
"""
|
||||||
model_class = model.InventoryBatch
|
model_class = model.InventoryBatch
|
||||||
model_title_plural = "Inventory Batches"
|
model_title_plural = "Inventory Batches"
|
||||||
batch_row_class = model.InventoryBatchRow
|
|
||||||
batch_handler_class = InventoryBatchHandler
|
batch_handler_class = InventoryBatchHandler
|
||||||
route_prefix = 'batch.inventory'
|
route_prefix = 'batch.inventory'
|
||||||
url_prefix = '/batch/inventory'
|
url_prefix = '/batch/inventory'
|
||||||
creatable = False
|
creatable = False
|
||||||
|
editable = False
|
||||||
|
refreshable = False
|
||||||
|
|
||||||
|
model_row_class = model.InventoryBatchRow
|
||||||
|
rows_editable = True
|
||||||
|
|
||||||
|
def _preconfigure_grid(self, g):
|
||||||
|
super(InventoryBatchView, self)._preconfigure_grid(g)
|
||||||
|
g.mode.set(renderer=forms.renderers.EnumFieldRenderer(enum.INVENTORY_MODE),
|
||||||
|
label="Count Mode")
|
||||||
|
|
||||||
|
def configure_grid(self, g):
|
||||||
|
super(InventoryBatchView, self).configure_grid(g)
|
||||||
|
g.append(g.mode)
|
||||||
|
|
||||||
|
def _preconfigure_fieldset(self, fs):
|
||||||
|
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
||||||
|
fs.handheld_batch.set(renderer=HandheldBatchFieldRenderer, readonly=True)
|
||||||
|
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(enum.INVENTORY_MODE),
|
||||||
|
label="Count Mode")
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
fs.configure(
|
fs.configure(
|
||||||
|
@ -69,24 +88,29 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.id,
|
fs.id,
|
||||||
fs.created,
|
fs.created,
|
||||||
fs.created_by,
|
fs.created_by,
|
||||||
fs.handheld_batch.with_renderer(HandheldBatchFieldRenderer),
|
fs.handheld_batch,
|
||||||
|
fs.mode,
|
||||||
fs.executed,
|
fs.executed,
|
||||||
fs.executed_by,
|
fs.executed_by,
|
||||||
])
|
])
|
||||||
if not self.viewing:
|
|
||||||
del fs.handheld_batch
|
def _preconfigure_row_grid(self, g):
|
||||||
|
super(InventoryBatchView, self)._preconfigure_row_grid(g)
|
||||||
|
g.upc.set(label="UPC")
|
||||||
|
g.brand_name.set(label="Brand")
|
||||||
|
g.status_code.set(label="Status")
|
||||||
|
|
||||||
def configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
g.configure(
|
g.configure(
|
||||||
include=[
|
include=[
|
||||||
g.sequence,
|
g.sequence,
|
||||||
g.upc.label("UPC"),
|
g.upc,
|
||||||
g.brand_name.label("Brand"),
|
g.brand_name,
|
||||||
g.description,
|
g.description,
|
||||||
g.size,
|
g.size,
|
||||||
g.cases,
|
g.cases,
|
||||||
g.units,
|
g.units,
|
||||||
g.status_code.label("Status"),
|
g.status_code,
|
||||||
],
|
],
|
||||||
readonly=True)
|
readonly=True)
|
||||||
|
|
||||||
|
@ -96,6 +120,27 @@ class InventoryBatchView(BatchMasterView):
|
||||||
attrs['class_'] = 'warning'
|
attrs['class_'] = 'warning'
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def _preconfigure_row_fieldset(self, fs):
|
||||||
|
super(InventoryBatchView, self)._preconfigure_row_fieldset(fs)
|
||||||
|
fs.upc.set(readonly=True, label="UPC", renderer=forms.renderers.GPCFieldRenderer,
|
||||||
|
attrs={'link': lambda r: self.request.route_url('products.view', uuid=r.product_uuid)})
|
||||||
|
fs.brand_name.set(readonly=True)
|
||||||
|
fs.description.set(readonly=True)
|
||||||
|
fs.size.set(readonly=True)
|
||||||
|
|
||||||
|
def configure_row_fieldset(self, fs):
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.sequence,
|
||||||
|
fs.upc,
|
||||||
|
fs.brand_name,
|
||||||
|
fs.description,
|
||||||
|
fs.size,
|
||||||
|
fs.status_code,
|
||||||
|
fs.cases,
|
||||||
|
fs.units,
|
||||||
|
])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,10 @@ class MasterView(View):
|
||||||
|
|
||||||
has_rows = False
|
has_rows = False
|
||||||
model_row_class = None
|
model_row_class = None
|
||||||
|
rows_viewable = True
|
||||||
|
rows_creatable = False
|
||||||
|
rows_editable = False
|
||||||
|
rows_deletable = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Session(self):
|
def Session(self):
|
||||||
|
@ -146,12 +150,16 @@ class MasterView(View):
|
||||||
form = self.make_form(instance)
|
form = self.make_form(instance)
|
||||||
if self.has_rows:
|
if self.has_rows:
|
||||||
|
|
||||||
|
# must make grid prior to redirecting from filter reset, b/c the
|
||||||
|
# grid will detect the filter reset request and store defaults in
|
||||||
|
# the session, that way redirect will then show The Right Thing
|
||||||
|
grid = self.make_row_grid(instance=instance)
|
||||||
|
|
||||||
# If user just refreshed the page with a reset instruction, issue a
|
# If user just refreshed the page with a reset instruction, issue a
|
||||||
# redirect in order to clear out the query string.
|
# redirect in order to clear out the query string.
|
||||||
if self.request.GET.get('reset-to-default-filters') == 'true':
|
if self.request.GET.get('reset-to-default-filters') == 'true':
|
||||||
return self.redirect(self.request.current_route_url(_query=None))
|
return self.redirect(self.request.current_route_url(_query=None))
|
||||||
|
|
||||||
grid = self.make_row_grid(instance=instance)
|
|
||||||
if self.request.params.get('partial'):
|
if self.request.params.get('partial'):
|
||||||
self.request.response.content_type = b'text/html'
|
self.request.response.content_type = b'text/html'
|
||||||
self.request.response.text = grid.render_grid()
|
self.request.response.text = grid.render_grid()
|
||||||
|
@ -176,16 +184,29 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
Make and return a new (configured) rows grid instance.
|
Make and return a new (configured) rows grid instance.
|
||||||
"""
|
"""
|
||||||
instance = kwargs.pop('instance', self.get_instance())
|
parent = kwargs.pop('instance', self.get_instance())
|
||||||
data = self.get_row_data(instance)
|
data = self.get_row_data(parent)
|
||||||
|
kwargs['instance'] = parent
|
||||||
kwargs = self.make_row_grid_kwargs(**kwargs)
|
kwargs = self.make_row_grid_kwargs(**kwargs)
|
||||||
key = '{}.{}'.format(self.get_grid_key(), self.request.matchdict[self.get_model_key()])
|
key = '{}.{}'.format(self.get_grid_key(), self.request.matchdict[self.get_model_key()])
|
||||||
grid = grids.AlchemyGrid(key, self.request, data=data, model_class=self.model_row_class, **kwargs)
|
factory = self.get_grid_factory()
|
||||||
|
grid = factory(key, self.request, data=data, model_class=self.model_row_class, **kwargs)
|
||||||
self._preconfigure_row_grid(grid)
|
self._preconfigure_row_grid(grid)
|
||||||
self.configure_row_grid(grid)
|
self.configure_row_grid(grid)
|
||||||
grid.load_settings()
|
grid.load_settings()
|
||||||
return grid
|
return grid
|
||||||
|
|
||||||
|
def get_effective_row_query(self):
|
||||||
|
"""
|
||||||
|
Convenience method which returns the "effective" query for the master
|
||||||
|
grid, filtered and sorted to match what would show on the UI, but not
|
||||||
|
paged etc.
|
||||||
|
"""
|
||||||
|
parent = self.get_instance()
|
||||||
|
grid = self.make_row_grid(instance=parent, sortable=False, pageable=False,
|
||||||
|
main_actions=[])
|
||||||
|
return grid._fa_grid.rows
|
||||||
|
|
||||||
def _preconfigure_row_grid(self, g):
|
def _preconfigure_row_grid(self, g):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -206,6 +227,14 @@ class MasterView(View):
|
||||||
"""
|
"""
|
||||||
return "{}.rows".format(cls.get_route_prefix())
|
return "{}.rows".format(cls.get_route_prefix())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_row_url_prefix(cls):
|
||||||
|
"""
|
||||||
|
Returns a prefix which (by default) applies to all URLs provided by the
|
||||||
|
master view class, for "row" views, e.g. '/products/rows'.
|
||||||
|
"""
|
||||||
|
return getattr(cls, 'row_url_prefix', '{}/rows'.format(cls.get_url_prefix()))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_row_permission_prefix(cls):
|
def get_row_permission_prefix(cls):
|
||||||
"""
|
"""
|
||||||
|
@ -232,17 +261,44 @@ class MasterView(View):
|
||||||
'permission_prefix': permission_prefix,
|
'permission_prefix': permission_prefix,
|
||||||
'route_prefix': route_prefix,
|
'route_prefix': route_prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.has_rows and 'main_actions' not in defaults:
|
||||||
|
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 action
|
||||||
|
if self.rows_editable:
|
||||||
|
actions.append(grids.GridAction('edit', icon='pencil', url=self.row_edit_action_url))
|
||||||
|
|
||||||
|
# delete action
|
||||||
|
if self.rows_deletable:
|
||||||
|
actions.append(grids.GridAction('delete', icon='trash', url=self.row_delete_action_url))
|
||||||
|
|
||||||
|
defaults['main_actions'] = actions
|
||||||
|
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
|
def row_edit_action_url(self, row, i):
|
||||||
|
return self.get_row_action_url('edit', row)
|
||||||
|
|
||||||
|
def row_delete_action_url(self, row, i):
|
||||||
|
return self.get_row_action_url('delete', row)
|
||||||
|
|
||||||
def row_grid_row_attrs(self, row, i):
|
def row_grid_row_attrs(self, row, i):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_row_model_title(self):
|
@classmethod
|
||||||
return "{} Row".format(self.get_model_title())
|
def get_row_model_title(cls):
|
||||||
|
return "{} Row".format(cls.get_model_title())
|
||||||
|
|
||||||
def get_row_model_title_plural(self):
|
@classmethod
|
||||||
return "{} Rows".format(self.get_model_title())
|
def get_row_model_title_plural(cls):
|
||||||
|
return "{} Rows".format(cls.get_model_title())
|
||||||
|
|
||||||
def view_index(self):
|
def view_index(self):
|
||||||
"""
|
"""
|
||||||
|
@ -459,8 +515,17 @@ class MasterView(View):
|
||||||
'action_url': self.get_action_url,
|
'action_url': self.get_action_url,
|
||||||
'grid_index': self.grid_index,
|
'grid_index': self.grid_index,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.grid_index:
|
if self.grid_index:
|
||||||
context['grid_count'] = self.grid_count
|
context['grid_count'] = self.grid_count
|
||||||
|
|
||||||
|
if self.has_rows:
|
||||||
|
context['row_route_prefix'] = self.get_row_route_prefix()
|
||||||
|
context['row_permission_prefix'] = self.get_row_permission_prefix()
|
||||||
|
context['row_model_title'] = self.get_row_model_title()
|
||||||
|
context['row_model_title_plural'] = self.get_row_model_title_plural()
|
||||||
|
context['row_action_url'] = self.get_row_action_url
|
||||||
|
|
||||||
context.update(data)
|
context.update(data)
|
||||||
context.update(self.template_kwargs(**context))
|
context.update(self.template_kwargs(**context))
|
||||||
if hasattr(self, 'template_kwargs_{}'.format(template)):
|
if hasattr(self, 'template_kwargs_{}'.format(template)):
|
||||||
|
@ -870,6 +935,155 @@ class MasterView(View):
|
||||||
return self.after_delete_url
|
return self.after_delete_url
|
||||||
return self.get_index_url()
|
return self.get_index_url()
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# Associated Rows Stuff
|
||||||
|
##############################
|
||||||
|
|
||||||
|
def view_row(self):
|
||||||
|
"""
|
||||||
|
View for viewing details of a single data row.
|
||||||
|
"""
|
||||||
|
self.viewing = True
|
||||||
|
row = self.get_row_instance()
|
||||||
|
form = self.make_row_form(row)
|
||||||
|
parent = self.get_parent(row)
|
||||||
|
return self.render_to_response('view_row', {
|
||||||
|
'instance': row,
|
||||||
|
'instance_title': self.get_row_instance_title(row),
|
||||||
|
'instance_editable': self.row_editable(row),
|
||||||
|
'instance_deletable': self.row_deletable(row),
|
||||||
|
'model_title': self.get_row_model_title(),
|
||||||
|
'model_title_plural': self.get_row_model_title_plural(),
|
||||||
|
'parent_model_title': self.get_model_title(),
|
||||||
|
'index_url': self.get_action_url('view', parent),
|
||||||
|
'index_title': '{} {}'.format(
|
||||||
|
self.get_model_title(),
|
||||||
|
self.get_instance_title(parent)),
|
||||||
|
'action_url': self.get_row_action_url,
|
||||||
|
'form': form})
|
||||||
|
|
||||||
|
def edit_row(self):
|
||||||
|
"""
|
||||||
|
View for editing an existing model record.
|
||||||
|
"""
|
||||||
|
self.editing = True
|
||||||
|
row = self.get_row_instance()
|
||||||
|
form = self.make_row_form(row)
|
||||||
|
|
||||||
|
if self.request.method == 'POST':
|
||||||
|
if form.validate():
|
||||||
|
self.save_edit_row_form(form)
|
||||||
|
return self.redirect_after_edit_row(row)
|
||||||
|
|
||||||
|
parent = self.get_parent(row)
|
||||||
|
return self.render_to_response('edit_row', {
|
||||||
|
'instance': row,
|
||||||
|
'instance_title': self.get_row_instance_title(row),
|
||||||
|
'instance_deletable': self.row_deletable(row),
|
||||||
|
'index_url': self.get_action_url('view', parent),
|
||||||
|
'index_title': '{} {}'.format(
|
||||||
|
self.get_model_title(),
|
||||||
|
self.get_instance_title(parent)),
|
||||||
|
'form': form})
|
||||||
|
|
||||||
|
def save_edit_row_form(self, form):
|
||||||
|
self.save_row_form(form)
|
||||||
|
self.after_edit_row(form.fieldset.model)
|
||||||
|
|
||||||
|
def save_row_form(self, form):
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
def after_edit_row(self, row):
|
||||||
|
"""
|
||||||
|
Event hook, called just after an existing row object is saved.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def redirect_after_edit_row(self, row):
|
||||||
|
return self.redirect(self.get_action_url('view', self.get_parent(row)))
|
||||||
|
|
||||||
|
def row_editable(self, row):
|
||||||
|
"""
|
||||||
|
Returns boolean indicating whether or not the given row can be
|
||||||
|
considered "editable". Returns ``True`` by default; override as
|
||||||
|
necessary.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def row_deletable(self, row):
|
||||||
|
"""
|
||||||
|
Returns boolean indicating whether or not the given row can be
|
||||||
|
considered "deletable". Returns ``True`` by default; override as
|
||||||
|
necessary.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def delete_row(self):
|
||||||
|
"""
|
||||||
|
"Delete" a sub-row from the parent.
|
||||||
|
"""
|
||||||
|
row = self.Session.query(self.model_row_class).get(self.request.matchdict['uuid'])
|
||||||
|
if not row:
|
||||||
|
raise httpexceptions.HTTPNotFound()
|
||||||
|
self.Session.delete(row)
|
||||||
|
return self.redirect(self.get_action_url('edit', self.get_parent(row)))
|
||||||
|
|
||||||
|
def get_parent(self, row):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_row_instance_title(self, instance):
|
||||||
|
return self.get_row_model_title()
|
||||||
|
|
||||||
|
def get_row_instance(self):
|
||||||
|
key = self.request.matchdict[self.get_model_key()]
|
||||||
|
instance = self.Session.query(self.model_row_class).get(key)
|
||||||
|
if not instance:
|
||||||
|
raise httpexceptions.HTTPNotFound()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def make_row_form(self, instance, **kwargs):
|
||||||
|
"""
|
||||||
|
Make a FormAlchemy form for use with CRUD views for a data *row*.
|
||||||
|
"""
|
||||||
|
# TODO: Some hacky stuff here, to accommodate old form cruft. Probably
|
||||||
|
# should refactor forms soon too, but trying to avoid it for the moment.
|
||||||
|
|
||||||
|
kwargs.setdefault('creating', self.creating)
|
||||||
|
kwargs.setdefault('editing', self.editing)
|
||||||
|
|
||||||
|
fieldset = self.make_fieldset(instance)
|
||||||
|
self._preconfigure_row_fieldset(fieldset)
|
||||||
|
self.configure_row_fieldset(fieldset)
|
||||||
|
|
||||||
|
kwargs.setdefault('action_url', self.request.current_route_url(_query=None))
|
||||||
|
if self.creating:
|
||||||
|
kwargs.setdefault('cancel_url', self.get_action_url('view', self.get_parent(instance)))
|
||||||
|
else:
|
||||||
|
kwargs.setdefault('cancel_url', self.get_row_action_url('view', instance))
|
||||||
|
|
||||||
|
form = forms.AlchemyForm(self.request, fieldset, **kwargs)
|
||||||
|
form.readonly = self.viewing
|
||||||
|
return form
|
||||||
|
|
||||||
|
def _preconfigure_row_fieldset(self, fs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def configure_row_fieldset(self, fs):
|
||||||
|
fs.configure()
|
||||||
|
|
||||||
|
def get_row_action_url(self, action, row):
|
||||||
|
"""
|
||||||
|
Generate a URL for the given action on the given row.
|
||||||
|
"""
|
||||||
|
return self.request.route_url('{}.{}'.format(self.get_row_route_prefix(), action),
|
||||||
|
**self.get_row_action_route_kwargs(row))
|
||||||
|
|
||||||
|
def get_row_action_route_kwargs(self, row):
|
||||||
|
"""
|
||||||
|
Hopefully generic kwarg generator for basic action routes.
|
||||||
|
"""
|
||||||
|
# TODO: make this smarter?
|
||||||
|
return {'uuid': row.uuid}
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# Config Stuff
|
# Config Stuff
|
||||||
##############################
|
##############################
|
||||||
|
@ -892,6 +1106,10 @@ class MasterView(View):
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
model_title = cls.get_model_title()
|
model_title = cls.get_model_title()
|
||||||
model_title_plural = cls.get_model_title_plural()
|
model_title_plural = cls.get_model_title_plural()
|
||||||
|
if cls.has_rows:
|
||||||
|
row_route_prefix = cls.get_row_route_prefix()
|
||||||
|
row_url_prefix = cls.get_row_url_prefix()
|
||||||
|
row_model_title = cls.get_row_model_title()
|
||||||
|
|
||||||
config.add_tailbone_permission_group(permission_prefix, model_title_plural, overwrite=False)
|
config.add_tailbone_permission_group(permission_prefix, model_title_plural, overwrite=False)
|
||||||
|
|
||||||
|
@ -940,3 +1158,23 @@ class MasterView(View):
|
||||||
permission='{0}.delete'.format(permission_prefix))
|
permission='{0}.delete'.format(permission_prefix))
|
||||||
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
|
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
|
||||||
"Delete {0}".format(model_title))
|
"Delete {0}".format(model_title))
|
||||||
|
|
||||||
|
### sub-rows stuff follows
|
||||||
|
|
||||||
|
# view row
|
||||||
|
if cls.has_rows and cls.rows_viewable:
|
||||||
|
config.add_route('{}.view'.format(row_route_prefix), '{}/{{uuid}}'.format(row_url_prefix))
|
||||||
|
config.add_view(cls, attr='view_row', route_name='{}.view'.format(row_route_prefix),
|
||||||
|
permission='{}.view'.format(permission_prefix))
|
||||||
|
|
||||||
|
# edit row
|
||||||
|
if cls.has_rows and cls.rows_editable:
|
||||||
|
config.add_route('{}.edit'.format(row_route_prefix), '{}/{{uuid}}/edit'.format(row_url_prefix))
|
||||||
|
config.add_view(cls, attr='edit_row', route_name='{}.edit'.format(row_route_prefix),
|
||||||
|
permission='{}.edit'.format(permission_prefix))
|
||||||
|
|
||||||
|
# delete row
|
||||||
|
if cls.has_rows and cls.rows_deletable:
|
||||||
|
config.add_route('{}.delete'.format(row_route_prefix), '{}/{{uuid}}/delete'.format(row_url_prefix))
|
||||||
|
config.add_view(cls, attr='delete_row', route_name='{}.delete'.format(row_route_prefix),
|
||||||
|
permission='{}.edit'.format(permission_prefix))
|
||||||
|
|
2
tailbone/views/vendors/catalogs.py
vendored
2
tailbone/views/vendors/catalogs.py
vendored
|
@ -47,7 +47,7 @@ class VendorCatalogsView(FileBatchMasterView):
|
||||||
Master view for vendor catalog batches.
|
Master view for vendor catalog batches.
|
||||||
"""
|
"""
|
||||||
model_class = model.VendorCatalog
|
model_class = model.VendorCatalog
|
||||||
batch_row_class = model.VendorCatalogRow
|
model_row_class = model.VendorCatalogRow
|
||||||
batch_handler_class = VendorCatalogHandler
|
batch_handler_class = VendorCatalogHandler
|
||||||
url_prefix = '/vendors/catalogs'
|
url_prefix = '/vendors/catalogs'
|
||||||
|
|
||||||
|
|
2
tailbone/views/vendors/invoices.py
vendored
2
tailbone/views/vendors/invoices.py
vendored
|
@ -41,7 +41,7 @@ class VendorInvoicesView(FileBatchMasterView):
|
||||||
Master view for vendor invoice batches.
|
Master view for vendor invoice batches.
|
||||||
"""
|
"""
|
||||||
model_class = model.VendorInvoice
|
model_class = model.VendorInvoice
|
||||||
batch_row_class = model.VendorInvoiceRow
|
model_row_class = model.VendorInvoiceRow
|
||||||
batch_handler_class = VendorInvoiceHandler
|
batch_handler_class = VendorInvoiceHandler
|
||||||
url_prefix = '/vendors/invoices'
|
url_prefix = '/vendors/invoices'
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue