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:
parent
c3f4a3d9ea
commit
fc830f60e8
13
docs/api/views/purchasing.ordering.rst
Normal file
13
docs/api/views/purchasing.ordering.rst
Normal 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
|
|
@ -53,6 +53,7 @@ Package API:
|
||||||
api/views/core
|
api/views/core
|
||||||
api/views/master
|
api/views/master
|
||||||
api/views/purchasing.batch
|
api/views/purchasing.batch
|
||||||
|
api/views/purchasing.ordering
|
||||||
|
|
||||||
|
|
||||||
Documentation To-Do
|
Documentation To-Do
|
||||||
|
|
|
@ -36,10 +36,16 @@
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
alert(data.error);
|
alert(data.error);
|
||||||
} else {
|
} else {
|
||||||
|
if (data.row_cases_ordered || data.row_units_ordered) {
|
||||||
row.find('input[name^="cases_ordered_"]').val(data.row_cases_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('input[name^="units_ordered_"]').val(data.row_units_ordered);
|
||||||
row.find('td.po-total').html(data.row_po_total);
|
row.find('td.po-total').html(data.row_po_total_calculated);
|
||||||
$('.po-total .field').html(data.batch_po_total);
|
} 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;
|
submitting = false;
|
||||||
});
|
});
|
||||||
|
@ -151,7 +157,8 @@
|
||||||
|
|
||||||
<div class="field-wrapper po-total">
|
<div class="field-wrapper po-total">
|
||||||
<label>PO Total</label>
|
<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>
|
||||||
|
|
||||||
</div><!-- form-wrapper -->
|
</div><!-- form-wrapper -->
|
||||||
|
@ -270,7 +277,8 @@ ${h.end_form()}
|
||||||
<td class="current-order">
|
<td class="current-order">
|
||||||
${h.text('units_ordered_{}'.format(cost.uuid), value=int(cost._batchrow.units_ordered or 0) if cost._batchrow else None)}
|
${h.text('units_ordered_{}'.format(cost.uuid), value=int(cost._batchrow.units_ordered or 0) if cost._batchrow else None)}
|
||||||
</td>
|
</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)}
|
${self.extra_td(cost)}
|
||||||
</tr>
|
</tr>
|
||||||
% endfor
|
% endfor
|
||||||
|
|
|
@ -43,7 +43,7 @@ from tailbone.views.purchasing import PurchasingBatchView
|
||||||
|
|
||||||
class OrderingBatchView(PurchasingBatchView):
|
class OrderingBatchView(PurchasingBatchView):
|
||||||
"""
|
"""
|
||||||
Master view for purchase order batches.
|
Master view for "ordering" batches.
|
||||||
"""
|
"""
|
||||||
route_prefix = 'ordering'
|
route_prefix = 'ordering'
|
||||||
url_prefix = '/ordering'
|
url_prefix = '/ordering'
|
||||||
|
@ -58,6 +58,33 @@ class OrderingBatchView(PurchasingBatchView):
|
||||||
mobile_rows_deletable = True
|
mobile_rows_deletable = True
|
||||||
has_worksheet = 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 = [
|
mobile_form_fields = [
|
||||||
'vendor',
|
'vendor',
|
||||||
'department',
|
'department',
|
||||||
|
@ -73,6 +100,10 @@ class OrderingBatchView(PurchasingBatchView):
|
||||||
'executed_by',
|
'executed_by',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
row_labels = {
|
||||||
|
'po_total_calculated': "PO Total",
|
||||||
|
}
|
||||||
|
|
||||||
row_grid_columns = [
|
row_grid_columns = [
|
||||||
'sequence',
|
'sequence',
|
||||||
'upc',
|
'upc',
|
||||||
|
@ -84,7 +115,7 @@ class OrderingBatchView(PurchasingBatchView):
|
||||||
'units_ordered',
|
'units_ordered',
|
||||||
# 'cases_received',
|
# 'cases_received',
|
||||||
# 'units_received',
|
# 'units_received',
|
||||||
'po_total',
|
'po_total_calculated',
|
||||||
# 'invoice_total',
|
# 'invoice_total',
|
||||||
# 'credits',
|
# 'credits',
|
||||||
'status_code',
|
'status_code',
|
||||||
|
@ -227,7 +258,24 @@ class OrderingBatchView(PurchasingBatchView):
|
||||||
|
|
||||||
def worksheet_update(self):
|
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()
|
batch = self.get_instance()
|
||||||
|
|
||||||
|
@ -250,41 +298,43 @@ class OrderingBatchView(PurchasingBatchView):
|
||||||
if not product:
|
if not product:
|
||||||
return {'error': "Product not found"}
|
return {'error': "Product not found"}
|
||||||
|
|
||||||
row = None
|
# first we find out which existing row(s) match the given product
|
||||||
rows = [r for r in batch.data_rows if r.product_uuid == uuid]
|
matches = [row for row in batch.active_rows()
|
||||||
if rows:
|
if row.product_uuid == product.uuid]
|
||||||
assert len(rows) == 1
|
if matches and len(matches) != 1:
|
||||||
row = rows[0]
|
raise RuntimeError("found too many ({}) matches for product {} in batch {}".format(
|
||||||
if row.po_total and not row.removed:
|
len(matches), product.uuid, batch.uuid))
|
||||||
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
|
|
||||||
|
|
||||||
elif cases_ordered or units_ordered:
|
row = None
|
||||||
row = model.PurchaseBatchRow()
|
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
|
row.product = product
|
||||||
row.cases_ordered = cases_ordered or None
|
|
||||||
row.units_ordered = units_ordered or None
|
|
||||||
self.handler.add_row(batch, row)
|
self.handler.add_row(batch, row)
|
||||||
if row.po_unit_cost:
|
|
||||||
row.po_total = row.po_unit_cost * self.handler.get_units_ordered(row)
|
# update row quantities
|
||||||
batch.po_total = (batch.po_total or 0) + row.po_total
|
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 {
|
return {
|
||||||
'row_cases_ordered': '' if not row or row.removed else int(row.cases_ordered or 0),
|
'row_cases_ordered': int(row.cases_ordered or 0) if row else None,
|
||||||
'row_units_ordered': '' if not row or row.removed else int(row.units_ordered or 0),
|
'row_units_ordered': int(row.units_ordered or 0) if row else None,
|
||||||
'row_po_total': '' if not row or row.removed else '${:0,.2f}'.format(row.po_total or 0),
|
'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': '${: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):
|
def render_mobile_listitem(self, batch, i):
|
||||||
|
|
Loading…
Reference in a new issue