almost all ready
This commit is contained in:
@@ -15,6 +15,7 @@ type ProductPageRequest struct {
|
||||
Slug string
|
||||
LanguageID int64
|
||||
ShopID int64
|
||||
CurrencyID int64
|
||||
}
|
||||
|
||||
type CategoryPageRequest struct {
|
||||
@@ -22,6 +23,9 @@ type CategoryPageRequest struct {
|
||||
Slug string
|
||||
LanguageID int64
|
||||
ShopID int64
|
||||
CurrencyID int64
|
||||
Page int
|
||||
PerPage int
|
||||
}
|
||||
|
||||
type ProductPageData struct {
|
||||
@@ -31,10 +35,55 @@ type ProductPageData struct {
|
||||
ShortDescription string
|
||||
Description string
|
||||
Price float64
|
||||
PriceTaxIncl float64 `gorm:"column:price_tax_incl"`
|
||||
TaxRate float64 `gorm:"column:tax_rate"`
|
||||
CurrencyID int64 `gorm:"column:currency_id"`
|
||||
CurrencyCode string `gorm:"column:currency_code"`
|
||||
CurrencySign string `gorm:"column:currency_sign"`
|
||||
ConversionRate float64 `gorm:"column:conversion_rate"`
|
||||
CoverImageID sql.NullInt64
|
||||
ImageURL string `gorm:"-"`
|
||||
GalleryImages []ProductImage `gorm:"-"`
|
||||
CategoryID int64
|
||||
CategorySlug string
|
||||
CategoryName string
|
||||
Features []ProductFeature `gorm:"-"`
|
||||
Accessories []CategoryProductCard `gorm:"-"`
|
||||
Combinations []ProductCombination `gorm:"-"`
|
||||
DefaultAttribute int64 `gorm:"-"`
|
||||
}
|
||||
|
||||
type ProductImage struct {
|
||||
ID int64 `gorm:"column:id_image"`
|
||||
Cover bool `gorm:"column:cover"`
|
||||
Position int `gorm:"column:position"`
|
||||
URL string `gorm:"-"`
|
||||
ThumbURL string `gorm:"-"`
|
||||
}
|
||||
|
||||
type ProductFeature struct {
|
||||
ID int64 `gorm:"column:id_feature"`
|
||||
Name string `gorm:"column:name"`
|
||||
Value string `gorm:"column:value"`
|
||||
}
|
||||
|
||||
type ProductCombination struct {
|
||||
ID int64 `gorm:"column:id_product_attribute"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
PriceTaxIncl float64 `gorm:"column:price_tax_incl"`
|
||||
DefaultOn bool `gorm:"column:default_on"`
|
||||
ImageID sql.NullInt64 `gorm:"-"`
|
||||
ImageURL string `gorm:"-"`
|
||||
ThumbURL string `gorm:"-"`
|
||||
Attributes []ProductCombinationAttribute `gorm:"-"`
|
||||
}
|
||||
|
||||
type ProductCombinationAttribute struct {
|
||||
Group string
|
||||
PublicName string
|
||||
Value string
|
||||
GroupType string
|
||||
Color string
|
||||
}
|
||||
|
||||
type CategoryPageData struct {
|
||||
@@ -43,16 +92,33 @@ type CategoryPageData struct {
|
||||
Slug string
|
||||
Description string
|
||||
Products []CategoryProductCard `gorm:"-"`
|
||||
Pagination CategoryPagination `gorm:"-"`
|
||||
}
|
||||
|
||||
type CategoryPagination struct {
|
||||
Page int
|
||||
PerPage int
|
||||
TotalItems int64
|
||||
TotalPages int
|
||||
}
|
||||
|
||||
type CategoryProductCard struct {
|
||||
ID int64
|
||||
Name string
|
||||
Slug string
|
||||
URL string `gorm:"-"`
|
||||
Price float64
|
||||
Description string
|
||||
EAN13 string
|
||||
ID int64
|
||||
Name string
|
||||
Slug string
|
||||
CategorySlug string `gorm:"column:category_slug"`
|
||||
URL string `gorm:"-"`
|
||||
ImageURL string `gorm:"-"`
|
||||
Price float64
|
||||
PriceTaxIncl float64 `gorm:"column:price_tax_incl"`
|
||||
TaxRate float64 `gorm:"column:tax_rate"`
|
||||
CurrencyID int64 `gorm:"column:currency_id"`
|
||||
CurrencyCode string `gorm:"column:currency_code"`
|
||||
CurrencySign string `gorm:"column:currency_sign"`
|
||||
ConversionRate float64 `gorm:"column:conversion_rate"`
|
||||
CoverImageID sql.NullInt64 `gorm:"column:cover_image_id"`
|
||||
ShortDescription string `gorm:"column:short_description"`
|
||||
EAN13 string
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
@@ -92,54 +158,85 @@ func NewService(db *gorm.DB, prefix string) *Service {
|
||||
|
||||
func (s *Service) GetProductPage(ctx context.Context, req ProductPageRequest) (*ProductPageData, error) {
|
||||
var product ProductPageData
|
||||
if req.CurrencyID == 0 {
|
||||
req.CurrencyID = 1
|
||||
}
|
||||
queryByID := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
pl.description_short AS short_description,
|
||||
pl.description AS description,
|
||||
ps.price AS price,
|
||||
(ps.price * curr.conversion_rate) AS price,
|
||||
((ps.price * curr.conversion_rate) * (1 + COALESCE(tax_data.tax_rate, 0) / 100)) AS price_tax_incl,
|
||||
COALESCE(tax_data.tax_rate, 0) AS tax_rate,
|
||||
curr.id_currency AS currency_id,
|
||||
curr.iso_code AS currency_code,
|
||||
curr.sign AS currency_sign,
|
||||
curr.conversion_rate AS conversion_rate,
|
||||
i.id_image AS cover_image_id,
|
||||
p.id_category_default AS category_id,
|
||||
cl.link_rewrite AS category_slug,
|
||||
cl.name AS category_name
|
||||
FROM %sproduct p
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product AND ps.id_shop = ?
|
||||
JOIN %scurrency curr ON curr.id_currency = ? AND curr.deleted = 0
|
||||
LEFT JOIN (
|
||||
SELECT tr.id_tax_rules_group,
|
||||
SUM(t.rate) AS tax_rate
|
||||
FROM %stax_rule tr
|
||||
JOIN %stax t ON t.id_tax = tr.id_tax AND t.active = 1
|
||||
GROUP BY tr.id_tax_rules_group
|
||||
) tax_data ON tax_data.id_tax_rules_group = ps.id_tax_rules_group
|
||||
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
|
||||
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
|
||||
WHERE p.id_product = ?
|
||||
AND ps.active = 1
|
||||
AND pl.id_lang = ?
|
||||
AND ps.id_shop = ?
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
queryBySlug := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
pl.description_short AS short_description,
|
||||
pl.description AS description,
|
||||
ps.price AS price,
|
||||
(ps.price * curr.conversion_rate) AS price,
|
||||
((ps.price * curr.conversion_rate) * (1 + COALESCE(tax_data.tax_rate, 0) / 100)) AS price_tax_incl,
|
||||
COALESCE(tax_data.tax_rate, 0) AS tax_rate,
|
||||
curr.id_currency AS currency_id,
|
||||
curr.iso_code AS currency_code,
|
||||
curr.sign AS currency_sign,
|
||||
curr.conversion_rate AS conversion_rate,
|
||||
i.id_image AS cover_image_id,
|
||||
p.id_category_default AS category_id,
|
||||
cl.link_rewrite AS category_slug,
|
||||
cl.name AS category_name
|
||||
FROM %sproduct p
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product AND ps.id_shop = ?
|
||||
JOIN %scurrency curr ON curr.id_currency = ? AND curr.deleted = 0
|
||||
LEFT JOIN (
|
||||
SELECT tr.id_tax_rules_group,
|
||||
SUM(t.rate) AS tax_rate
|
||||
FROM %stax_rule tr
|
||||
JOIN %stax t ON t.id_tax = tr.id_tax AND t.active = 1
|
||||
GROUP BY tr.id_tax_rules_group
|
||||
) tax_data ON tax_data.id_tax_rules_group = ps.id_tax_rules_group
|
||||
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
|
||||
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
|
||||
WHERE pl.link_rewrite = ?
|
||||
AND ps.active = 1
|
||||
AND pl.id_lang = ?
|
||||
AND ps.id_shop = ?
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
var result *gorm.DB
|
||||
if req.ID != 0 {
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryByID), req.ID, req.LanguageID, req.ShopID).Scan(&product)
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryByID), req.ShopID, req.CurrencyID, req.ID, req.LanguageID).Scan(&product)
|
||||
} else {
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryBySlug), req.Slug, req.LanguageID, req.ShopID).Scan(&product)
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryBySlug), req.ShopID, req.CurrencyID, req.Slug, req.LanguageID).Scan(&product)
|
||||
}
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
@@ -147,12 +244,316 @@ LIMIT 1
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
features, err := s.loadProductFeatures(ctx, product.ID, req.LanguageID, req.ShopID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
product.Features = features
|
||||
images, err := s.loadProductImages(ctx, product.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
product.GalleryImages = images
|
||||
accessories, err := s.loadProductAccessories(ctx, product.ID, req.LanguageID, req.ShopID, req.CurrencyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
product.Accessories = accessories
|
||||
combinations, err := s.loadProductCombinations(ctx, product.ID, req.LanguageID, req.ShopID, req.CurrencyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
product.Combinations = combinations
|
||||
for _, combination := range combinations {
|
||||
if combination.DefaultOn {
|
||||
product.DefaultAttribute = combination.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
if product.DefaultAttribute == 0 && len(combinations) > 0 {
|
||||
product.DefaultAttribute = combinations[0].ID
|
||||
}
|
||||
|
||||
return &product, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadProductImages(ctx context.Context, productID int64) ([]ProductImage, error) {
|
||||
if productID == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT i.id_image,
|
||||
CASE WHEN COALESCE(i.cover, 0) = 1 THEN 1 ELSE 0 END AS cover,
|
||||
COALESCE(i.position, 0) AS position
|
||||
FROM %simage i
|
||||
WHERE i.id_product = ?
|
||||
ORDER BY CASE WHEN COALESCE(i.cover, 0) = 1 THEN 0 ELSE 1 END,
|
||||
COALESCE(i.position, 0) ASC,
|
||||
i.id_image ASC
|
||||
`, s.prefix)
|
||||
|
||||
images := make([]ProductImage, 0)
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(query), productID).Scan(&images).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadProductAccessories(ctx context.Context, productID, languageID, shopID, currencyID int64) ([]CategoryProductCard, error) {
|
||||
if productID == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
p.ean13 AS ean13,
|
||||
cl.link_rewrite AS category_slug,
|
||||
(ps.price * curr.conversion_rate) AS price,
|
||||
((ps.price * curr.conversion_rate) * (1 + COALESCE(tax_data.tax_rate, 0) / 100)) AS price_tax_incl,
|
||||
COALESCE(tax_data.tax_rate, 0) AS tax_rate,
|
||||
curr.id_currency AS currency_id,
|
||||
curr.iso_code AS currency_code,
|
||||
curr.sign AS currency_sign,
|
||||
curr.conversion_rate AS conversion_rate,
|
||||
i.id_image AS cover_image_id,
|
||||
pl.description_short AS short_description
|
||||
FROM %saccessory a
|
||||
JOIN %sproduct p ON p.id_product = a.id_product_2
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product AND ps.id_shop = ?
|
||||
JOIN %scurrency curr ON curr.id_currency = ? AND curr.deleted = 0
|
||||
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
|
||||
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
|
||||
LEFT JOIN (
|
||||
SELECT tr.id_tax_rules_group,
|
||||
SUM(t.rate) AS tax_rate
|
||||
FROM %stax_rule tr
|
||||
JOIN %stax t ON t.id_tax = tr.id_tax AND t.active = 1
|
||||
GROUP BY tr.id_tax_rules_group
|
||||
) tax_data ON tax_data.id_tax_rules_group = ps.id_tax_rules_group
|
||||
WHERE a.id_product_1 = ?
|
||||
AND ps.active = 1
|
||||
AND pl.id_lang = ?
|
||||
ORDER BY p.id_product ASC
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
products := make([]CategoryProductCard, 0)
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(query), shopID, currencyID, productID, languageID).Scan(&products).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return products, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadProductFeatures(ctx context.Context, productID, languageID, shopID int64) ([]ProductFeature, error) {
|
||||
if productID == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT pf.id_feature,
|
||||
fl.name AS name,
|
||||
fvl.value AS value
|
||||
FROM %sfeature_product pf
|
||||
LEFT JOIN %sfeature_lang fl
|
||||
ON fl.id_feature = pf.id_feature
|
||||
AND fl.id_lang = ?
|
||||
LEFT JOIN %sfeature_value_lang fvl
|
||||
ON fvl.id_feature_value = pf.id_feature_value
|
||||
AND fvl.id_lang = ?
|
||||
LEFT JOIN %sfeature f
|
||||
ON f.id_feature = pf.id_feature
|
||||
LEFT JOIN %sfeature_shop fs
|
||||
ON fs.id_feature = f.id_feature
|
||||
AND fs.id_shop = ?
|
||||
WHERE pf.id_product = ?
|
||||
ORDER BY f.position ASC
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
features := make([]ProductFeature, 0)
|
||||
if err := s.db.WithContext(ctx).Raw(
|
||||
strings.TrimSpace(query),
|
||||
languageID,
|
||||
languageID,
|
||||
shopID,
|
||||
productID,
|
||||
).Scan(&features).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return features, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadProductCombinations(ctx context.Context, productID, languageID, shopID, currencyID int64) ([]ProductCombination, error) {
|
||||
if productID == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type combinationRow struct {
|
||||
ID int64 `gorm:"column:id_product_attribute"`
|
||||
Price float64 `gorm:"column:price"`
|
||||
PriceTaxIncl float64 `gorm:"column:price_tax_incl"`
|
||||
DefaultOn bool `gorm:"column:default_on"`
|
||||
GroupName string `gorm:"column:group_name"`
|
||||
PublicName string `gorm:"column:public_group_name"`
|
||||
Attribute string `gorm:"column:attribute_name"`
|
||||
GroupType string `gorm:"column:group_type"`
|
||||
Color string `gorm:"column:attribute_color"`
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT pa.id_product_attribute,
|
||||
((ps.price + COALESCE(pas.price, 0)) * curr.conversion_rate) AS price,
|
||||
(((ps.price + COALESCE(pas.price, 0)) * curr.conversion_rate) * (1 + COALESCE(tax_data.tax_rate, 0) / 100)) AS price_tax_incl,
|
||||
CASE WHEN COALESCE(pas.default_on, pa.default_on, 0) = 1 THEN 1 ELSE 0 END AS default_on,
|
||||
agl.name AS group_name,
|
||||
agl.public_name AS public_group_name,
|
||||
al.name AS attribute_name,
|
||||
ag.group_type AS group_type,
|
||||
a.color AS attribute_color
|
||||
FROM %sproduct_attribute pa
|
||||
JOIN %sproduct_shop ps
|
||||
ON ps.id_product = pa.id_product
|
||||
AND ps.id_shop = ?
|
||||
JOIN %scurrency curr
|
||||
ON curr.id_currency = ?
|
||||
AND curr.deleted = 0
|
||||
LEFT JOIN (
|
||||
SELECT tr.id_tax_rules_group,
|
||||
SUM(t.rate) AS tax_rate
|
||||
FROM %stax_rule tr
|
||||
JOIN %stax t ON t.id_tax = tr.id_tax AND t.active = 1
|
||||
GROUP BY tr.id_tax_rules_group
|
||||
) tax_data
|
||||
ON tax_data.id_tax_rules_group = ps.id_tax_rules_group
|
||||
LEFT JOIN %sproduct_attribute_shop pas
|
||||
ON pas.id_product_attribute = pa.id_product_attribute
|
||||
AND pas.id_shop = ?
|
||||
JOIN %sproduct_attribute_combination pac
|
||||
ON pac.id_product_attribute = pa.id_product_attribute
|
||||
JOIN %sattribute a
|
||||
ON a.id_attribute = pac.id_attribute
|
||||
JOIN %sattribute_lang al
|
||||
ON al.id_attribute = a.id_attribute
|
||||
AND al.id_lang = ?
|
||||
JOIN %sattribute_group ag
|
||||
ON ag.id_attribute_group = a.id_attribute_group
|
||||
JOIN %sattribute_group_lang agl
|
||||
ON agl.id_attribute_group = ag.id_attribute_group
|
||||
AND agl.id_lang = ?
|
||||
WHERE pa.id_product = ?
|
||||
ORDER BY CASE WHEN COALESCE(pas.default_on, pa.default_on, 0) = 1 THEN 0 ELSE 1 END,
|
||||
pa.id_product_attribute ASC,
|
||||
ag.position ASC,
|
||||
a.position ASC,
|
||||
al.name ASC
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
rows := make([]combinationRow, 0)
|
||||
if err := s.db.WithContext(ctx).Raw(
|
||||
strings.TrimSpace(query),
|
||||
shopID,
|
||||
currencyID,
|
||||
shopID,
|
||||
languageID,
|
||||
languageID,
|
||||
productID,
|
||||
).Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
combinations := make([]ProductCombination, 0)
|
||||
indexByID := make(map[int64]int)
|
||||
for _, row := range rows {
|
||||
idx, exists := indexByID[row.ID]
|
||||
if !exists {
|
||||
combinations = append(combinations, ProductCombination{
|
||||
ID: row.ID,
|
||||
Price: row.Price,
|
||||
PriceTaxIncl: row.PriceTaxIncl,
|
||||
DefaultOn: row.DefaultOn,
|
||||
})
|
||||
idx = len(combinations) - 1
|
||||
indexByID[row.ID] = idx
|
||||
}
|
||||
if strings.TrimSpace(row.GroupName) != "" || strings.TrimSpace(row.Attribute) != "" {
|
||||
combinations[idx].Attributes = append(combinations[idx].Attributes, ProductCombinationAttribute{
|
||||
Group: row.GroupName,
|
||||
PublicName: row.PublicName,
|
||||
Value: row.Attribute,
|
||||
GroupType: row.GroupType,
|
||||
Color: row.Color,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.loadCombinationImageIDs(ctx, &combinations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return combinations, nil
|
||||
}
|
||||
|
||||
func (s *Service) loadCombinationImageIDs(ctx context.Context, combinations *[]ProductCombination) error {
|
||||
if combinations == nil || len(*combinations) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
combinationIDs := make([]int64, 0, len(*combinations))
|
||||
indexByID := make(map[int64]int, len(*combinations))
|
||||
for i, combination := range *combinations {
|
||||
if combination.ID == 0 {
|
||||
continue
|
||||
}
|
||||
combinationIDs = append(combinationIDs, combination.ID)
|
||||
indexByID[combination.ID] = i
|
||||
}
|
||||
if len(combinationIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type combinationImageRow struct {
|
||||
ID int64 `gorm:"column:id_product_attribute"`
|
||||
ImageID int64 `gorm:"column:id_image"`
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT pai.id_product_attribute,
|
||||
MIN(pai.id_image) AS id_image
|
||||
FROM %sproduct_attribute_image pai
|
||||
WHERE pai.id_product_attribute IN ?
|
||||
GROUP BY pai.id_product_attribute
|
||||
`, s.prefix)
|
||||
|
||||
rows := make([]combinationImageRow, 0)
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(query), combinationIDs).Scan(&rows).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
idx, exists := indexByID[row.ID]
|
||||
if !exists || row.ImageID == 0 {
|
||||
continue
|
||||
}
|
||||
(*combinations)[idx].ImageID = sql.NullInt64{Int64: row.ImageID, Valid: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) GetCategoryPage(ctx context.Context, req CategoryPageRequest) (*CategoryPageData, error) {
|
||||
var category CategoryPageData
|
||||
if req.CurrencyID == 0 {
|
||||
req.CurrencyID = 1
|
||||
}
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PerPage <= 0 {
|
||||
req.PerPage = 20
|
||||
}
|
||||
categoryQuery := fmt.Sprintf(`
|
||||
SELECT c.id_category AS id,
|
||||
cl.name AS name,
|
||||
@@ -213,25 +614,67 @@ LIMIT 1
|
||||
}
|
||||
}
|
||||
|
||||
countQuery := fmt.Sprintf(`
|
||||
SELECT COUNT(*) AS total_items
|
||||
FROM %scategory_product cp
|
||||
JOIN %sproduct p ON p.id_product = cp.id_product
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product AND ps.id_shop = ?
|
||||
WHERE cp.id_category = ?
|
||||
AND ps.active = 1
|
||||
AND pl.id_lang = ?
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
var countRow struct {
|
||||
TotalItems int64 `gorm:"column:total_items"`
|
||||
}
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(countQuery), req.ShopID, category.ID, req.LanguageID).Scan(&countRow).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
category.Pagination = CategoryPagination{
|
||||
Page: req.Page,
|
||||
PerPage: req.PerPage,
|
||||
TotalItems: countRow.TotalItems,
|
||||
TotalPages: totalPages(countRow.TotalItems, req.PerPage),
|
||||
}
|
||||
offset := (req.Page - 1) * req.PerPage
|
||||
|
||||
productQuery := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
p.ean13 AS ean13,
|
||||
ps.price AS price,
|
||||
pl.description_short AS description
|
||||
(ps.price * curr.conversion_rate) AS price,
|
||||
((ps.price * curr.conversion_rate) * (1 + COALESCE(tax_data.tax_rate, 0) / 100)) AS price_tax_incl,
|
||||
COALESCE(tax_data.tax_rate, 0) AS tax_rate,
|
||||
curr.id_currency AS currency_id,
|
||||
curr.iso_code AS currency_code,
|
||||
curr.sign AS currency_sign,
|
||||
curr.conversion_rate AS conversion_rate,
|
||||
i.id_image AS cover_image_id,
|
||||
pl.description_short AS short_description
|
||||
FROM %scategory_product cp
|
||||
JOIN %sproduct p ON p.id_product = cp.id_product
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product AND ps.id_shop = ?
|
||||
JOIN %scurrency curr ON curr.id_currency = ? AND curr.deleted = 0
|
||||
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
|
||||
LEFT JOIN (
|
||||
SELECT tr.id_tax_rules_group,
|
||||
SUM(t.rate) AS tax_rate
|
||||
FROM %stax_rule tr
|
||||
JOIN %stax t ON t.id_tax = tr.id_tax AND t.active = 1
|
||||
GROUP BY tr.id_tax_rules_group
|
||||
) tax_data ON tax_data.id_tax_rules_group = ps.id_tax_rules_group
|
||||
WHERE cp.id_category = ?
|
||||
AND ps.active = 1
|
||||
AND pl.id_lang = ?
|
||||
AND ps.id_shop = ?
|
||||
ORDER BY cp.position ASC, p.id_product ASC
|
||||
LIMIT 48
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
LIMIT ?
|
||||
OFFSET ?
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(productQuery), category.ID, req.LanguageID, req.ShopID).Scan(&category.Products).Error; err != nil {
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(productQuery), req.ShopID, req.CurrencyID, category.ID, req.LanguageID, req.PerPage, offset).Scan(&category.Products).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -266,6 +709,17 @@ func (s *Service) ResolveLanguageID(ctx context.Context, req *http.Request, fall
|
||||
return row.ID
|
||||
}
|
||||
|
||||
func totalPages(totalItems int64, perPage int) int {
|
||||
if totalItems <= 0 || perPage <= 0 {
|
||||
return 0
|
||||
}
|
||||
pages := int(totalItems / int64(perPage))
|
||||
if totalItems%int64(perPage) != 0 {
|
||||
pages++
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
func (s *Service) GetCategoryMenu(ctx context.Context, languageID int64, shopID int64) ([]MenuItem, error) {
|
||||
rootCategoryID, err := s.rootCategoryID(ctx)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user