module = Module::getInstanceByName("ecomzone"); $this->defaultLangId = (int) Configuration::get("PS_LANG_DEFAULT"); $this->defaultShopId = (int) Context::getContext()->shop->id; } public function importProduct($productData) { try { $data = $productData["data"]; // Start transaction Db::getInstance()->execute("START TRANSACTION"); // Get or create product $product = $this->getOrCreateProduct($data["sku"]); // Update basic information $this->updateBasicInfo($product, $data); // Update categories $this->updateCategories($product, $data); // Update images $this->updateImages($product, $data); // Update specifications $this->updateSpecifications($product, $data); // Update stock $this->updateStock($product, $data); // Commit transaction Db::getInstance()->execute("COMMIT"); EcomZoneLogger::log("Product imported successfully", "INFO", [ "sku" => $data["sku"], ]); return true; } catch (Exception $e) { Db::getInstance()->execute("ROLLBACK"); EcomZoneLogger::log("Product import failed", "ERROR", [ "sku" => $data["sku"] ?? "unknown", "error" => $e->getMessage(), ]); throw $e; } } private function getOrCreateProduct($sku) { $productId = Product::getIdByReference($sku); if ($productId) { $product = new Product($productId); } else { $product = new Product(); $product->reference = $sku; } return $product; } private function updateBasicInfo($product, $data) { $product->name[$this->defaultLangId] = $data["product_name"]; $product->description[$this->defaultLangId] = $data["long_description"]; $product->description_short[$this->defaultLangId] = $data["description"]; $product->price = (float) $data["product_price"]; $product->wholesale_price = (float) ($data["recommended_retail_price"] ?? 0); $product->ean13 = $data["ean"] ?? ""; // Product settings $product->active = true; $product->visibility = "both"; $product->available_for_order = true; $product->show_price = true; $product->indexed = 1; // Dimensions if (!empty($data["dimensions"])) { $dimensions = json_decode($data["dimensions"], true); $product->weight = (float) ($dimensions["weight"] ?? 0); $product->height = (float) ($dimensions["height"] ?? 0); $product->width = (float) ($dimensions["width"] ?? 0); $product->depth = (float) ($dimensions["length"] ?? 0); } if (!$product->id) { $product->add(); } else { $product->update(); } } private function updateCategories($product, $data) { $categoryIds = []; // Process main categories if (!empty($data["categories"])) { $categories = json_decode($data["categories"], true); foreach ($categories as $categoryName) { if (strpos($categoryName, "0") === 0) { continue; } // Skip special categories $categoryId = $this->getOrCreateCategory($categoryName); if ($categoryId) { $categoryIds[] = $categoryId; } } } // Process Google categories if (!empty($data["google_categories"])) { $googleCategories = json_decode($data["google_categories"], true); foreach ($googleCategories as $categoryName) { $categoryId = $this->getOrCreateCategory($categoryName); if ($categoryId) { $categoryIds[] = $categoryId; } } } // Ensure we have at least default category if (empty($categoryIds)) { $categoryIds[] = (int) Configuration::get("PS_HOME_CATEGORY"); } $categoryIds = array_unique($categoryIds); $product->id_category_default = reset($categoryIds); $product->updateCategories($categoryIds); } private function getOrCreateCategory($name) { $name = trim($name); if (empty($name)) { return null; } // Try to find existing category $categoryId = (int) Db::getInstance()->getValue( 'SELECT c.id_category FROM `' . _DB_PREFIX_ . 'category_lang` cl JOIN `' . _DB_PREFIX_ . 'category` c ON c.id_category = cl.id_category WHERE cl.name = "' . pSQL($name) . '" AND cl.id_lang = ' . (int) $this->defaultLangId ); if ($categoryId) { return $categoryId; } // Create new category $category = new Category(); $category->name = [$this->defaultLangId => $name]; $category->link_rewrite = [ $this->defaultLangId => Tools::str2url($name), ]; $category->id_parent = (int) Configuration::get("PS_HOME_CATEGORY"); $category->active = true; if ($category->add()) { return (int) $category->id; } return null; } private function updateImages($product, $data) { $imageUrls = []; // Main image if (!empty($data["image"])) { $imageUrls[] = $data["image"]; } // Additional images if (!empty($data["images"])) { $additionalImages = json_decode($data["images"], true); if (is_array($additionalImages)) { $imageUrls = array_merge($imageUrls, $additionalImages); } } // GIF or video thumbnail if (!empty($data["gif_or_video"])) { $imageUrls[] = $data["gif_or_video"]; } if (empty($imageUrls)) { return; } // Delete existing images $product->deleteImages(); // Import new images foreach ($imageUrls as $index => $imageUrl) { $this->importImage($product, $imageUrl, $index === 0); } } private function importImage($product, $imageUrl, $cover = false) { $tmpFile = tempnam(_PS_TMP_IMG_DIR_, "ecomzone_"); try { if (!copy($imageUrl, $tmpFile)) { throw new Exception("Failed to download image"); } $image = new Image(); $image->id_product = $product->id; $image->position = Image::getHighestPosition($product->id) + 1; $image->cover = $cover; if (!$image->add()) { throw new Exception("Failed to create image record"); } $imagesTypes = ImageType::getImagesTypes("products"); foreach ($imagesTypes as $type) { ImageManager::resize( $tmpFile, _PS_PROD_IMG_DIR_ . $image->getExistingImgPath() . "." . $type["name"] . ".jpg", $type["width"], $type["height"] ); } // Save original image ImageManager::resize( $tmpFile, _PS_PROD_IMG_DIR_ . $image->getExistingImgPath() . ".jpg" ); } catch (Exception $e) { EcomZoneLogger::log("Image import failed", "ERROR", [ "url" => $imageUrl, "error" => $e->getMessage(), ]); } finally { if (file_exists($tmpFile)) { unlink($tmpFile); } } } private function updateSpecifications($product, $data) { if (empty($data["specifications"])) { return; } try { $specifications = json_decode($data["specifications"], true); if (!is_array($specifications)) { return; } foreach ($specifications as $name => $value) { if (empty($value)) { continue; } // Get or create feature $featureId = Feature::getIdByName($name); if (!$featureId) { $feature = new Feature(); $feature->name[$this->defaultLangId] = $name; if (!$feature->add()) { continue; } $featureId = $feature->id; } // Get or create feature value $featureValue = new FeatureValue(); $featureValue->id_feature = $featureId; $featureValue->value[$this->defaultLangId] = $value; if (!$featureValue->add()) { continue; } // Associate with product $product->addFeaturesToDB([$featureId => $featureValue->id]); } } catch (Exception $e) { EcomZoneLogger::log("Failed to update specifications", "ERROR", [ "sku" => $product->reference, "error" => $e->getMessage(), ]); } } private function updateStock($product, $data) { try { $quantity = (int) ($data["stock_quantity"] ?? $data["stock"] ?? 0); StockAvailable::setQuantity($product->id, 0, $quantity); // Update out of stock behavior StockAvailable::setProductOutOfStock( $product->id, $quantity > 0 ? 2 : 0, // 2 = Allow orders, 0 = Deny orders $this->defaultShopId ); } catch (Exception $e) { throw new EcomZoneException( "Failed to update stock: " . $e->getMessage(), EcomZoneException::INVALID_PRODUCT_DATA, $e ); } } }