// Package statuses manages the configurable list of asset statuses. package statuses import ( "context" "database/sql" "errors" "time" "inventree/internal/models" ) var ( ErrNotFound = errors.New("status not found") ErrNameRequired = errors.New("status name is required") ErrInUse = errors.New("status is assigned to one or more assets") ) type Store struct{ db *sql.DB } func NewStore(db *sql.DB) *Store { return &Store{db: db} } const timeLayout = "2006-01-02T15:04:05.000Z" func parseTime(s string) time.Time { t, _ := time.Parse(timeLayout, s) return t } func scan(row interface{ Scan(...any) error }) (*models.Status, error) { var st models.Status var created, updated string if err := row.Scan(&st.ID, &st.Name, &st.Color, &st.SortOrder, &created, &updated); err != nil { return nil, err } st.CreatedAt = parseTime(created) st.UpdatedAt = parseTime(updated) return &st, nil } func (s *Store) List(ctx context.Context) ([]models.Status, error) { rows, err := s.db.QueryContext(ctx, `SELECT id, name, color, sort_order, created_at, updated_at FROM statuses ORDER BY sort_order ASC, name ASC`) if err != nil { return nil, err } defer rows.Close() var out []models.Status for rows.Next() { st, err := scan(rows) if err != nil { return nil, err } out = append(out, *st) } return out, rows.Err() } func (s *Store) Get(ctx context.Context, id int64) (*models.Status, error) { row := s.db.QueryRowContext(ctx, `SELECT id, name, color, sort_order, created_at, updated_at FROM statuses WHERE id = ?`, id) st, err := scan(row) if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return st, err } func (s *Store) Create(ctx context.Context, st *models.Status) error { if st.Name == "" { return ErrNameRequired } if st.Color == "" { st.Color = "secondary" } res, err := s.db.ExecContext(ctx, `INSERT INTO statuses (name, color, sort_order) VALUES (?, ?, ?)`, st.Name, st.Color, st.SortOrder) if err != nil { return err } id, err := res.LastInsertId() if err != nil { return err } st.ID = id return nil } func (s *Store) Update(ctx context.Context, st *models.Status) error { if st.Name == "" { return ErrNameRequired } _, err := s.db.ExecContext(ctx, `UPDATE statuses SET name = ?, color = ?, sort_order = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id = ?`, st.Name, st.Color, st.SortOrder, st.ID) return err } func (s *Store) Delete(ctx context.Context, id int64) error { var count int if err := s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM assets WHERE status_id = ?`, id).Scan(&count); err != nil { return err } if count > 0 { return ErrInUse } _, err := s.db.ExecContext(ctx, `DELETE FROM statuses WHERE id = ?`, id) return err } // Reorder sets sort_order for each status ID in the given order (1-indexed). func (s *Store) Reorder(ctx context.Context, orderedIDs []int64) error { tx, err := s.db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() for i, id := range orderedIDs { if _, err := tx.ExecContext(ctx, `UPDATE statuses SET sort_order = ? WHERE id = ?`, i+1, id); err != nil { return err } } return tx.Commit() }