Three real bugs, two of which would have quietly corrupted inventory numbers. CopyGarden carried seed_lot_id into the copied plantings, so copying a garden double-counted the original lot's usage — the copy is a plan, not a second planting out of the same packet. It also quietly attached a private lot to a garden that may later be shared, contradicting the "lots are never shared with a garden" invariant this feature was built on. The copy now drops the link. RestorePlanting restored a seed_lot_id verbatim from a history snapshot. Live rows get their link nulled by ON DELETE SET NULL when a lot is deleted, but the snapshot still holds the old id, so undoing a planting deletion after retiring its lot would violate the foreign key and abort the whole revert. The revert now drops a dangling link before restoring: the plant really was in the ground, which is the part worth restoring. CreateSeedLot returned a lot with Used/Remaining at their zero values, so a packet you just bought reported "0 remaining" — the exact opposite of the truth. The version-conflict path had the same gap, and that row is what the client rebases on, so a 409 reporting remaining=0 is worse than no number at all. Both fill them now. Remaining can go negative and that is deliberate — it means more was planted than the lot recorded buying, which happens (a miscounted packet, a lot entered after the fact). Clamping to zero would hide the discrepancy at exactly the moment it is worth seeing. Now documented and tested rather than incidental. Performance and tidiness: SeedLotUsage is scoped to the lots being filled instead of scanning every planting the owner has, so reading one lot is one lot's work; ListSeedLotsForOwner gained the LIMIT every other list read has; parseNullable moved out of seed_lots.go into the shared request helpers, since every nullable field in the API needs that three-way absent/null/value distinction; finalizePlant validates against its own maxPlantVendorLen rather than borrowing a constant named for seed lots; seedLotForPlanting became checkSeedLotForPlanting, since it validates rather than fetches; and the duplicate intPtr test helper gave way to the existing ptrInt. Declined: recording seed-lot mutations in the revision history. change_sets are anchored to a garden and lots are user-scoped, deliberately — they belong to you, not to any one garden, so there is no garden to record them against. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -107,7 +107,7 @@ func (s *Service) CreatePlanting(ctx context.Context, actorID, objectID int64, i
|
||||
if err := finalizePlanting(p, o, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.seedLotForPlanting(ctx, actorID, p.SeedLotID, p.PlantID); err != nil {
|
||||
if err := s.checkSeedLotForPlanting(ctx, actorID, p.SeedLotID, p.PlantID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
created, err := s.store.CreatePlanting(ctx, p)
|
||||
@@ -159,7 +159,7 @@ func (s *Service) UpdatePlanting(ctx context.Context, actorID, plantingID int64,
|
||||
}
|
||||
// Re-checked on every update, not just when the lot changes: a plant swap can
|
||||
// leave a previously-valid attribution pointing at a lot of the wrong variety.
|
||||
if err := s.seedLotForPlanting(ctx, actorID, pl.SeedLotID, pl.PlantID); err != nil {
|
||||
if err := s.checkSeedLotForPlanting(ctx, actorID, pl.SeedLotID, pl.PlantID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pl.Version = version
|
||||
|
||||
Reference in New Issue
Block a user