Tweak worksheet_update() of ordering batch view, to leverage handler

specifically this is to make use of handler's `update_row_quantity()` method,
when user enters new order quantities via worksheet
This commit is contained in:
Lance Edgar 2020-02-24 12:36:47 -06:00
parent c3f4a3d9ea
commit fc830f60e8
4 changed files with 112 additions and 40 deletions

View file

@ -0,0 +1,13 @@
``tailbone.views.purchasing.ordering``
======================================
.. automodule:: tailbone.views.purchasing.ordering
.. autoclass:: OrderingBatchView
.. autoattribute:: model_class
.. autoattribute:: default_handler_spec
.. automethod:: worksheet_update

View file

@ -53,6 +53,7 @@ Package API:
api/views/core
api/views/master
api/views/purchasing.batch
api/views/purchasing.ordering
Documentation To-Do

View file

@ -36,10 +36,16 @@
if (data.error) {
alert(data.error);
} else {
row.find('input[name^="cases_ordered_"]').val(data.row_cases_ordered);
row.find('input[name^="units_ordered_"]').val(data.row_units_ordered);
row.find('td.po-total').html(data.row_po_total);
$('.po-total .field').html(data.batch_po_total);
if (data.row_cases_ordered || data.row_units_ordered) {
row.find('input[name^="cases_ordered_"]').val(data.row_cases_ordered);
row.find('input[name^="units_ordered_"]').val(data.row_units_ordered);
row.find('td.po-total').html(data.row_po_total_calculated);
} else {
row.find('input[name^="cases_ordered_"]').val('');
row.find('input[name^="units_ordered_"]').val('');
row.find('td.po-total').html('');
}
$('.po-total .field').html(data.batch_po_total_calculated);
}
submitting = false;
});
@ -151,7 +157,8 @@
<div class="field-wrapper po-total">
<label>PO Total</label>
<div class="field">$${'{:0,.2f}'.format(batch.po_total or 0)}</div>
## TODO: should not fall back to po_total
<div class="field">$${'{:0,.2f}'.format(batch.po_total_calculated or batch.po_total or 0)}</div>
</div>
</div><!-- form-wrapper -->
@ -270,7 +277,8 @@ ${h.end_form()}
<td class="current-order">
${h.text('units_ordered_{}'.format(cost.uuid), value=int(cost._batchrow.units_ordered or 0) if cost._batchrow else None)}
</td>
<td class="po-total">${'${:0,.2f}'.format(cost._batchrow.po_total or 0) if cost._batchrow else ''}</td>
## TODO: should not fall back to po_total
<td class="po-total">${'${:0,.2f}'.format(cost._batchrow.po_total_calculated or cost._batchrow.po_total or 0) if cost._batchrow else ''}</td>
${self.extra_td(cost)}
</tr>
% endfor

View file

@ -43,7 +43,7 @@ from tailbone.views.purchasing import PurchasingBatchView
class OrderingBatchView(PurchasingBatchView):
"""
Master view for purchase order batches.
Master view for "ordering" batches.
"""
route_prefix = 'ordering'
url_prefix = '/ordering'
@ -58,6 +58,33 @@ class OrderingBatchView(PurchasingBatchView):
mobile_rows_deletable = True
has_worksheet = True
labels = {
'po_total_calculated': "PO Total",
}
form_fields = [
'id',
'store',
'buyer',
'vendor',
'department',
'purchase',
'vendor_email',
'vendor_fax',
'vendor_contact',
'vendor_phone',
'date_ordered',
'po_number',
'po_total_calculated',
'notes',
'created',
'created_by',
'status_code',
'complete',
'executed',
'executed_by',
]
mobile_form_fields = [
'vendor',
'department',
@ -73,6 +100,10 @@ class OrderingBatchView(PurchasingBatchView):
'executed_by',
]
row_labels = {
'po_total_calculated': "PO Total",
}
row_grid_columns = [
'sequence',
'upc',
@ -84,7 +115,7 @@ class OrderingBatchView(PurchasingBatchView):
'units_ordered',
# 'cases_received',
# 'units_received',
'po_total',
'po_total_calculated',
# 'invoice_total',
# 'credits',
'status_code',
@ -227,7 +258,24 @@ class OrderingBatchView(PurchasingBatchView):
def worksheet_update(self):
"""
Handles AJAX requests to update current batch, from Order Form view.
Handles AJAX requests to update the order quantities for some row
within the current batch, from the worksheet view. POST data should
include:
* ``product_uuid``
* ``cases_ordered``
* ``units_ordered``
If a row already exists for the given product, it will be updated;
otherwise a new row is created for the product and then that is
updated. The handler's
:meth:`~rattail:rattail.batch.purchase.PurchaseBatchHandler.update_row_quantity()`
method is invoked to update the row.
However, if both of the quantities given are empty, and a row exists
for the given product, then that row is removed from the batch, instead
of being updated. If a matching row is not found, it will not be
created.
"""
batch = self.get_instance()
@ -250,41 +298,43 @@ class OrderingBatchView(PurchasingBatchView):
if not product:
return {'error': "Product not found"}
row = None
rows = [r for r in batch.data_rows if r.product_uuid == uuid]
if rows:
assert len(rows) == 1
row = rows[0]
if row.po_total and not row.removed:
batch.po_total -= row.po_total
if cases_ordered or units_ordered:
row.cases_ordered = cases_ordered or None
row.units_ordered = units_ordered or None
if row.removed:
row.removed = False
batch.rowcount += 1
self.handler.refresh_row(row)
if row.po_unit_cost:
row.po_total = row.po_unit_cost * self.handler.get_units_ordered(row)
batch.po_total = (batch.po_total or 0) + row.po_total
else:
row.removed = True
# first we find out which existing row(s) match the given product
matches = [row for row in batch.active_rows()
if row.product_uuid == product.uuid]
if matches and len(matches) != 1:
raise RuntimeError("found too many ({}) matches for product {} in batch {}".format(
len(matches), product.uuid, batch.uuid))
elif cases_ordered or units_ordered:
row = model.PurchaseBatchRow()
row.product = product
row.cases_ordered = cases_ordered or None
row.units_ordered = units_ordered or None
self.handler.add_row(batch, row)
if row.po_unit_cost:
row.po_total = row.po_unit_cost * self.handler.get_units_ordered(row)
batch.po_total = (batch.po_total or 0) + row.po_total
row = None
if cases_ordered or units_ordered:
# make a new row if necessary
if matches:
row = matches[0]
else:
row = self.handler.make_row()
row.product = product
self.handler.add_row(batch, row)
# update row quantities
self.handler.update_row_quantity(row, cases_ordered=cases_ordered,
units_ordered=units_ordered)
else: # empty order quantities
# remove row if present
if matches:
row = matches[0]
self.handler.do_remove_row(row)
row = None
return {
'row_cases_ordered': '' if not row or row.removed else int(row.cases_ordered or 0),
'row_units_ordered': '' if not row or row.removed else int(row.units_ordered or 0),
'row_po_total': '' if not row or row.removed else '${:0,.2f}'.format(row.po_total or 0),
'row_cases_ordered': int(row.cases_ordered or 0) if row else None,
'row_units_ordered': int(row.units_ordered or 0) if row else None,
'row_po_total': '${:0,.2f}'.format(row.po_total or 0) if row else None,
'row_po_total_calculated': '${:0,.2f}'.format(row.po_total_calculated or 0) if row else None,
'batch_po_total': '${:0,.2f}'.format(batch.po_total or 0),
'batch_po_total_calculated': '${:0,.2f}'.format(batch.po_total_calculated or 0),
}
def render_mobile_listitem(self, batch, i):