445 lines
17 KiB
PHP
445 lines
17 KiB
PHP
<?php
|
|
|
|
if (!defined('_PS_VERSION_')) {
|
|
exit;
|
|
}
|
|
|
|
class EcomZone extends Module
|
|
{
|
|
private $errors = [];
|
|
private $api;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->name = 'ecomzone';
|
|
$this->tab = 'market_place';
|
|
$this->version = '1.0.0';
|
|
$this->author = 'Your Name';
|
|
$this->need_instance = 0;
|
|
$this->bootstrap = true;
|
|
$this->ps_versions_compliancy = [
|
|
'min' => '1.7.0.0',
|
|
'max' => _PS_VERSION_
|
|
];
|
|
|
|
parent::__construct();
|
|
|
|
$this->displayName = $this->l('EcomZone Integration');
|
|
$this->description = $this->l('Integrates PrestaShop with EcomZone Dropshipping API');
|
|
|
|
// Initialize API
|
|
require_once(dirname(__FILE__) . '/classes/EcomZoneAPI.php');
|
|
$this->api = new EcomZoneAPI();
|
|
}
|
|
|
|
public function install()
|
|
{
|
|
if (!parent::install()) {
|
|
$this->errors[] = $this->l('Could not install the module');
|
|
return false;
|
|
}
|
|
|
|
if (!$this->registerHook('actionProductUpdate') ||
|
|
!$this->registerHook('actionCronJob')) {
|
|
$this->errors[] = $this->l('Could not register hooks');
|
|
return false;
|
|
}
|
|
|
|
// Set default API configuration
|
|
if (!Configuration::updateValue('ECOMZONE_API_KEY', 'klRyAdrXaxL0s6PEUp7LDlH6T8aPSCtBY8NiEHsHiWpc6646K2TZPi5KMxUg') ||
|
|
!Configuration::updateValue('ECOMZONE_API_URL', 'https://dropship.ecomzone.eu/api') ||
|
|
!Configuration::updateValue('ECOMZONE_LAST_CRON', time())) {
|
|
$this->errors[] = $this->l('Could not set default configuration');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function getContent()
|
|
{
|
|
$output = '';
|
|
$debugOutput = '';
|
|
|
|
if (Tools::isSubmit('submitEcomZone')) {
|
|
$apiKey = Tools::getValue('ECOMZONE_API_KEY');
|
|
|
|
if (!$apiKey) {
|
|
$output .= $this->displayError($this->l('API Key is required'));
|
|
} else {
|
|
Configuration::updateValue('ECOMZONE_API_KEY', $apiKey);
|
|
$output .= $this->displayConfirmation($this->l('Settings updated'));
|
|
}
|
|
}
|
|
|
|
// Handle manual fetch
|
|
if (Tools::isSubmit('fetchProducts')) {
|
|
try {
|
|
$debugOutput .= "Starting manual product fetch...\n";
|
|
$result = $this->cronRefreshProducts();
|
|
|
|
if ($result['success']) {
|
|
$catalog = $this->api->getCatalog(1000);
|
|
if (isset($catalog['data']) && is_array($catalog['data'])) {
|
|
$totalProducts = count($catalog['data']);
|
|
$debugOutput .= sprintf("Found %d products\n\nPreview of first 5 products:\n", $totalProducts);
|
|
|
|
// Show first 5 products as preview
|
|
for ($i = 0; $i < min(5, $totalProducts); $i++) {
|
|
$product = $catalog['data'][$i];
|
|
$debugOutput .= sprintf(
|
|
"Product %d/%d: %s - %s (Price: %s EUR, Stock: %s)\n",
|
|
$i + 1,
|
|
$totalProducts,
|
|
$product['sku'],
|
|
$product['product_name'],
|
|
$product['product_price'],
|
|
$product['stock']
|
|
);
|
|
}
|
|
|
|
if ($totalProducts > 5) {
|
|
$debugOutput .= "\n... and " . ($totalProducts - 5) . " more products\n";
|
|
}
|
|
|
|
$debugOutput .= sprintf("\nTotal products updated: %d\n", $result['updated']);
|
|
}
|
|
$output .= $this->displayConfirmation($this->l('Products fetched successfully'));
|
|
}
|
|
} catch (Exception $e) {
|
|
$debugOutput .= "Error: " . $e->getMessage() . "\n";
|
|
$output .= $this->displayError($this->l('Error fetching products'));
|
|
}
|
|
}
|
|
|
|
// Store debug output in smarty variable
|
|
$this->context->smarty->assign('debug_output', $debugOutput);
|
|
|
|
return $output . $this->displayForm();
|
|
}
|
|
|
|
public function displayForm()
|
|
{
|
|
$fields_form[0]['form'] = [
|
|
'legend' => [
|
|
'title' => $this->l('Settings'),
|
|
],
|
|
'input' => [
|
|
[
|
|
'type' => 'text',
|
|
'label' => $this->l('API Key'),
|
|
'name' => 'ECOMZONE_API_KEY',
|
|
'required' => true,
|
|
'size' => 50
|
|
],
|
|
],
|
|
'buttons' => [
|
|
[
|
|
'type' => 'submit',
|
|
'title' => $this->l('Fetch Products'),
|
|
'icon' => 'process-icon-download',
|
|
'name' => 'fetchProducts',
|
|
'class' => 'btn btn-primary pull-left'
|
|
]
|
|
],
|
|
'submit' => [
|
|
'title' => $this->l('Save'),
|
|
'class' => 'btn btn-default pull-right'
|
|
]
|
|
];
|
|
|
|
$helper = new HelperForm();
|
|
$helper->module = $this;
|
|
$helper->name_controller = $this->name;
|
|
$helper->token = Tools::getAdminTokenLite('AdminModules');
|
|
$helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name;
|
|
$helper->default_form_language = Configuration::get('PS_LANG_DEFAULT');
|
|
$helper->fields_value['ECOMZONE_API_KEY'] = Configuration::get('ECOMZONE_API_KEY');
|
|
|
|
// Add debug console after form
|
|
$form = $helper->generateForm($fields_form);
|
|
$debug_console = '<div class="panel">
|
|
<div class="panel-heading">
|
|
<i class="icon-list"></i> ' . $this->l('Debug Console') . '
|
|
</div>
|
|
<div class="panel-content">
|
|
<pre id="debug-console" style="max-height: 300px; overflow-y: auto; background: #f5f5f5; padding: 10px;">
|
|
' . $this->context->smarty->getTemplateVars('debug_output') . '
|
|
</pre>
|
|
</div>
|
|
</div>';
|
|
|
|
return $form . $debug_console;
|
|
}
|
|
|
|
public function getErrors()
|
|
{
|
|
return $this->errors;
|
|
}
|
|
|
|
public function hookActionProductUpdate($params)
|
|
{
|
|
try {
|
|
$product = $params['product'];
|
|
$productId = $product->id;
|
|
|
|
// Get product reference (SKU)
|
|
$reference = $product->reference;
|
|
|
|
if (!empty($reference)) {
|
|
// Get product data from EcomZone
|
|
$ecomZoneProduct = $this->api->getProduct($reference);
|
|
|
|
if ($ecomZoneProduct && isset($ecomZoneProduct['data'])) {
|
|
// Update stock
|
|
if (isset($ecomZoneProduct['data']['stock'])) {
|
|
StockAvailable::setQuantity($productId, 0, (int)$ecomZoneProduct['data']['stock']);
|
|
}
|
|
|
|
// Update price
|
|
if (isset($ecomZoneProduct['data']['product_price'])) {
|
|
$product->price = (float)$ecomZoneProduct['data']['product_price'];
|
|
$product->update();
|
|
}
|
|
|
|
PrestaShopLogger::addLog(
|
|
sprintf('EcomZone: Updated product %s (ID: %d)', $reference, $productId),
|
|
1, // Notice level
|
|
null,
|
|
'Product',
|
|
$productId,
|
|
true
|
|
);
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
PrestaShopLogger::addLog(
|
|
sprintf('EcomZone: Error updating product %d: %s', $productId, $e->getMessage()),
|
|
3, // Error level
|
|
null,
|
|
'Product',
|
|
$productId,
|
|
true
|
|
);
|
|
}
|
|
}
|
|
|
|
public function cronRefreshProducts()
|
|
{
|
|
try {
|
|
PrestaShopLogger::addLog(
|
|
'EcomZone: Starting scheduled product refresh',
|
|
1,
|
|
null,
|
|
'EcomZone'
|
|
);
|
|
|
|
$catalog = $this->api->getCatalog(1000);
|
|
|
|
if (!isset($catalog['data']) || !is_array($catalog['data'])) {
|
|
throw new Exception('Invalid catalog data received from API');
|
|
}
|
|
|
|
$totalProducts = count($catalog['data']);
|
|
$updatedCount = 0;
|
|
$createdCount = 0;
|
|
$defaultLangId = (int)Configuration::get('PS_LANG_DEFAULT');
|
|
$defaultShopId = (int)Configuration::get('PS_SHOP_DEFAULT');
|
|
|
|
foreach ($catalog['data'] as $ecomZoneProduct) {
|
|
$reference = $ecomZoneProduct['sku'];
|
|
$productId = Product::getIdByReference($reference);
|
|
|
|
try {
|
|
if (!$productId) {
|
|
// Create new product
|
|
$product = new Product();
|
|
$product->reference = $reference;
|
|
$product->name = [$defaultLangId => $ecomZoneProduct['product_name']];
|
|
$product->description = [$defaultLangId => $ecomZoneProduct['long_description'] ?? ''];
|
|
$product->description_short = [$defaultLangId => $ecomZoneProduct['description'] ?? ''];
|
|
$product->price = (float)$ecomZoneProduct['product_price'];
|
|
$product->active = true; // Make sure product is active
|
|
$product->visibility = 'both'; // Show in catalog and search
|
|
$product->available_for_order = true;
|
|
$product->show_price = true;
|
|
$product->id_shop_default = $defaultShopId;
|
|
$product->id_category_default = 2; // Home category
|
|
|
|
// Add to all shops if multistore
|
|
$product->id_shop_list = Shop::getShops(true, null, true);
|
|
|
|
if ($product->add()) {
|
|
$productId = $product->id;
|
|
|
|
// Associate with Home category
|
|
$product->addToCategories([2]);
|
|
|
|
// Set stock
|
|
StockAvailable::setQuantity($productId, 0, (int)$ecomZoneProduct['stock']);
|
|
|
|
// Set stock settings
|
|
StockAvailable::setProductOutOfStock($productId, 1); // Allow orders when out of stock
|
|
|
|
// Update shop association
|
|
$product->updateCategories($product->id_shop_list);
|
|
|
|
// Add minimum quantity of 1
|
|
$product->minimal_quantity = 1;
|
|
$product->update();
|
|
|
|
$createdCount++;
|
|
|
|
// Download and set product image if available
|
|
if (!empty($ecomZoneProduct['image'])) {
|
|
$this->importProductImage($product, $ecomZoneProduct['image']);
|
|
}
|
|
}
|
|
} else {
|
|
// Update existing product
|
|
$product = new Product($productId);
|
|
$product->name[$defaultLangId] = $ecomZoneProduct['product_name'];
|
|
$product->description[$defaultLangId] = $ecomZoneProduct['long_description'] ?? '';
|
|
$product->description_short[$defaultLangId] = $ecomZoneProduct['description'] ?? '';
|
|
$product->price = (float)$ecomZoneProduct['product_price'];
|
|
$product->active = true; // Make sure product is active
|
|
$product->visibility = 'both';
|
|
$product->available_for_order = true;
|
|
$product->show_price = true;
|
|
$product->update();
|
|
|
|
StockAvailable::setQuantity($productId, 0, (int)$ecomZoneProduct['stock']);
|
|
$updatedCount++;
|
|
}
|
|
|
|
// Clear cache for this product
|
|
$this->clearProductCache($productId);
|
|
|
|
} catch (Exception $e) {
|
|
PrestaShopLogger::addLog(
|
|
sprintf('EcomZone: Error processing product %s: %s', $reference, $e->getMessage()),
|
|
3,
|
|
null,
|
|
'Product'
|
|
);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Clear cache after all products are processed
|
|
$this->clearCache();
|
|
|
|
PrestaShopLogger::addLog(
|
|
sprintf('EcomZone: Created %d new products, Updated %d existing products',
|
|
$createdCount,
|
|
$updatedCount
|
|
),
|
|
1,
|
|
null,
|
|
'EcomZone'
|
|
);
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => sprintf('Created %d new products, Updated %d existing products', $createdCount, $updatedCount),
|
|
'total' => $totalProducts,
|
|
'created' => $createdCount,
|
|
'updated' => $updatedCount
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
PrestaShopLogger::addLog(
|
|
'EcomZone Error: ' . $e->getMessage(),
|
|
3,
|
|
null,
|
|
'EcomZone'
|
|
);
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private function importProductImage($product, $imageUrl)
|
|
{
|
|
try {
|
|
$tempFile = tempnam(_PS_TMP_IMG_DIR_, 'import');
|
|
if (@file_put_contents($tempFile, @file_get_contents($imageUrl))) {
|
|
$product->deleteImages(); // Remove existing images
|
|
$image = new Image();
|
|
$image->id_product = $product->id;
|
|
$image->position = 1;
|
|
$image->cover = true;
|
|
|
|
if ($image->add()) {
|
|
return (bool)@rename($tempFile, _PS_PROD_IMG_DIR_ . $image->getImgPath() . '.jpg');
|
|
}
|
|
}
|
|
return false;
|
|
} catch (Exception $e) {
|
|
PrestaShopLogger::addLog(
|
|
sprintf('EcomZone: Error importing image for product %d: %s', $product->id, $e->getMessage()),
|
|
3,
|
|
null,
|
|
'Product'
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function hookActionCronJob()
|
|
{
|
|
$lastRun = (int)Configuration::get('ECOMZONE_LAST_CRON');
|
|
$currentTime = time();
|
|
|
|
// Check if 2 hours have passed (7200 seconds)
|
|
if (($currentTime - $lastRun) >= 7200) {
|
|
$this->cronRefreshProducts();
|
|
Configuration::updateValue('ECOMZONE_LAST_CRON', $currentTime);
|
|
}
|
|
}
|
|
|
|
public function uninstall()
|
|
{
|
|
return parent::uninstall() &&
|
|
Configuration::deleteByName('ECOMZONE_API_KEY') &&
|
|
Configuration::deleteByName('ECOMZONE_API_URL') &&
|
|
Configuration::deleteByName('ECOMZONE_LAST_CRON');
|
|
}
|
|
|
|
private function clearProductCache($productId)
|
|
{
|
|
try {
|
|
Tools::clearSmartyCache();
|
|
Tools::clearXMLCache();
|
|
Media::clearCache();
|
|
|
|
$sql = 'DELETE FROM ' . _DB_PREFIX_ . 'smarty_cache
|
|
WHERE template LIKE "%product%"';
|
|
Db::getInstance()->execute($sql);
|
|
|
|
} catch (Exception $e) {
|
|
PrestaShopLogger::addLog(
|
|
sprintf('EcomZone: Error clearing cache for product %d: %s', $productId, $e->getMessage()),
|
|
3,
|
|
null,
|
|
'Product'
|
|
);
|
|
}
|
|
}
|
|
|
|
private function clearCache()
|
|
{
|
|
try {
|
|
Tools::clearSmartyCache();
|
|
Tools::clearXMLCache();
|
|
Media::clearCache();
|
|
Tools::generateIndex();
|
|
} catch (Exception $e) {
|
|
PrestaShopLogger::addLog(
|
|
'EcomZone: Error clearing cache: ' . $e->getMessage(),
|
|
3,
|
|
null,
|
|
'EcomZone'
|
|
);
|
|
}
|
|
}
|
|
}
|