package services import ( "context" "errors" "fmt" "github.com/google/uuid" "github.com/hay-kot/homebox/backend/internal/repo" "github.com/rs/zerolog/log" ) var ( ErrNotFound = errors.New("not found") ErrFileNotFound = errors.New("file not found") ) type ItemService struct { repo *repo.AllRepos filepath string // at is a map of tokens to attachment IDs. This is used to store the attachment ID // for issued URLs at attachmentTokens } func (svc *ItemService) GetOne(ctx context.Context, gid uuid.UUID, id uuid.UUID) (repo.ItemOut, error) { return svc.repo.Items.GetOneByGroup(ctx, gid, id) } func (svc *ItemService) GetAll(ctx context.Context, gid uuid.UUID) ([]repo.ItemSummary, error) { return svc.repo.Items.GetAll(ctx, gid) } func (svc *ItemService) Create(ctx context.Context, gid uuid.UUID, data repo.ItemCreate) (repo.ItemOut, error) { return svc.repo.Items.Create(ctx, gid, data) } func (svc *ItemService) Delete(ctx context.Context, gid uuid.UUID, id uuid.UUID) error { return svc.repo.Items.DeleteByGroup(ctx, gid, id) } func (svc *ItemService) Update(ctx context.Context, gid uuid.UUID, data repo.ItemUpdate) (repo.ItemOut, error) { return svc.repo.Items.UpdateByGroup(ctx, gid, data) } func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]string) error { loaded := []csvRow{} // Skip first row for _, row := range data[1:] { // Skip empty rows if len(row) == 0 { continue } if len(row) != NumOfCols { return ErrInvalidCsv } r := newCsvRow(row) loaded = append(loaded, r) } // Bootstrap the locations and labels so we can reuse the created IDs for the items locations := map[string]uuid.UUID{} existingLocation, err := svc.repo.Locations.GetAll(ctx, gid) if err != nil { return err } for _, loc := range existingLocation { locations[loc.Name] = loc.ID } labels := map[string]uuid.UUID{} existingLabels, err := svc.repo.Labels.GetAll(ctx, gid) if err != nil { return err } for _, label := range existingLabels { labels[label.Name] = label.ID } for _, row := range loaded { // Locations if _, ok := locations[row.Location]; ok { continue } fmt.Println("Creating Location: ", row.Location) result, err := svc.repo.Locations.Create(ctx, gid, repo.LocationCreate{ Name: row.Location, Description: "", }) if err != nil { return err } locations[row.Location] = result.ID // Labels for _, label := range row.getLabels() { if _, ok := labels[label]; ok { continue } result, err := svc.repo.Labels.Create(ctx, gid, repo.LabelCreate{ Name: label, Description: "", }) if err != nil { return err } labels[label] = result.ID } } // Create the items for _, row := range loaded { locationID := locations[row.Location] labelIDs := []uuid.UUID{} for _, label := range row.getLabels() { labelIDs = append(labelIDs, labels[label]) } log.Info(). Str("name", row.Item.Name). Str("location", row.Location). Strs("labels", row.getLabels()). Str("locationId", locationID.String()). Msgf("Creating Item: %s", row.Item.Name) result, err := svc.repo.Items.Create(ctx, gid, repo.ItemCreate{ ImportRef: row.Item.ImportRef, Name: row.Item.Name, Description: row.Item.Description, LabelIDs: labelIDs, LocationID: locationID, }) if err != nil { return err } // Update the item with the rest of the data _, err = svc.repo.Items.UpdateByGroup(ctx, gid, repo.ItemUpdate{ // Edges LocationID: locationID, LabelIDs: labelIDs, // General Fields ID: result.ID, Name: result.Name, Description: result.Description, Insured: row.Item.Insured, Notes: row.Item.Notes, Quantity: row.Item.Quantity, // Identifies the item as imported SerialNumber: row.Item.SerialNumber, ModelNumber: row.Item.ModelNumber, Manufacturer: row.Item.Manufacturer, // Purchase PurchaseFrom: row.Item.PurchaseFrom, PurchasePrice: row.Item.PurchasePrice, PurchaseTime: row.Item.PurchaseTime, // Warranty LifetimeWarranty: row.Item.LifetimeWarranty, WarrantyExpires: row.Item.WarrantyExpires, WarrantyDetails: row.Item.WarrantyDetails, SoldTo: row.Item.SoldTo, SoldPrice: row.Item.SoldPrice, SoldTime: row.Item.SoldTime, SoldNotes: row.Item.SoldNotes, }) if err != nil { return err } } return nil }