Replace the missing delete-cascade triggers with deletes in code - #12
Merged
Merged
Conversation
…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>
There was a problem hiding this comment.
🟡 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.
* 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>
There was a problem hiding this comment.
🟡 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
or project left child rows behind. Each delete now removes its own child rows,
transactionally on the InnoDB tables.
billed.
rather than touching it, and a failed export rolls back the invoice it created.
mid-delete failure orphans at worst the blob, not a link to a missing file.
Test plan
two fail-loud paths refuse and report the orphan.
Co-Authored-By: Claude noreply@anthropic.com