Various tweaks for support of native inventory
certaianly some other things made it in here too..
This commit is contained in:
parent
eb68eec520
commit
61d504afb8
|
@ -29,6 +29,7 @@ from __future__ import unicode_literals, absolute_import
|
||||||
import datetime
|
import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from rattail.time import localtime, make_utc
|
||||||
from rattail.util import pretty_quantity
|
from rattail.util import pretty_quantity
|
||||||
|
|
||||||
from webhelpers2.html import *
|
from webhelpers2.html import *
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
var has_execution_options = ${'true' if master.has_execution_options else 'false'};
|
var has_execution_options = ${'true' if master.has_execution_options else 'false'};
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
% if master.has_worksheet:
|
||||||
|
$('.load-worksheet').click(function() {
|
||||||
|
$(this).button('disable').button('option', 'label', "Working, please wait...");
|
||||||
|
location.href = '${url('{}.worksheet'.format(route_prefix), uuid=batch.uuid)}';
|
||||||
|
});
|
||||||
|
% endif
|
||||||
$('#refresh-data').click(function() {
|
$('#refresh-data').click(function() {
|
||||||
$(this)
|
$(this)
|
||||||
.button('option', 'disabled', true)
|
.button('option', 'disabled', true)
|
||||||
|
@ -50,7 +56,11 @@
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="leading_buttons()"></%def>
|
<%def name="leading_buttons()">
|
||||||
|
% if master.has_worksheet and not batch.executed and request.has_perm('{}.worksheet'.format(permission_prefix)):
|
||||||
|
<button type="button" class="load-worksheet">Edit as Worksheet</button>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="refresh_button()">
|
<%def name="refresh_button()">
|
||||||
% if master.viewing and master.batch_refreshable(batch) and request.has_perm('{}.refresh'.format(permission_prefix)):
|
% if master.viewing and master.batch_refreshable(batch) and request.has_perm('{}.refresh'.format(permission_prefix)):
|
||||||
|
|
45
tailbone/templates/batch/worksheet.mako
Normal file
45
tailbone/templates/batch/worksheet.mako
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/base.mako" />
|
||||||
|
|
||||||
|
<%def name="extra_javascript()">
|
||||||
|
${parent.extra_javascript()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
$('.worksheet .current-entry input').focus(function(event) {
|
||||||
|
$(this).parents('tr:first').addClass('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.worksheet .current-entry input').blur(function(event) {
|
||||||
|
$(this).parents('tr:first').removeClass('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="extra_styles()">
|
||||||
|
${parent.extra_styles()}
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
.worksheet tr.active {
|
||||||
|
border: 5px solid Blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worksheet .current-entry {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worksheet .current-entry input {
|
||||||
|
text-align: center;
|
||||||
|
width: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="worksheet_grid()"></%def>
|
||||||
|
|
||||||
|
|
||||||
|
${self.worksheet_grid()}
|
|
@ -8,7 +8,7 @@
|
||||||
## ##############################################################################
|
## ##############################################################################
|
||||||
<%inherit file="/base.mako" />
|
<%inherit file="/base.mako" />
|
||||||
|
|
||||||
<%def name="title()">${model_title_plural}</%def>
|
<%def name="title()">${index_title}</%def>
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
<%def name="extra_javascript()">
|
||||||
${parent.extra_javascript()}
|
${parent.extra_javascript()}
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
|
|
||||||
<%def name="title()">${model_title}</%def>
|
<%def name="title()">${model_title}</%def>
|
||||||
|
|
||||||
|
<%def name="content_title()">
|
||||||
|
<h1>Row ${instance.sequence}</h1>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="context_menu_items()">
|
<%def name="context_menu_items()">
|
||||||
<li>${h.link_to("Back to {}".format(parent_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)):
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
% if master:
|
% if master:
|
||||||
<span class="global">»</span>
|
<span class="global">»</span>
|
||||||
% if master.listing:
|
% if master.listing:
|
||||||
<span class="global">${model_title_plural}</span>
|
<span class="global">${index_title}</span>
|
||||||
% else:
|
% else:
|
||||||
${h.link_to(index_title, index_url, class_='global')}
|
${h.link_to(index_title, index_url, class_='global')}
|
||||||
% if instance_url is not Undefined:
|
% if instance_url is not Undefined:
|
||||||
|
|
|
@ -72,6 +72,7 @@ class BatchMasterView(MasterView):
|
||||||
supports_mobile = True
|
supports_mobile = True
|
||||||
mobile_filterable = True
|
mobile_filterable = True
|
||||||
mobile_rows_viewable = True
|
mobile_rows_viewable = True
|
||||||
|
has_worksheet = False
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
super(BatchMasterView, self).__init__(request)
|
super(BatchMasterView, self).__init__(request)
|
||||||
|
@ -422,6 +423,9 @@ class BatchMasterView(MasterView):
|
||||||
def editable_instance(self, batch):
|
def editable_instance(self, batch):
|
||||||
return not bool(batch.executed)
|
return not bool(batch.executed)
|
||||||
|
|
||||||
|
def after_edit_row(self, row):
|
||||||
|
self.handler.refresh_row(row)
|
||||||
|
|
||||||
def executable(self, batch=None):
|
def executable(self, batch=None):
|
||||||
return self.handler.executable(batch)
|
return self.handler.executable(batch)
|
||||||
|
|
||||||
|
@ -954,6 +958,17 @@ class BatchMasterView(MasterView):
|
||||||
config.add_view(cls, attr='prefill', route_name='{}.prefill'.format(route_prefix),
|
config.add_view(cls, attr='prefill', route_name='{}.prefill'.format(route_prefix),
|
||||||
permission='{}.create'.format(permission_prefix))
|
permission='{}.create'.format(permission_prefix))
|
||||||
|
|
||||||
|
# worksheet
|
||||||
|
if cls.has_worksheet:
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{}.worksheet'.format(permission_prefix),
|
||||||
|
"Edit {} data as worksheet".format(model_title))
|
||||||
|
config.add_route('{}.worksheet'.format(route_prefix), '{}/{{{}}}/worksheet'.format(url_prefix, model_key))
|
||||||
|
config.add_view(cls, attr='worksheet', route_name='{}.worksheet'.format(route_prefix),
|
||||||
|
permission='{}.worksheet'.format(permission_prefix))
|
||||||
|
config.add_route('{}.worksheet_update'.format(route_prefix), '{}/{{{}}}/worksheet/update'.format(url_prefix, model_key))
|
||||||
|
config.add_view(cls, attr='worksheet_update', route_name='{}.worksheet_update'.format(route_prefix),
|
||||||
|
renderer='json', permission='{}.worksheet'.format(permission_prefix))
|
||||||
|
|
||||||
# refresh batch data
|
# refresh batch 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),
|
||||||
|
|
|
@ -103,6 +103,7 @@ class BatchMasterView2(MasterView2, BatchMasterView):
|
||||||
|
|
||||||
g.set_label('sequence', "Seq.")
|
g.set_label('sequence', "Seq.")
|
||||||
g.set_label('status_code', "Status")
|
g.set_label('status_code', "Status")
|
||||||
|
g.set_label('item_id', "Item ID")
|
||||||
|
|
||||||
def render_row_status(self, row, column):
|
def render_row_status(self, row, column):
|
||||||
code = row.status_code
|
code = row.status_code
|
||||||
|
|
|
@ -59,10 +59,11 @@ class InventoryBatchView(BatchMasterView):
|
||||||
'id',
|
'id',
|
||||||
'created',
|
'created',
|
||||||
'created_by',
|
'created_by',
|
||||||
|
'mode',
|
||||||
'rowcount',
|
'rowcount',
|
||||||
|
'total_cost',
|
||||||
'executed',
|
'executed',
|
||||||
'executed_by',
|
'executed_by',
|
||||||
'mode',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
model_row_class = model.InventoryBatchRow
|
model_row_class = model.InventoryBatchRow
|
||||||
|
@ -71,18 +72,21 @@ class InventoryBatchView(BatchMasterView):
|
||||||
row_grid_columns = [
|
row_grid_columns = [
|
||||||
'sequence',
|
'sequence',
|
||||||
'upc',
|
'upc',
|
||||||
|
'item_id',
|
||||||
'brand_name',
|
'brand_name',
|
||||||
'description',
|
'description',
|
||||||
'size',
|
'size',
|
||||||
'cases',
|
'cases',
|
||||||
'units',
|
'units',
|
||||||
'unit_cost',
|
'unit_cost',
|
||||||
|
'total_cost',
|
||||||
'status_code',
|
'status_code',
|
||||||
]
|
]
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def configure_grid(self, g):
|
||||||
super(InventoryBatchView, self).configure_grid(g)
|
super(InventoryBatchView, self).configure_grid(g)
|
||||||
g.set_enum('mode', self.enum.INVENTORY_MODE)
|
g.set_enum('mode', self.enum.INVENTORY_MODE)
|
||||||
|
g.set_type('total_cost', 'currency')
|
||||||
g.set_label('mode', "Count Mode")
|
g.set_label('mode', "Count Mode")
|
||||||
|
|
||||||
def render_mobile_listitem(self, batch, i):
|
def render_mobile_listitem(self, batch, i):
|
||||||
|
@ -96,6 +100,7 @@ class InventoryBatchView(BatchMasterView):
|
||||||
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
super(InventoryBatchView, self)._preconfigure_fieldset(fs)
|
||||||
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE),
|
fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE),
|
||||||
label="Count Mode")
|
label="Count Mode")
|
||||||
|
fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
||||||
fs.append(fa.Field('handheld_batches', renderer=forms.renderers.HandheldBatchesFieldRenderer, readonly=True,
|
fs.append(fa.Field('handheld_batches', renderer=forms.renderers.HandheldBatchesFieldRenderer, readonly=True,
|
||||||
value=lambda b: b._handhelds))
|
value=lambda b: b._handhelds))
|
||||||
|
|
||||||
|
@ -114,6 +119,22 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.executed_by,
|
fs.executed_by,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def save_edit_row_form(self, form):
|
||||||
|
row = form.fieldset.model
|
||||||
|
batch = row.batch
|
||||||
|
if batch.total_cost is not None and row.total_cost is not None:
|
||||||
|
batch.total_cost -= row.total_cost
|
||||||
|
return super(InventoryBatchView, self).save_edit_row_form(form)
|
||||||
|
|
||||||
|
def delete_row(self):
|
||||||
|
row = self.Session.query(model.InventoryBatchRow).get(self.request.matchdict['uuid'])
|
||||||
|
if not row:
|
||||||
|
raise self.notfound()
|
||||||
|
batch = row.batch
|
||||||
|
if batch.total_cost is not None and row.total_cost is not None:
|
||||||
|
batch.total_cost -= row.total_cost
|
||||||
|
return super(InventoryBatchView, self).delete_row()
|
||||||
|
|
||||||
def configure_mobile_fieldset(self, fs):
|
def configure_mobile_fieldset(self, fs):
|
||||||
fs.configure(include=[
|
fs.configure(include=[
|
||||||
fs.mode,
|
fs.mode,
|
||||||
|
@ -220,9 +241,15 @@ class InventoryBatchView(BatchMasterView):
|
||||||
def configure_row_grid(self, g):
|
def configure_row_grid(self, g):
|
||||||
super(InventoryBatchView, self).configure_row_grid(g)
|
super(InventoryBatchView, self).configure_row_grid(g)
|
||||||
|
|
||||||
g.set_renderer('cases', 'quantity')
|
g.set_type('cases', 'quantity')
|
||||||
g.set_renderer('units', 'quantity')
|
g.set_type('units', 'quantity')
|
||||||
g.set_renderer('unit_cost', 'currency')
|
g.set_type('unit_cost', 'currency')
|
||||||
|
g.set_type('total_cost', 'currency')
|
||||||
|
|
||||||
|
# TODO: i guess row grids don't support this properly yet?
|
||||||
|
# g.set_link('upc')
|
||||||
|
# g.set_link('item_id')
|
||||||
|
# g.set_link('description')
|
||||||
|
|
||||||
g.set_label('upc', "UPC")
|
g.set_label('upc', "UPC")
|
||||||
g.set_label('brand_name', "Brand")
|
g.set_label('brand_name', "Brand")
|
||||||
|
@ -242,10 +269,14 @@ class InventoryBatchView(BatchMasterView):
|
||||||
super(InventoryBatchView, self)._preconfigure_row_fieldset(fs)
|
super(InventoryBatchView, self)._preconfigure_row_fieldset(fs)
|
||||||
fs.upc.set(readonly=True, label="UPC", renderer=forms.renderers.GPCFieldRenderer,
|
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)})
|
attrs={'link': lambda r: self.request.route_url('products.view', uuid=r.product_uuid)})
|
||||||
|
fs.item_id.set(readonly=True)
|
||||||
fs.brand_name.set(readonly=True)
|
fs.brand_name.set(readonly=True)
|
||||||
fs.description.set(readonly=True)
|
fs.description.set(readonly=True)
|
||||||
fs.size.set(readonly=True)
|
fs.size.set(readonly=True)
|
||||||
fs.unit_cost.set(renderer=forms.renderers.CurrencyFieldRenderer)
|
fs.cases.set(renderer=forms.renderers.QuantityFieldRenderer)
|
||||||
|
fs.units.set(renderer=forms.renderers.QuantityFieldRenderer)
|
||||||
|
fs.unit_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
||||||
|
fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer)
|
||||||
|
|
||||||
def configure_row_fieldset(self, fs):
|
def configure_row_fieldset(self, fs):
|
||||||
fs.configure(
|
fs.configure(
|
||||||
|
@ -259,18 +290,22 @@ class InventoryBatchView(BatchMasterView):
|
||||||
fs.cases,
|
fs.cases,
|
||||||
fs.units,
|
fs.units,
|
||||||
fs.unit_cost,
|
fs.unit_cost,
|
||||||
|
fs.total_cost,
|
||||||
])
|
])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
|
cls._inventory_defaults(config)
|
||||||
|
cls._batch_defaults(config)
|
||||||
|
cls._defaults(config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _inventory_defaults(cls, config):
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
route_prefix = cls.get_route_prefix()
|
route_prefix = cls.get_route_prefix()
|
||||||
url_prefix = cls.get_url_prefix()
|
url_prefix = cls.get_url_prefix()
|
||||||
row_permission_prefix = cls.get_row_permission_prefix()
|
row_permission_prefix = cls.get_row_permission_prefix()
|
||||||
|
|
||||||
cls._batch_defaults(config)
|
|
||||||
cls._defaults(config)
|
|
||||||
|
|
||||||
# mobile - make new row from UPC
|
# mobile - make new row from UPC
|
||||||
config.add_route('mobile.{}.row_from_upc'.format(route_prefix), '/mobile{}/{{{}}}/row-from-upc'.format(url_prefix, model_key))
|
config.add_route('mobile.{}.row_from_upc'.format(route_prefix), '/mobile{}/{{{}}}/row-from-upc'.format(url_prefix, model_key))
|
||||||
config.add_view(cls, attr='mobile_row_from_upc', route_name='mobile.{}.row_from_upc'.format(route_prefix),
|
config.add_view(cls, attr='mobile_row_from_upc', route_name='mobile.{}.row_from_upc'.format(route_prefix),
|
||||||
|
|
|
@ -1340,17 +1340,15 @@ class MasterView(View):
|
||||||
parent = self.get_parent(row)
|
parent = self.get_parent(row)
|
||||||
return self.render_to_response('view_row', {
|
return self.render_to_response('view_row', {
|
||||||
'instance': row,
|
'instance': row,
|
||||||
'instance_title': self.get_row_instance_title(row),
|
'batch': row.batch,
|
||||||
|
'instance_title': self.get_instance_title(row.batch),
|
||||||
|
'instance_url': self.get_action_url('view', parent),
|
||||||
'instance_editable': self.row_editable(row),
|
'instance_editable': self.row_editable(row),
|
||||||
'instance_deletable': self.row_deletable(row),
|
'instance_deletable': self.row_deletable(row),
|
||||||
'rows_creatable': self.rows_creatable and self.rows_creatable_for(parent),
|
'rows_creatable': self.rows_creatable and self.rows_creatable_for(parent),
|
||||||
'model_title': self.get_row_model_title(),
|
'model_title': self.get_row_model_title(),
|
||||||
'model_title_plural': self.get_row_model_title_plural(),
|
'model_title_plural': self.get_row_model_title_plural(),
|
||||||
'parent_model_title': self.get_model_title(),
|
'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,
|
'action_url': self.get_row_action_url,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
|
|
@ -291,6 +291,10 @@ class ProductsView(MasterView):
|
||||||
fs.last_sold.set(readonly=True)
|
fs.last_sold.set(readonly=True)
|
||||||
fs.append(fa.Field('current_price_ends', type=fa.types.DateTime, readonly=True,
|
fs.append(fa.Field('current_price_ends', type=fa.types.DateTime, readonly=True,
|
||||||
value=lambda p: p.current_price.ends if p.current_price else None))
|
value=lambda p: p.current_price.ends if p.current_price else None))
|
||||||
|
fs.append(fa.Field('inventory_on_hand', readonly=True, label="On Hand",
|
||||||
|
value=lambda p: p.inventory.on_hand if p.inventory else None))
|
||||||
|
fs.append(fa.Field('inventory_on_order', readonly=True, label="On Order",
|
||||||
|
value=lambda p: p.inventory.on_order if p.inventory else None))
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
fs.configure(
|
fs.configure(
|
||||||
|
|
Loading…
Reference in a new issue