diff --git a/buildtime/server/examples/household_example/productGroups.json b/buildtime/server/examples/household_example/productGroups.json index 9fb78fd09..57a4392c4 100644 --- a/buildtime/server/examples/household_example/productGroups.json +++ b/buildtime/server/examples/household_example/productGroups.json @@ -1,5 +1,4 @@ [{ - "id": 1000007, "label": "Simply CPQ", "description": "Product groups for Simply CPQ", "productGroups": [ @@ -10,6 +9,18 @@ { "label": "Radio", "description": "All HIFI products", + "properties": [ + { "property": "Spannungsversorgung (V)"}, + { "property": "Leistung (W)"}, + { "property": "Ausgangsimpedanz (Ω)"}, + { "property": "Eingangsimpedanz (Ω)"}, + { "property": "Gewicht (kg)"}, + { "property": "Abmessungen (mm)"}, + { "property": "Frequenzbereich (Hz‑kHz)"}, + { "property": "Signal‑Rausch‑Verhältnis (dB)"}, + { "property": "Anschlüsse"}, + { "property": "Preis (USD)"} + ], "products": [ { "externalId": "Radio_RAD-001", @@ -195,6 +206,18 @@ { "label": "Kassette", "description": "All Kassette products", + "properties": [ + { "property": "Spannungsversorgung (V)"}, + { "property": "Leistung (W)"}, + { "property": "Kapazität/Volumen (L)"}, + { "property": "Abmessungen (mm)"}, + { "property": "Gewicht (kg)"}, + { "property": "Energieeffizienzklasse"}, + { "property": "Bedienungsart"}, + { "property": "Material Innenraum"}, + { "property": "Zusatzfunktionen"}, + { "property": "Preis (USD)"} + ], "products": [ { "externalId": "Kassette_CAS-001", @@ -381,6 +404,18 @@ { "label": "Verstärker", "description": "All Verstärker products", + "properties": [ + { "property": "Spannungsversorgung (V)"}, + { "property": "Leistung (W)"}, + { "property": "Kapazität/Volumen (L)"}, + { "property": "Abmessungen (mm)"}, + { "property": "Gewicht (kg)"}, + { "property": "Energieeffizienzklasse"}, + { "property": "Bedienungsart"}, + { "property": "Material Innenraum"}, + { "property": "Zusatzfunktionen"}, + { "property": "Preis (USD)"} + ], "products": [ { "externalId": "Verstärker_AMP-001", @@ -566,6 +601,18 @@ { "label": "CD_Player", "description": "All CD_Player products", + "properties": [ + { "property": "Spannungsversorgung (V)"}, + { "property": "Leistung (W)"}, + { "property": "Kapazität/Volumen (L)"}, + { "property": "Abmessungen (mm)"}, + { "property": "Gewicht (kg)"}, + { "property": "Energieeffizienzklasse"}, + { "property": "Bedienungsart"}, + { "property": "Material Innenraum"}, + { "property": "Zusatzfunktionen"}, + { "property": "Preis (USD)"} + ], "products": [ { "externalId": "CD‑Player_CDP-001", @@ -752,6 +799,18 @@ { "label": "Machines", "description": "All machines products", + "properties": [ + { "property": "Spannungsversorgung (V)"}, + { "property": "Leistung (W)"}, + { "property": "Kapazität (kg)"}, + { "property": "Energieeffizienzklasse"}, + { "property": "Schleuderdrehzahl (U/min)"}, + { "property": "Programms/Features"}, + { "property": "Wasserverbrauch pro Zyklus (L)"}, + { "property": "Abmessungen (mm)"}, + { "property": "Gewicht (kg)"}, + { "property": "Preis (USD)"} + ], "products": [ { "externalId": "Waschmaschine_WAS-001", @@ -1120,6 +1179,7 @@ { "label": "components", "description": "All components", + "properties": [], "productGroups": [] } ] diff --git a/buildtime/server/examples/household_example/propertyTypeGroups.json b/buildtime/server/examples/household_example/propertyTypeGroups.json index a458798d4..87fbdab79 100644 --- a/buildtime/server/examples/household_example/propertyTypeGroups.json +++ b/buildtime/server/examples/household_example/propertyTypeGroups.json @@ -12,27 +12,27 @@ }, { "label": "Leistung (W)", - "description": "All kitchen products" + "description": "Leistung" }, { "label": "Eingangsimpedanz (Ω)", - "description": "All kitchen products" + "description": "Wiederstand der Eingangsimpedanz" }, { "label": "Frequenzbereich (Hz‑kHz)", - "description": "All kitchen products" + "description": "Frequenzbereich (Hz‑kHz)" }, { "label": "Signal‑Rausch‑Verhältnis (dB)", - "description": "All kitchen products" + "description": "Signal‑Rausch‑Verhältnis (dB)" }, { "label": "Anschlüsse", - "description": "All kitchen products" + "description": "Anschlüsse" }, { "label": "Ausgangsimpedanz (Ω)", - "description": "All machines products" + "description": "Ausgangsimpedanz (Ω)" } ] }, diff --git a/buildtime/server/models/import.go b/buildtime/server/models/import.go index be6896b8c..77092ab0a 100644 --- a/buildtime/server/models/import.go +++ b/buildtime/server/models/import.go @@ -15,9 +15,12 @@ type ImportPropertyType struct { } type ImportProperty struct { - ID int `json:"id,omitempty"` - Label string `json:"property"` - Value string `json:"value"` + ID int `json:"id,omitempty"` + PropertyTypeID int `json:"propertyTypeId,omitempty"` + ProductID int `json:"productId,omitempty"` + ProductGroupID int `json:"productGroupId,omitempty"` + Label string `json:"property"` + Value string `json:"value"` } type ImportProductGroup struct { @@ -26,13 +29,14 @@ type ImportProductGroup struct { Description string `json:"description"` ProductGroups []ImportProductGroup `json:"productGroups,omitempty"` Products []ImportProduct `json:"products,omitempty"` + Properties []ImportProperty `json:"properties,omitempty"` } type ImportProduct struct { - ID int `json:"id,omitempty"` - ExternalID string `json:"externalId"` - Label string `json:"label"` - Description string `json:"description"` - ProductGroupLabel string `json:"productGroupLabel,omitempty"` - Properties []ImportProperty `json:"properties,omitempty"` + ID int `json:"id,omitempty"` + ExternalID string `json:"externalId"` + Label string `json:"label"` + Description string `json:"description"` + ProductGroupID int `json:"productGroupId,omitempty"` + Properties []ImportProperty `json:"properties,omitempty"` } diff --git a/buildtime/server/repositories/importRepository.go b/buildtime/server/repositories/importRepository.go index cbe3460e1..8374395f9 100644 --- a/buildtime/server/repositories/importRepository.go +++ b/buildtime/server/repositories/importRepository.go @@ -2,6 +2,7 @@ package repositories import ( "database/sql" + "log" "simplycpq/models" ) @@ -95,158 +96,173 @@ func (r *ImportRepository) ImportPropertyTypes(importData []models.ImportPropert return nil } -func (r *ImportRepository) ImportProductGroups(importData []models.ImportProductGroup, parentGroupId int) error { - // Implement the logic to import ProductGroups into the database - // Implement the logic to import PropertyTypeGroups into the database - // This is a placeholder implementation; you need to write the actual logic - for _, data := range importData { - if data.ID != 0 { - // Update existing record - query := "UPDATE productgroups SET label = $1, description = $2 WHERE id = $3" - _, err := r.db.Exec(query, data.Label, data.Description, data.ID) +// Implement the ImportProducts method +func (r *ImportRepository) ImportProductGroups(importData []models.ImportProductGroup, parentProductGroupID int) error { + // check if ProductGroup allready exists + for _, productGroup := range importData { + if productGroup.ID != 0 { + // Update existing ProductGroup + query := "UPDATE productGroups SET label = $1, description = $2, parentGroupId = $3 WHERE id = $4" + _, err := r.db.Exec(query, productGroup.Label, productGroup.Description, parentProductGroupID, productGroup.ID) if err != nil { return err } - // - if err := r.ImportProducts(data.Products, data.ID); err != nil { - return err - } - // update subgroups if they exist - if data.ProductGroups != nil { - return r.ImportProductGroups(data.ProductGroups, data.ID) - } } else { - // Insert new record - err := r.db.QueryRow("INSERT INTO productgroups (label, description, parentGroupId) VALUES ($1, $2, $3) RETURNING id", data.Label, data.Description, parentGroupId).Scan(&data.ID) + // Insert new ProductGroup + err := r.db.QueryRow("INSERT INTO productGroups (label, description, parentGroupId) VALUES ($1, $2, $3) RETURNING id", productGroup.Label, productGroup.Description, parentProductGroupID).Scan(&productGroup.ID) if err != nil { return err } - if err := r.ImportProducts(data.Products, data.ID); err != nil { + } + + // Import Properties for the ProductGroup + for _, property := range productGroup.Properties { + property.ProductGroupID = productGroup.ID + property.ProductID = 0 // Clear ProductID for ProductGroup properties + // get PropertyTypeID by label + log.Printf("Getting PropertyTypeID for label: %s", property.Label) + ptid, err := r.valueRepo.GetPropertyTypeIDByLabel(property.Label) + if err != nil { return err } - if data.ProductGroups != nil { - return r.ImportProductGroups(data.ProductGroups, data.ID) + log.Printf("Retrieved PropertyTypeID: %d for label: %s", ptid, property.Label) + property.PropertyTypeID = ptid // Set PropertyTypeID to the retrieved ID for ProductGroup properties + err = r.ImportProperty(property) + if err != nil { + return err + } + } + + // Import Products for the ProductGroup + for _, product := range productGroup.Products { + product.ProductGroupID = productGroup.ID // Set the ProductGroupID for the Product + err := r.ImportProduct(product) + if err != nil { + return err + } + } + + // Import sub ProductGroups recursively + if productGroup.ProductGroups != nil { + err := r.ImportProductGroups(productGroup.ProductGroups, productGroup.ID) + if err != nil { + return err } } } return nil } -func (r *ImportRepository) ImportProducts(importData []models.ImportProduct, productGroupID int) error { - // Implement the logic to import Products into the database - for _, product := range importData { - if product.ID != 0 { - // Update existing Product - query := "UPDATE products SET externalId = $1, label = $2, description = $3, productGroupId = $4 WHERE id = $5" - _, err := r.db.Exec(query, product.ExternalID, product.Label, product.Description, productGroupID, product.ID) - if err != nil { - return err - } - if product.Properties != nil { - if err := r.ImportProperties(product.Properties, product.ID, productGroupID); err != nil { - return err - } - } - } else { - // Insert new Product - query := "INSERT INTO products (externalId, label, description, productGroupId) VALUES ($1, $2, $3, $4) RETURNING id" - err := r.db.QueryRow(query, product.ExternalID, product.Label, product.Description, productGroupID).Scan(&product.ID) - if err != nil { - return err - } - if product.Properties != nil { - if err := r.ImportProperties(product.Properties, product.ID, productGroupID); err != nil { - return err - } - } - } - } - return nil -} - -func (r *ImportRepository) ImportProperties(importData []models.ImportProperty, productID int, productGroupID int) error { - for _, property := range importData { - var propertyType models.ImportPropertyType - var propertyIDs []int - propertyType, err := r.findPropertyType(property.Label) +func (r *ImportRepository) ImportProduct(product models.ImportProduct) error { + if product.ID != 0 { + // Update existing Product + query := "UPDATE products SET label = $1, description = $2, productGroupId = $3 WHERE id = $4" + _, err := r.db.Exec(query, product.Label, product.Description, product.ProductGroupID, product.ID) if err != nil { return err } - if property.ID != 0 { - if err := r.updateProperty(property, property.ID, productID, productGroupID, propertyType); err != nil { - return err - } - } else { - propertyIDs, err = r.insertProperty(property, productID, productGroupID, propertyType) - if err != nil { - return err - } - + } else { + // Insert new Product + err := r.db.QueryRow("INSERT INTO products (label, description, productGroupId) VALUES ($1, $2, $3) RETURNING id", product.Label, product.Description, product.ProductGroupID).Scan(&product.ID) + if err != nil { + return err } - for _, propertyId := range propertyIDs { - property.ID = propertyId - if err := r.upsertValue(property, property.Value, propertyType.ID); err != nil { - return err - } + } + + // Import Properties for the Product + for _, property := range product.Properties { + property.ProductID = product.ID // Set the ProductID for the Property + property.ProductGroupID = product.ProductGroupID // Clear ProductGroupID for Product properties + err := r.ImportProperty(property) + if err != nil { + return err + } + } + + return nil +} + +func (r *ImportRepository) ImportProperty(property models.ImportProperty) error { + if property.ID != 0 { + // Update existing Property + query := "UPDATE properties SET label = $1, description = $2, propertyTypeId = $3, productId = $4, productGroupId = $5 WHERE id = $6" + _, err := r.db.Exec(query, property.Label, property.Label, property.PropertyTypeID, property.ProductID, property.ProductGroupID, property.ID) + if err != nil { + return err + } + } else { + // Insert new Property + query := "INSERT INTO properties (label, description, propertyTypeId, productId, productGroupId) VALUES ($1, $2, $3, $4, $5) RETURNING id" + err := r.db.QueryRow(query, property.Label, property.Label, property.PropertyTypeID, property.ProductID, property.ProductGroupID).Scan(&property.ID) + if err != nil { + return err + } + } + // Import Property Value for the Property + if property.Value != "" { + err := r.ImportPropertyValue(property.Value, property) + if err != nil { + return err + } + err2 := r.ImportPropertyTypeValue(property.Value, property.PropertyTypeID, property.Label) + if err2 != nil { + return err2 + } + err3 := r.ImportPropertyProductValue(property.Value, property.ProductID, property.Label) + if err3 != nil { + return err3 } } return nil } -func (r *ImportRepository) findPropertyType(label string) (models.ImportPropertyType, error) { - var propertyType models.ImportPropertyType - err := r.db.QueryRow("SELECT id, label FROM propertyTypes WHERE label = $1", label).Scan(&propertyType.ID, &propertyType.Label) - if err != nil { - if err == sql.ErrNoRows { - return models.ImportPropertyType{}, nil - } - return models.ImportPropertyType{}, err - } - - return propertyType, nil -} - -func (r *ImportRepository) updateProperty(property models.ImportProperty, propertyID int, productID int, productGroupID int, propertyType models.ImportPropertyType) error { - query := "UPDATE properties SET label = $1, description = $2, propertyTypeId = $3, productId = $4, productGroupId = $5 WHERE id = $6" - _, err := r.db.Exec(query, property.Label, propertyType.Label, propertyType.ID, productID, productGroupID, propertyID) - return err -} - -func (r *ImportRepository) insertProperty(property models.ImportProperty, productID int, productGroupID int, propertyType models.ImportPropertyType) ([]int, error) { - query := "INSERT INTO properties (label, description, productId, propertyTypeId, productGroupId) VALUES ($1, $2, $3, $4, $5) RETURNING id" - var propertyID int - var propertyID2 int - err := r.db.QueryRow(query, property.Label, propertyType.Label, productID, propertyType.ID, productGroupID).Scan(&propertyID) - if err != nil { - return nil, err - } - err2 := r.db.QueryRow(query, property.Label, propertyType.Label, 0, propertyType.ID, productGroupID).Scan(&propertyID2) - if err2 != nil { - return nil, err2 - } - - return []int{propertyID, propertyID2}, nil -} - -func (r *ImportRepository) upsertValue(property models.ImportProperty, value string, propertyTypeId int) error { +func (r *ImportRepository) ImportPropertyValue(value string, property models.ImportProperty) error { + // Check if a value already exists for the property var existingValueID int err := r.db.QueryRow("SELECT id FROM values WHERE label = $1 AND propertyId = $2", value, property.ID).Scan(&existingValueID) - if err != nil { - if err != sql.ErrNoRows { + if err == sql.ErrNoRows { + // Insert new Property Value + query := "INSERT INTO values (label, description, propertyId) VALUES ($1, $2, $3)" + _, err := r.db.Exec(query, value, value, property.ID) + if err != nil { return err } - _, err = r.db.Exec("INSERT INTO values (label, description, propertyId) VALUES ($1, $2, $3)", value, property.Label, property.ID) - // check if value exists for propertyType, if not insert it, if yes update it - query3 := "SELECT id FROM values WHERE label = $1 AND propertyId = $2" - _, err = r.db.Exec(query3, value, propertyTypeId) - if err == sql.ErrNoRows { - _, err = r.db.Exec("INSERT INTO values (label, description, propertyId) VALUES ($1, $2, $3)", value, property.Label, propertyTypeId) - return err - } - return err - } else { - _, err = r.db.Exec("UPDATE values SET label = $1, description = $2 WHERE id = $3", value, property.Label, existingValueID) + } else if err != nil { return err } + return nil +} +func (r *ImportRepository) ImportPropertyTypeValue(value string, propertyTypeID int, description string) error { + // Check if a value already exists for the property + var existingValueID int + err := r.db.QueryRow("SELECT id FROM values WHERE label = $1 AND propertyId = $2", value, propertyTypeID).Scan(&existingValueID) + if err == sql.ErrNoRows { + // Insert new Property Value + query := "INSERT INTO values (label, description, propertyId) VALUES ($1, $2, $3)" + _, err := r.db.Exec(query, value, description, propertyTypeID) + if err != nil { + return err + } + } else if err != nil { + return err + } + return nil +} + +func (r *ImportRepository) ImportPropertyProductValue(value string, productID int, description string) error { + // Check if a value already exists for the property + var existingValueID int + err := r.db.QueryRow("SELECT id FROM values WHERE label = $1 AND propertyId = $2", value, productID).Scan(&existingValueID) + if err == sql.ErrNoRows { + // Insert new Property Value + query := "INSERT INTO values (label, description, propertyId) VALUES ($1, $2, $3)" + _, err := r.db.Exec(query, value, description, productID) + if err != nil { + return err + } + } else if err != nil { + return err + } + return nil } diff --git a/buildtime/server/repositories/valueRepository.go b/buildtime/server/repositories/valueRepository.go index 55264d626..be50d4377 100644 --- a/buildtime/server/repositories/valueRepository.go +++ b/buildtime/server/repositories/valueRepository.go @@ -143,6 +143,18 @@ func (vr *ValueRepository) GetPropertyType(id int) (*models.PropertyType, error) return &propertyType, nil } +func (vr *ValueRepository) GetPropertyTypeIDByLabel(label string) (int, error) { + // Implement the logic to retrieve the propertyType ID from the database by label + query := `SELECT id FROM propertytypes WHERE label=$1` + row := vr.db.QueryRow(query, label) + var id int + err := row.Scan(&id) + if err != nil { + return 0, err + } + return id, nil +} + func (vr *ValueRepository) GetPropertyTypesByGroupId(groupId int) ([]*models.PropertyType, error) { // Implement the logic to retrieve the propertyTypes by groupId from the database query := `SELECT id, label, description, propertytypegroupid FROM propertytypes WHERE propertytypegroupid=$1`