sarga_updater/repositories/ImportRepository.go

561 lines
16 KiB
Go
Raw Normal View History

2023-05-03 08:07:34 +00:00
package repositories
import (
"errors"
"fmt"
"gorm.io/gorm"
"log"
"math"
gm "sarga_updater/bagisto_models"
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
}
2023-06-04 15:05:05 +00:00
func (importer *Importer) UpdateOrCreate(product models.Product) (instance *Importer) {
2023-05-03 08:07:34 +00:00
2023-06-04 15:05:05 +00:00
//var newProducts []gm.Product
var firstProductFlat *gm.ProductFlat
err := importer.Baza.Preload("Product").Preload("Variants").First(&firstProductFlat, "sku = ?", product.ProductNumber).Error
2023-06-04 12:56:39 +00:00
//haryt satuwdan ayrlan bolsa bagistoda disable et
2023-05-03 08:07:34 +00:00
2023-06-04 15:05:05 +00:00
if err == nil {
if !product.IsSellable {
importer.disableVariant(*firstProductFlat)
} else {
importer.updateVariant(product, *firstProductFlat)
}
2023-05-03 08:07:34 +00:00
}
2023-06-04 15:05:05 +00:00
//else if err != nil && errors.Is(err, gorm.ErrRecordNotFound){
// if firstProduct, err := importer.importVariant(product); err !=nil{
// newProducts = append(newProducts, *firstProduct)
// firstProductFlat = &gm.ProductFlat{}
// firstProductFlat.Product = *firstProduct
// }
//}
2023-05-03 08:07:34 +00:00
2023-06-04 12:56:39 +00:00
if product.ColorVariants != nil && len(*product.ColorVariants) > 0 {
2023-06-04 15:05:05 +00:00
linkedProducts := []gm.Product{firstProductFlat.Product}
2023-06-04 12:56:39 +00:00
for _, colorVariant := range *product.ColorVariants {
var (
2023-06-04 15:05:05 +00:00
//variant *gm.Product
2023-06-04 12:56:39 +00:00
variantFlat *gm.ProductFlat
err error
)
2023-05-03 08:07:34 +00:00
2023-06-04 12:56:39 +00:00
err = importer.Baza.Preload("Product").Preload("Variants").First(&variantFlat, "sku = ?", colorVariant.ProductNumber).Error
2023-05-03 08:07:34 +00:00
2023-06-04 15:05:05 +00:00
//if err != nil && errors.Is(err, gorm.ErrRecordNotFound) && colorVariant.IsSellable {
// if variant, err = importer.importVariant(colorVariant); err == nil {
// newProducts = append(newProducts, *variant)
// }
//
//} else
2023-05-03 08:07:34 +00:00
2023-06-04 15:05:05 +00:00
if err == nil && !colorVariant.IsSellable {
importer.disableVariant(*variantFlat)
linkedProducts = append(linkedProducts, variantFlat.Product)
2023-05-03 08:07:34 +00:00
2023-06-04 12:56:39 +00:00
} else {
2023-06-04 15:05:05 +00:00
importer.updateVariant(colorVariant, *variantFlat)
linkedProducts = append(linkedProducts, variantFlat.Product)
2023-05-03 08:07:34 +00:00
}
}
2023-06-04 15:05:05 +00:00
//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)
// }
//}
2023-05-03 08:07:34 +00:00
}
return importer
}
func (importer *Importer) importVariant(product models.Product) (*gm.Product, error) {
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 {
2023-06-04 15:05:05 +00:00
tx.Commit()
2023-05-03 15:19:22 +00:00
log.Println(err, "er1")
return nil, err
2023-05-03 08:07:34 +00:00
}
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 {
2023-05-03 16:04:00 +00:00
sku := fmt.Sprintf("%s-%d", product.ProductNumber, variant.ItemNumber)
//todo finde variant first if not exists create
var variantFlat gm.ProductFlat
err := tx.Preload("Product").Preload("Variants").First(&variantFlat, "sku = ?", sku).Error
2023-05-03 08:07:34 +00:00
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)
}
2023-06-04 12:56:39 +00:00
if err == nil {
err = tx.Model(&variantFlat).Updates(map[string]interface{}{"status": variant.Sellable, "parent_id": mainFlat.ID, "size": sizeOPtion.ID, "size_label": variant.AttributeValue}).Error
err = tx.Model(&gm.ProductAttributeValue{}).Where("attribute_id=8 AND product_id=?", variantFlat.ProductID).Update("boolean_value", variant.Sellable).Error
err = tx.Model(&variantFlat.Product).Update("parent_id", mainPorduct.ID).Error
if err == nil && variant.Sellable {
if variant.Price.OriginalPrice.Value > variant.Price.DiscountedPrice.Value {
err = tx.Model(&variantFlat).Updates(map[string]interface{}{
"price": variant.Price.OriginalPrice.Value,
"special_price": variant.Price.DiscountedPrice.Value,
"min_price": variant.Price.DiscountedPrice.Value,
"max_price": variant.Price.OriginalPrice.Value,
}).Error
err = tx.Model(&gm.ProductAttributeValue{}).
Where("attribute_id = 11 and product_id = ?", variantFlat.ProductID).
Update("float_value", variant.Price.OriginalPrice.Value).Error
err = tx.Model(&gm.ProductAttributeValue{}).
Where("attribute_id = 13 and product_id = ?", variantFlat.ProductID).
Update("float_value", variant.Price.DiscountedPrice.Value).Error
} else {
err = tx.Model(&variantFlat).Updates(map[string]interface{}{
"price": variant.Price.DiscountedPrice.Value,
"special_price": nil,
"min_price": variant.Price.DiscountedPrice.Value,
"max_price": variant.Price.DiscountedPrice.Value,
}).Error
err = tx.Model(&gm.ProductAttributeValue{}).
Where("attribute_id = 11 and product_id = ?", variantFlat.ProductID).
Update("float_value", variant.Price.DiscountedPrice.Value).Error
err = tx.Where("attribute_id = 13 and product_id = ?", variantFlat.ProductID).
Delete(&gm.ProductAttributeValue{}).Error
}
if err != nil {
tx.RollbackTo(savePoint)
}
} else {
tx.RollbackTo(savePoint)
}
continue
}
2023-05-03 08:07:34 +00:00
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
}
2023-05-03 16:04:00 +00:00
variantFlat = productRepo.makeVariantFlat(variant, sizeOPtion.ID, mainFlat.ID, variantProduct.ID)
2023-05-03 08:07:34 +00:00
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
}
2023-06-04 12:56:39 +00:00
2023-06-04 15:05:05 +00:00
func (importer *Importer) disableVariant(productFlat gm.ProductFlat) {
2023-05-03 14:45:50 +00:00
2023-06-04 15:05:05 +00:00
importer.Error = importer.Baza.Model(&productFlat).Update("status", false).Error
2023-05-03 14:45:50 +00:00
importer.Error = importer.Baza.Model(&gm.ProductAttributeValue{}).
2023-06-04 15:05:05 +00:00
Where("attribute_id=8 AND product_id=?", productFlat.ProductID).
2023-05-03 14:45:50 +00:00
Update("boolean_value", false).Error
2023-06-04 15:05:05 +00:00
if importer.Error != nil {
log.Println(importer.Error)
importer.Error = nil
}
2023-05-03 14:45:50 +00:00
}
2023-05-03 08:07:34 +00:00
2023-06-04 15:05:05 +00:00
func (importer *Importer) enableVariant(productFlat gm.ProductFlat) {
importer.Error = importer.Baza.Model(&productFlat).Update("status", true).Error
2023-06-04 12:56:39 +00:00
2023-06-04 15:05:05 +00:00
importer.Error = importer.Baza.Model(&gm.ProductAttributeValue{}).
Where("attribute_id=8 AND product_id=?", productFlat.ProductID).
Update("boolean_value", true).Error
2023-06-04 12:56:39 +00:00
if importer.Error != nil {
2023-06-04 15:05:05 +00:00
log.Println(importer.Error)
importer.Error = nil
2023-05-03 08:07:34 +00:00
}
2023-06-04 15:05:05 +00:00
}
func (importer *Importer) updateVariant(product models.Product, flat gm.ProductFlat) {
2023-05-03 08:07:34 +00:00
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 {
2023-06-04 15:05:05 +00:00
log.Println(err)
return
2023-05-03 08:07:34 +00:00
} 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 {
2023-06-04 15:05:05 +00:00
importer.disableVariant(flatVariant)
2023-05-03 08:07:34 +00:00
} else {
importer.updatePrice(variant.Price, &flatVariant)
if !flatVariant.Status {
2023-06-04 15:05:05 +00:00
importer.enableVariant(flatVariant)
2023-05-03 08:07:34 +00:00
}
}
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)
}
2023-06-04 15:05:05 +00:00
2023-05-03 08:07:34 +00:00
}
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 {
2023-06-05 06:09:13 +00:00
log.Println(flat)
2023-05-03 08:07:34 +00:00
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{})
}
}