seed_lots.plant_id is ON DELETE CASCADE, so deleting a plant would have silently destroyed the purchase records attached to it — vendor, cost, germination rate, gone with no warning. Plantings were already guarded with ErrPlantInUse because their FK is RESTRICT; lots needed the same guard for the opposite reason, since their FK would have obliged rather than refused. Refusing lets the owner delete the lots deliberately if that is really what they meant, which is the same shape as the existing clones-and-edits path. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -140,6 +140,18 @@ func (d *DB) DeleteSeedLot(ctx context.Context, id int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountSeedLotsForPlant returns how many lots reference a plant. Used to refuse
|
||||
// deleting a plant that has purchase records: the FK is ON DELETE CASCADE, so
|
||||
// without this check the delete would silently destroy them.
|
||||
func (d *DB) CountSeedLotsForPlant(ctx context.Context, plantID int64) (int, error) {
|
||||
var n int
|
||||
if err := d.sql.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM seed_lots WHERE plant_id = ?`, plantID).Scan(&n); err != nil {
|
||||
return 0, fmt.Errorf("store: count seed lots for plant: %w", err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SeedLotUse is one active planting attributed to a lot, carrying just enough to
|
||||
// compute its effective plant count.
|
||||
type SeedLotUse struct {
|
||||
|
||||
Reference in New Issue
Block a user