Allow update of row unit cost directly from receiving batch view
This commit is contained in:
parent
b7c710cddd
commit
c14cf3022c
|
@ -3,11 +3,120 @@
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
<%def name="extra_javascript()">
|
||||||
${parent.extra_javascript()}
|
${parent.extra_javascript()}
|
||||||
% if request.has_perm('{}.edit_row'.format(permission_prefix)):
|
% if master.has_perm('edit_row'):
|
||||||
|
${h.javascript_link(request.static_url('tailbone:static/js/numeric.js'))}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
% if not batch.executed:
|
||||||
|
// keep track of which cost value is currently being edited
|
||||||
|
var editing = null;
|
||||||
|
|
||||||
|
function start_editing(td) {
|
||||||
|
var value = 0;
|
||||||
|
var text = td.text().replace(/^\s+|\s+$/g, '');
|
||||||
|
if (text) {
|
||||||
|
td.data('previous-value', text);
|
||||||
|
td.text('');
|
||||||
|
value = parseFloat(text.replace('$', ''));
|
||||||
|
}
|
||||||
|
var input = $('<input type="text" />');
|
||||||
|
td.append(input);
|
||||||
|
input.val(value.toString()).select().focus();
|
||||||
|
editing = td;
|
||||||
|
}
|
||||||
|
|
||||||
|
function start_editing_next() {
|
||||||
|
var tr = editing.parents('tr:first');
|
||||||
|
var next = tr.next('tr:first');
|
||||||
|
if (next.length) {
|
||||||
|
start_editing(next.find('td.invoice_unit_cost'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel_edit() {
|
||||||
|
var input = editing.find('input');
|
||||||
|
input.blur();
|
||||||
|
input.remove();
|
||||||
|
var value = editing.data('previous-value');
|
||||||
|
if (value) {
|
||||||
|
editing.text(value);
|
||||||
|
}
|
||||||
|
editing = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
|
% if not batch.executed:
|
||||||
|
$('.grid-wrapper').on('click', '.grid td.invoice_unit_cost', function() {
|
||||||
|
if (editing) {
|
||||||
|
editing.find('input').focus();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var td = $(this);
|
||||||
|
start_editing(td);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.grid-wrapper').on('keyup', '.grid td.invoice_unit_cost input', function(event) {
|
||||||
|
var input = $(this);
|
||||||
|
|
||||||
|
// let numeric keys modify input value
|
||||||
|
if (! key_modifies(event)) {
|
||||||
|
|
||||||
|
// when user presses Enter while editing cost value, submit
|
||||||
|
// value to server for immediate persistence
|
||||||
|
if (event.which == 13) {
|
||||||
|
$('.grid-wrapper').mask("Updating cost...");
|
||||||
|
var url = '${url('receiving.update_row_cost', uuid=batch.uuid)}';
|
||||||
|
var td = input.parents('td:first');
|
||||||
|
var tr = td.parents('tr:first');
|
||||||
|
var data = {
|
||||||
|
'_csrf': $('[name="_csrf"]').val(),
|
||||||
|
'row_uuid': tr.data('uuid'),
|
||||||
|
'invoice_unit_cost': input.val()
|
||||||
|
};
|
||||||
|
$.post(url, data, function(data) {
|
||||||
|
if (data.error) {
|
||||||
|
alert(data.error);
|
||||||
|
} else {
|
||||||
|
var total = null;
|
||||||
|
|
||||||
|
// update unit cost for row
|
||||||
|
td.text(data.row.invoice_unit_cost);
|
||||||
|
|
||||||
|
// update invoice total for row
|
||||||
|
total = tr.find('td.invoice_total_calculated');
|
||||||
|
total.text('$' + data.row.invoice_total_calculated);
|
||||||
|
|
||||||
|
// update invoice total for batch
|
||||||
|
total = $('.form .field-wrapper.invoice_total_calculated .field');
|
||||||
|
total.text('$' + data.batch.invoice_total_calculated);
|
||||||
|
|
||||||
|
// mark cost as confirmed
|
||||||
|
if (data.row.invoice_cost_confirmed) {
|
||||||
|
tr.addClass('invoice_cost_confirmed');
|
||||||
|
}
|
||||||
|
|
||||||
|
input.blur();
|
||||||
|
input.remove();
|
||||||
|
start_editing_next();
|
||||||
|
}
|
||||||
|
$('.grid-wrapper').unmask();
|
||||||
|
});
|
||||||
|
|
||||||
|
// When user presses Escape while editing totals, cancel the edit.
|
||||||
|
} else if (event.which == 27) {
|
||||||
|
cancel_edit();
|
||||||
|
|
||||||
|
// Most other keys at this point should be unwanted...
|
||||||
|
} else if (! key_allowed(event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
% endif
|
||||||
|
|
||||||
$('.grid-wrapper').on('click', '.grid .actions a.transform', function() {
|
$('.grid-wrapper').on('click', '.grid .actions a.transform', function() {
|
||||||
|
|
||||||
var form = $('form[name="transform-unit-form"]');
|
var form = $('form[name="transform-unit-form"]');
|
||||||
|
@ -54,6 +163,24 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="extra_styles()">
|
||||||
|
${parent.extra_styles()}
|
||||||
|
% if not batch.executed and master.has_perm('edit_row'):
|
||||||
|
<style type="text/css">
|
||||||
|
.grid tr:not(.header) td.invoice_unit_cost {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fcc;
|
||||||
|
}
|
||||||
|
.grid tr.invoice_cost_confirmed:not(.header) td.invoice_unit_cost {
|
||||||
|
background-color: #cfc;
|
||||||
|
}
|
||||||
|
.grid td.invoice_unit_cost input {
|
||||||
|
width: 4rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="object_helpers()">
|
<%def name="object_helpers()">
|
||||||
${parent.object_helpers()}
|
${parent.object_helpers()}
|
||||||
% if not request.rattail_config.production() and not batch.executed and not batch.complete and request.has_perm('admin') and batch.is_truck_dump_parent() and batch.truck_dump_children_first:
|
% if not request.rattail_config.production() and not batch.executed and not batch.complete and request.has_perm('admin') and batch.is_truck_dump_parent() and batch.truck_dump_children_first:
|
||||||
|
|
|
@ -694,13 +694,12 @@ class BatchMasterView(MasterView):
|
||||||
if not batch.executed and not batch.complete:
|
if not batch.executed and not batch.complete:
|
||||||
|
|
||||||
# edit action
|
# edit action
|
||||||
if self.rows_editable:
|
if self.rows_editable and self.has_perm('edit_row'):
|
||||||
icon = 'edit' if use_buefy else 'pencil'
|
icon = 'edit' if use_buefy else 'pencil'
|
||||||
actions.append(self.make_action('edit', icon=icon, url=self.row_edit_action_url))
|
actions.append(self.make_action('edit', icon=icon, url=self.row_edit_action_url))
|
||||||
|
|
||||||
# delete action
|
# delete action
|
||||||
permission_prefix = self.get_permission_prefix()
|
if self.rows_deletable and self.has_perm('delete_row'):
|
||||||
if self.rows_deletable and self.request.has_perm('{}.delete_row'.format(permission_prefix)):
|
|
||||||
actions.append(self.make_action('delete', icon='trash', url=self.row_delete_action_url))
|
actions.append(self.make_action('delete', icon='trash', url=self.row_delete_action_url))
|
||||||
kwargs.setdefault('delete_speedbump', self.rows_deletable_speedbump)
|
kwargs.setdefault('delete_speedbump', self.rows_deletable_speedbump)
|
||||||
|
|
||||||
|
|
|
@ -489,7 +489,7 @@ class MasterView(View):
|
||||||
actions.append(self.make_action('view', icon=icon, url=view))
|
actions.append(self.make_action('view', icon=icon, url=view))
|
||||||
|
|
||||||
# edit action
|
# edit action
|
||||||
if self.rows_editable:
|
if self.rows_editable and self.has_perm('edit_row'):
|
||||||
icon = 'edit' if use_buefy else 'pencil'
|
icon = 'edit' if use_buefy else 'pencil'
|
||||||
actions.append(self.make_action('edit', icon=icon, url=self.row_edit_action_url))
|
actions.append(self.make_action('edit', icon=icon, url=self.row_edit_action_url))
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ Views for 'receiving' (purchasing) batches
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import decimal
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
@ -222,6 +223,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
'cases_received',
|
'cases_received',
|
||||||
'units_received',
|
'units_received',
|
||||||
# 'po_total',
|
# 'po_total',
|
||||||
|
'invoice_unit_cost',
|
||||||
'invoice_total_calculated',
|
'invoice_total_calculated',
|
||||||
'credits',
|
'credits',
|
||||||
'status_code',
|
'status_code',
|
||||||
|
@ -254,6 +256,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
'po_total',
|
'po_total',
|
||||||
'invoice_line_number',
|
'invoice_line_number',
|
||||||
'invoice_unit_cost',
|
'invoice_unit_cost',
|
||||||
|
'invoice_cost_confirmed',
|
||||||
'invoice_total',
|
'invoice_total',
|
||||||
'invoice_total_calculated',
|
'invoice_total_calculated',
|
||||||
'status_code',
|
'status_code',
|
||||||
|
@ -780,6 +783,11 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
super(ReceivingBatchView, self).configure_row_grid(g)
|
super(ReceivingBatchView, self).configure_row_grid(g)
|
||||||
g.set_label('department_name', "Department")
|
g.set_label('department_name', "Department")
|
||||||
|
|
||||||
|
# invoice_unit_cost
|
||||||
|
g.set_renderer('invoice_unit_cost', self.render_row_grid_unit_cost)
|
||||||
|
g.set_label('invoice_unit_cost', "Unit Cost")
|
||||||
|
g.filters['invoice_unit_cost'].label = "Invoice Unit Cost"
|
||||||
|
|
||||||
# credits
|
# credits
|
||||||
# note that sorting by credits involves a subquery with group by clause.
|
# note that sorting by credits involves a subquery with group by clause.
|
||||||
# seems likely there may be a better way? but this seems to work fine
|
# seems likely there may be a better way? but this seems to work fine
|
||||||
|
@ -816,6 +824,20 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
else:
|
else:
|
||||||
g.set_enum('truck_dump_status', model.PurchaseBatchRow.STATUS)
|
g.set_enum('truck_dump_status', model.PurchaseBatchRow.STATUS)
|
||||||
|
|
||||||
|
def row_grid_extra_class(self, row, i):
|
||||||
|
css_class = super(ReceivingBatchView, self).row_grid_extra_class(row, i)
|
||||||
|
|
||||||
|
if row.invoice_cost_confirmed:
|
||||||
|
css_class = '{} invoice_cost_confirmed'.format(css_class or '')
|
||||||
|
|
||||||
|
return css_class
|
||||||
|
|
||||||
|
def render_row_grid_unit_cost(self, row, field):
|
||||||
|
cost = getattr(row, field)
|
||||||
|
if cost is None:
|
||||||
|
return ""
|
||||||
|
return "{:0,.3f}".format(cost)
|
||||||
|
|
||||||
def transform_unit_url(self, row, i):
|
def transform_unit_url(self, row, i):
|
||||||
# grid action is shown only when we return a URL here
|
# grid action is shown only when we return a URL here
|
||||||
if self.row_editable(row):
|
if self.row_editable(row):
|
||||||
|
@ -1383,6 +1405,42 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
def redirect_after_edit_row(self, row, mobile=False):
|
def redirect_after_edit_row(self, row, mobile=False):
|
||||||
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
|
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
|
||||||
|
|
||||||
|
def update_row_cost(self):
|
||||||
|
"""
|
||||||
|
AJAX view for updating the invoice (actual) unit cost for a row.
|
||||||
|
"""
|
||||||
|
batch = self.get_instance()
|
||||||
|
data = self.request.POST
|
||||||
|
|
||||||
|
# validate row
|
||||||
|
uuid = data.get('row_uuid')
|
||||||
|
row = self.Session.query(model.PurchaseBatchRow).get(uuid) if uuid else None
|
||||||
|
if not row or row.batch is not batch:
|
||||||
|
return {'error': "Row not found"}
|
||||||
|
|
||||||
|
# validate cost
|
||||||
|
cost = data.get('invoice_unit_cost')
|
||||||
|
if cost is None:
|
||||||
|
return {'error': "You must specify a cost"}
|
||||||
|
try:
|
||||||
|
cost = decimal.Decimal(six.text_type(cost))
|
||||||
|
except decimal.InvalidOperation:
|
||||||
|
return {'error': "Cost is not valid!"}
|
||||||
|
|
||||||
|
# okay, update our row
|
||||||
|
self.handler.update_row_cost(row, invoice_unit_cost=cost)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'row': {
|
||||||
|
'invoice_unit_cost': '{:0.3f}'.format(row.invoice_unit_cost),
|
||||||
|
'invoice_cost_confirmed': row.invoice_cost_confirmed,
|
||||||
|
'invoice_total_calculated': '{:0.2f}'.format(row.invoice_total_calculated),
|
||||||
|
},
|
||||||
|
'batch': {
|
||||||
|
'invoice_total_calculated': '{:0.2f}'.format(batch.invoice_total_calculated),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def render_mobile_row_listitem(self, row, i):
|
def render_mobile_row_listitem(self, row, i):
|
||||||
key = self.render_product_key_value(row)
|
key = self.render_product_key_value(row)
|
||||||
description = row.product.full_description if row.product else row.description
|
description = row.product.full_description if row.product else row.description
|
||||||
|
@ -1721,6 +1779,7 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
rattail_config = config.registry.settings.get('rattail_config')
|
rattail_config = config.registry.settings.get('rattail_config')
|
||||||
route_prefix = cls.get_route_prefix()
|
route_prefix = cls.get_route_prefix()
|
||||||
url_prefix = cls.get_url_prefix()
|
url_prefix = cls.get_url_prefix()
|
||||||
|
instance_url_prefix = cls.get_instance_url_prefix()
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
|
||||||
|
@ -1737,6 +1796,12 @@ class ReceivingBatchView(PurchasingBatchView):
|
||||||
config.add_view(cls, attr='declare_credit', route_name='{}.declare_credit'.format(route_prefix),
|
config.add_view(cls, attr='declare_credit', route_name='{}.declare_credit'.format(route_prefix),
|
||||||
permission='{}.edit_row'.format(permission_prefix))
|
permission='{}.edit_row'.format(permission_prefix))
|
||||||
|
|
||||||
|
# update row cost
|
||||||
|
config.add_route('{}.update_row_cost'.format(route_prefix), '{}/update-row-cost'.format(instance_url_prefix))
|
||||||
|
config.add_view(cls, attr='update_row_cost', route_name='{}.update_row_cost'.format(route_prefix),
|
||||||
|
permission='{}.edit_row'.format(permission_prefix),
|
||||||
|
renderer='json')
|
||||||
|
|
||||||
if cls.allow_truck_dump:
|
if cls.allow_truck_dump:
|
||||||
|
|
||||||
# add TD child batch, from invoice file
|
# add TD child batch, from invoice file
|
||||||
|
|
Loading…
Reference in a new issue