|
@@ -13,10 +13,13 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
`sync_enabled` TINYINT(1) NOT NULL DEFAULT 0,
|
|
`sync_enabled` TINYINT(1) NOT NULL DEFAULT 0,
|
|
|
`condition_uuid` VARCHAR(64) NOT NULL DEFAULT '',
|
|
`condition_uuid` VARCHAR(64) NOT NULL DEFAULT '',
|
|
|
`reverb_category_uuid` VARCHAR(64) NOT NULL DEFAULT '',
|
|
`reverb_category_uuid` VARCHAR(64) NOT NULL DEFAULT '',
|
|
|
|
|
+ `handmade` TINYINT(1) NOT NULL DEFAULT 0,
|
|
|
|
|
+ `upc_does_not_apply` TINYINT(1) NOT NULL DEFAULT 1,
|
|
|
`last_synced_at` DATETIME NULL DEFAULT NULL,
|
|
`last_synced_at` DATETIME NULL DEFAULT NULL,
|
|
|
PRIMARY KEY (`product_id`)
|
|
PRIMARY KEY (`product_id`)
|
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
|
|
");
|
|
");
|
|
|
|
|
+ $this->migrate();
|
|
|
|
|
|
|
|
$this->db->query("
|
|
$this->db->query("
|
|
|
CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "reverb_sync_log` (
|
|
CREATE TABLE IF NOT EXISTS `" . DB_PREFIX . "reverb_sync_log` (
|
|
@@ -38,11 +41,32 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
$this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "reverb_sync_log`");
|
|
$this->db->query("DROP TABLE IF EXISTS `" . DB_PREFIX . "reverb_sync_log`");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public function migrate() {
|
|
|
|
|
+ static $done = false;
|
|
|
|
|
+ if ($done) return;
|
|
|
|
|
+ $done = true;
|
|
|
|
|
+ $this->addColumnIfMissing(DB_PREFIX . 'reverb_product_map', 'handmade', 'TINYINT(1) NOT NULL DEFAULT 0');
|
|
|
|
|
+ $this->addColumnIfMissing(DB_PREFIX . 'reverb_product_map', 'upc_does_not_apply', 'TINYINT(1) NOT NULL DEFAULT 1');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function addColumnIfMissing($table, $column, $definition) {
|
|
|
|
|
+ $r = $this->db->query("
|
|
|
|
|
+ SELECT COUNT(*) AS cnt FROM information_schema.COLUMNS
|
|
|
|
|
+ WHERE TABLE_SCHEMA = DATABASE()
|
|
|
|
|
+ AND TABLE_NAME = '" . $this->db->escape($table) . "'
|
|
|
|
|
+ AND COLUMN_NAME = '" . $this->db->escape($column) . "'
|
|
|
|
|
+ ");
|
|
|
|
|
+ if (empty($r->row['cnt'])) {
|
|
|
|
|
+ $this->db->query("ALTER TABLE `" . $table . "` ADD COLUMN `" . $column . "` " . $definition);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// -------------------------------------------------------------------------
|
|
// -------------------------------------------------------------------------
|
|
|
// Product map CRUD
|
|
// Product map CRUD
|
|
|
// -------------------------------------------------------------------------
|
|
// -------------------------------------------------------------------------
|
|
|
|
|
|
|
|
public function getProductMap($product_id) {
|
|
public function getProductMap($product_id) {
|
|
|
|
|
+ $this->migrate();
|
|
|
$query = $this->db->query("
|
|
$query = $this->db->query("
|
|
|
SELECT * FROM `" . DB_PREFIX . "reverb_product_map`
|
|
SELECT * FROM `" . DB_PREFIX . "reverb_product_map`
|
|
|
WHERE `product_id` = '" . (int)$product_id . "'
|
|
WHERE `product_id` = '" . (int)$product_id . "'
|
|
@@ -51,6 +75,7 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function saveProductMap($product_id, array $data) {
|
|
public function saveProductMap($product_id, array $data) {
|
|
|
|
|
+ $this->migrate();
|
|
|
$existing = $this->getProductMap($product_id);
|
|
$existing = $this->getProductMap($product_id);
|
|
|
|
|
|
|
|
$sync_enabled = isset($data['sync_enabled']) ? (int)(bool)$data['sync_enabled'] : 0;
|
|
$sync_enabled = isset($data['sync_enabled']) ? (int)(bool)$data['sync_enabled'] : 0;
|
|
@@ -58,13 +83,17 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
$reverb_category_uuid = isset($data['reverb_category_uuid']) ? $this->db->escape($data['reverb_category_uuid']) : '';
|
|
$reverb_category_uuid = isset($data['reverb_category_uuid']) ? $this->db->escape($data['reverb_category_uuid']) : '';
|
|
|
$reverb_listing_id = isset($data['reverb_listing_id']) ? $this->db->escape($data['reverb_listing_id']) : '';
|
|
$reverb_listing_id = isset($data['reverb_listing_id']) ? $this->db->escape($data['reverb_listing_id']) : '';
|
|
|
$last_synced_at = isset($data['last_synced_at']) ? "'" . $this->db->escape($data['last_synced_at']) . "'" : 'NULL';
|
|
$last_synced_at = isset($data['last_synced_at']) ? "'" . $this->db->escape($data['last_synced_at']) . "'" : 'NULL';
|
|
|
|
|
+ $handmade = isset($data['handmade']) ? (int)(bool)$data['handmade'] : 0;
|
|
|
|
|
+ $upc_does_not_apply = isset($data['upc_does_not_apply']) ? (int)(bool)$data['upc_does_not_apply'] : 1;
|
|
|
|
|
|
|
|
if ($existing) {
|
|
if ($existing) {
|
|
|
$this->db->query("
|
|
$this->db->query("
|
|
|
UPDATE `" . DB_PREFIX . "reverb_product_map`
|
|
UPDATE `" . DB_PREFIX . "reverb_product_map`
|
|
|
SET `sync_enabled` = $sync_enabled,
|
|
SET `sync_enabled` = $sync_enabled,
|
|
|
`condition_uuid` = '$condition_uuid',
|
|
`condition_uuid` = '$condition_uuid',
|
|
|
- `reverb_category_uuid` = '$reverb_category_uuid'"
|
|
|
|
|
|
|
+ `reverb_category_uuid` = '$reverb_category_uuid',
|
|
|
|
|
+ `handmade` = $handmade,
|
|
|
|
|
+ `upc_does_not_apply` = $upc_does_not_apply"
|
|
|
. (!empty($reverb_listing_id) ? ", `reverb_listing_id` = '$reverb_listing_id'" : '')
|
|
. (!empty($reverb_listing_id) ? ", `reverb_listing_id` = '$reverb_listing_id'" : '')
|
|
|
. (isset($data['last_synced_at']) ? ", `last_synced_at` = $last_synced_at" : '')
|
|
. (isset($data['last_synced_at']) ? ", `last_synced_at` = $last_synced_at" : '')
|
|
|
. " WHERE `product_id` = '" . (int)$product_id . "'"
|
|
. " WHERE `product_id` = '" . (int)$product_id . "'"
|
|
@@ -72,13 +101,16 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
} else {
|
|
} else {
|
|
|
$this->db->query("
|
|
$this->db->query("
|
|
|
INSERT INTO `" . DB_PREFIX . "reverb_product_map`
|
|
INSERT INTO `" . DB_PREFIX . "reverb_product_map`
|
|
|
- (`product_id`, `sync_enabled`, `condition_uuid`, `reverb_category_uuid`, `reverb_listing_id`, `last_synced_at`)
|
|
|
|
|
|
|
+ (`product_id`, `sync_enabled`, `condition_uuid`, `reverb_category_uuid`,
|
|
|
|
|
+ `reverb_listing_id`, `handmade`, `upc_does_not_apply`, `last_synced_at`)
|
|
|
VALUES (
|
|
VALUES (
|
|
|
'" . (int)$product_id . "',
|
|
'" . (int)$product_id . "',
|
|
|
$sync_enabled,
|
|
$sync_enabled,
|
|
|
'$condition_uuid',
|
|
'$condition_uuid',
|
|
|
'$reverb_category_uuid',
|
|
'$reverb_category_uuid',
|
|
|
'$reverb_listing_id',
|
|
'$reverb_listing_id',
|
|
|
|
|
+ $handmade,
|
|
|
|
|
+ $upc_does_not_apply,
|
|
|
$last_synced_at
|
|
$last_synced_at
|
|
|
)
|
|
)
|
|
|
");
|
|
");
|
|
@@ -109,7 +141,9 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
|
|
|
|
|
$query = $this->db->query("
|
|
$query = $this->db->query("
|
|
|
SELECT p.product_id, pd.name, pd.description, p.model, p.price, p.quantity, p.image,
|
|
SELECT p.product_id, pd.name, pd.description, p.model, p.price, p.quantity, p.image,
|
|
|
- r.reverb_listing_id, r.condition_uuid, r.reverb_category_uuid
|
|
|
|
|
|
|
+ r.reverb_listing_id, r.condition_uuid, r.reverb_category_uuid,
|
|
|
|
|
+ r.handmade, r.upc_does_not_apply,
|
|
|
|
|
+ m.name AS manufacturer
|
|
|
FROM `" . DB_PREFIX . "product` p
|
|
FROM `" . DB_PREFIX . "product` p
|
|
|
INNER JOIN `" . DB_PREFIX . "product_description` pd
|
|
INNER JOIN `" . DB_PREFIX . "product_description` pd
|
|
|
ON pd.product_id = p.product_id AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'
|
|
ON pd.product_id = p.product_id AND pd.language_id = '" . (int)$this->config->get('config_language_id') . "'
|
|
@@ -117,6 +151,8 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
ON ptc.product_id = p.product_id AND ptc.category_id IN ($ids)
|
|
ON ptc.product_id = p.product_id AND ptc.category_id IN ($ids)
|
|
|
INNER JOIN `" . DB_PREFIX . "reverb_product_map` r
|
|
INNER JOIN `" . DB_PREFIX . "reverb_product_map` r
|
|
|
ON r.product_id = p.product_id AND r.sync_enabled = 1
|
|
ON r.product_id = p.product_id AND r.sync_enabled = 1
|
|
|
|
|
+ LEFT JOIN `" . DB_PREFIX . "manufacturer` m
|
|
|
|
|
+ ON m.manufacturer_id = p.manufacturer_id
|
|
|
WHERE p.status = 1
|
|
WHERE p.status = 1
|
|
|
GROUP BY p.product_id
|
|
GROUP BY p.product_id
|
|
|
");
|
|
");
|
|
@@ -265,6 +301,29 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
$api = $this->getApi();
|
|
$api = $this->getApi();
|
|
|
$payload = ProductMapper::toReverb($product, $reverb_data, $settings);
|
|
$payload = ProductMapper::toReverb($product, $reverb_data, $settings);
|
|
|
|
|
|
|
|
|
|
+ // Photos are plain URL strings sent inside the listing payload (Reverb API v3)
|
|
|
|
|
+ $store_url = $settings['store_url'] ?? '';
|
|
|
|
|
+ $images = $this->getProductImages($product['product_id']);
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($store_url) && !empty($images)) {
|
|
|
|
|
+ $base = rtrim($store_url, '/') . '/image/';
|
|
|
|
|
+ $photos = [];
|
|
|
|
|
+ foreach ($images as $path) {
|
|
|
|
|
+ if (!empty($path)) {
|
|
|
|
|
+ $photos[] = $base . ltrim($path, '/');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!empty($photos)) {
|
|
|
|
|
+ $payload['photos'] = $photos;
|
|
|
|
|
+ $payload['publish'] = true;
|
|
|
|
|
+ $this->log($product['product_id'], 'push', 'success', 'Including ' . count($photos) . ' photo(s): ' . implode(', ', $photos));
|
|
|
|
|
+ }
|
|
|
|
|
+ } elseif (empty($store_url)) {
|
|
|
|
|
+ $this->log($product['product_id'], 'push', 'error', 'Photo upload skipped: store URL not configured (check System > Settings > Store URL)');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $this->log($product['product_id'], 'push', 'error', 'No images found for this product in OpenCart.');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$listing_id = !empty($reverb_data['reverb_listing_id']) ? $reverb_data['reverb_listing_id'] : null;
|
|
$listing_id = !empty($reverb_data['reverb_listing_id']) ? $reverb_data['reverb_listing_id'] : null;
|
|
|
|
|
|
|
|
if ($listing_id) {
|
|
if ($listing_id) {
|
|
@@ -278,18 +337,6 @@ class ModelExtensionModuleReverb extends Model {
|
|
|
$this->updateListingId($product['product_id'], $listing_id);
|
|
$this->updateListingId($product['product_id'], $listing_id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Upload images
|
|
|
|
|
- $images = $this->getProductImages($product['product_id']);
|
|
|
|
|
- $store_url = $settings['store_url'] ?? '';
|
|
|
|
|
- foreach (ProductMapper::buildPhotoPayloads($images, $store_url) as $photo) {
|
|
|
|
|
- try {
|
|
|
|
|
- $api->uploadPhoto($listing_id, $photo['image_url']);
|
|
|
|
|
- } catch (Exception $e) {
|
|
|
|
|
- // Non-fatal: log but continue
|
|
|
|
|
- $this->log($product['product_id'], 'push', 'error', 'Photo upload failed: ' . $e->getMessage());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
return $listing_id;
|
|
return $listing_id;
|
|
|
}
|
|
}
|
|
|
|
|
|