package postgres import ( "database/sql" "log" "github.com/dustinpianalto/errors" "github.com/dustinpianalto/quartermaster" "github.com/dustinpianalto/quartermaster/internal/utils" ) type itemService struct { db *sql.DB } func (s itemService) Item(id int, user *quartermaster.User) (*quartermaster.Item, error) { var method errors.Method = "postgres/Items" var i quartermaster.Item queryString := "SELECT id, name, description, size, unit, barcode, nutrition_id FROM items WHERE id = $1 AND owner_id = $2" row := s.db.QueryRow(queryString, id, user.ID) var nutrition_id sql.NullInt32 var unit string err := row.Scan(&i.ID, &i.Name, &i.Description, &i.Size, &unit, &i.Barcode, &nutrition_id) if err != nil { return nil, errors.E(method, errors.Internal, "error getting item data", err) } i.Unit, err = utils.UnitFromString(unit) if err != nil { return nil, errors.E(method, err) } if nutrition_id.Valid { n, err := NutritionService.Nutrition(int(nutrition_id.Int32)) if err != nil { return nil, errors.E(method, "error getting nutrition data", err) } i.Nutrition = n } else { i.Nutrition = nil } return &i, nil } func (s itemService) AddItem(i *quartermaster.Item, l *quartermaster.Location, user *quartermaster.User) (*quartermaster.Item, error) { var err error if i.ID == 0 { if i.Nutrition != nil { i.Nutrition, err = NutritionService.AddNutrition(i.Nutrition) if err != nil { log.Println(err) } queryString := "INSERT INTO items (name, description, size, unit, barcode, nutrition_id, owner_id) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id" err = s.db.QueryRow(queryString, i.Name, i.Description, i.Size, i.Unit.String(), i.Barcode, i.Nutrition.ID, user.ID).Scan(&i.ID) } else { queryString := "INSERT INTO items (name, description, size, unit, barcode, nutrition_id, owner_id) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id" err = s.db.QueryRow(queryString, i.Name, i.Description, i.Size, i.Unit.String(), i.Barcode, nil, user.ID).Scan(&i.ID) } if err != nil { return nil, err } } queryString := "INSERT INTO x_items_locations (item_id, location_id, count) VALUES ($1, $2, 1) ON CONFLICT (item_id, location_id) DO UPDATE SET count = x_items_locations.count + 1" _, err = s.db.Exec(queryString, i.ID, l.ID) return i, err } func (s itemService) AddCategory(i *quartermaster.Item, c *quartermaster.Category) error { queryString := "INSERT INTO x_items_categories (item_id, category_id) VALUES ($1, $2) ON DUPLICATE KEY DO NOTHING" _, err := s.db.Exec(queryString, i.ID, c.ID) return err } func (s itemService) AddGroup(i *quartermaster.Item, g *quartermaster.Group) error { queryString := "INSERT INTO x_items_groups (item_id, group_id) VALUES ($1, $2) ON DUPLICATE KEY DO NOTHING" _, err := s.db.Exec(queryString, i.ID, g.ID) return err } func (s itemService) RemoveCategory(i *quartermaster.Item, c *quartermaster.Category) error { queryString := "DELETE FROM x_items_categories WHERE item_id = $1 AND category_id = $2" _, err := s.db.Exec(queryString, i.ID, c.ID) return err } func (s itemService) RemoveGroup(i *quartermaster.Item, g *quartermaster.Group) error { queryString := "DELETE FROM x_items_groups WHERE item_id = $1 AND group_id = $2" _, err := s.db.Exec(queryString, i.ID, g.ID) return err } func (s itemService) DeleteItem(i *quartermaster.Item, user *quartermaster.User) error { queryString := "DELETE FROM items WHERE id = $1 AND owner_id = $2" _, err := s.db.Exec(queryString, i.ID, user.ID) return err } func (s itemService) GetItemByBarcode(b string, user *quartermaster.User) (*quartermaster.Item, error) { queryString := "SELECT id FROM items WHERE barcode = $1 AND owner_id = $2" row := s.db.QueryRow(queryString, b, user.ID) var id int err := row.Scan(&id) if err != nil { return nil, err } return s.Item(id, user) } func (s itemService) RemoveItem(i *quartermaster.Item, l *quartermaster.Location, u *quartermaster.User) error { const method errors.Method = "itemService/RemoveItem" if ok, err := s.IsOwner(i, u); !ok { return errors.E(method, err) } queryString := "UPDATE x_items_locations SET count = count - 1 WHERE item_id = $1 AND location_id = $2 RETURNING count" var count int err := s.db.QueryRow(queryString, i.ID, l.ID).Scan(&count) if err != nil { return err } if count <= 0 { queryString = "DELETE FROM x_items_locations WHERE item_id = $1 AND location_id = $2" _, err := s.db.Exec(queryString, i.ID, l.ID) return err } return nil } func (s itemService) MoveItem(i *quartermaster.Item, old, new *quartermaster.Location, user *quartermaster.User) error { err := s.RemoveItem(i, old, user) if err != nil { return err } _, err = s.AddItem(i, new, user) return err } func (s itemService) UpdateItem(i *quartermaster.Item, user *quartermaster.User) error { var err error if i.Nutrition != nil { if i.Nutrition.ID == 0 { i.Nutrition, err = NutritionService.AddNutrition(i.Nutrition) if err != nil { return err } } else { err = NutritionService.UpdateNutrition(i.Nutrition) if err != nil { return err } } } queryString := "UPDATE items SET name = $2, description = $3, size = $4, unit = $5, barcode = $6, nutrition_id = $7, WHERE id = $1 AND owner_id = $8" _, err = s.db.Exec(queryString, i.ID, i.Name, i.Description, i.Size, i.Unit, i.Barcode, i.Nutrition.ID, user.ID) return err } func (s itemService) IsOwner(i *quartermaster.Item, u *quartermaster.User) (bool, error) { const method errors.Method = "itemService/IsOwner" var ownerID int queryString := "SELECT owner_id FROM items WHERE id = $1" row := s.db.QueryRow(queryString, i.ID) err := row.Scan(&ownerID) if err != nil { return false, errors.E(method, errors.Internal, "error getting owner", err) } if ownerID == u.ID { return true, nil } return false, nil } func (s itemService) GetItemLocations(i *quartermaster.Item, u *quartermaster.User) (map[int]int, error) { const method errors.Method = "itemService/GetItemLocations" if ok, err := s.IsOwner(i, u); !ok { return nil, errors.E(method, err) } queryString := "SELECT location_id, count FROM x_items_locations WHERE item_id = $1" rows, err := s.db.Query(queryString, i.ID) if err != nil { return nil, errors.E(method, errors.Internal, "error getting locations", err) } out := make(map[int]int) for rows.Next() { var lID int var count int err := rows.Scan(&lID, &count) if err != nil { log.Println(errors.E(method, errors.Internal, err)) } out[lID] = count } return out, nil }