package storage import ( "strings" "time" "gardomatic.kleiax.de/internal/platform/validate" ) // PlantLocationModelInterface persists assignments between plants and locations. type PlantLocationModelInterface interface { Insert(gardenID int, plantLocation PlantLocation) (PlantLocation, error) Get(gardenID, id int) (PlantLocation, error) GetAllForPlant(gardenID, plantID int) ([]PlantLocation, error) GetAllForLocation(gardenID, locationID int) ([]PlantLocation, error) Update(gardenID int, plantLocation PlantLocation) (PlantLocation, error) Delete(gardenID, id int) error } // PlantLocation records where and when a quantity of plants was planted. type PlantLocation struct { ID int `json:"id"` PlantID int `json:"plant_id"` LocationID int `json:"location_id"` Quantity int `json:"quantity"` PlantedAt *time.Time `json:"planted_at,omitempty"` RemovedAt *time.Time `json:"removed_at,omitempty"` Notes string `json:"notes"` CreatedAt time.Time `json:"created_at"` Version int `json:"version"` } // ValidatePlantLocation applies persistence-independent assignment validation rules. func ValidatePlantLocation(v *validate.Validator, assignment PlantLocation) { v.Check(assignment.PlantID > 0, "plant_id", "must be a positive integer") v.Check(assignment.LocationID > 0, "location_id", "must be a positive integer") v.Check(assignment.Quantity > 0, "quantity", "must be greater than zero") v.Check(len(strings.TrimSpace(assignment.Notes)) <= 10_000, "notes", "must not be more than 10000 bytes long") if assignment.PlantedAt != nil && assignment.RemovedAt != nil { v.Check(!assignment.RemovedAt.Before(*assignment.PlantedAt), "removed_at", "must not be before planted_at") } }