Skip to content

Replace the missing delete-cascade triggers with deletes in code - #12

Merged
vagisha merged 8 commits into
masterfrom
cascade-deletes-in-code
Sep 16, 2026
Merged

vagisha merged 8 commits into
masterfrom
cascade-deletes-in-code

Conversation

@vagisha

@vagisha vagisha commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Production has no delete-cascade triggers, so deleting a usage block, payment method,
    or project left child rows behind. Each delete now removes its own child rows,
    transactionally on the InnoDB tables.
  • Two read guards inner-join their parent, so existing orphans no longer look in-use or
    billed.
  • The payment-method delete and the billing export fail loud on a remaining orphan
    rather than touching it, and a failed export rolls back the invoice it created.
  • The data-file delete removes its projectFiles link row before the file blob, so a
    mid-delete failure orphans at worst the blob, not a link to a missing file.

Test plan

  • Manual UI tests against a rebuilt war: clean deletes clear their child rows, and the
    two fail-loud paths refuse and report the orphan.

Co-Authored-By: Claude noreply@anthropic.com

vagisha and others added 5 commits September 14, 2026 00:01
…since no trigger does

Production has no triggers, so the "there is a trigger" comments in these delete paths were false and
the child rows were left as orphans.  This replaces the missing triggers with explicit deletes, each
child before its parent so a failure leaves a loadable parent rather than a dangling child.

* InstrumentUsageDAO.delete now deletes each block's instrumentUsagePayment and invoiceInstrumentUsage
  rows on the caller's connection before the block.  Removed the two false trigger comments
* InvoiceDAO.delete deletes the invoice's invoiceInstrumentUsage links first.  This also covers the
  billing-export failure handler, which deletes the invoice through this method
* InvoiceInstrumentUsageDAO.getInvoiceBlock inner-joins invoice, so a link left by a deleted invoice
  no longer reports its block as billed.  Added deleteBlocksForInvoice and deleteBlocksForUsage
* InstrumentUsagePaymentDAO.hasInstrumentUsageForPayment inner-joins instrumentUsage, so a split whose
  block is gone no longer makes a payment method look in use.  Added deletePaymentsForPaymentMethod,
  and removed the broken deletePaymentsForUsage(int), which passed a null connection and had no callers
* ProjectPaymentMethodDAO.deletePaymentMethod uncomments the projectPaymentMethod unlink (the direct
  cause of the 6 orphaned links on prod), clears the method's instrumentUsagePayment splits, and
  deletes both before the payment method

Not tested.  See TESTS in ai-uwpr-webapp.

Co-Authored-By: Claude <noreply@anthropic.com>
* DeleteProjectAction and the canDelete link in ViewProjectAction counted only scheduled blocks, so a
  project whose blocks were all cancelled could still be deleted.  Those deleted=1 rows stay in
  instrumentUsage and getCostOld still bills them, and any leftover row breaks the monthly export.
  Both now count every block through getUsageBlockCountForProject, and error.project.hasinstrumenttime
  says cancelled time counts too
* BilledProject.delete and Collaboration.delete deleted the child rows before checking the subtype
  row existed.  A project missing its tblBilledProject or tblCollaboration row (the unloadable
  husks) would be stripped of its researchers, payment links and external data and then throw.  Each
  now validates the subtype row first, in requireBilledProjectRow / requireCollaborationRow

Not tested.  See TESTS in ai-uwpr-webapp.

Co-Authored-By: Claude <noreply@anthropic.com>
DataFileDeleter.deleteDataFile deleted the files row first and its location-table links afterward.  A
failure between them left a link pointing at a deleted file, the same orphan the cascade-deletes work
is closing.  Delete the links first, then the blob.

Not tested.  See TESTS in ai-uwpr-webapp.

Co-Authored-By: Claude <noreply@anthropic.com>
Found by /code-review max on this branch.  No logic changed.

* InstrumentUsageDAO -- removed getScheduledUsageBlockCountForProject, which had no callers left once
  DeleteProjectAction and ViewProjectAction switched to getUsageBlockCountForProject
* DeleteProjectAction -- the catch-block comment and log line still said "scheduled" instrument time,
  though the guard now counts cancelled blocks too
* InvoiceDAO -- reworded an ungrammatical comment on the invoiceInstrumentUsage cleanup

Not tested.  See TESTS in ai-uwpr-webapp.

Co-Authored-By: Claude <noreply@anthropic.com>
* InvoiceDAO.delete and ProjectPaymentMethodDAO.deletePaymentMethod now delete their
  child and parent rows in one transaction, so a mid-way failure rolls back instead of
  stranding half a delete.  invoice and paymentMethod are MyISAM, so rollback covers
  only the InnoDB child rows.
