Skip to content

Loan preparation availability rule no longer runs on insert — a preparation can be placed on two open loans (regression from #7665 fix) #8188

Description

@foozleface

Describe the bug
The fix for #7665 (commit 79c3a4f) correctly made the loan-preparation quantity math null-safe, but it also added an early return that disables the availability business rule for all new rows, in specifyweb/backend/businessrules/rules/interaction_rules.pyloanprep_quantity_must_be_lte_availability:

@orm_signal_handler('pre_save', 'Loanpreparation')
def loanprep_quantity_must_be_lte_availability(ipreparation):
    if ipreparation.id is None:  # added by the #7665 fix
        return
    ...

The rule no longer runs on INSERT, so the server no longer enforces availability when a loan preparation is created — a specimen can be placed on two open loans at once. Over-loan prevention now depends entirely on the frontend InteractionDialog availability pre-query. Any creation path that does not go through that filter inserts an over-loan silently:

  • "Add Unassociated Item" on a loan
  • the REST API (POST /api/specify/loanpreparation/)
  • WorkBench loan uploads

The sibling rules in the same file — giftprep_quantity_must_be_lte_availability and exchangeoutprep_quantity_must_be_lte_availability — do not early-return on insert; they validate creation. Loans are the only interaction missing insert-time enforcement.

To Reproduce

  1. Find a preparation fully out on an open loan (e.g. countAmt = 1, on loan A, unresolved).
  2. Add that same preparation to loan B via "Add Unassociated Item" (or POST a loanpreparation row directly).
  3. Result: HTTP 201, no warning — the single-count preparation is now unresolved-out on two open loans at once.

get_availability already returns 0 (or negative) for such a prep, so the check would catch it if it ran on insert.

Expected behavior
Creating a loan preparation whose quantity - quantityresolved exceeds the preparation's availability should raise a BusinessRuleException, the same way gift and exchange-out preparations are validated on creation.

Impact
At the California Academy of Sciences we found 76 preparations currently out on two or more open loans in a single collection. Most originate from a Sp6→Sp7 conversion, but we also reproduced it live: a curator added already-out lots to a new loan through the UI with no warning.

Proposed fix (narrow)
Keep the null-safe quantity handling from the #7665 fix (int(x or 0) already treats an empty Quantity Resolved as 0), but remove the if ipreparation.id is None: return so the availability check also runs on insert — matching the gift/exchange rules. This restores over-loan protection without reintroducing the #7665 WorkBench failure, since #7665 was a TypeError from int(None) and the null-safe ints alone fix that.

PR with fix and regression tests to follow.

Crash Report
N/A — the bug is the absence of an error.

  • OS: any
  • Browser: any
  • Specify 7 Version: any version including commit 79c3a4f (after v7.10.x)
  • Database Name: casiz (CAS Invertebrate Zoology)

Reported By
California Academy of Sciences

Metadata

Metadata

Assignees

No one assigned

    Labels

    regressionThis is behavior that once worked that has broken. Must be resolved before the next release.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions