Add update_row_quantity(), order_row() methods for purchase batch handler

This commit is contained in:
Lance Edgar 2020-02-23 20:12:17 -06:00
parent a39c095543
commit cf8b0282a5
2 changed files with 109 additions and 0 deletions

View file

@ -19,6 +19,8 @@
.. automethod:: refresh_row
.. automethod:: order_row
.. automethod:: receive_row
.. automethod:: receiving_update_row_attrs
@ -31,6 +33,8 @@
.. automethod:: receiving_find_best_child_row
.. automethod:: update_row_quantity
.. automethod:: update_row_cost
.. automethod:: refresh

View file

@ -1553,6 +1553,111 @@ class PurchaseBatchHandler(BatchHandler):
session.flush()
self.refresh_row(row)
def update_row_quantity(self, row, **kwargs):
"""
Update quantity value(s) for the given row, and calculate new totals
accordingly. This will handle updating the row as well as the batch,
as necessary. Which kwargs this method accepts, and which values are
updated, will depend on the batch mode.
*Ordering Mode*
Possible kwargs are:
* ``cases_ordered``
* ``units_ordered``
Logic will figure out the "diff" between the given quantites, and the
row's existing values at the time. It then invokes :meth:`order_row()`
with the diff values.
"""
batch = row.batch
if batch.mode == self.enum.PURCHASE_BATCH_MODE_ORDERING:
if 'cases_ordered' in kwargs:
cases_diff = kwargs['cases_ordered'] - (row.cases_ordered or 0)
if cases_diff:
self.order_row(row, cases=cases_diff)
if 'units_ordered' in kwargs:
units_diff = kwargs['units_ordered'] - (row.units_ordered or 0)
if units_diff:
self.order_row(row, units=units_diff)
def order_row(self, row, cases=None, units=None, **kwargs):
"""
This method is conceptually similar to :meth:`receive_row()` and, while
the latter is more "necessary" than this one is, this method tries to
match its style just for consistency. Callers may or may not need this
method directly, but are welcome to use it.
Each call to this method must include the row to be updated, as well as
the details of the update. These details should reflect "changes"
which are to be made, as opposed to "final values" for the row. In
other words if a row already has ``cases_ordered == 1`` and the user
is ordering a second case, this method should be called like so::
handler.order_row(row, cases=1)
The row will be updated such that ``cases_ordered == 2``; the main
point here is that the caller should *not* specify ``cases=2`` because
it is the handler's job to "apply changes" from the caller. (If the
caller speficies ``cases=2`` then the row would end up with
``cases_ordered == 3``.)
See also :meth:`update_row_quantity()` which allows the caller to
specify the final values instead.
For "undo" type adjustments, caller can just send a negative amount,
and the handler will apply the changes as expected::
handler.order_row(row, cases=-1)
Note that each call must specify *either* a (non-empty) ``cases`` or
``units`` value, but *not* both! If you need to adjust both then you
must make two separate calls.
:param ~rattail.db.model.batch.purchase.PurchaseBatchRow row: Batch row
which is to be updated with the given order data. The row must
exist, i.e. this method will not create a new row for you.
:param ~decimal.Decimal cases: Case quantity for the update, if applicable.
:param ~decimal.Decimal units: Unit quantity for the update, if applicable.
"""
# make sure we have cases *or* units
if not (cases or units):
raise ValueError("must provide amount for cases *or* units")
if cases and units:
raise ValueError("must provide amount for cases *or* units (but not both)")
batch = row.batch
# make sure we have a (non-executed) ordering batch
if batch.mode != self.enum.PURCHASE_BATCH_MODE_ORDERING:
raise NotImplementedError("order_row() is only for ordering batches")
if batch.executed:
raise NotImplementedError("order_row() is only for *non-executed* batches")
# add values as-is to existing case/unit amounts.
# TODO: what if this gives us negative values?
if cases:
row.cases_ordered = (row.cases_ordered or 0) + cases
if units:
row.units_ordered = (row.units_ordered or 0) + units
# TODO: pretty sure this isn't needed?
# # refresh row status etc.
# self.refresh_row(row)
# update calculated PO totals
po_amount = 0
if cases:
po_amount += cases * (row.case_quantity or 0) * (row.po_unit_cost or 0)
if units:
po_amount += units * (row.po_unit_cost or 0)
row.po_total_calculated = (row.po_total_calculated or 0) + po_amount
batch.po_total_calculated = (batch.po_total_calculated or 0) + po_amount
def populate_credit(self, credit, row):
"""
Populate all basic attributes for the given credit, from the given row.