Add support for editing "claim" quantities for truck dump child row
at least i think this gets it all...guess we'll see
This commit is contained in:
parent
a348755be2
commit
ac451757b4
|
@ -237,19 +237,23 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
'credits',
|
||||
]
|
||||
|
||||
# convenience list of all quantity attributes involved for a truck dump claim
|
||||
claim_keys = [
|
||||
'cases_received',
|
||||
'units_received',
|
||||
'cases_damaged',
|
||||
'units_damaged',
|
||||
'cases_expired',
|
||||
'units_expired',
|
||||
]
|
||||
|
||||
@property
|
||||
def batch_mode(self):
|
||||
return self.enum.PURCHASE_BATCH_MODE_RECEIVING
|
||||
|
||||
def row_editable(self, row):
|
||||
batch = row.batch
|
||||
if batch.truck_dump_batch:
|
||||
return False
|
||||
return True
|
||||
|
||||
def row_deletable(self, row):
|
||||
batch = row.batch
|
||||
if batch.truck_dump:
|
||||
if batch.is_truck_dump_parent():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -681,6 +685,245 @@ class ReceivingBatchView(PurchasingBatchView):
|
|||
f.set_readonly('po_total')
|
||||
f.set_readonly('invoice_total')
|
||||
|
||||
def validate_row_form(self, form):
|
||||
|
||||
# if normal validation fails, stop there
|
||||
if not super(ReceivingBatchView, self).validate_row_form(form):
|
||||
return False
|
||||
|
||||
# if user is editing row from truck dump child, then we must further
|
||||
# validate the form to ensure whatever new amounts they've requested
|
||||
# would in fact fall within the bounds of what is available from the
|
||||
# truck dump parent batch...
|
||||
if self.editing:
|
||||
batch = self.get_instance()
|
||||
if batch.is_truck_dump_child():
|
||||
old_row = self.get_row_instance()
|
||||
case_quantity = old_row.case_quantity
|
||||
|
||||
# get all "existing" (old) claim amounts
|
||||
old_claims = {}
|
||||
for claim in old_row.truck_dump_claims:
|
||||
for key in self.claim_keys:
|
||||
amount = getattr(claim, key)
|
||||
if amount is not None:
|
||||
old_claims[key] = old_claims.get(key, 0) + amount
|
||||
|
||||
# get all "proposed" (new) claim amounts
|
||||
new_claims = {}
|
||||
for key in self.claim_keys:
|
||||
amount = form.validated[key]
|
||||
if amount is not colander.null and amount is not None:
|
||||
# do not allow user to request a negative claim amount
|
||||
if amount < 0:
|
||||
self.request.session.flash("Cannot claim a negative amount for: {}".format(key), 'error')
|
||||
return False
|
||||
new_claims[key] = amount
|
||||
|
||||
# figure out what changes are actually being requested
|
||||
claim_diff = {}
|
||||
for key in new_claims:
|
||||
if key not in old_claims:
|
||||
claim_diff[key] = new_claims[key]
|
||||
elif new_claims[key] != old_claims[key]:
|
||||
claim_diff[key] = new_claims[key] - old_claims[key]
|
||||
# do not allow user to request a negative claim amount
|
||||
if claim_diff[key] < (0 - old_claims[key]):
|
||||
self.request.session.flash("Cannot claim a negative amount for: {}".format(key), 'error')
|
||||
return False
|
||||
for key in old_claims:
|
||||
if key not in new_claims:
|
||||
claim_diff[key] = 0 - old_claims[key]
|
||||
|
||||
# find all rows from truck dump parent which "may" pertain to child row
|
||||
# TODO: perhaps would need to do a more "loose" match on UPC also?
|
||||
if not old_row.product_uuid:
|
||||
raise NotImplementedError("Don't (yet) know how to handle edit for row with no product")
|
||||
parent_rows = [row for row in batch.truck_dump_batch.active_rows()
|
||||
if row.product_uuid == old_row.product_uuid]
|
||||
|
||||
# get existing "confirmed" and "claimed" amounts for all
|
||||
# (possibly related) truck dump parent rows
|
||||
confirmed = {}
|
||||
claimed = {}
|
||||
for parent_row in parent_rows:
|
||||
for key in self.claim_keys:
|
||||
amount = getattr(parent_row, key)
|
||||
if amount is not None:
|
||||
confirmed[key] = confirmed.get(key, 0) + amount
|
||||
for claim in parent_row.claims:
|
||||
for key in self.claim_keys:
|
||||
amount = getattr(claim, key)
|
||||
if amount is not None:
|
||||
claimed[key] = claimed.get(key, 0) + amount
|
||||
|
||||
# now to see if user's request is possible, given what is
|
||||
# available...
|
||||
|
||||
# first we must (pretend to) "relinquish" any claims which are
|
||||
# to be reduced or eliminated, according to our diff
|
||||
for key, amount in claim_diff.items():
|
||||
if amount < 0:
|
||||
amount = abs(amount) # make positive, for more readable math
|
||||
if key not in claimed or claimed[key] < amount:
|
||||
self.request.session.flash("Cannot relinquish more claims than the "
|
||||
"parent batch has to offer.", 'error')
|
||||
return False
|
||||
claimed[key] -= amount
|
||||
|
||||
# next we must determine if any "new" requests would increase
|
||||
# the claim(s) beyond what is available
|
||||
for key, amount in claim_diff.items():
|
||||
if amount > 0:
|
||||
claimed[key] = claimed.get(key, 0) + amount
|
||||
if key not in confirmed or confirmed[key] < claimed[key]:
|
||||
self.request.session.flash("Cannot request to claim more product than "
|
||||
"is available in Truck Dump Parent batch", 'error')
|
||||
return False
|
||||
|
||||
# looks like the claim diff is all good, so let's attach that
|
||||
# to the form now and then pick this up again in save()
|
||||
form._claim_diff = claim_diff
|
||||
|
||||
# all validation went ok
|
||||
return True
|
||||
|
||||
def save_edit_row_form(self, form):
|
||||
batch = self.get_instance()
|
||||
row = self.objectify(form)
|
||||
|
||||
# editing a row for truck dump child batch can be complicated...
|
||||
if batch.is_truck_dump_child():
|
||||
|
||||
# grab the claim diff which we attached to the form during validation
|
||||
claim_diff = form._claim_diff
|
||||
|
||||
# first we must "relinquish" any claims which are to be reduced or
|
||||
# eliminated, according to our diff
|
||||
for key, amount in claim_diff.items():
|
||||
if amount < 0:
|
||||
amount = abs(amount) # make positive, for more readable math
|
||||
|
||||
# we'd prefer to find an exact match, i.e. there was a 1CS
|
||||
# claim and our diff said to reduce by 1CS
|
||||
matches = [claim for claim in row.truck_dump_claims
|
||||
if getattr(claim, key) == amount]
|
||||
if matches:
|
||||
claim = matches[0]
|
||||
setattr(claim, key, None)
|
||||
|
||||
else:
|
||||
# but if no exact match(es) then we'll just whittle
|
||||
# away at whatever (smallest) claims we do find
|
||||
possible = [claim for claim in row.truck_dump_claims
|
||||
if getattr(claim, key) is not None]
|
||||
for claim in sorted(possible, key=lambda claim: getattr(claim, key)):
|
||||
previous = getattr(claim, key)
|
||||
if previous:
|
||||
if previous >= amount:
|
||||
if (previous - amount):
|
||||
setattr(claim, key, previous - amount)
|
||||
else:
|
||||
setattr(claim, key, None)
|
||||
amount = 0
|
||||
break
|
||||
else:
|
||||
setattr(claim, key, None)
|
||||
amount -= previous
|
||||
|
||||
if amount:
|
||||
raise NotImplementedError("Had leftover amount when \"relinquishing\" claim(s)")
|
||||
|
||||
# next we must stake all new claim(s) as requested, per our diff
|
||||
for key, amount in claim_diff.items():
|
||||
if amount > 0:
|
||||
|
||||
# if possible, we'd prefer to add to an existing claim
|
||||
# which already has an amount for this key
|
||||
existing = [claim for claim in row.truck_dump_claims
|
||||
if getattr(claim, key) is not None]
|
||||
if existing:
|
||||
claim = existing[0]
|
||||
setattr(claim, key, getattr(claim, key) + amount)
|
||||
|
||||
# next we'd prefer to add to an existing claim, of any kind
|
||||
elif row.truck_dump_claims:
|
||||
claim = row.truck_dump_claims[0]
|
||||
setattr(claim, key, getattr(claim, key) + amount)
|
||||
|
||||
else:
|
||||
# otherwise we must create a new claim...
|
||||
|
||||
# find all rows from truck dump parent which "may" pertain to child row
|
||||
# TODO: perhaps would need to do a more "loose" match on UPC also?
|
||||
if not row.product_uuid:
|
||||
raise NotImplementedError("Don't (yet) know how to handle edit for row with no product")
|
||||
parent_rows = [parent_row for parent_row in batch.truck_dump_batch.active_rows()
|
||||
if parent_row.product_uuid == row.product_uuid]
|
||||
|
||||
# remove any parent rows which are fully claimed
|
||||
# TODO: should perhaps leverage actual amounts for this, instead
|
||||
parent_rows = [parent_row for parent_row in parent_rows
|
||||
if parent_row.status_code != parent_row.STATUS_TRUCKDUMP_CLAIMED]
|
||||
|
||||
# try to find a parent row which is exact match on claim amount
|
||||
matches = [parent_row for parent_row in parent_rows
|
||||
if getattr(parent_row, key) == amount]
|
||||
if matches:
|
||||
|
||||
# make the claim against first matching parent row
|
||||
claim = model.PurchaseBatchRowClaim()
|
||||
claim.claimed_row = parent_rows[0]
|
||||
setattr(claim, key, amount)
|
||||
row.truck_dump_claims.append(claim)
|
||||
|
||||
else:
|
||||
# but if no exact match(es) then we'll just whittle
|
||||
# away at whatever (smallest) parent rows we do find
|
||||
for parent_row in sorted(parent_rows, lambda prow: getattr(prow, key)):
|
||||
|
||||
available = getattr(parent_row, key) - sum([getattr(claim, key) for claim in parent_row.claims])
|
||||
if available:
|
||||
if available >= amount:
|
||||
# make claim against this parent row, making it fully claimed
|
||||
claim = model.PurchaseBatchRowClaim()
|
||||
claim.claimed_row = parent_row
|
||||
setattr(claim, key, amount)
|
||||
row.truck_dump_claims.append(claim)
|
||||
amount = 0
|
||||
break
|
||||
else:
|
||||
# make partial claim against this parent row
|
||||
claim = model.PurchaseBatchRowClaim()
|
||||
claim.claimed_row = parent_row
|
||||
setattr(claim, key, available)
|
||||
row.truck_dump_claims.append(claim)
|
||||
amount -= available
|
||||
|
||||
if amount:
|
||||
raise NotImplementedError("Had leftover amount when \"staking\" claim(s)")
|
||||
|
||||
# now we must be sure to refresh all truck dump parent batch rows
|
||||
# which were affected. but along with that we also should purge
|
||||
# any empty claims, i.e. those which were fully relinquished
|
||||
pending_refresh = set()
|
||||
for claim in list(row.truck_dump_claims):
|
||||
parent_row = claim.claimed_row
|
||||
if claim.is_empty():
|
||||
row.truck_dump_claims.remove(claim)
|
||||
self.Session.flush()
|
||||
pending_refresh.add(parent_row)
|
||||
for parent_row in pending_refresh:
|
||||
self.handler.refresh_row(parent_row)
|
||||
self.handler.refresh_batch_status(batch.truck_dump_batch)
|
||||
|
||||
self.after_edit_row(row)
|
||||
self.Session.flush()
|
||||
return row
|
||||
|
||||
def redirect_after_edit_row(self, row, mobile=False):
|
||||
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
|
||||
|
||||
def render_mobile_row_listitem(self, row, i):
|
||||
key = self.render_product_key_value(row)
|
||||
description = row.product.full_description if row.product else row.description
|
||||
|
|
Loading…
Reference in a new issue