package storage import ( "context" "dana/internal/domain" ) // PriceList возвращает прайс деревом разделов с позициями. // Если задан sectionSlug, отдаётся только этот раздел с потомками. func (s *Storage) PriceList(ctx context.Context, sectionSlug string) ([]*domain.PriceSection, error) { rows, err := s.pool.Query(ctx, ` SELECT id, parent_id, slug, name FROM price_sections WHERE published ORDER BY sort, name`) if err != nil { return nil, err } defer rows.Close() byID := map[int64]*domain.PriceSection{} var all []*domain.PriceSection for rows.Next() { sec := &domain.PriceSection{} if err := rows.Scan(&sec.ID, &sec.ParentID, &sec.Slug, &sec.Name); err != nil { return nil, err } byID[sec.ID] = sec all = append(all, sec) } if err := rows.Err(); err != nil { return nil, err } itemRows, err := s.pool.Query(ctx, ` SELECT id, section_id, service_id, name, price, unit, price_from FROM price_items WHERE published ORDER BY sort, name`) if err != nil { return nil, err } defer itemRows.Close() for itemRows.Next() { var it domain.PriceItem var sectionID int64 if err := itemRows.Scan(&it.ID, §ionID, &it.ServiceID, &it.Name, &it.Price, &it.Unit, &it.From); err != nil { return nil, err } if sec, ok := byID[sectionID]; ok { sec.Items = append(sec.Items, it) } } if err := itemRows.Err(); err != nil { return nil, err } var roots []*domain.PriceSection for _, sec := range all { if sec.ParentID == nil { roots = append(roots, sec) continue } if parent, ok := byID[*sec.ParentID]; ok { parent.Children = append(parent.Children, sec) } } if sectionSlug != "" { for _, sec := range all { if sec.Slug == sectionSlug { return []*domain.PriceSection{sec}, nil } } return nil, ErrNotFound } return roots, nil }