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 { } )
}
}