diff --git a/ChangeLog.md b/ChangeLog.md index 92bca4c..fd83f84 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # ChangeLog +## 4.0.78.0 +- Ajout la gestion des actions de restauration et de suppression de produit dans les webhooks +- Delie les produits pour les produits distants standard transformés en variants ou les variants qui auraient été supprimés + ## 4.0.77.0 - Fix requête sql qui supprimer les liens en double pour un même ID distant sur les produits diff --git a/class/business/eCommercePendingWebHook.class.php b/class/business/eCommercePendingWebHook.class.php index 866e393..d1d97cc 100755 --- a/class/business/eCommercePendingWebHook.class.php +++ b/class/business/eCommercePendingWebHook.class.php @@ -165,12 +165,23 @@ public function check() if (!($this->site_id > 0) || empty($this->delivery_id) || empty($this->webhook_id) || empty($this->webhook_topic) || empty($this->webhook_resource) || empty($this->webhook_event) || empty($this->webhook_data) || empty($this->webhook_signature) || empty($this->webhook_source) ) { + if (!($this->site_id > 0)) $this->errors[] = 'Bad site_id'; + if (empty($this->delivery_id)) $this->errors[] = 'Bad delivery_id'; + if (empty($this->webhook_id)) $this->errors[] = 'Bad webhook_id'; + if (empty($this->webhook_topic)) $this->errors[] = 'Bad webhook_topic'; + if (empty($this->webhook_resource)) $this->errors[] = 'Bad webhook_resource'; + if (empty($this->webhook_event)) $this->errors[] = 'Bad webhook_event'; + if (empty($this->webhook_data)) $this->errors[] = 'Bad webhook_data'; + if (empty($this->webhook_signature)) $this->errors[] = 'Bad webhook_signature'; + if (empty($this->webhook_source)) $this->errors[] = 'Bad webhook_source'; + // Bad values return -1; } $site = $this->getSite($this->site_id); if (!is_object($site)) { + $this->errors[] = "Site {$this->site_id} not found."; // Bad values return -1; } @@ -481,12 +492,18 @@ public function synchronize($site_id = 0, $webhook_topic = '', $webhook_resource // Product if ($webhook_resource == 'product') { - if ($webhook_event == 'created' || $webhook_event == 'updated') { + if ($webhook_event == 'created' || $webhook_event == 'updated' || $webhook_event == 'restored') { $result = $synchro->synchronizeProductFromData($data); if ($result == 0 && !empty($synchro->warnings)) { $this->warnings = array_merge($synchro->warnings, $this->warnings); return -2; } + } elseif ($webhook_event == 'deleted') { + $result = $synchro->deleteProductLink($data->id); + if ($result == 0 && !empty($synchro->warnings)) { + $this->warnings = array_merge($synchro->warnings, $this->warnings); + return -2; + } } } diff --git a/class/business/eCommerceSynchro.class.php b/class/business/eCommerceSynchro.class.php index 356732d..1e0399b 100755 --- a/class/business/eCommerceSynchro.class.php +++ b/class/business/eCommerceSynchro.class.php @@ -2823,6 +2823,68 @@ public function synchronizeProductFromData($raw_data) return -1; } + /** + * Delete product link + * + * @param string $remote_id Remote ID + * @return int >0 if OK, <0 if KO + */ + public function deleteProductLink($remote_id) + { + dol_syslog(__METHOD__ . " remote_id=$remote_id", LOG_DEBUG); + + $this->error = ''; + $this->errors = array(); + $error = 0; + + if (empty($remote_id)) { + $this->langs->load('errors'); + $this->errors[] = $this->langs->trans('ErrorBadParameters') . '; remote_id=' . $remote_id; + $error++; + } + + if (!$error) { + $this->db->begin(); + + try { + // Check if product already synchronized + $this->initECommerceProduct(); + $result = $this->eCommerceProduct->fetchByRemoteId($remote_id, $this->eCommerceSite->id); + if ($result < 0 && !empty($this->eCommerceProduct->error)) { + $this->errors[] = $this->langs->trans('ECommerceErrorFetchProductLinkByRemoteId', $remote_id, $this->eCommerceSite->id); + $this->errors[] = $this->eCommerceProduct->error; + $error++; + } + + // Delete the link of the synchronization + if (!$error && $this->eCommerceProduct->id > 0) { + $result = $this->eCommerceProduct->delete($this->user); + if ($result < 0) { + $this->errors[] = $this->langs->trans('ECommerceErrorDeleteProductLink', $remote_id); + $this->errors = array_merge($this->errors, $this->eCommerceProduct->errors); + $error++; + } + } + } catch (Exception $e) { + $this->errors[] = $e->getMessage(); + $error++; + } + + // Commit / rollback actions + if ($error) { + $this->db->rollback(); + } else { + $this->db->commit(); + } + } + + if ($error) { + return -1; + } else { + return 1; + } + } + /** * Check if order is too old (create date) in dolibarr from the remote order data * @@ -3358,13 +3420,46 @@ public function synchronizeProduct($product_data, $object_origin = null) $this->errors[] = $this->langs->trans('ECommerceErrorProductRefMandatory'); $error++; } else { + // Unlink product of remote product removed + if (!empty($product_data['variations'])) { + $variations = array(); + foreach ($product_data['variations']['list'] as $v) { + $variations[] = $this->db->escape($v); + } + + // Get all product to unlink (product variations removed) + $sql = "SELECT remote_id FROM " . MAIN_DB_PREFIX . "ecommerce_product" . + " WHERE fk_site = " . $this->eCommerceSite->id . + " AND (remote_id = '" . $this->db->escape($product_data['variations']['parent_remote_id']) . "'" . + " OR (remote_id LIKE '" . $this->db->escape($product_data['variations']['filter']) . "'" . + " AND remote_id NOT IN ('" . implode("','", $variations) . "')))"; + + $resql = $this->db->query($sql); + if (!$resql) { + dol_syslog(__METHOD__ . ' SQL: ' . $sql . '; Errors: ' . $this->db->lasterror(), LOG_ERR); + $errors[] = $this->langs->trans('ECommerceErrorWhenGetProductToUnlink', $this->eCommerceSite->name, json_encode($product_data['variations'])); + $errors[] = $this->db->lasterror(); + $error++; + } else { + while($obj = $this->db->fetch_object($resql)) { + $result = $this->unlinkProduct($this->eCommerceSite->id, 0, $obj->remote_id); + if ($result < 0) { + $error++; + break; + } + } + } + } + // Check if product already synchronized - $this->initECommerceProduct(); - $result = $this->eCommerceProduct->fetchByRemoteId($product_data['remote_id'], $this->eCommerceSite->id); - if ($result < 0 && !empty($this->eCommerceProduct->error)) { - $this->errors[] = $this->langs->trans('ECommerceErrorFetchProductLinkByRemoteId', $product_data['remote_id'], $this->eCommerceSite->id); - $this->errors[] = $this->eCommerceProduct->error; - $error++; + if (!$error) { + $this->initECommerceProduct(); + $result = $this->eCommerceProduct->fetchByRemoteId($product_data['remote_id'], $this->eCommerceSite->id); + if ($result < 0 && !empty($this->eCommerceProduct->error)) { + $this->errors[] = $this->langs->trans('ECommerceErrorFetchProductLinkByRemoteId', $product_data['remote_id'], $this->eCommerceSite->id); + $this->errors[] = $this->eCommerceProduct->error; + $error++; + } } // Fetch product @@ -3734,6 +3829,80 @@ public function synchronizeProduct($product_data, $object_origin = null) } } + /** + * Unlink product + * + * @param int $site_id Site ID + * @param int $product_id Product ID + * @param string $remote_id Remote ID + * @return int <0 if KO, >0 if OK + */ + public function unlinkProduct($site_id, $product_id = 0, $remote_id = '') + { + dol_syslog(__METHOD__ . " site_id=$site_id, product_id=$product_id, remote_id=$remote_id", LOG_DEBUG); + global $user; + + $error = 0; + $this->db->begin(); + + // Delete link to ecommerce + $eCommerceProduct = new eCommerceProduct($this->db); + if ($product_id > 0 && $eCommerceProduct->fetchByProductId($product_id, $site_id) > 0) { + if ($eCommerceProduct->delete($user) < 0) { + $errors[] = $this->langs->trans('ECommerceErrorWhenUnlinkProductByProductId', $this->eCommerceSite->name, $product_id); + $error++; + } + } elseif ($remote_id > 0 && $eCommerceProduct->fetchByRemoteId($remote_id, $site_id) > 0) { + if ($eCommerceProduct->delete($user) < 0) { + $errors[] = $this->langs->trans('ECommerceErrorWhenUnlinkProductByRemoteId', $this->eCommerceSite->name, $remote_id); + $error++; + } + } else { + $errors[] = $this->langs->trans('ECommerceErrorWhenUnlinkProduct', $this->eCommerceSite->name, $product_id, $remote_id); + $error++; + } + + // Delete all categories of the ecommerce + if (!$error) { + require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; + $eCommerceSite = new eCommerceSite($this->db); + $product = new Product($this->db); + if ($eCommerceSite->fetch($site_id) > 0 && $product->fetch($eCommerceProduct->fk_product) > 0) { + require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; + $cat = new Categorie($this->db); + $cat_root = $eCommerceSite->fk_cat_product; + $all_cat_full_arbo = $cat->get_full_arbo('product'); + $cats_full_arbo = array(); + foreach ($all_cat_full_arbo as $category) { + $cats_full_arbo[$category['id']] = $category['fullpath']; + } + $categories = $cat->containing($product->id, 'product', 'id'); + foreach ($categories as $cat_id) { + if (isset($cats_full_arbo[$cat_id]) && + (preg_match("/^{$cat_root}$/", $cats_full_arbo[$cat_id]) || preg_match("/^{$cat_root}_/", $cats_full_arbo[$cat_id]) || + preg_match("/_{$cat_root}_/", $cats_full_arbo[$cat_id]) || preg_match("/_{$cat_root}$/", $cats_full_arbo[$cat_id]) + ) + ) { + if ($cat->fetch($cat_id) > 0) { + if ($cat->del_type($product, 'product') < 0) { + $errors[] = $this->langs->trans('ECommerceErrorWhenUnlinkProductCategory', $this->eCommerceSite->name, $product->id, $cat_id); + $error++; + } + } + } + } + } + } + + if ($error) { + $this->db->rollback(); + return -1; + } else { + $this->db->commit(); + return 1; + } + } + /** * Synchronize a list of order remote id * diff --git a/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php b/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php index e004bd5..564b32a 100755 --- a/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php +++ b/class/data/woocommerce/eCommerceRemoteAccessWoocommerce.class.php @@ -987,6 +987,31 @@ public function convertProductDataIntoProcessedData($remote_data, $parent_remote $date_on_sale_to = ''; } + // Manage the variations products removed + $variations = array(); + if (!$product_variation_mode_all_to_one) { + $variations_list = array(); + if (!empty($parent_remote_data->variations)) { + foreach ($parent_remote_data->variations as $v) { + $variations_list[] = $parent_id . '|' . $v; + } + $variations = array( + 'parent_remote_id' => $parent_id, + 'filter' => $parent_id . '|%', + 'list' => $variations_list, + ); + } elseif (!empty($remote_data->variations)) { + foreach ($remote_data->variations as $v) { + $variations_list[] = $remote_data->id . '|' . $v; + } + $variations = array( + 'parent_remote_id' => $remote_data->id, + 'filter' => $remote_data->id . '|%', + 'list' => $variations_list, + ); + } + } + $product = [ 'create_date' => strtotime($remote_data->date_created), 'remote_id' => ($isVariation ? ($product_variation_mode_all_to_one ? $parent_id . '|' . implode('|', $parent_remote_data->variations) : $parent_id . '|' . $remote_data->id) : $remote_data->id), @@ -1008,6 +1033,7 @@ public function convertProductDataIntoProcessedData($remote_data, $parent_remote // Stock 'stock_qty' => $remote_data->stock_quantity, 'is_in_stock' => $remote_data->in_stock, // not used + 'variations' => $variations, 'extrafields' => [ "ecommerceng_wc_regular_price_{$this->site->id}_{$conf->entity}" => $remote_data->regular_price, "ecommerceng_wc_sale_price_{$this->site->id}_{$conf->entity}" => $remote_data->sale_price, diff --git a/core/modules/modECommerceNg.class.php b/core/modules/modECommerceNg.class.php index 6045456..35ee7ce 100644 --- a/core/modules/modECommerceNg.class.php +++ b/core/modules/modECommerceNg.class.php @@ -61,7 +61,7 @@ function __construct($db) $this->editor_url = 'http://www.open-dsi.fr'; // Possible values for version are: 'development', 'experimental', 'dolibarr' or version - $this->version = '4.0.77'; + $this->version = '4.0.78'; // Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase) $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); // Where to store the module in setup page (0=common,1=interface,2=others,3=very specific) diff --git a/langs/en_US/ecommerce.lang b/langs/en_US/ecommerce.lang index 5c7470e..3c47f1c 100755 --- a/langs/en_US/ecommerce.lang +++ b/langs/en_US/ecommerce.lang @@ -514,6 +514,12 @@ ECommerceErrorWebHooksStatusNotActivated = Le crochet WEB (WebHook) " ECommerceErrorCheckWebHooksStatus = Erreur lors de la vérification des crochet WEB (WebHook) du site "%s" (ID: %s) ECommerceErrorWhenCheckingWebHooksStatus = Erreur lors de la vérification des crochet WEB (WebHook) ECommerceErrorTooManyProductLinkToTheRemoteID = Trop de produits (IDs: %s) liés au même produit distant (ID distant: %s, Site ID: %s) +ECommerceErrorDeleteProductLink = Erreur lors de la suppression du lien avec produit distant (ID distant: %s) +ECommerceErrorWhenGetProductToUnlink = Erreur lors de la récupération des produits à délier (Site: %s; Données: %s) +ECommerceErrorWhenUnlinkProductByProductId = Erreur lors de la suppression du lien du produit (Site: %s; ID: %s) +ECommerceErrorWhenUnlinkProductByRemoteId = Erreur lors de la suppression du lien du produit (Site: %s; ID distant: %s) +ECommerceErrorWhenUnlinkProduct = Erreur lors de la suppression du lien du produit (Site: %s; ID: %s; ID distant: %s) +ECommerceErrorWhenUnlinkProductCategory = Erreur lors de la suppression de la catégorie sur le produit (Site: %s; ID: %s; Catégorie ID: %s) ############################################################################################ # Warnings diff --git a/langs/fr_FR/ecommerce.lang b/langs/fr_FR/ecommerce.lang index ab0a7e2..c0fdf19 100644 --- a/langs/fr_FR/ecommerce.lang +++ b/langs/fr_FR/ecommerce.lang @@ -504,6 +504,12 @@ ECommerceErrorWebHooksStatusNotActivated = Le crochet WEB (WebHook) " ECommerceErrorCheckWebHooksStatus = Erreur lors de la vérification des crochet WEB (WebHook) du site "%s" (ID: %s) ECommerceErrorWhenCheckingWebHooksStatus = Erreur lors de la vérification des crochet WEB (WebHook) ECommerceErrorTooManyProductLinkToTheRemoteID = Trop de produits (IDs: %s) liés au même produit distant (ID distant: %s, Site ID: %s) +ECommerceErrorDeleteProductLink = Erreur lors de la suppression du lien avec produit distant (ID distant: %s) +ECommerceErrorWhenGetProductToUnlink = Erreur lors de la récupération des produits à délier (Site: %s; Données: %s) +ECommerceErrorWhenUnlinkProductByProductId = Erreur lors de la suppression du lien du produit (Site: %s; ID: %s) +ECommerceErrorWhenUnlinkProductByRemoteId = Erreur lors de la suppression du lien du produit (Site: %s; ID distant: %s) +ECommerceErrorWhenUnlinkProduct = Erreur lors de la suppression du lien du produit (Site: %s; ID: %s; ID distant: %s) +ECommerceErrorWhenUnlinkProductCategory = Erreur lors de la suppression de la catégorie sur le produit (Site: %s; ID: %s; Catégorie ID: %s) ############################################################################################ # Warnings diff --git a/webhooks.php b/webhooks.php index 6193178..6c487bf 100644 --- a/webhooks.php +++ b/webhooks.php @@ -29,6 +29,7 @@ if (! $res && file_exists("../main.inc.php")) $res=@include '../main.inc.php'; // to work if your module directory is into a subdir of root htdocs directory if (! $res && file_exists("../../main.inc.php")) $res=@include '../../main.inc.php'; // to work if your module directory is into a subdir of root htdocs directory if (! $res) die("Include of main fails"); +require_once DOL_DOCUMENT_ROOT . '/core/lib/functions.lib.php'; dol_include_once("/ecommerceng/class/business/eCommercePendingWebHook.class.php"); /*dol_syslog('_SERVER : ' . json_encode($_SERVER), LOG_ERR); @@ -45,6 +46,7 @@ $site_id = GETPOST('ecommerce_id', 'int'); if (!($site_id > 0)) { // Bad values + dol_syslog("webhooks.php - Error bad values: ecommerce_id", LOG_ERR); http_response_code(400); die(); } @@ -71,10 +73,12 @@ $result = $webhook->check(); if ($result == -1) { // Bad values + dol_syslog("webhooks.php - Error bad values: " . $webhook->errorsToString(), LOG_ERR); http_response_code(400); die(); } elseif ($result == -2) { // Unauthorized + dol_syslog("webhooks.php - Error check signature failed", LOG_ERR); http_response_code(401); die(); } @@ -82,6 +86,7 @@ $result = $webhook->create(); if ($result < 0) { // Error + dol_syslog("webhooks.php - Error create webhook: " . $webhook->errorsToString(), LOG_ERR); http_response_code(500); die(); }