Expose catalog cost, allow updating, for receiving batch rows

This commit is contained in:
Lance Edgar 2019-11-26 11:19:55 -06:00
parent 3e1409afc5
commit 675660e130
2 changed files with 162 additions and 42 deletions

View file

@ -9,10 +9,11 @@
% if not batch.executed: % if not batch.executed:
// keep track of which cost value is currently being edited // keep track of which cost value is currently being edited
var editing = null; var editing_catalog_cost = null;
var editing_invoice_cost = null;
function start_editing(td) { function start_editing(td) {
var value = 0; var value = null;
var text = td.text().replace(/^\s+|\s+$/g, ''); var text = td.text().replace(/^\s+|\s+$/g, '');
if (text) { if (text) {
td.data('previous-value', text); td.data('previous-value', text);
@ -21,27 +22,58 @@
} }
var input = $('<input type="text" />'); var input = $('<input type="text" />');
td.append(input); td.append(input);
input.val(value.toString()).select().focus(); value = value ? value.toString() : '';
editing = td; input.val(value).select().focus();
} }
function start_editing_next() { function start_editing_catalog_cost(td) {
var tr = editing.parents('tr:first'); start_editing(td);
editing_catalog_cost = td;
}
function start_editing_invoice_cost(td) {
start_editing(td);
editing_invoice_cost = td;
}
function start_editing_next_catalog_cost() {
var tr = editing_catalog_cost.parents('tr:first');
var next = tr.next('tr:first'); var next = tr.next('tr:first');
if (next.length) { if (next.length) {
start_editing(next.find('td.invoice_unit_cost')); start_editing_catalog_cost(next.find('td.catalog_unit_cost'));
} else {
editing_catalog_cost = null;
} }
} }
function cancel_edit() { function start_editing_next_invoice_cost() {
var input = editing.find('input'); var tr = editing_invoice_cost.parents('tr:first');
var next = tr.next('tr:first');
if (next.length) {
start_editing_invoice_cost(next.find('td.invoice_unit_cost'));
} else {
editing_invoice_cost = null;
}
}
function cancel_edit(td) {
var input = td.find('input');
input.blur(); input.blur();
input.remove(); input.remove();
var value = editing.data('previous-value'); var value = td.data('previous-value');
if (value) { if (value) {
editing.text(value); td.text(value);
} }
editing = null; }
function cancel_edit_catalog_cost() {
cancel_edit(editing_catalog_cost);
editing_catalog_cost = null;
}
function cancel_edit_invoice_cost() {
cancel_edit(editing_invoice_cost);
editing_invoice_cost = null;
} }
% endif % endif
@ -49,13 +81,80 @@
$(function() { $(function() {
% if not batch.executed: % if not batch.executed:
$('.grid-wrapper').on('click', '.grid td.invoice_unit_cost', function() { $('.grid-wrapper').on('click', '.grid td.catalog_unit_cost', function() {
if (editing) { if (editing_catalog_cost) {
editing.find('input').focus(); editing_catalog_cost.find('input').focus();
return
}
if (editing_invoice_cost) {
editing_invoice_cost.find('input').focus();
return return
} }
var td = $(this); var td = $(this);
start_editing(td); start_editing_catalog_cost(td);
});
$('.grid-wrapper').on('click', '.grid td.invoice_unit_cost', function() {
if (editing_invoice_cost) {
editing_invoice_cost.find('input').focus();
return
}
if (editing_catalog_cost) {
editing_catalog_cost.find('input').focus();
return
}
var td = $(this);
start_editing_invoice_cost(td);
});
$('.grid-wrapper').on('keyup', '.grid td.catalog_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'),
'catalog_unit_cost': input.val()
};
$.post(url, data, function(data) {
if (data.error) {
alert(data.error);
} else {
var total = null;
// update catalog cost for row
td.text(data.row.catalog_unit_cost);
// mark cost as confirmed
if (data.row.catalog_cost_confirmed) {
tr.addClass('catalog_cost_confirmed');
}
input.blur();
input.remove();
start_editing_next_catalog_cost();
}
$('.grid-wrapper').unmask();
});
// When user presses Escape while editing totals, cancel the edit.
} else if (event.which == 27) {
cancel_edit_catalog_cost();
// Most other keys at this point should be unwanted...
} else if (! key_allowed(event)) {
return false;
}
}
}); });
$('.grid-wrapper').on('keyup', '.grid td.invoice_unit_cost input', function(event) { $('.grid-wrapper').on('keyup', '.grid td.invoice_unit_cost input', function(event) {
@ -100,14 +199,14 @@
input.blur(); input.blur();
input.remove(); input.remove();
start_editing_next(); start_editing_next_invoice_cost();
} }
$('.grid-wrapper').unmask(); $('.grid-wrapper').unmask();
}); });
// When user presses Escape while editing totals, cancel the edit. // When user presses Escape while editing totals, cancel the edit.
} else if (event.which == 27) { } else if (event.which == 27) {
cancel_edit(); cancel_edit_invoice_cost();
// Most other keys at this point should be unwanted... // Most other keys at this point should be unwanted...
} else if (! key_allowed(event)) { } else if (! key_allowed(event)) {
@ -167,13 +266,16 @@
${parent.extra_styles()} ${parent.extra_styles()}
% if not batch.executed and master.has_perm('edit_row'): % if not batch.executed and master.has_perm('edit_row'):
<style type="text/css"> <style type="text/css">
.grid tr:not(.header) td.catalog_unit_cost,
.grid tr:not(.header) td.invoice_unit_cost { .grid tr:not(.header) td.invoice_unit_cost {
cursor: pointer; cursor: pointer;
background-color: #fcc; background-color: #fcc;
} }
.grid tr.catalog_cost_confirmed:not(.header) td.catalog_unit_cost,
.grid tr.invoice_cost_confirmed:not(.header) td.invoice_unit_cost { .grid tr.invoice_cost_confirmed:not(.header) td.invoice_unit_cost {
background-color: #cfc; background-color: #cfc;
} }
.grid td.catalog_unit_cost input,
.grid td.invoice_unit_cost input { .grid td.invoice_unit_cost input {
width: 4rem; width: 4rem;
} }
@ -183,16 +285,20 @@
<%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():
<div class="object-helper"> % if not batch.executed and not batch.complete and request.has_perm('admin'):
<h3>Development Tools</h3> % if (batch.is_truck_dump_parent() and batch.truck_dump_children_first) or not batch.is_truck_dump_related():
<div class="object-helper-content"> <div class="object-helper">
${h.form(url('{}.auto_receive'.format(route_prefix), uuid=batch.uuid), class_='autodisable')} <h3>Development Tools</h3>
${h.csrf_token(request)} <div class="object-helper-content">
${h.submit('submit', "Auto-Receive All Items")} ${h.form(url('{}.auto_receive'.format(route_prefix), uuid=batch.uuid), class_='autodisable')}
${h.end_form()} ${h.csrf_token(request)}
</div> ${h.submit('submit', "Auto-Receive All Items")}
</div> ${h.end_form()}
</div>
</div>
% endif
% endif
% endif % endif
</%def> </%def>

View file

@ -223,7 +223,7 @@ class ReceivingBatchView(PurchasingBatchView):
'units_shipped', 'units_shipped',
'cases_received', 'cases_received',
'units_received', 'units_received',
# 'po_total', 'catalog_unit_cost',
'invoice_unit_cost', 'invoice_unit_cost',
'invoice_total_calculated', 'invoice_total_calculated',
'credits', 'credits',
@ -789,9 +789,14 @@ class ReceivingBatchView(PurchasingBatchView):
g.filters['vendor_code'].default_active = True g.filters['vendor_code'].default_active = True
g.filters['vendor_code'].default_verb = 'contains' g.filters['vendor_code'].default_verb = 'contains'
# catalog_unit_cost
g.set_renderer('catalog_unit_cost', self.render_row_grid_cost)
g.set_label('catalog_unit_cost', "Catalog Cost")
g.filters['catalog_unit_cost'].label = "Catalog Unit Cost"
# invoice_unit_cost # invoice_unit_cost
g.set_renderer('invoice_unit_cost', self.render_row_grid_unit_cost) g.set_renderer('invoice_unit_cost', self.render_row_grid_cost)
g.set_label('invoice_unit_cost', "Unit Cost") g.set_label('invoice_unit_cost', "Invoice Cost")
g.filters['invoice_unit_cost'].label = "Invoice Unit Cost" g.filters['invoice_unit_cost'].label = "Invoice Unit Cost"
# credits # credits
@ -833,12 +838,15 @@ class ReceivingBatchView(PurchasingBatchView):
def row_grid_extra_class(self, row, i): def row_grid_extra_class(self, row, i):
css_class = super(ReceivingBatchView, self).row_grid_extra_class(row, i) css_class = super(ReceivingBatchView, self).row_grid_extra_class(row, i)
if row.catalog_cost_confirmed:
css_class = '{} catalog_cost_confirmed'.format(css_class or '')
if row.invoice_cost_confirmed: if row.invoice_cost_confirmed:
css_class = '{} invoice_cost_confirmed'.format(css_class or '') css_class = '{} invoice_cost_confirmed'.format(css_class or '')
return css_class return css_class
def render_row_grid_unit_cost(self, row, field): def render_row_grid_cost(self, row, field):
cost = getattr(row, field) cost = getattr(row, field)
if cost is None: if cost is None:
return "" return ""
@ -1419,7 +1427,7 @@ class ReceivingBatchView(PurchasingBatchView):
AJAX view for updating the invoice (actual) unit cost for a row. AJAX view for updating the invoice (actual) unit cost for a row.
""" """
batch = self.get_instance() batch = self.get_instance()
data = self.request.POST data = dict(self.request.POST)
# validate row # validate row
uuid = data.get('row_uuid') uuid = data.get('row_uuid')
@ -1427,20 +1435,26 @@ class ReceivingBatchView(PurchasingBatchView):
if not row or row.batch is not batch: if not row or row.batch is not batch:
return {'error': "Row not found"} return {'error': "Row not found"}
# validate cost # validate/normalize cost value(s)
cost = data.get('invoice_unit_cost') for field in ('catalog_unit_cost', 'invoice_unit_cost'):
if cost is None: if field in data:
return {'error': "You must specify a cost"} cost = data[field]
try: if cost == '':
cost = decimal.Decimal(six.text_type(cost)) return {'error': "You must specify a cost"}
except decimal.InvalidOperation: try:
return {'error': "Cost is not valid!"} cost = decimal.Decimal(six.text_type(cost))
except decimal.InvalidOperation:
return {'error': "Cost is not valid!"}
else:
data[field] = cost
# okay, update our row # okay, update our row
self.handler.update_row_cost(row, invoice_unit_cost=cost) self.handler.update_row_cost(row, **data)
return { return {
'row': { 'row': {
'catalog_unit_cost': '{:0.3f}'.format(row.catalog_unit_cost),
'catalog_cost_confirmed': row.catalog_cost_confirmed,
'invoice_unit_cost': '{:0.3f}'.format(row.invoice_unit_cost), 'invoice_unit_cost': '{:0.3f}'.format(row.invoice_unit_cost),
'invoice_cost_confirmed': row.invoice_cost_confirmed, 'invoice_cost_confirmed': row.invoice_cost_confirmed,
'invoice_total_calculated': '{:0.2f}'.format(row.invoice_total_calculated), 'invoice_total_calculated': '{:0.2f}'.format(row.invoice_total_calculated),