* PaymentMethodDAO.deletePaymentMethod and unlinkProjectPaymentMethod gained
  Connection-taking overloads so the delete runs on one connection.  The old signatures
  stay as wrappers.
* deletePaymentMethod and InvoiceBlockCreator.blockExported now fail loudly on an
  orphaned row (its parent already deleted) instead of cleaning it silently.  The error
  names every offending row id.
* DeletePaymentMethodAction, which a regular researcher can reach, shows a generic
  message with a reference to quote rather than the raw row ids, and logs the detail
  under that reference for an admin, the same pattern as error.jsp.  Its logger, wrongly
  named for SaveNewPaymentMethodAction, is also fixed.
* InstrumentUsageDAO.delete builds the audit-log prefix in a local variable, fixing the
  ": " that accumulated across a multi-block delete.
* The delete helpers in InstrumentUsagePaymentDAO and InvoiceInstrumentUsageDAO now bind
  the id as a PreparedStatement parameter.
* BilledProject.delete -- fixed a comment that described a collaboration's child rows,
  not a billed project's.

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failed re-exports can erase existing invoices, and some conflicting or orphaned invoice links remain undetected.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Replaces missing database cascade behavior with explicit child-row cleanup and orphan safeguards.

Changes:

  • Deletes dependent payment, invoice, usage, and file-link rows.
  • Tightens project deletion checks.
  • Adds orphan detection and safer user-facing errors.
File summaries
File Description
src/PRMessageResources.properties Updates deletion errors.
src/org/yeastrc/www/project/ViewProjectAction.java Hides deletion when any usage exists.
src/org/yeastrc/www/project/payment/DeletePaymentMethodAction.java Logs referenced deletion failures.
src/org/yeastrc/www/project/DeleteProjectAction.java Blocks deletion for all usage records.
src/org/yeastrc/project/payment/ProjectPaymentMethodDAO.java Deletes payment-method links transactionally.
src/org/yeastrc/project/payment/PaymentMethodDAO.java Supports connection-scoped deletion.
src/org/yeastrc/project/Collaboration.java Validates subtype rows before deletion.
src/org/yeastrc/project/BilledProject.java Validates billed-project rows before deletion.
src/org/yeastrc/files/DataFileDeleter.java Deletes file links before files.
src/org/uwpr/instrumentlog/InstrumentUsagePaymentDAO.java Adds child cleanup and orphan-aware reads.
src/org/uwpr/instrumentlog/InstrumentUsageDAO.java Explicitly deletes usage children.
src/org/uwpr/costcenter/InvoiceInstrumentUsageDAO.java Adds invoice-link cleanup and orphan queries.
src/org/uwpr/costcenter/InvoiceDAO.java Cascades invoice-link deletion.
src/org/uwpr/costcenter/InvoiceBlockCreator.java Refuses selected orphaned billing rows.
Review details
  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/org/uwpr/costcenter/InvoiceBlockCreator.java Outdated
Comment thread src/org/uwpr/costcenter/InvoiceDAO.java
vagisha and others added 3 commits September 16, 2026 12:26
* ExportBillingInformationAction deletes the invoice on a failed export only when this
  request created it.  A re-export reuses the period's invoice and delete() cascades to
  its links, so deleting it on failure erased a committed invoice.  (Copilot finding 2)
* Replaced getInvoiceBlock with isBlockInvoiced -- any invoiceInstrumentUsage row means
  billed.  Every caller only needed a yes-or-no check, and this is fail-safe, so an
  orphaned link now protects a block from edit and delete instead of leaving it open.
* blockExported checks every row, skips one already on the invoice being built (no
  duplicate on re-export), and refuses one tied to another invoice.  (Copilot finding 1)

Co-Authored-By: Claude <noreply@anthropic.com>
The already-billed messages on the edit and delete paths rendered under
error.costcenter.invalidaccess / error.scheduler.invalidaccess, whose template is
"Invalid access: {0}" -- wrong for a billing condition.  Added error.costcenter.notallowed
({0}, no prefix) and pointed the billed messages in EditBlockDetailsAction,
EditBlockDetailsFormAction, and DeleteProjectInstrumentTimeAction at it.  Genuine access
checks keep the old key.

Co-Authored-By: Claude <noreply@anthropic.com>
[Delete] and [Edit Dates & Operator] were already hidden when a tooltip's block group had
no editable (non-billed) blocks, but [Edit Project & Payment Method] rendered
unconditionally, so clicking it hit an empty-selection alert.  Moved it inside the same
hasEditableBlocks check.

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Concurrent billing or payment updates can still recreate orphan rows during deletion.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 21/21 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/org/uwpr/instrumentlog/InstrumentUsageDAO.java
Comment thread src/org/yeastrc/project/payment/ProjectPaymentMethodDAO.java
@vagisha
vagisha merged commit 2cb84bf into master Sep 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants