init
This commit is contained in:
commit
0cb03a74a9
|
|
@ -0,0 +1,2 @@
|
||||||
|
DB_DSN="usr:pwd@tcp(127.0.0.1:3306)/test_baza?parseTime=true"
|
||||||
|
UPDATE_PERIOD=30m
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
.env
|
||||||
|
vendor
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/sarga_updater.iml" filepath="$PROJECT_DIR$/.idea/sarga_updater.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package bagisto_models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gosimple/slug"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Brand struct {
|
||||||
|
ID uint `gorm:"primary_key"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
Name string `gorm:"default:TESTBRAND"`
|
||||||
|
Code string
|
||||||
|
Status bool
|
||||||
|
Categories []Category `gorm:"many2many:category_brands;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindOrCreateBrand(db *gorm.DB, brand string, categories []Category) (Brand, error) {
|
||||||
|
|
||||||
|
var brandObject Brand
|
||||||
|
|
||||||
|
if brand != "" {
|
||||||
|
|
||||||
|
code := slug.Make(brand)
|
||||||
|
|
||||||
|
err := db.FirstOrCreate(&brandObject, Brand{Name: brand, Code: code}).Error
|
||||||
|
//err := db.Model(Brand{Name: brand, Code: code}).First(&brandObject).Error
|
||||||
|
//log.Println(Brand{Name: brand, Code: code})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
//err := db.Omit("Categories.*").FirstOrCreate(&brandObject, Brand{Name: brand, Code: code, Categories: categories}).Error
|
||||||
|
log.Println("ERR0000000000000001" + err.Error())
|
||||||
|
return brandObject, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Model(&brandObject).Association("Categories").Append(categories)
|
||||||
|
|
||||||
|
return brandObject, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// var brandObject Brand
|
||||||
|
|
||||||
|
return brandObject, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package bagisto_models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CategoryTranslation struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Slug string
|
||||||
|
CategoryID uint
|
||||||
|
MetaDescription string
|
||||||
|
MetaKeywords string
|
||||||
|
Locale string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Category struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Status int8
|
||||||
|
Position int
|
||||||
|
ParentId uint
|
||||||
|
DisplayMode string
|
||||||
|
Translations []CategoryTranslation
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMainCategories(db *gorm.DB) ([]Category, error) {
|
||||||
|
var categories []Category
|
||||||
|
err := db.Model(&Category{}).Preload("Translations").Find(&categories, "status=1 AND parent_id=1 AND display_mode!='promotion'").Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCatKeywords(db *gorm.DB, catIDs []int) ([]Category, error) {
|
||||||
|
var categories []Category
|
||||||
|
|
||||||
|
if errCat := db.Preload("Translations", "locale=?", "tm").Find(&categories, catIDs).Error; errCat != nil {
|
||||||
|
log.Println(errCat)
|
||||||
|
return categories, errCat
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
package bagisto_models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AttributeOption struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
AttributeID uint
|
||||||
|
AdminName string
|
||||||
|
SortOrder int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Translations []AttributeOptionTranslation
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttributeOptionTranslation struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
AttributeOptionID uint
|
||||||
|
Locale string
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attribute struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Code string
|
||||||
|
AttributeOptions []AttributeOption
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttributeFamily struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Code string
|
||||||
|
Groups []AttributeGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttributeGroup struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Name string
|
||||||
|
AttributeFamilyID uint
|
||||||
|
Attributes []Attribute `gorm:"many2many:attribute_group_mappings;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFamilies(db *gorm.DB) ([]AttributeFamily, error) {
|
||||||
|
var families []AttributeFamily
|
||||||
|
|
||||||
|
err := db.Model(&AttributeFamily{}).Preload("Groups").Order("id ASC").Find(&families).Error
|
||||||
|
|
||||||
|
return families, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroups(db *gorm.DB) []AttributeGroup {
|
||||||
|
var groups []AttributeGroup
|
||||||
|
err := db.Model(&AttributeGroup{}).Preload("Family").Find(&groups).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
|
||||||
|
}
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAttributes(db *gorm.DB) ([]Attribute, error) {
|
||||||
|
var attributes []Attribute
|
||||||
|
|
||||||
|
err := db.Model(&Attribute{}).Find(&attributes).Error
|
||||||
|
|
||||||
|
return attributes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAttributeOption(db *gorm.DB, attrbuteID uint, value string) AttributeOption {
|
||||||
|
var option AttributeOption
|
||||||
|
|
||||||
|
err := db.Where("attribute_id=? and admin_name=?", attrbuteID, value).First(&option).Error
|
||||||
|
|
||||||
|
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
option = AttributeOption{
|
||||||
|
AttributeID: attrbuteID,
|
||||||
|
AdminName: value,
|
||||||
|
SortOrder: 1000,
|
||||||
|
Translations: []AttributeOptionTranslation{
|
||||||
|
{Locale: "tm", Label: value},
|
||||||
|
{Locale: "ru", Label: value},
|
||||||
|
{Locale: "tr", Label: value},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Create(&option).Error; err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAttrOptions(db *gorm.DB, attrbuteID uint) ([]AttributeOption, error) {
|
||||||
|
|
||||||
|
var options []AttributeOption
|
||||||
|
|
||||||
|
err := db.Find(&options, AttributeOption{AttributeID: attrbuteID}).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return options, err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
package bagisto_models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
ID uint `gorm:"primary_key"`
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
Sku string
|
||||||
|
Type string
|
||||||
|
ParentID uint `sql:"DEFAULT:NULL"`
|
||||||
|
AttributeFamilyID uint
|
||||||
|
AttributeFamily AttributeFamily
|
||||||
|
BrandID uint `sql:"DEFAULT:NULL"`
|
||||||
|
Brand Brand
|
||||||
|
Images []ProductImage
|
||||||
|
Categories []Category `gorm:"many2many:product_categories;"`
|
||||||
|
AttributeValues []ProductAttributeValue
|
||||||
|
SuperAttributes []Attribute `gorm:"many2many:product_super_attributes;"`
|
||||||
|
//Variants []*Product `gorm:"many2many:product_relations;foreignKey:child_id;primaryKey:parent_id;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductRelation struct {
|
||||||
|
ParentID uint
|
||||||
|
ChildID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductFlat struct {
|
||||||
|
ID uint `gorm:"primary_key"`
|
||||||
|
Sku string
|
||||||
|
ProductNumber string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Name string `sql:"DEFAULT:''" gorm:"default:''"`
|
||||||
|
Weight float64 `sql:"DEFAULT:NULL" gorm:"type:decimal(12,4);default:null"`
|
||||||
|
Status bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
VisibleIndividually bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Price float64 `gorm:"type:decimal(12,4)"`
|
||||||
|
MinPrice float64 `gorm:"type:decimal(12,4)"`
|
||||||
|
MaxPrice float64 `gorm:"type:decimal(12,4)"`
|
||||||
|
SpecialPrice float64 `sql:"DEFAULT:NULL" gorm:"type:decimal(12,4);default:null"`
|
||||||
|
UrlKey string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
ShortDescription string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Description string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
FavoritesCount uint `sql:"DEFAULT:NULL" gorm:"default:null;column:favoritesCount"`
|
||||||
|
CreatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
UpdatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
ProductID uint
|
||||||
|
Product Product
|
||||||
|
Channel string `sql:"DEFAULT:default" gorm:"default:default"`
|
||||||
|
Locale string `sql:"DEFAULT:tm" gorm:"default:tm"`
|
||||||
|
ParentID uint `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Variants []ProductFlat `gorm:"foreignKey:ParentID"`
|
||||||
|
Color int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
ColorLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Size int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
SizeLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Boyut int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
BoyutLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
MetaTitle string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
MetaKeywords string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
BrandID uint `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Brand Brand
|
||||||
|
Cinsiyet int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
CinsiyetLabel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductImage struct {
|
||||||
|
ID uint `gorm:"primary_key"`
|
||||||
|
Type string
|
||||||
|
Path string
|
||||||
|
ProductID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductAttributeValue struct {
|
||||||
|
ID uint `gorm:"primary_key"`
|
||||||
|
Locale string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Channel string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
ProductID uint
|
||||||
|
AttributeID uint
|
||||||
|
TextValue string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
BooleanValue bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
IntegerValue int `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
FloatValue float64 `sql:"DEFAULT:NULL" gorm:"type:decimal(12,4);default:null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductSuperAttribute struct {
|
||||||
|
ProductID uint
|
||||||
|
AttributeID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tabler interface {
|
||||||
|
TableName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName overrides the table name used by User to `profiles`
|
||||||
|
func (ProductFlat) TableName() string {
|
||||||
|
return "product_flat"
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteProducts(db *gorm.DB) error {
|
||||||
|
//todo delete from elastico
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
//qb := "DELETE FROM products WHERE id NOT IN (select product_id as id from wishlist) AND id NOT IN (select product_id as id from order_items) AND id NOT IN (select parent_id as idfrom order_items);"
|
||||||
|
qb := "DELETE p FROM products p " +
|
||||||
|
"LEFT JOIN order_items oi ON p.id = oi.product_id " +
|
||||||
|
"LEFT JOIN order_items op ON p.id = op.parent_id " +
|
||||||
|
"LEFT JOIN wishlist wp ON p.id = wp.product_id " +
|
||||||
|
"LEFT JOIN wishlist wl ON wl.product_id = p.parent_id " +
|
||||||
|
"JOIN marketplace_products mp ON p.id = mp.product_id " +
|
||||||
|
"WHERE oi.id IS NULL AND op.id IS NULL AND wp.id IS NULL AND wl.id IS NULL AND mp.marketplace_seller_id=1;"
|
||||||
|
db.WithContext(ctx).Exec(qb)
|
||||||
|
//db.WithContext(ctx).Exec("UPDATE product_flat set sku=concat(id,\"-ordered\"), status=0 where status=1 AND product_id IN (SELECT product_id from marketplace_products where marketplace_seller_id=1)" )
|
||||||
|
//db.WithContext(ctx).Exec("UPDATE products set sku=concat(id,\"-ordered\") WHERE id IN (SELECT product_id from marketplace_products where marketplace_seller_id=1)")
|
||||||
|
return db.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWishlistProducts(db *gorm.DB) ([]Product, error) {
|
||||||
|
var products []Product
|
||||||
|
err := db.Joins("JOIN wishlist wp ON products.id = wp.product_id").
|
||||||
|
Joins("JOIN marketplace_products mp ON products.id = mp.product_id where marketplace_seller_id=1").
|
||||||
|
Find(&products).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return products, nil
|
||||||
|
//qb := "SELECT p.id,p.sku FROM products p " +
|
||||||
|
// "JOIN wishlist wp ON p.id = wp.product_id " +
|
||||||
|
// "JOIN marketplace_products mp ON p.id = mp.product_id where marketplace_seller_id=1);"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFlatSources(db *gorm.DB) ([]ProductAttributeValue, error) {
|
||||||
|
var productSources []ProductAttributeValue
|
||||||
|
err := db.Joins("JOIN wishlist wp ON product_attribute_values.product_id = wp.product_id").
|
||||||
|
Joins("JOIN marketplace_products mp ON product_attribute_values.product_id = mp.product_id where marketplace_seller_id=1").
|
||||||
|
Find(&productSources, "text_value IS NOT NULL AND attribute_id=31").Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return productSources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func DisableProducts (db *gorm.DB) error {
|
||||||
|
// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||||
|
// defer cancel()
|
||||||
|
// db.WithContext(ctx).Exec("UPDATE product_flat set sku=concat(sku,\"-ordered\"), status=0")
|
||||||
|
// db.WithContext(ctx).Exec("UPDATE products set sku=concat(sku,\"-ordered\")")
|
||||||
|
// return db.Error
|
||||||
|
//}
|
||||||
|
|
||||||
|
//func Flush() error {
|
||||||
|
// _, err := helper.SendRequest("GET", os.Getenv("scout_flash"), nil, "")
|
||||||
|
//
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package bagisto_models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MarketplaceProduct struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
CreatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
UpdatedAt time.Time `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
ProductID uint
|
||||||
|
//Product Product
|
||||||
|
ParentID *uint `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Condition string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
Price float64
|
||||||
|
Description string `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
IsApproved bool `sql:"DEFAULT:NULL" gorm:"default:null"`
|
||||||
|
IsOwner bool
|
||||||
|
MarketplaceSellerID uint
|
||||||
|
MarketplaceSeller MarketplaceSeller
|
||||||
|
Variants []MarketplaceProduct `gorm:"foreignKey:ParentID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketplaceSeller struct {
|
||||||
|
ID uint `gorm:"primaryKey"`
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSellers(db *gorm.DB) ([]MarketplaceSeller, error) {
|
||||||
|
var sellers []MarketplaceSeller
|
||||||
|
|
||||||
|
err := db.Model(&MarketplaceSeller{}).Find(&sellers).Error
|
||||||
|
|
||||||
|
return sellers, err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func StartUpdate(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
module sarga_updater
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gosimple/slug v1.13.1
|
||||||
|
github.com/leesper/couchdb-golang v1.2.1
|
||||||
|
gorm.io/driver/mysql v1.5.0
|
||||||
|
gorm.io/gorm v1.25.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
|
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
|
||||||
|
github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
||||||
|
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
||||||
|
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/leesper/couchdb-golang v1.2.1 h1:FqSaTxxT2mVRLbxGQVkZakRRoSzWhPmV8UEKYjA/GWc=
|
||||||
|
github.com/leesper/couchdb-golang v1.2.1/go.mod h1:OU3FDAM3mazHx15oi8Hm+egTMneBUqepwnh0LuBSH54=
|
||||||
|
gorm.io/driver/mysql v1.5.0 h1:6hSAT5QcyIaty0jfnff0z0CLDjyRgZ8mlMHLqSt7uXM=
|
||||||
|
gorm.io/driver/mysql v1.5.0/go.mod h1:FFla/fJuCvyTi7rJQd27qlNX2v3L6deTR1GgTjSOLPo=
|
||||||
|
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
|
gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
|
||||||
|
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/leesper/couchdb-golang"
|
||||||
|
models "sarga_updater/trendyol_models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CdbServer is global couchdb server
|
||||||
|
var CdbServer *couchdb.Server
|
||||||
|
|
||||||
|
// Categories stores all categories and their weight
|
||||||
|
var Categories []models.CouchCategory = []models.CouchCategory{}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
models "sarga_updater/trendyol_models"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckDBExists(endpoint string) bool {
|
||||||
|
|
||||||
|
const ConnectMaxWaitTime = 30 * time.Second
|
||||||
|
const RequestMaxWaitTime = 60 * time.Second
|
||||||
|
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: ConnectMaxWaitTime,
|
||||||
|
}).DialContext,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
|
||||||
|
req.Proto = "HTTP/1.0"
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
req.Header.Set("Connection", "close")
|
||||||
|
req.Close = true
|
||||||
|
client.CloseIdleConnections()
|
||||||
|
|
||||||
|
response, err := client.Do(req)
|
||||||
|
|
||||||
|
// handle timeout
|
||||||
|
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
log.Println(err.Error())
|
||||||
|
return false
|
||||||
|
} else if err != nil {
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
log.Println(err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCategoryWeight(slug string) string {
|
||||||
|
|
||||||
|
db, err := CdbServer.Get(os.Getenv("db_ty_categories"))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
Error("can not get db. Err: " + err.Error())
|
||||||
|
return "0.5"
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := db.Get(slug, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
Error("can not get by slug. Err: " + err.Error())
|
||||||
|
return "0.5"
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc["weight"].(string)
|
||||||
|
}
|
||||||
|
func IsLCWSizeVariantsAdded(items []models.Variant, keyItem models.Variant) bool {
|
||||||
|
for _, option := range items {
|
||||||
|
if option.ItemNumber == keyItem.ItemNumber {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsImageAdded(images []string, image string) bool {
|
||||||
|
for _, img := range images {
|
||||||
|
if img == image {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func IsVariantsAdded(variants []models.Variant, itemNumber int) bool {
|
||||||
|
for _, variant := range variants {
|
||||||
|
if variant.ItemNumber == itemNumber {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level int
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultPrefix = ""
|
||||||
|
DefaultCallerDepth = 2
|
||||||
|
|
||||||
|
logger *log.Logger
|
||||||
|
logPrefix = ""
|
||||||
|
levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEBUG Level = iota
|
||||||
|
INFO
|
||||||
|
WARNING
|
||||||
|
ERROR
|
||||||
|
FATAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// Setup initialize the log instance
|
||||||
|
func Setup() {
|
||||||
|
logger = log.New(os.Stdout, DefaultPrefix, log.Flags()&^(log.Ldate|log.Ltime))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output logs at debug level
|
||||||
|
func Debug(v ...interface{}) {
|
||||||
|
setPrefix(DEBUG)
|
||||||
|
logger.Println(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info output logs at info level
|
||||||
|
func Info(v ...interface{}) {
|
||||||
|
setPrefix(INFO)
|
||||||
|
logger.Println(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn output logs at warn level
|
||||||
|
func Warn(v ...interface{}) {
|
||||||
|
setPrefix(WARNING)
|
||||||
|
logger.Println(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error output logs at error level
|
||||||
|
func Error(v ...interface{}) {
|
||||||
|
prefix := setPrefix(ERROR)
|
||||||
|
logger.Println(v...)
|
||||||
|
|
||||||
|
prefix.msg = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal output logs at fatal level
|
||||||
|
func Fatal(v ...interface{}) {
|
||||||
|
setPrefix(FATAL)
|
||||||
|
logger.Fatalln(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPrefix set the prefix of the log output
|
||||||
|
func setPrefix(level Level) prefix {
|
||||||
|
_, file, line, ok := runtime.Caller(DefaultCallerDepth)
|
||||||
|
if ok {
|
||||||
|
logPrefix = fmt.Sprintf("[%s:%d] [%s] ", filepath.Base(file), line, levelFlags[level])
|
||||||
|
} else {
|
||||||
|
logPrefix = fmt.Sprintf("[%s] ", levelFlags[level])
|
||||||
|
}
|
||||||
|
|
||||||
|
logPrefix += "| " + time.Now().Format("01.02.2006 15:04:05") + " | "
|
||||||
|
|
||||||
|
logger.SetPrefix(logPrefix)
|
||||||
|
return prefix{prefix: logPrefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
type prefix struct {
|
||||||
|
prefix string
|
||||||
|
msg interface{}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SendRequest(method string, endpoint string, values []byte, authKey string) ([]byte, error) {
|
||||||
|
|
||||||
|
const ConnectMaxWaitTime = time.Minute
|
||||||
|
const RequestMaxWaitTime = 5 * time.Minute
|
||||||
|
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: ConnectMaxWaitTime,
|
||||||
|
}).DialContext,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
emptyBody := []byte{}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, method, endpoint, bytes.NewBuffer(values))
|
||||||
|
req.Proto = "HTTP/2.0"
|
||||||
|
if err != nil {
|
||||||
|
Error(err)
|
||||||
|
return emptyBody, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
req.Header.Set("Connection", "close")
|
||||||
|
req.Header.Add("Authorization", authKey)
|
||||||
|
req.Close = true
|
||||||
|
client.CloseIdleConnections()
|
||||||
|
|
||||||
|
response, err := client.Do(req)
|
||||||
|
|
||||||
|
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
Error(err)
|
||||||
|
return emptyBody, err
|
||||||
|
} else if err != nil {
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
Error(err)
|
||||||
|
return emptyBody, err
|
||||||
|
}
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("response: code: %d, body: %v", response.StatusCode, response.Body)
|
||||||
|
Error(err)
|
||||||
|
return emptyBody, err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
Error(err)
|
||||||
|
if response != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
return emptyBody, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you call it in loop, don't use defer
|
||||||
|
response.Body.Close()
|
||||||
|
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
func NewHttpClient() (http.Client, context.Context) {
|
||||||
|
const ConnectMaxWaitTime = 30 * time.Second
|
||||||
|
const RequestMaxWaitTime = 60 * time.Second
|
||||||
|
//proxyUrl, _ := url.Parse("http://79.98.129.183:3128")
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: ConnectMaxWaitTime,
|
||||||
|
}).DialContext,
|
||||||
|
//Proxy: http.ProxyURL(proxyUrl),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
return client, ctx
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package sarga_updater
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sarga_updater/bagisto_models"
|
||||||
|
"sarga_updater/repositories"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Retrieve database connection information from environment variables
|
||||||
|
dsn := os.Getenv("DB_DSN")
|
||||||
|
|
||||||
|
// Connect to the database
|
||||||
|
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error connecting to database: %s", err)
|
||||||
|
}
|
||||||
|
importer, _ := repositories.ParseImporterInstance(db)
|
||||||
|
|
||||||
|
updatePeriodStr := os.Getenv("UPDATE_PERIOD")
|
||||||
|
updatePeriod, err := time.ParseDuration(updatePeriodStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error parsing update period:", err)
|
||||||
|
}
|
||||||
|
// Start the worker
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
go worker(importer, stopCh, updatePeriod)
|
||||||
|
|
||||||
|
// Wait for termination signal
|
||||||
|
signalCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
|
||||||
|
<-signalCh
|
||||||
|
|
||||||
|
// Stop the worker
|
||||||
|
close(stopCh)
|
||||||
|
|
||||||
|
// Close the database connection
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error getting database connection from gorm: %s", err)
|
||||||
|
}
|
||||||
|
err = sqlDB.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error closing database connection: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func worker(importer *repositories.Importer, stopCh <-chan struct{}, updatePeriod time.Duration) {
|
||||||
|
ticker := time.NewTicker(updatePeriod)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopCh:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
|
||||||
|
result, err := bagisto_models.GetFlatSources(importer.Baza)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error retrieving products:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update products as needed
|
||||||
|
for _, product := range result {
|
||||||
|
// Retrieve the latest information from the source URL
|
||||||
|
parser := repositories.NewLinkParser(product.TextValue)
|
||||||
|
data, err := parser.ParseLink()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error decoding product information:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonProduct, err := parser.GetProductDetailWithOptions(data.ID, data.ProductGroupID)
|
||||||
|
|
||||||
|
// Update the product in the database
|
||||||
|
if err := importer.UpdateOrCreate(jsonProduct).Error; err != nil {
|
||||||
|
log.Println("Error decoding product information:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Product %d updated: %+v\n", product.ID, product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,842 @@
|
||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
gm "sarga_updater/bagisto_models"
|
||||||
|
helper "sarga_updater/helpers"
|
||||||
|
models "sarga_updater/trendyol_models"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Importer struct {
|
||||||
|
mainCategories []gm.Category
|
||||||
|
Baza *gorm.DB
|
||||||
|
families []gm.AttributeFamily
|
||||||
|
sellers map[string]gm.MarketplaceSeller
|
||||||
|
//wishlist map[string]gm.Product
|
||||||
|
AttributesMap map[string]gm.Attribute
|
||||||
|
Error error
|
||||||
|
ImportWGroup sync.WaitGroup
|
||||||
|
ColorOptions map[string]gm.AttributeOption
|
||||||
|
SexOptions map[string]gm.AttributeOption
|
||||||
|
ColorMutex sync.Mutex
|
||||||
|
SexMutex sync.Mutex
|
||||||
|
}
|
||||||
|
type GormErr struct {
|
||||||
|
Number int `json:"Number"`
|
||||||
|
Message string `json:"Message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseImporterInstance(db *gorm.DB) (instance *Importer, err error) {
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = &Importer{Baza: db}
|
||||||
|
instance.ImportWGroup.Add(3)
|
||||||
|
//load families to memory
|
||||||
|
go func() {
|
||||||
|
defer instance.ImportWGroup.Done()
|
||||||
|
instance.families, instance.Error = gm.GetFamilies(db)
|
||||||
|
}()
|
||||||
|
|
||||||
|
//load attributes to memory
|
||||||
|
go func() {
|
||||||
|
defer instance.ImportWGroup.Done()
|
||||||
|
|
||||||
|
if attributes, err := gm.GetAttributes(db); err != nil {
|
||||||
|
instance.Error = err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
instance.AttributesMap = make(map[string]gm.Attribute, len(attributes))
|
||||||
|
|
||||||
|
for _, attribute := range attributes {
|
||||||
|
instance.AttributesMap[attribute.Code] = attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if colorOptions, err := gm.GetAttrOptions(db, instance.AttributesMap["color"].ID); err != nil {
|
||||||
|
instance.Error = err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
instance.ColorOptions = make(map[string]gm.AttributeOption, len(colorOptions))
|
||||||
|
|
||||||
|
for _, option := range colorOptions {
|
||||||
|
instance.ColorOptions[option.AdminName] = option
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sexOPtions, err := gm.GetAttrOptions(db, instance.AttributesMap["cinsiyet"].ID); err != nil {
|
||||||
|
instance.Error = err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
instance.SexOptions = make(map[string]gm.AttributeOption, len(sexOPtions))
|
||||||
|
|
||||||
|
for _, option := range sexOPtions {
|
||||||
|
instance.SexOptions[option.AdminName] = option
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
//load sellers to memory
|
||||||
|
go func() {
|
||||||
|
defer instance.ImportWGroup.Done()
|
||||||
|
|
||||||
|
var vendors, err = gm.GetSellers(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
instance.Error = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//init sellers map
|
||||||
|
instance.sellers = make(map[string]gm.MarketplaceSeller, len(vendors))
|
||||||
|
|
||||||
|
for _, vendor := range vendors {
|
||||||
|
instance.sellers[vendor.Url] = vendor
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if instance.Error != nil {
|
||||||
|
log.Println(instance.Error)
|
||||||
|
return nil, instance.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) ImportProduct(product models.Product) (instance *Importer) {
|
||||||
|
|
||||||
|
var linkedProducts []gm.Product
|
||||||
|
var firstProduct *gm.Product
|
||||||
|
if firstProduct, importer.Error = importer.importVariant(product); importer.Error != nil {
|
||||||
|
return importer
|
||||||
|
} else if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
|
||||||
|
linkedProducts = append(linkedProducts, *firstProduct)
|
||||||
|
for _, colorVariant := range *product.ColorVariants {
|
||||||
|
|
||||||
|
if !colorVariant.IsSellable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if variant, err := importer.importVariant(colorVariant); err == nil {
|
||||||
|
linkedProducts = append(linkedProducts, *variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(linkedProducts) > 1 {
|
||||||
|
|
||||||
|
var relation []gm.ProductRelation
|
||||||
|
for index, variant := range linkedProducts {
|
||||||
|
//spoint := "color" + strconv.Itoa(index)
|
||||||
|
temp := make([]gm.Product, len(linkedProducts))
|
||||||
|
|
||||||
|
copy(temp, linkedProducts)
|
||||||
|
|
||||||
|
if index+1 <= len(temp) {
|
||||||
|
temp = append(temp[:index], temp[index+1:]...)
|
||||||
|
|
||||||
|
for _, item := range temp {
|
||||||
|
relation = append(relation, gm.ProductRelation{ParentID: variant.ID, ChildID: item.ID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := importer.Baza.Create(&relation).Error; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return importer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) importVariant(product models.Product) (*gm.Product, error) {
|
||||||
|
|
||||||
|
// check if wishlisted then update if.
|
||||||
|
//if _, ok := importer.wishlist[product.ProductNumber]; ok {
|
||||||
|
// delete(importer.wishlist,product.ProductNumber)
|
||||||
|
// return importer.updateVariant(product)
|
||||||
|
//}
|
||||||
|
|
||||||
|
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||||
|
|
||||||
|
if categories, err := gm.GetCatKeywords(importer.Baza, product.Categories); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
productRepo.SetCategories(categories)
|
||||||
|
}
|
||||||
|
|
||||||
|
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
productRepo.Brand = brand
|
||||||
|
}
|
||||||
|
|
||||||
|
mainPorduct := productRepo.makeProduct(importer)
|
||||||
|
|
||||||
|
//BEGIN TRANSACTION
|
||||||
|
tx := importer.Baza.Begin()
|
||||||
|
|
||||||
|
if err := tx.Omit("Categories.*", "SuperAttributes.*", "ParentID").Create(&mainPorduct).Error; err != nil {
|
||||||
|
//todo update categories
|
||||||
|
byteErr, _ := json.Marshal(err)
|
||||||
|
var newError GormErr
|
||||||
|
|
||||||
|
if err1 := json.Unmarshal((byteErr), &newError); err1 != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
log.Println(err1, "err2")
|
||||||
|
return nil, err1
|
||||||
|
}
|
||||||
|
|
||||||
|
if newError.Number == 1062 {
|
||||||
|
var barProduct gm.Product
|
||||||
|
if err2 := tx.First(&barProduct, "sku = ?", mainPorduct.Sku).Error; err2 != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
log.Println(err2, "err3")
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
if err3 := tx.Model(&barProduct).Association("Categories").Append(mainPorduct.Categories); err3 != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
log.Println(err3, "err4")
|
||||||
|
return nil, err3
|
||||||
|
}
|
||||||
|
|
||||||
|
if err4 := tx.Commit().Error; err4 == nil {
|
||||||
|
return importer.updateVariant(product)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx.Rollback()
|
||||||
|
log.Println(err, "er1")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mainFlat := productRepo.makeProductFlat(mainPorduct.ID)
|
||||||
|
|
||||||
|
if err := tx.Create(&mainFlat).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
log.Println(err, "er5")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if productRepo.HasSizeVariants() {
|
||||||
|
var sizeVariants []gm.ProductFlat
|
||||||
|
for index, variant := range *product.SizeVariants {
|
||||||
|
if !variant.Sellable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
savePoint := "size" + strconv.Itoa(index)
|
||||||
|
tx.SavePoint(savePoint)
|
||||||
|
|
||||||
|
var sizeOPtion gm.AttributeOption
|
||||||
|
|
||||||
|
if variant.AttributeName == "Beden" {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||||
|
} else {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||||
|
|
||||||
|
variantProduct := productRepo.makeVariant(mainPorduct.ID, mainPorduct.AttributeFamilyID, sku)
|
||||||
|
|
||||||
|
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||||
|
|
||||||
|
if err := tx.Omit("Categories.*").Create(&variantProduct).Error; err != nil {
|
||||||
|
log.Println("Variant Product Create Error: " + err.Error())
|
||||||
|
tx.RollbackTo(savePoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, mainFlat.ID, variantProduct.ID)
|
||||||
|
|
||||||
|
if err := tx.Create(&variantFlat).Error; err != nil {
|
||||||
|
log.Println("Variant Flat Create Error: " + err.Error())
|
||||||
|
tx.RollbackTo(savePoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeVariants = append(sizeVariants, variantFlat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sizeVariants) == 0 {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errors.New("size variantlary yok bolsa main productam girayenok")
|
||||||
|
} else {
|
||||||
|
calcPrice(sizeVariants, &mainFlat)
|
||||||
|
|
||||||
|
err := tx.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&mainFlat).Error
|
||||||
|
|
||||||
|
mainFlat.Variants = sizeVariants
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
log.Println(err, "er6")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sProduct := importer.createSellerProduct(&mainFlat, product.Vendor)
|
||||||
|
|
||||||
|
if errSProduct := tx.Create(&sProduct).Error; errSProduct != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, errSProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mainPorduct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) createSellerProduct(flat *gm.ProductFlat, sellerURL string) gm.MarketplaceProduct {
|
||||||
|
sellerID := importer.sellers[sellerURL].ID
|
||||||
|
|
||||||
|
if sellerID == 0 {
|
||||||
|
sellerID = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sellerProduct := gm.MarketplaceProduct{
|
||||||
|
MarketplaceSellerID: sellerID,
|
||||||
|
IsApproved: true,
|
||||||
|
Condition: "new",
|
||||||
|
Description: "scraped",
|
||||||
|
IsOwner: true,
|
||||||
|
ProductID: flat.ProductID,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, variant := range flat.Variants {
|
||||||
|
sellerProduct.Variants = append(sellerProduct.Variants, gm.MarketplaceProduct{
|
||||||
|
ProductID: variant.ProductID,
|
||||||
|
IsOwner: true,
|
||||||
|
IsApproved: true,
|
||||||
|
MarketplaceSellerID: sellerID,
|
||||||
|
Condition: "new",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return sellerProduct
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcPrice(variants []gm.ProductFlat, flat *gm.ProductFlat) {
|
||||||
|
for _, variant := range variants {
|
||||||
|
|
||||||
|
if !variant.Status {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if flat.MinPrice == 0 || flat.MinPrice > variant.MinPrice {
|
||||||
|
flat.MinPrice = variant.MinPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
flat.MaxPrice = math.Max(flat.MaxPrice, variant.MaxPrice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) GetColorOption(optionName string) gm.AttributeOption {
|
||||||
|
if optionName == "" {
|
||||||
|
return gm.AttributeOption{}
|
||||||
|
}
|
||||||
|
|
||||||
|
importer.ColorMutex.Lock()
|
||||||
|
var option gm.AttributeOption
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if option, ok = importer.ColorOptions[optionName]; !ok {
|
||||||
|
option := gm.GetAttributeOption(importer.Baza, importer.AttributesMap["color"].ID, optionName)
|
||||||
|
|
||||||
|
importer.ColorOptions[optionName] = option
|
||||||
|
}
|
||||||
|
importer.ColorMutex.Unlock()
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) GetSexOption(optionName string) gm.AttributeOption {
|
||||||
|
if optionName == "" {
|
||||||
|
return gm.AttributeOption{}
|
||||||
|
}
|
||||||
|
importer.SexMutex.Lock()
|
||||||
|
var option gm.AttributeOption
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if option, ok = importer.ColorOptions[optionName]; !ok {
|
||||||
|
option = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["cinsiyet"].ID, optionName)
|
||||||
|
importer.SexOptions[optionName] = option
|
||||||
|
}
|
||||||
|
importer.SexMutex.Unlock()
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) updateVariant(product models.Product) (*gm.Product, error) {
|
||||||
|
|
||||||
|
var flat gm.ProductFlat
|
||||||
|
err := importer.Baza.Preload("Product").Preload("Variants").First(&flat, "sku = ?", product.ProductNumber).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return importer.importVariant(product)
|
||||||
|
}
|
||||||
|
//todo not found bolsa create etmeli
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if flat.Product.Type == "configurable" {
|
||||||
|
|
||||||
|
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||||
|
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
productRepo.Brand = brand
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, variant := range *product.SizeVariants {
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, flatVariant := range flat.Variants {
|
||||||
|
|
||||||
|
if variant.AttributeValue == flatVariant.BoyutLabel || variant.AttributeValue == flatVariant.SizeLabel {
|
||||||
|
|
||||||
|
if !variant.Sellable {
|
||||||
|
importer.Baza.Model(&flatVariant).Update("status", false)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
importer.updatePrice(variant.Price, &flatVariant)
|
||||||
|
|
||||||
|
if !flatVariant.Status {
|
||||||
|
|
||||||
|
importer.Baza.Model(&flatVariant).Update("status", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if variant.Sellable && !found {
|
||||||
|
// insert variant
|
||||||
|
var sizeOPtion gm.AttributeOption
|
||||||
|
|
||||||
|
if variant.AttributeName == "Beden" {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||||
|
} else {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||||
|
variantProduct := productRepo.makeVariant(flat.ProductID, flat.Product.AttributeFamilyID, sku)
|
||||||
|
|
||||||
|
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||||
|
|
||||||
|
if err := importer.Baza.Omit("Categories.*").Create(&variantProduct).Error; err != nil {
|
||||||
|
log.Println("Variant Product Create Error: " + err.Error())
|
||||||
|
} else {
|
||||||
|
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, flat.ID, variantProduct.ID)
|
||||||
|
|
||||||
|
if err := importer.Baza.Create(&variantFlat).Error; err != nil {
|
||||||
|
log.Println("Variant Flat Create Error: " + err.Error())
|
||||||
|
|
||||||
|
}
|
||||||
|
flat.Variants = append(flat.Variants, variantFlat)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calcPrice(flat.Variants, &flat)
|
||||||
|
importer.Baza.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&flat)
|
||||||
|
|
||||||
|
} else { //simple
|
||||||
|
|
||||||
|
importer.updatePrice(product.Price, &flat)
|
||||||
|
|
||||||
|
}
|
||||||
|
return &flat.Product, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) updatePrice(price models.Price, flat *gm.ProductFlat) {
|
||||||
|
if price.OriginalPrice.Value > price.DiscountedPrice.Value {
|
||||||
|
|
||||||
|
importer.Baza.Model(&flat).Updates(map[string]interface{}{
|
||||||
|
"price": price.OriginalPrice.Value,
|
||||||
|
"special_price": price.DiscountedPrice.Value,
|
||||||
|
"min_price": price.DiscountedPrice.Value,
|
||||||
|
"max_price": price.OriginalPrice.Value,
|
||||||
|
})
|
||||||
|
|
||||||
|
importer.Baza.Model(&gm.ProductAttributeValue{}).
|
||||||
|
Where("attribute_id = 11 and product_id = ?", flat.ProductID).
|
||||||
|
Update("float_value", price.OriginalPrice.Value)
|
||||||
|
|
||||||
|
importer.Baza.Model(&gm.ProductAttributeValue{}).
|
||||||
|
Where("attribute_id = 13 and product_id = ?", flat.ProductID).
|
||||||
|
Update("float_value", price.DiscountedPrice.Value)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
importer.Baza.Model(&flat).Updates(map[string]interface{}{
|
||||||
|
"price": price.DiscountedPrice.Value,
|
||||||
|
"special_price": nil,
|
||||||
|
"min_price": price.DiscountedPrice.Value,
|
||||||
|
"max_price": price.DiscountedPrice.Value,
|
||||||
|
})
|
||||||
|
|
||||||
|
importer.Baza.Model(&gm.ProductAttributeValue{}).
|
||||||
|
Where("attribute_id = 11 and product_id = ?", flat.ProductID).
|
||||||
|
Update("float_value", price.DiscountedPrice.Value)
|
||||||
|
|
||||||
|
importer.Baza.Where("attribute_id = 13 and product_id = ?", flat.ProductID).
|
||||||
|
Delete(&gm.ProductAttributeValue{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) importParsedVariant(product models.Product) (gm.Product, error) {
|
||||||
|
|
||||||
|
//todo search if exists
|
||||||
|
|
||||||
|
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||||
|
productRepo.ImageType = "lcw"
|
||||||
|
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||||
|
return gm.Product{}, err
|
||||||
|
} else {
|
||||||
|
productRepo.Brand = brand
|
||||||
|
}
|
||||||
|
mainPorduct := productRepo.makeProduct(importer)
|
||||||
|
|
||||||
|
//BEGIN TRANSACTION
|
||||||
|
tx := importer.Baza.Begin()
|
||||||
|
|
||||||
|
if err := tx.Omit("Categories", "SuperAttributes.*", "ParentID").Create(&mainPorduct).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return gm.Product{}, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mainFlat := productRepo.makeProductFlat(mainPorduct.ID)
|
||||||
|
|
||||||
|
if err := tx.Create(&mainFlat).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return gm.Product{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if productRepo.HasSizeVariants() {
|
||||||
|
var sizeVariants []gm.ProductFlat
|
||||||
|
for index, variant := range *product.SizeVariants {
|
||||||
|
if !variant.Sellable {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
savePoint := "size" + strconv.Itoa(index)
|
||||||
|
tx.SavePoint(savePoint)
|
||||||
|
|
||||||
|
var sizeOPtion gm.AttributeOption
|
||||||
|
|
||||||
|
if variant.AttributeName == "Beden" {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||||
|
} else {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(tx, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||||
|
log.Println(sku)
|
||||||
|
log.Println(variant)
|
||||||
|
|
||||||
|
variantProduct := productRepo.makeVariant(mainPorduct.ID, mainPorduct.AttributeFamilyID, sku)
|
||||||
|
|
||||||
|
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||||
|
|
||||||
|
if err := tx.Omit("Categories.*").Create(&variantProduct).Error; err != nil {
|
||||||
|
log.Println("Variant Product Create Error: " + err.Error())
|
||||||
|
tx.RollbackTo(savePoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, mainFlat.ID, variantProduct.ID)
|
||||||
|
|
||||||
|
if err := tx.Create(&variantFlat).Error; err != nil {
|
||||||
|
log.Println("Variant Flat Create Error: " + err.Error())
|
||||||
|
tx.RollbackTo(savePoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeVariants = append(sizeVariants, variantFlat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sizeVariants) == 0 {
|
||||||
|
tx.Rollback()
|
||||||
|
return gm.Product{}, errors.New("siz variantlary yok bolsa main productam girayenok")
|
||||||
|
} else {
|
||||||
|
calcPrice(sizeVariants, &mainFlat)
|
||||||
|
|
||||||
|
err := tx.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&mainFlat).Error
|
||||||
|
|
||||||
|
mainFlat.Variants = sizeVariants
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return gm.Product{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(product.Vendor)
|
||||||
|
|
||||||
|
sProduct := importer.createSellerProduct(&mainFlat, product.Vendor)
|
||||||
|
|
||||||
|
if errSProduct := tx.Create(&sProduct).Error; errSProduct != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return gm.Product{}, errSProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return gm.Product{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainPorduct, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) updateParsedVariant(product models.Product) (gm.Product, error) {
|
||||||
|
var flat gm.ProductFlat
|
||||||
|
err := importer.Baza.Preload("Product").Preload("Variants").First(&flat, "sku = ?", product.ProductNumber).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return importer.importParsedVariant(product)
|
||||||
|
}
|
||||||
|
//todo not found bolsa create etmeli
|
||||||
|
return gm.Product{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if flat.Product.Type == "configurable" {
|
||||||
|
|
||||||
|
productRepo := InitProductRepo(&product, importer.GetColorOption(product.Color), importer.GetSexOption(product.Cinsiyet))
|
||||||
|
productRepo.ImageType = "lcw"
|
||||||
|
if brand, err := gm.FindOrCreateBrand(importer.Baza, product.Brand, productRepo.Categories); err != nil {
|
||||||
|
return gm.Product{}, err
|
||||||
|
} else {
|
||||||
|
productRepo.Brand = brand
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, variant := range *product.SizeVariants {
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, flatVariant := range flat.Variants {
|
||||||
|
|
||||||
|
if variant.AttributeValue == flatVariant.BoyutLabel || variant.AttributeValue == flatVariant.SizeLabel {
|
||||||
|
|
||||||
|
if !variant.Sellable {
|
||||||
|
importer.Baza.Model(&flatVariant).Update("status", false)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
importer.updatePrice(variant.Price, &flatVariant)
|
||||||
|
|
||||||
|
if !flatVariant.Status {
|
||||||
|
|
||||||
|
importer.Baza.Model(&flatVariant).Update("status", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if variant.Sellable && !found {
|
||||||
|
// insert variant
|
||||||
|
var sizeOPtion gm.AttributeOption
|
||||||
|
|
||||||
|
if variant.AttributeName == "Beden" {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["size"].ID, variant.AttributeValue)
|
||||||
|
} else {
|
||||||
|
sizeOPtion = gm.GetAttributeOption(importer.Baza, importer.AttributesMap["boyut"].ID, variant.AttributeValue)
|
||||||
|
}
|
||||||
|
log.Println(variant)
|
||||||
|
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
|
||||||
|
log.Println(sku)
|
||||||
|
|
||||||
|
variantProduct := productRepo.makeVariant(flat.ProductID, flat.Product.AttributeFamilyID, sku)
|
||||||
|
|
||||||
|
variantProduct.AttributeValues = productRepo.getVariantAttributes(importer.AttributesMap, &variant, sizeOPtion.ID)
|
||||||
|
|
||||||
|
if err := importer.Baza.Omit("Categories").Create(&variantProduct).Error; err != nil {
|
||||||
|
log.Println("Variant Product Create Error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
variantFlat := productRepo.makeVariantFlat(variant, sizeOPtion.ID, flat.ID, variantProduct.ID)
|
||||||
|
|
||||||
|
if err := importer.Baza.Create(&variantFlat).Error; err != nil {
|
||||||
|
log.Println("Variant Flat Create Error: " + err.Error())
|
||||||
|
|
||||||
|
}
|
||||||
|
flat.Variants = append(flat.Variants, variantFlat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calcPrice(flat.Variants, &flat)
|
||||||
|
importer.Baza.Omit("ParentID", "CreatedAt", "Variants", "SpecialPrice").Save(&flat)
|
||||||
|
|
||||||
|
} else { //simple
|
||||||
|
|
||||||
|
importer.updatePrice(product.Price, &flat)
|
||||||
|
|
||||||
|
}
|
||||||
|
return flat.Product, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) UpdateOrCreateLCW(product models.Product) (instance *Importer) {
|
||||||
|
var firstProduct gm.Product
|
||||||
|
result := importer.Baza.First(&firstProduct, "sku=?", product.ProductNumber)
|
||||||
|
|
||||||
|
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||||
|
firstProduct, importer.Error = importer.importParsedVariant(product)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
firstProduct, importer.Error = importer.updateParsedVariant(product)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var newProducts []gm.Product
|
||||||
|
|
||||||
|
if importer.Error != nil {
|
||||||
|
return importer
|
||||||
|
} else if &firstProduct != nil {
|
||||||
|
newProducts = append(newProducts, firstProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
|
||||||
|
linkedProducts := []gm.Product{firstProduct}
|
||||||
|
|
||||||
|
for _, colorVariant := range *product.ColorVariants {
|
||||||
|
|
||||||
|
var (
|
||||||
|
variant gm.Product
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if !colorVariant.IsSellable {
|
||||||
|
|
||||||
|
if err = importer.Baza.Model(&gm.ProductFlat{}).Where("sku=?", colorVariant.ProductNumber).Update("status", false).Error; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//todo check parsed import variant
|
||||||
|
if variant, err = importer.importParsedVariant(colorVariant); err != nil {
|
||||||
|
|
||||||
|
if variant, importer.Error = importer.updateParsedVariant(colorVariant); importer.Error != nil {
|
||||||
|
return importer
|
||||||
|
}
|
||||||
|
|
||||||
|
linkedProducts = append(linkedProducts, variant)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
newProducts = append(newProducts, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newProducts) > 0 {
|
||||||
|
// relation
|
||||||
|
|
||||||
|
var relation []gm.ProductRelation
|
||||||
|
for _, linkedProduct := range linkedProducts {
|
||||||
|
|
||||||
|
for _, newProd := range newProducts {
|
||||||
|
relation = append(relation, gm.ProductRelation{ParentID: linkedProduct.ID, ChildID: newProd.ID})
|
||||||
|
relation = append(relation, gm.ProductRelation{ParentID: newProd.ID, ChildID: linkedProduct.ID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := importer.Baza.Create(&relation).Error; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return importer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (importer *Importer) UpdateOrCreate(product models.Product) (instance *Importer) {
|
||||||
|
|
||||||
|
firstProduct, err := importer.updateVariant(product)
|
||||||
|
var newProducts []gm.Product
|
||||||
|
if err != nil {
|
||||||
|
helper.Error(err)
|
||||||
|
|
||||||
|
firstProduct, importer.Error = importer.updateVariant(product)
|
||||||
|
|
||||||
|
if importer.Error != nil {
|
||||||
|
return importer
|
||||||
|
}
|
||||||
|
} else if &firstProduct != nil {
|
||||||
|
newProducts = append(newProducts, *firstProduct)
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
|
||||||
|
linkedProducts := []gm.Product{*firstProduct}
|
||||||
|
|
||||||
|
for _, colorVariant := range *product.ColorVariants {
|
||||||
|
|
||||||
|
var (
|
||||||
|
variant *gm.Product
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if !colorVariant.IsSellable {
|
||||||
|
|
||||||
|
if err = importer.Baza.Model(&gm.ProductFlat{}).Where("sku=?", colorVariant.ProductNumber).Update("status", false).Error; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if variant, err = importer.importVariant(colorVariant); err != nil {
|
||||||
|
|
||||||
|
if variant, importer.Error = importer.updateVariant(colorVariant); importer.Error != nil {
|
||||||
|
return importer
|
||||||
|
}
|
||||||
|
|
||||||
|
linkedProducts = append(linkedProducts, *variant)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
newProducts = append(newProducts, *variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newProducts) > 0 {
|
||||||
|
// relation
|
||||||
|
|
||||||
|
var relation []gm.ProductRelation
|
||||||
|
for _, linkedProduct := range linkedProducts {
|
||||||
|
|
||||||
|
for _, newProd := range newProducts {
|
||||||
|
relation = append(relation, gm.ProductRelation{ParentID: linkedProduct.ID, ChildID: newProd.ID})
|
||||||
|
relation = append(relation, gm.ProductRelation{ParentID: newProd.ID, ChildID: linkedProduct.ID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := importer.Baza.Create(&relation).Error; err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return importer
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,375 @@
|
||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
helper "sarga_updater/helpers"
|
||||||
|
models "sarga_updater/trendyol_models"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkParser struct {
|
||||||
|
link string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinkParser(link string) LinkParser {
|
||||||
|
return LinkParser{link: link}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LinkParser) ParseLink() (models.TrendyolProductDetailModel, error) {
|
||||||
|
|
||||||
|
helper.Info("link: ", l.link)
|
||||||
|
|
||||||
|
productId := ""
|
||||||
|
|
||||||
|
if isShortLink(l.link) {
|
||||||
|
productId = getProductIdFromShortLink(l.link)
|
||||||
|
} else {
|
||||||
|
productId = getProductIdFromLink(l.link)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(productId) == 0 {
|
||||||
|
parseErr := errors.New("can not parse product id")
|
||||||
|
helper.Error(parseErr)
|
||||||
|
return models.TrendyolProductDetailModel{}, parseErr
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.Info("productId: ", productId)
|
||||||
|
|
||||||
|
return GetProductDetails(productId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProductIdFromShortLink(shortLink string) string {
|
||||||
|
|
||||||
|
var productId string
|
||||||
|
|
||||||
|
client, _ := helper.NewHttpClient()
|
||||||
|
req, err := http.NewRequest("GET", shortLink, nil)
|
||||||
|
req.Proto = "HTTP/2.0"
|
||||||
|
if err != nil {
|
||||||
|
helper.Error(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
req.Header.Set("Connection", "close")
|
||||||
|
req = req.WithContext(req.Context())
|
||||||
|
req.Close = true
|
||||||
|
client.CloseIdleConnections()
|
||||||
|
|
||||||
|
response, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.Error(err)
|
||||||
|
return productId
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
url := response.Request.URL.Path
|
||||||
|
|
||||||
|
helper.Info("link url: ", url)
|
||||||
|
|
||||||
|
productId = getProductIdFromLink(url)
|
||||||
|
|
||||||
|
helper.Info("productId: ", productId)
|
||||||
|
|
||||||
|
return productId
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProductIdFromLink(link string) string {
|
||||||
|
var productId string
|
||||||
|
|
||||||
|
if strings.Contains(link, "?") {
|
||||||
|
link = strings.Split(link, "?")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
strArr := strings.Split(link, "-")
|
||||||
|
productId = strArr[len(strArr)-1]
|
||||||
|
|
||||||
|
return productId
|
||||||
|
}
|
||||||
|
|
||||||
|
func isShortLink(link string) bool {
|
||||||
|
return !strings.Contains(link, "trendyol.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProductDetails return JSON object. Merges color option
|
||||||
|
func GetProductDetails(productId string) (models.TrendyolProductDetailModel, error) {
|
||||||
|
|
||||||
|
var response models.TrendyolProductDetailResponse
|
||||||
|
productDetailModel := models.TrendyolProductDetailModel{}
|
||||||
|
|
||||||
|
// set linearVariants false to get variants
|
||||||
|
url := "https://public.trendyol.com/discovery-web-productgw-service/api/productDetail/" + productId + "?storefrontId=1&culture=tr-TR&linearVariants=false"
|
||||||
|
|
||||||
|
body, err := helper.SendRequest("GET", url, nil, "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return productDetailModel, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &response)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helper.Error(err)
|
||||||
|
return productDetailModel, err
|
||||||
|
}
|
||||||
|
|
||||||
|
productDetailModel = response.Result
|
||||||
|
|
||||||
|
return productDetailModel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getProductDetailWithOptions returns JSON with variants
|
||||||
|
func (l LinkParser) GetProductDetailWithOptions(productId, productGroupId int) (models.Product, error) {
|
||||||
|
|
||||||
|
primaryProductDetail, err := GetProductDetails(strconv.Itoa(productId))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return models.Product{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
productDetailJSON := CreateJSONFromModel(primaryProductDetail)
|
||||||
|
|
||||||
|
if productDetailJSON != nil {
|
||||||
|
colorVariants, err := GetProductColorVariants(productGroupId)
|
||||||
|
|
||||||
|
var colorVariantsJson []map[string]interface{}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return models.Product{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the color variant products
|
||||||
|
for _, slicingAttribute := range colorVariants.Result.SlicingAttributes {
|
||||||
|
for _, attribute := range slicingAttribute.Attributes {
|
||||||
|
for _, content := range attribute.Contents {
|
||||||
|
// Don't fetch primary product again.
|
||||||
|
if content.ID != primaryProductDetail.ID {
|
||||||
|
productVariantDetail, errGPD := GetProductDetails(strconv.Itoa(content.ID))
|
||||||
|
if errGPD == nil {
|
||||||
|
productVariantJSON := CreateJSONFromModel(productVariantDetail)
|
||||||
|
if productVariantJSON != nil {
|
||||||
|
colorVariantsJson = append(colorVariantsJson, productVariantJSON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Color variant count
|
||||||
|
if len(slicingAttribute.Attributes) > 0 {
|
||||||
|
productDetailJSON["color_variant_count"] = len(slicingAttribute.Attributes)
|
||||||
|
productDetailJSON["color_variants"] = colorVariantsJson
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonString, _ := json.Marshal(productDetailJSON)
|
||||||
|
|
||||||
|
// convert json to struct
|
||||||
|
converted := models.Product{}
|
||||||
|
json.Unmarshal(jsonString, &converted)
|
||||||
|
|
||||||
|
return converted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getProductColorVariants returns color options of product
|
||||||
|
func GetProductColorVariants(productGroupId int) (models.TrendyolProductVariantsResponse, error) {
|
||||||
|
|
||||||
|
url := "https://public.trendyol.com/discovery-web-productgw-service/api/productGroup/" + strconv.Itoa(productGroupId) + "?storefrontId=1&culture=tr-TR"
|
||||||
|
|
||||||
|
var response models.TrendyolProductVariantsResponse
|
||||||
|
|
||||||
|
body, err := helper.SendRequest("GET", url, nil, "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &response)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
helper.Error(err)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createJSONFromModel creates json from [trendyol.TrendyolProductDetailModel] and returns
|
||||||
|
func CreateJSONFromModel(model models.TrendyolProductDetailModel) map[string]interface{} {
|
||||||
|
|
||||||
|
json := map[string]interface{}{}
|
||||||
|
|
||||||
|
// get weight from categories (Stored in helper array (RAM))
|
||||||
|
weight := helper.GetCategoryWeight(model.Category.BeautifiedName)
|
||||||
|
productGroupId := strconv.Itoa(model.ProductGroupID)
|
||||||
|
|
||||||
|
json["_id"] = productGroupId
|
||||||
|
json["product_group_id"] = productGroupId
|
||||||
|
json["vendor"] = "trendyol"
|
||||||
|
json["sku"] = "p-" + strconv.Itoa(model.ID)
|
||||||
|
json["product_number"] = strconv.Itoa(model.ID)
|
||||||
|
json["product_code"] = model.ProductCode
|
||||||
|
json["name"] = model.Name
|
||||||
|
json["sellable"] = model.IsSellable
|
||||||
|
json["favorite_count"] = model.FavoriteCount
|
||||||
|
json["weight"] = weight
|
||||||
|
json["name_with_product_code"] = model.NameWithProductCode
|
||||||
|
json["url_key"] = "https://www.trendyol.com" + model.URL
|
||||||
|
json["images"] = model.Images
|
||||||
|
json["brand"] = model.Brand.Name
|
||||||
|
json["cinsiyet"] = model.Gender.Name
|
||||||
|
json["description"] = model.Description
|
||||||
|
json["descriptions"] = model.ContentDescriptions
|
||||||
|
json["short_description"] = model.Description
|
||||||
|
// nested structure
|
||||||
|
json["price"] = make(map[string]interface{})
|
||||||
|
json["price"].(map[string]interface{})["originalPrice"] = model.Price.OriginalPrice
|
||||||
|
json["price"].(map[string]interface{})["sellingPrice"] = model.Price.SellingPrice
|
||||||
|
json["price"].(map[string]interface{})["discountedPrice"] = model.Price.DiscountedPrice
|
||||||
|
|
||||||
|
attrLen := len(model.Attributes)
|
||||||
|
|
||||||
|
if attrLen != 0 {
|
||||||
|
for i := 0; i < attrLen; i++ {
|
||||||
|
attribute := model.Attributes[i]
|
||||||
|
if attribute.Key.Name == "Renk" {
|
||||||
|
json["color"] = attribute.Value.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set categories with value 1
|
||||||
|
json["categories"] = []int{1}
|
||||||
|
|
||||||
|
attributes := make([]map[string]string, 0)
|
||||||
|
for _, attr := range model.Attributes {
|
||||||
|
var re = regexp.MustCompile(`/[^A-Z0-9]/ig`)
|
||||||
|
keyStr := re.ReplaceAllString(attr.Key.Name, `_`)
|
||||||
|
key := strings.ToLower(keyStr)
|
||||||
|
attribute := map[string]string{
|
||||||
|
key: attr.Value.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes = append(attributes, attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
json["attributes"] = attributes
|
||||||
|
|
||||||
|
var variants []models.Variant
|
||||||
|
|
||||||
|
// if show variants, then it is configurable product.
|
||||||
|
if model.ShowVariants {
|
||||||
|
|
||||||
|
for i := 0; i < len(model.Variants); i++ {
|
||||||
|
variant := model.Variants[i]
|
||||||
|
|
||||||
|
if variant.Sellable {
|
||||||
|
stockType := reflect.TypeOf(variant.Stock)
|
||||||
|
|
||||||
|
if stockType == nil {
|
||||||
|
variants = append(variants, variant)
|
||||||
|
} else {
|
||||||
|
// scrape is via link parse
|
||||||
|
// we need to parse other variants
|
||||||
|
|
||||||
|
// convert stock to int
|
||||||
|
stock, ok := variant.Stock.(float64)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
if stock > 0 {
|
||||||
|
variants = append(variants, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(model.AllVariants); i++ {
|
||||||
|
singleVariant := model.AllVariants[i]
|
||||||
|
|
||||||
|
// get the first variant for attribute info
|
||||||
|
fv := variants[0]
|
||||||
|
|
||||||
|
variant := models.Variant{
|
||||||
|
AttributeID: fv.AttributeID, //
|
||||||
|
AttributeName: fv.AttributeName, // Sample: "Beden"
|
||||||
|
AttributeType: fv.AttributeType, // Sample: "Size"
|
||||||
|
AttributeValue: singleVariant.Value,
|
||||||
|
Price: models.Price{
|
||||||
|
ProfitMargin: 0,
|
||||||
|
DiscountedPrice: struct {
|
||||||
|
Text string "json:\"text\""
|
||||||
|
Value float64 "json:\"value\""
|
||||||
|
}{
|
||||||
|
Text: fmt.Sprintf("%f", singleVariant.Price),
|
||||||
|
Value: singleVariant.Price,
|
||||||
|
},
|
||||||
|
SellingPrice: struct {
|
||||||
|
Text string "json:\"text\""
|
||||||
|
Value float64 "json:\"value\""
|
||||||
|
}{
|
||||||
|
Text: fmt.Sprintf("%f", singleVariant.Price),
|
||||||
|
Value: singleVariant.Price,
|
||||||
|
},
|
||||||
|
OriginalPrice: struct {
|
||||||
|
Text string "json:\"text\""
|
||||||
|
Value float64 "json:\"value\""
|
||||||
|
}{
|
||||||
|
Text: fmt.Sprintf("%f", singleVariant.Price),
|
||||||
|
Value: singleVariant.Price,
|
||||||
|
},
|
||||||
|
Currency: singleVariant.Currency,
|
||||||
|
},
|
||||||
|
ItemNumber: singleVariant.ItemNumber,
|
||||||
|
Sellable: singleVariant.InStock,
|
||||||
|
}
|
||||||
|
|
||||||
|
exists := helper.IsVariantsAdded(variants, variant.ItemNumber)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
variants = append(variants, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(model.AlternativeVariants); i++ {
|
||||||
|
alternativeVariant := model.AlternativeVariants[i]
|
||||||
|
|
||||||
|
if len(variants) > 0 {
|
||||||
|
|
||||||
|
// get the first variant for attribute info
|
||||||
|
fv := variants[0]
|
||||||
|
|
||||||
|
variant := models.Variant{
|
||||||
|
AttributeID: fv.AttributeID, //
|
||||||
|
AttributeName: fv.AttributeName, // Sample: "Beden"
|
||||||
|
AttributeType: fv.AttributeType, // Sample: "Size"
|
||||||
|
AttributeValue: alternativeVariant.AttributeValue,
|
||||||
|
Price: alternativeVariant.Price,
|
||||||
|
ItemNumber: alternativeVariant.ItemNumber,
|
||||||
|
Sellable: alternativeVariant.Quantity > 0,
|
||||||
|
}
|
||||||
|
exists := helper.IsVariantsAdded(variants, variant.ItemNumber)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
variants = append(variants, variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json["size_variants"] = variants
|
||||||
|
}
|
||||||
|
|
||||||
|
if model.ShowVariants && len(variants) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,308 @@
|
||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
gm "sarga_updater/bagisto_models"
|
||||||
|
models "sarga_updater/trendyol_models"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductRepo struct {
|
||||||
|
Categories []gm.Category
|
||||||
|
Brand gm.Brand
|
||||||
|
Weight float64
|
||||||
|
Description string
|
||||||
|
Keywords string
|
||||||
|
Data *models.Product
|
||||||
|
Error error
|
||||||
|
ColorOption gm.AttributeOption
|
||||||
|
SexOption gm.AttributeOption
|
||||||
|
ImageType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitProductRepo(data *models.Product, color, sex gm.AttributeOption) *ProductRepo {
|
||||||
|
|
||||||
|
weight, wError := strconv.ParseFloat(data.Weight, 64)
|
||||||
|
|
||||||
|
if wError != nil || weight == 0 {
|
||||||
|
weight = 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
var description string
|
||||||
|
|
||||||
|
for _, desc := range data.Descriptions {
|
||||||
|
description += "<p>" + desc.Description + "</p>"
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := &ProductRepo{
|
||||||
|
Weight: weight,
|
||||||
|
Description: description,
|
||||||
|
Data: data,
|
||||||
|
ColorOption: color,
|
||||||
|
SexOption: sex,
|
||||||
|
ImageType: "cdn",
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) SetCategories(categories []gm.Category) {
|
||||||
|
pr.Categories = categories
|
||||||
|
pr.Keywords = pr.Data.Brand
|
||||||
|
|
||||||
|
for _, cat := range categories {
|
||||||
|
//log.Println(cat)
|
||||||
|
if len(cat.Translations) > 0 && cat.Translations[0].MetaKeywords != "" {
|
||||||
|
translation := cat.Translations[0]
|
||||||
|
|
||||||
|
pr.Keywords += "," + translation.MetaKeywords
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) HasSizeVariants() bool {
|
||||||
|
return pr.Data.SizeVariants != nil && len(*pr.Data.SizeVariants) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) HasColorVariants() bool {
|
||||||
|
return pr.Data.ColorVariants != nil && len(*pr.Data.ColorVariants) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) makeProduct(imp *Importer) gm.Product {
|
||||||
|
var famID uint = 1
|
||||||
|
|
||||||
|
if len(imp.families) > 0 { //todo make real fam function
|
||||||
|
famID = imp.families[0].ID
|
||||||
|
}
|
||||||
|
|
||||||
|
product := gm.Product{
|
||||||
|
Sku: pr.Data.ProductNumber,
|
||||||
|
AttributeFamilyID: famID,
|
||||||
|
Categories: pr.Categories,
|
||||||
|
AttributeValues: pr.getProductAttributes(imp.AttributesMap, pr.Data),
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Brand != "" {
|
||||||
|
product.BrandID = pr.Brand.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.HasSizeVariants() {
|
||||||
|
product.Type = "configurable"
|
||||||
|
if (*pr.Data.SizeVariants)[0].AttributeName == "Beden" {
|
||||||
|
product.SuperAttributes = []gm.Attribute{imp.AttributesMap["size"]}
|
||||||
|
} else {
|
||||||
|
product.SuperAttributes = []gm.Attribute{imp.AttributesMap["boyut"]}
|
||||||
|
product.AttributeFamilyID = 4 // todo fix hardcode
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
product.Type = "simple"
|
||||||
|
price := pr.Data.Price
|
||||||
|
|
||||||
|
if price.OriginalPrice.Value > price.DiscountedPrice.Value {
|
||||||
|
product.AttributeValues = append(product.AttributeValues, []gm.ProductAttributeValue{
|
||||||
|
{AttributeID: imp.AttributesMap["price"].ID, FloatValue: price.OriginalPrice.Value},
|
||||||
|
{AttributeID: imp.AttributesMap["special_price"].ID, FloatValue: price.DiscountedPrice.Value},
|
||||||
|
}...)
|
||||||
|
} else {
|
||||||
|
product.AttributeValues = append(product.AttributeValues, gm.ProductAttributeValue{
|
||||||
|
AttributeID: imp.AttributesMap["price"].ID, FloatValue: price.DiscountedPrice.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, element := range pr.Data.Images {
|
||||||
|
product.Images = append(product.Images, gm.ProductImage{Type: pr.ImageType, Path: element})
|
||||||
|
}
|
||||||
|
|
||||||
|
return product
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) makeVariant(parentID, famID uint, sku string) gm.Product {
|
||||||
|
|
||||||
|
product := gm.Product{
|
||||||
|
Sku: sku,
|
||||||
|
Type: "simple",
|
||||||
|
AttributeFamilyID: famID,
|
||||||
|
Categories: pr.Categories,
|
||||||
|
ParentID: parentID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Brand != "" {
|
||||||
|
product.BrandID = pr.Brand.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return product
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) makeProductFlat(productId uint) gm.ProductFlat {
|
||||||
|
|
||||||
|
flat := gm.ProductFlat{
|
||||||
|
ProductID: productId,
|
||||||
|
Status: true,
|
||||||
|
VisibleIndividually: true,
|
||||||
|
Name: pr.Data.Name,
|
||||||
|
Sku: pr.Data.ProductNumber,
|
||||||
|
//ProductNumber: pr.Data.ProductNumber,
|
||||||
|
Description: pr.Description,
|
||||||
|
//UrlKey: pr.Data.ProductGroupID,
|
||||||
|
Weight: pr.Weight,
|
||||||
|
FavoritesCount: uint(pr.Data.FavoriteCount),
|
||||||
|
MetaKeywords: pr.Keywords,
|
||||||
|
MaxPrice: 0,
|
||||||
|
MinPrice: 0,
|
||||||
|
Price: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Color != "" {
|
||||||
|
flat.Color = int(pr.ColorOption.ID)
|
||||||
|
flat.ColorLabel = pr.Data.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Brand != "" {
|
||||||
|
flat.BrandID = pr.Brand.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Cinsiyet != "" {
|
||||||
|
flat.Cinsiyet = int(pr.SexOption.ID)
|
||||||
|
flat.CinsiyetLabel = pr.Data.Cinsiyet
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pr.HasSizeVariants() {
|
||||||
|
flat.MinPrice = pr.Data.Price.DiscountedPrice.Value
|
||||||
|
flat.MaxPrice = math.Max(pr.Data.Price.OriginalPrice.Value, pr.Data.Price.DiscountedPrice.Value)
|
||||||
|
flat.Price = flat.MaxPrice
|
||||||
|
|
||||||
|
if flat.MinPrice < flat.MaxPrice {
|
||||||
|
flat.SpecialPrice = flat.MinPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//else {//todo calculate price after variants inserted
|
||||||
|
// for _, variant := range *pr.Data.SizeVariants {
|
||||||
|
// price := variant.Price
|
||||||
|
//
|
||||||
|
// if flat.MinPrice == 0 || flat.MinPrice > price.DiscountedPrice.Value {
|
||||||
|
// flat.MaxPrice = price.DiscountedPrice.Value
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// maxPrice := math.Max(price.OriginalPrice.Value, price.DiscountedPrice.Value)
|
||||||
|
// if flat.MaxPrice == 0 || flat.MaxPrice < maxPrice {
|
||||||
|
// flat.MaxPrice = maxPrice
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
return flat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) makeVariantFlat(variant models.Variant, SizID, parentID, productID uint) gm.ProductFlat {
|
||||||
|
|
||||||
|
maxPRice := math.Max(variant.Price.OriginalPrice.Value, variant.Price.DiscountedPrice.Value)
|
||||||
|
sku := fmt.Sprintf("%s-%d", pr.Data.ProductNumber, variant.ItemNumber)
|
||||||
|
|
||||||
|
flat := gm.ProductFlat{
|
||||||
|
ParentID: parentID,
|
||||||
|
Status: true,
|
||||||
|
Name: pr.Data.Name,
|
||||||
|
Sku: sku,
|
||||||
|
ProductNumber: fmt.Sprintf("%d", variant.ItemNumber),
|
||||||
|
Weight: pr.Weight,
|
||||||
|
FavoritesCount: uint(pr.Data.FavoriteCount),
|
||||||
|
//SizeLabel: variant.AttributeValue,
|
||||||
|
//Size: int(SizID),
|
||||||
|
MaxPrice: maxPRice,
|
||||||
|
MinPrice: variant.Price.DiscountedPrice.Value,
|
||||||
|
Price: maxPRice,
|
||||||
|
ProductID: productID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if variant.AttributeName == "Beden" {
|
||||||
|
flat.Size = int(SizID)
|
||||||
|
flat.SizeLabel = variant.AttributeValue
|
||||||
|
} else {
|
||||||
|
flat.Boyut = int(SizID)
|
||||||
|
flat.BoyutLabel = variant.AttributeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if flat.MaxPrice > flat.MinPrice {
|
||||||
|
flat.SpecialPrice = flat.MinPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Color != "" {
|
||||||
|
flat.Color = int(pr.ColorOption.ID)
|
||||||
|
flat.ColorLabel = pr.Data.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Brand != "" {
|
||||||
|
flat.BrandID = pr.Brand.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Cinsiyet != "" {
|
||||||
|
flat.Cinsiyet = int(pr.SexOption.ID)
|
||||||
|
flat.CinsiyetLabel = pr.Data.Cinsiyet
|
||||||
|
}
|
||||||
|
|
||||||
|
return flat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) getProductAttributes(AttributesMap map[string]gm.Attribute, product *models.Product) []gm.ProductAttributeValue {
|
||||||
|
attributes := []gm.ProductAttributeValue{
|
||||||
|
{AttributeID: AttributesMap["source"].ID, TextValue: product.URLKey},
|
||||||
|
{AttributeID: AttributesMap["favoritesCount"].ID, IntegerValue: product.FavoriteCount},
|
||||||
|
{AttributeID: AttributesMap["sku"].ID, TextValue: product.ProductNumber},
|
||||||
|
{AttributeID: AttributesMap["name"].ID, TextValue: product.Name, Channel: "default", Locale: "tm"},
|
||||||
|
{AttributeID: AttributesMap["weight"].ID, TextValue: product.Weight},
|
||||||
|
{AttributeID: AttributesMap["status"].ID, BooleanValue: true},
|
||||||
|
{AttributeID: AttributesMap["visible_individually"].ID, BooleanValue: true},
|
||||||
|
{AttributeID: AttributesMap["description"].ID, TextValue: pr.Description, Channel: "default", Locale: "tm"},
|
||||||
|
{AttributeID: AttributesMap["meta_keywords"].ID, TextValue: pr.Keywords, Channel: "default", Locale: "tm"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.Color != "" {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["color"].ID, IntegerValue: int(pr.ColorOption.ID)})
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.Cinsiyet != "" {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["cinsiyet"].ID, IntegerValue: int(pr.SexOption.ID)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProductRepo) getVariantAttributes(AttributesMap map[string]gm.Attribute, product *models.Variant, SizID uint) []gm.ProductAttributeValue {
|
||||||
|
|
||||||
|
price := math.Max(product.Price.OriginalPrice.Value, product.Price.DiscountedPrice.Value)
|
||||||
|
sku := fmt.Sprintf("%s-%d", pr.Data.ProductNumber, product.ItemNumber)
|
||||||
|
attributes := []gm.ProductAttributeValue{
|
||||||
|
{AttributeID: AttributesMap["source"].ID, TextValue: pr.Data.URLKey},
|
||||||
|
{AttributeID: AttributesMap["sku"].ID, TextValue: sku}, //todo unique
|
||||||
|
{AttributeID: AttributesMap["product_number"].ID, TextValue: fmt.Sprintf("%d", product.ItemNumber)}, //todo unique
|
||||||
|
{AttributeID: AttributesMap["name"].ID, TextValue: pr.Data.Name, Channel: "default", Locale: "tm"},
|
||||||
|
{AttributeID: AttributesMap["weight"].ID, TextValue: pr.Data.Weight},
|
||||||
|
{AttributeID: AttributesMap["status"].ID, BooleanValue: true},
|
||||||
|
//{AttributeID: AttributesMap["size"].ID, IntegerValue: int(SizID)},
|
||||||
|
{AttributeID: AttributesMap["price"].ID, FloatValue: price},
|
||||||
|
}
|
||||||
|
if product.AttributeName == "Beden" {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["size"].ID, IntegerValue: int(SizID)})
|
||||||
|
} else {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["boyut"].ID, IntegerValue: int(SizID)})
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Color != "" {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["color"].ID, IntegerValue: int(pr.ColorOption.ID)})
|
||||||
|
}
|
||||||
|
|
||||||
|
if pr.Data.Cinsiyet != "" {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["cinsiyet"].ID, IntegerValue: int(pr.SexOption.ID)})
|
||||||
|
}
|
||||||
|
|
||||||
|
if product.Price.OriginalPrice.Value > product.Price.DiscountedPrice.Value {
|
||||||
|
attributes = append(attributes, gm.ProductAttributeValue{AttributeID: AttributesMap["special_price"].ID, FloatValue: product.Price.DiscountedPrice.Value})
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package trendyol_models
|
||||||
|
|
||||||
|
type Category struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
TrendyolURL string `json:"trendyol_url"`
|
||||||
|
LcwURL string `json:"lcw_url"`
|
||||||
|
DisplayMode string `json:"display_mode"`
|
||||||
|
ImageURL interface{} `json:"image_url"`
|
||||||
|
CategoryIconPath interface{} `json:"category_icon_path"`
|
||||||
|
ProductLimit int `json:"product_limit"`
|
||||||
|
Children []Category
|
||||||
|
}
|
||||||
|
|
||||||
|
type BagistoResponse struct {
|
||||||
|
Data []Category
|
||||||
|
}
|
||||||
|
type CouchCategory struct {
|
||||||
|
Rev string `json:"_rev"`
|
||||||
|
CreatedAt string `json:"createdAt"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Order string `json:"order"`
|
||||||
|
ParentID string `json:"parent_id"`
|
||||||
|
SargaID string `json:"sarga_id"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
UpdatedAt string `json:"updatedAt"`
|
||||||
|
Weight string `json:"weight"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
package trendyol_models
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
Attributes []map[string]string `json:"attributes"`
|
||||||
|
Brand string `json:"brand"`
|
||||||
|
Categories []int `json:"categories"`
|
||||||
|
Cinsiyet string `json:"cinsiyet"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
ColorVariantCount int `json:"color_variant_count"`
|
||||||
|
ColorVariants *[]Product `json:"color_variants"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
IsSellable bool `json:"sellable"`
|
||||||
|
FavoriteCount int `json:"favorite_count"`
|
||||||
|
Descriptions []struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Bold bool `json:"bold"`
|
||||||
|
} `json:"descriptions"`
|
||||||
|
Images []string `json:"images"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NameWithProductCode string `json:"name_with_product_code"`
|
||||||
|
Price Price `json:"price"`
|
||||||
|
ProductCode string `json:"product_code"`
|
||||||
|
ProductGroupID string `json:"product_group_id"`
|
||||||
|
ProductNumber string `json:"product_number"`
|
||||||
|
ShortDescription string `json:"short_description"`
|
||||||
|
SizeVariants *[]Variant `json:"size_variants"`
|
||||||
|
Sku string `json:"sku"`
|
||||||
|
Stock interface{} `json:"stock"`
|
||||||
|
URLKey string `json:"url_key"`
|
||||||
|
Vendor string `json:"vendor"`
|
||||||
|
Weight string `json:"weight"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Price struct {
|
||||||
|
ProfitMargin int `json:"profitMargin"`
|
||||||
|
DiscountedPrice PriceValue `json:"discountedPrice"`
|
||||||
|
SellingPrice PriceValue `json:"sellingPrice"`
|
||||||
|
OriginalPrice PriceValue `json:"originalPrice"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PriceValue struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Variant struct {
|
||||||
|
AttributeID int `json:"attributeId"`
|
||||||
|
AttributeName string `json:"attributeName"`
|
||||||
|
AttributeType string `json:"attributeType"`
|
||||||
|
AttributeValue string `json:"attributeValue"`
|
||||||
|
Stamps []interface{} `json:"stamps"`
|
||||||
|
Price Price `json:"price"`
|
||||||
|
FulfilmentType string `json:"fulfilmentType"`
|
||||||
|
AttributeBeautifiedValue string `json:"attributeBeautifiedValue"`
|
||||||
|
IsWinner bool `json:"isWinner"`
|
||||||
|
ListingID string `json:"listingId"`
|
||||||
|
Stock interface{} `json:"stock"`
|
||||||
|
Sellable bool `json:"sellable"`
|
||||||
|
AvailableForClaim bool `json:"availableForClaim"`
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
ItemNumber int `json:"itemNumber"`
|
||||||
|
DiscountedPriceInfo string `json:"discountedPriceInfo"`
|
||||||
|
HasCollectable bool `json:"hasCollectable"`
|
||||||
|
RushDeliveryMerchantListingExist bool `json:"rushDeliveryMerchantListingExist"`
|
||||||
|
LowerPriceMerchantListingExist bool `json:"lowerPriceMerchantListingExist"`
|
||||||
|
}
|
||||||
|
type Row struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value map[string]interface{} `json:"value"`
|
||||||
|
Doc Product
|
||||||
|
}
|
||||||
|
type BagistoModelResponse struct {
|
||||||
|
Rows []Row `json:"rows"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrendyolProductDetailResponse struct {
|
||||||
|
IsSuccess bool `json:"isSuccess"`
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
Error interface{} `json:"error"`
|
||||||
|
Result TrendyolProductDetailModel `json:"result"`
|
||||||
|
Headers struct {
|
||||||
|
Tysidecarcachable string `json:"tysidecarcachable"`
|
||||||
|
} `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrendyolProductDetailModel struct {
|
||||||
|
AlternativeVariants []AlternativeVariant `json:"alternativeVariants"`
|
||||||
|
Attributes []struct {
|
||||||
|
Key struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
} `json:"key"`
|
||||||
|
Value struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
} `json:"value"`
|
||||||
|
Starred bool `json:"starred"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
Variants []Variant `json:"variants"`
|
||||||
|
OtherMerchants []interface{} `json:"otherMerchants"`
|
||||||
|
Campaign struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
StartDate string `json:"startDate"`
|
||||||
|
EndDate string `json:"endDate"`
|
||||||
|
IsMultipleSupplied bool `json:"isMultipleSupplied"`
|
||||||
|
StockTypeID int `json:"stockTypeId"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
ShowTimer bool `json:"showTimer"`
|
||||||
|
} `json:"campaign"`
|
||||||
|
Category struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Hierarchy string `json:"hierarchy"`
|
||||||
|
Refundable bool `json:"refundable"`
|
||||||
|
BeautifiedName string `json:"beautifiedName"`
|
||||||
|
IsVASEnabled bool `json:"isVASEnabled"`
|
||||||
|
} `json:"category"`
|
||||||
|
Brand struct {
|
||||||
|
IsVirtual bool `json:"isVirtual"`
|
||||||
|
BeautifiedName string `json:"beautifiedName"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
} `json:"brand"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
MetaBrand struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
BeautifiedName string `json:"beautifiedName"`
|
||||||
|
IsVirtual bool `json:"isVirtual"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
} `json:"metaBrand"`
|
||||||
|
ShowVariants bool `json:"showVariants"`
|
||||||
|
ShowSexualContent bool `json:"showSexualContent"`
|
||||||
|
BrandCategoryBanners []interface{} `json:"brandCategoryBanners"`
|
||||||
|
AllVariants []struct {
|
||||||
|
ItemNumber int `json:"itemNumber"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
InStock bool `json:"inStock"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
} `json:"allVariants"`
|
||||||
|
OtherMerchantVariants []interface{} `json:"otherMerchantVariants"`
|
||||||
|
InstallmentBanner interface{} `json:"installmentBanner"`
|
||||||
|
IsVasEnabled bool `json:"isVasEnabled"`
|
||||||
|
OriginalCategory struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Hierarchy string `json:"hierarchy"`
|
||||||
|
Refundable bool `json:"refundable"`
|
||||||
|
BeautifiedName string `json:"beautifiedName"`
|
||||||
|
IsVASEnabled bool `json:"isVASEnabled"`
|
||||||
|
} `json:"originalCategory"`
|
||||||
|
Landings []interface{} `json:"landings"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
ProductCode string `json:"productCode"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
NameWithProductCode string `json:"nameWithProductCode"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ContentDescriptions []struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Bold bool `json:"bold"`
|
||||||
|
} `json:"contentDescriptions"`
|
||||||
|
ProductGroupID int `json:"productGroupId"`
|
||||||
|
Tax int `json:"tax"`
|
||||||
|
BusinessUnit string `json:"businessUnit"`
|
||||||
|
MaxInstallment int `json:"maxInstallment"`
|
||||||
|
Gender struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
} `json:"gender"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Images []string `json:"images"`
|
||||||
|
IsSellable bool `json:"isSellable"`
|
||||||
|
IsBasketDiscount bool `json:"isBasketDiscount"`
|
||||||
|
HasStock bool `json:"hasStock"`
|
||||||
|
Price Price `json:"price"`
|
||||||
|
IsFreeCargo bool `json:"isFreeCargo"`
|
||||||
|
Promotions []struct {
|
||||||
|
PromotionRemainingTime string `json:"promotionRemainingTime"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
} `json:"promotions"`
|
||||||
|
Merchant struct {
|
||||||
|
IsSearchableMerchant bool `json:"isSearchableMerchant"`
|
||||||
|
Stickers []interface{} `json:"stickers"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
OfficialName string `json:"officialName"`
|
||||||
|
CityName string `json:"cityName"`
|
||||||
|
TaxNumber string `json:"taxNumber"`
|
||||||
|
SellerScore float64 `json:"sellerScore"`
|
||||||
|
SellerScoreColor string `json:"sellerScoreColor"`
|
||||||
|
DeliveryProviderName string `json:"deliveryProviderName"`
|
||||||
|
SellerLink string `json:"sellerLink"`
|
||||||
|
} `json:"merchant"`
|
||||||
|
DeliveryInformation struct {
|
||||||
|
IsRushDelivery bool `json:"isRushDelivery"`
|
||||||
|
DeliveryDate string `json:"deliveryDate"`
|
||||||
|
} `json:"deliveryInformation"`
|
||||||
|
CargoRemainingDays int `json:"cargoRemainingDays"`
|
||||||
|
IsMarketplace bool `json:"isMarketplace"`
|
||||||
|
ProductStamps []struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ImageURL string `json:"imageUrl"`
|
||||||
|
Position string `json:"position"`
|
||||||
|
AspectRatio float64 `json:"aspectRatio"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
PriceTagStamp bool `json:"priceTagStamp,omitempty"`
|
||||||
|
} `json:"productStamps"`
|
||||||
|
HasHTMLContent bool `json:"hasHtmlContent"`
|
||||||
|
FavoriteCount int `json:"favoriteCount"`
|
||||||
|
UxLayout string `json:"uxLayout"`
|
||||||
|
IsDigitalGood bool `json:"isDigitalGood"`
|
||||||
|
IsRunningOut bool `json:"isRunningOut"`
|
||||||
|
ScheduledDelivery bool `json:"scheduledDelivery"`
|
||||||
|
RatingScore struct {
|
||||||
|
AverageRating float64 `json:"averageRating"`
|
||||||
|
TotalRatingCount int `json:"totalRatingCount"`
|
||||||
|
TotalCommentCount int `json:"totalCommentCount"`
|
||||||
|
} `json:"ratingScore"`
|
||||||
|
ShowStarredAttributes bool `json:"showStarredAttributes"`
|
||||||
|
ReviewsURL string `json:"reviewsUrl"`
|
||||||
|
QuestionsURL string `json:"questionsUrl"`
|
||||||
|
SellerQuestionEnabled bool `json:"sellerQuestionEnabled"`
|
||||||
|
SizeExpectationAvailable bool `json:"sizeExpectationAvailable"`
|
||||||
|
CrossPromotionAward struct {
|
||||||
|
AwardType interface{} `json:"awardType"`
|
||||||
|
AwardValue interface{} `json:"awardValue"`
|
||||||
|
ContentID int `json:"contentId"`
|
||||||
|
MerchantID int `json:"merchantId"`
|
||||||
|
} `json:"crossPromotionAward"`
|
||||||
|
RushDeliveryMerchantListingExist bool `json:"rushDeliveryMerchantListingExist"`
|
||||||
|
LowerPriceMerchantListingExist bool `json:"lowerPriceMerchantListingExist"`
|
||||||
|
ShowValidFlashSales bool `json:"showValidFlashSales"`
|
||||||
|
ShowExpiredFlashSales bool `json:"showExpiredFlashSales"`
|
||||||
|
WalletRebate struct {
|
||||||
|
MinPrice int `json:"minPrice"`
|
||||||
|
MaxPrice int `json:"maxPrice"`
|
||||||
|
RebateRatio float64 `json:"rebateRatio"`
|
||||||
|
} `json:"walletRebate"`
|
||||||
|
IsArtWork bool `json:"isArtWork"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrendyolProductVariantsResponse struct {
|
||||||
|
IsSuccess bool `json:"isSuccess"`
|
||||||
|
StatusCode int `json:"statusCode"`
|
||||||
|
Error interface{} `json:"error"`
|
||||||
|
Result struct {
|
||||||
|
SlicingAttributes []struct {
|
||||||
|
Brand struct {
|
||||||
|
BeautifiedName string `json:"beautifiedName"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
IsVirtual bool `json:"isVirtual"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
} `json:"brand"`
|
||||||
|
Attributes []struct {
|
||||||
|
Contents []struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
ImageURL string `json:"imageUrl"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Price struct {
|
||||||
|
DiscountedPrice struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
} `json:"discountedPrice"`
|
||||||
|
OriginalPrice struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
} `json:"originalPrice"`
|
||||||
|
SellingPrice struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
} `json:"sellingPrice"`
|
||||||
|
} `json:"price"`
|
||||||
|
} `json:"contents"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
BeautifiedName string `json:"beautifiedName"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
|
Order int `json:"order"`
|
||||||
|
DisplayType int `json:"displayType"`
|
||||||
|
} `json:"slicingAttributes"`
|
||||||
|
} `json:"result"`
|
||||||
|
Headers struct {
|
||||||
|
Tysidecarcachable string `json:"tysidecarcachable"`
|
||||||
|
} `json:"headers"`
|
||||||
|
}
|
||||||
|
type AlternativeVariant struct {
|
||||||
|
AttributeValue string `json:"attributeValue"`
|
||||||
|
AttributeBeautifiedValue string `json:"attributeBeautifiedValue"`
|
||||||
|
CampaignID int `json:"campaignId"`
|
||||||
|
MerchantID int `json:"merchantId"`
|
||||||
|
URLQuery string `json:"urlQuery"`
|
||||||
|
ListingID string `json:"listingId"`
|
||||||
|
ItemNumber int `json:"itemNumber"`
|
||||||
|
Barcode string `json:"barcode"`
|
||||||
|
Stock interface{} `json:"stock"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
Price Price `json:"price"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package trendyol_models
|
||||||
|
|
||||||
|
type SizeVariants struct {
|
||||||
|
Price float64 `json:"price"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
Stock int `json:"stock"`
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue