namespace OS\WC_StockXL\API; use OS\WC_StockXL\Includes\StockXL_Image_Logger; defined("ABSPATH") || exit; if (!class_exists("\\OS\\WC_StockXL\\API\\StockXL")) { require_once(ABSPATH . "wp-admin" . "/includes/image.php"); require_once(ABSPATH . "wp-admin" . "/includes/file.php"); require_once(ABSPATH . "wp-admin" . "/includes/media.php"); class StockXL { const API_URL = "https://api.xlprog.nc/stockxl/v2"; /** @var array */ private $options; /** @var array */ private $order_statuses; /** @var string */ private $api_url; /** @var string */ private $api_user; /** @var string */ private $api_password; /** @var string */ private $api_customer_id; /** @var string */ private $api_discount_id; /** @var string */ private $api_shipping_id; /** @var string */ public $api_photos_url; /** @var string */ private $only_drive; /** @var string */ private $http_requests_frequency; /** @var string */ private $sync_manufacturers; /** @var string */ private $cron_tasks; /** @var string */ private $categories_type; /** @var string */ private $product_attributes; /** @var array */ public $photos = []; /** @var array */ private $stop_words; /** @var \WC_Logger|null */ public $logger; /** @var StockXL_Image_Logger|null */ public $image_logger; public function __construct() { $options = get_option("woocommerce_wc-stockxl_settings"); $this->options = $options; $this->api_url = !empty($options["api_url"]) ? $options["api_url"] : self::API_URL; $this->api_user = !empty($options["api_user"]) ? $options["api_user"] : ""; $this->api_password = !empty($options["api_password"]) ? $options["api_password"] : ""; $this->api_customer_id = !empty($options["api_customer_default_id"]) ? trim($options["api_customer_default_id"]) : "0"; $this->api_discount_id = !empty($options["api_discount_item_id"]) ? trim($options["api_discount_item_id"]) : ""; $this->api_shipping_id = !empty($options["api_shipping_item_id"]) ? trim($options["api_shipping_item_id"]) : ""; $this->api_photos_url = !empty($options["api_photos_url"]) && strlen(trim($options["api_photos_url"])) > 10 ? trim($options["api_photos_url"]) : null; $this->only_drive = isset($options["only_drive"]) ? $options["only_drive"] : 0; $this->http_requests_frequency = isset($options["http_requests_frequency"]) && is_numeric($options["http_requests_frequency"]) ? $options["http_requests_frequency"] : 0; $this->sync_manufacturers = isset($options["cron_sync_manufacturers"]) ? $options["cron_sync_manufacturers"] : 0; $this->cron_tasks = isset($options["cron_tasks"]) ? $options["cron_tasks"] : 0; $this->categories_type = isset($options["categories_type"]) ? $options["categories_type"] : "groups"; $this->product_attributes = isset($options["product_attributes"]) ? $options["product_attributes"] : ""; $this->stop_words = array("N/A", "NON RENSEIGNE"); $this->logger = \wc_get_logger(); if (class_exists("OS\WC_StockXL\Includes\StockXL_Image_Logger")) { $this->image_logger = new StockXL_Image_Logger(); } else { $this->logger->warning("La classe StockXL_Image_Logger n'a pas pu être chargée.", ["source" => "wc-stockxl"]); $this->image_logger = null; } } public function request($method, $endpoint, $headers = [], $body = []) { sleep($this->http_requests_frequency); $headers["Authorization"] = "Basic " . base64_encode($this->api_user . ":" . $this->api_password); $args = [ "method" => $method, "headers" => $headers, "timeout" => 60 ]; if (!empty($body)) $args["body"] = json_encode($body); $response = wp_remote_request($this->api_url . $endpoint, $args); if (is_wp_error($response) || empty($response["body"])) { $this->logger->error(print_r($response, true), ["source" => "wc-stockxl"]); return false; } return $response; } public function get_categories($starting_from = 1, $number_of = 100) { $data = null; switch ($this->categories_type) { case "families": $data = $this->get_familles($starting_from, $number_of); break; default: $data = $this->get_groupes($starting_from, $number_of); } return $data; } public function get_familles($starting_from = 1, $number_of = 100) { $method = "GET"; $endpoint = "/famille/liste/" . $starting_from . "/" . $number_of . ""; $response = $this->request($method, $endpoint); if (!$response || empty($response)) return false; $data = json_decode($response["body"]); if (!$data || isset($data->erreur)) { $this->logger->error("Error fetching " . $endpoint . " : " . (isset($data->erreur) ? $data->erreur : "Unknown error"), ["source" => "wc-stockxl"]); return false; } return $data; } public function get_groupes($starting_from = 1, $number_of = 100) { $method = "GET"; $endpoint = "/groupe/liste/" . $starting_from . "/" . $number_of . ""; $response = $this->request($method, $endpoint); if (!$response || empty($response)) return false; $data = json_decode($response["body"]); if (!$data || isset($data->erreur)) { $this->logger->error("Error fetching " . $endpoint . " : " . (isset($data->erreur) ? $data->erreur : "Unknown error"), ["source" => "wc-stockxl"]); return false; } return $data; } public function get_article($sku, $fields = "*") { $method = "GET"; $endpoint = "/article/detail/" . $sku . "?champs=" . $fields; $response = $this->request($method, $endpoint); if (!$response || empty($response)) return false; $data = json_decode($response["body"]); if (!$data || isset($data->erreur)) { $this->logger->error("Error fetching " . $endpoint . " : " . (isset($data->erreur) ? $data->erreur : "Unknown error"), ["source" => "wc-stockxl"]); return false; } else { $this->logger->notice("API fetching " . $endpoint, ["source" => "wc-stockxl"]); } return isset($data[0]) ? $data[0] : null; } public function get_articles($starting_from = 1, $number_of = 100, $fields = "*") { $method = "GET"; $endpoint = "/article/liste/" . $starting_from . "/" . $number_of . "?champs=" . $fields; $response = $this->request($method, $endpoint); if (!$response || empty($response)) return false; $data = json_decode($response["body"]); if (isset($data->erreur)) { $this->logger->error("Error fetching " . $endpoint . " :" . $data->erreur, ["source" => "wc-stockxl"]); return false; } return $data; } public function get_customers($starting_from = 1, $number_of = 100) { $method = "GET"; $endpoint = "/client/liste/" . $starting_from . "/" . $number_of . ""; $response = $this->request($method, $endpoint); if (!$response || empty($response)) return false; $data = json_decode($response["body"]); if (!$data || isset($data->erreur)) { $this->logger->error("Error fetching " . $endpoint . " : " . (isset($data->erreur) ? $data->erreur : "Unknown error"), ["source" => "wc-stockxl"]); return false; } return $data; } public function get_manufacturer($manufacturer_id) { $method = "GET"; $endpoint = "/fournisseur/detail/" . $manufacturer_id; $response = $this->request($method, $endpoint); if (!$response || empty($response)) return false; $data = json_decode($response["body"]); if (!$data || isset($data->erreur)) { $this->logger->error("Error fetching " . $endpoint . " : " . (isset($data->erreur) ? $data->erreur : "Unknown error"), ["source" => "wc-stockxl"]); return false; } return count($data) == 1 ? $data[0] : null; } public function load_photos() { if (!$this->api_photos_url) { $this->logger->info("L'URL de l'API photos n'est pas configurée. Impossible de charger les photos.", ["source" => "wc-stockxl"]); $this->photos = []; return; } // Cette méthode doit être implémentée pour récupérer la liste des images par SKU. // Elle doit peupler $this->photos sous la forme ["SKU1" => ["img1.jpg", "img2.png"], ...] $this->logger->warning("La méthode load_photos() doit être implémentée pour récupérer la liste des images par SKU depuis l'API StockXL ou une source de données.", ["source" => "wc-stockxl"]); $this->photos = []; // Assurer qu'elle est un tableau en attendant l'implémentation } public function import_categories() { $number_of = 100; $options = get_option("woocommerce_wc-stockxl_settings"); $starting_from = isset($options["categories_starting_from"]) ? $options["categories_starting_from"] : 1; $this->logger->debug(__METHOD__ . " importing " . $number_of . " categories starting from " . $starting_from, ["source" => "wc-stockxl"]); $all_categories = $this->get_categories($starting_from, $number_of); if (!$all_categories) { $options = get_option("woocommerce_wc-stockxl_settings"); $options["categories_starting_from"] = 1; update_option("woocommerce_wc-stockxl_settings", $options); return false; } foreach ($all_categories as $category) { $terms = get_terms([ "taxonomy" => "product_cat", "hide_empty" => false, "meta_query" => [[ "key" => "_stockxl_classnum", "value" => (string) $category->classnum, ]], ]); if ($terms instanceof \WP_Error) { $this->logger->error($terms->get_error_message(), ["source" => "wc-stockxl"]); } elseif (count($terms) <= 0) { $term_data = wp_insert_term($category->classlib, "product_cat"); if (is_array($term_data)) { update_term_meta($term_data["term_id"], "_stockxl_classnum", $category->classnum); } elseif ($term_data instanceof \WP_Error) { $this->logger->debug($term_data->get_error_message() . " " . json_encode($category), ["source" => "wc-stockxl"]); $term = get_term_by("name", $category->classlib, "product_cat"); if ($term instanceof \WP_Term) { update_term_meta($term->term_id, "_stockxl_classnum", $category->classnum); } } } $options = get_option("woocommerce_wc-stockxl_settings"); $options["categories_starting_from"] = ++$starting_from; update_option("woocommerce_wc-stockxl_settings", $options); } return true; } public function import_products() { $number_of = 10000; $options = get_option("woocommerce_wc-stockxl_settings"); $starting_from = isset($options["products_starting_from"]) ? $options["products_starting_from"] : 1; $this->logger->debug(__METHOD__ . " importing " . $number_of . " products starting from " . $starting_from, ["source" => "wc-stockxl"]); $all_products = $this->get_articles($starting_from, $number_of); if (!$all_products) { $options = get_option("woocommerce_wc-stockxl_settings"); $options["products_starting_from"] = 1; update_option("woocommerce_wc-stockxl_settings", $options); $this->logger->info("Aucun produit à importer ou erreur lors de la récupération des articles.", ["source" => "wc-stockxl"]); return false; } foreach ($all_products as $p) { $update = true; if (!$this->is_visible($p)) { $this->before_continue($p); $options = get_option("woocommerce_wc-stockxl_settings"); $options["products_starting_from"] = ++$starting_from; update_option("woocommerce_wc-stockxl_settings", $options); continue; } $product_id = \wc_get_product_id_by_sku($p->nart); $product = \wc_get_product($product_id); if (!$product) { $product = new \WC_Product_Simple(); $product->set_sku($p->nart); $product->set_name($p->design); $update = false; } else { $product->set_name($p->design); } $product = $this->set_product_data($product, $p); $product_id = $product->save(); if($product_id instanceof \WP_Error) { $this->logger->error("Erreur lors de la sauvegarde du produit SKU: " . $p->nart . " - " . $product_id->get_error_message(), ["source" => "wc-stockxl"]); $options = get_option("woocommerce_wc-stockxl_settings"); $options["products_starting_from"] = ++$starting_from; update_option("woocommerce_wc-stockxl_settings", $options); continue; } if (!$update) { $this->logger->info("Produit créé SKU: " . $p->nart . " ID: " . $product_id, ["source" => "wc-stockxl"]); } else { $this->logger->info("Produit mis à jour SKU: " . $p->nart . " ID: " . $product_id, ["source" => "wc-stockxl"]); } // La gestion des images est maintenant séparée et se fait via run_manual_image_import(). $options = get_option("woocommerce_wc-stockxl_settings"); $options["products_starting_from"] = ++$starting_from; update_option("woocommerce_wc-stockxl_settings", $options); } $this->logger->info("Importation des produits terminée pour le batch.", ["source" => "wc-stockxl"]); return true; } protected function set_product_data(\WC_Product $product, $api_data) { $product = $this->set_drive($product, $this->is_visible($api_data)); $product = $this->set_visibility($product, $api_data); $product = $this->set_stock($product, $api_data); $product = $this->set_price($product, $api_data); if (!empty($api_data->api_parent)) { $product->update_meta_data("_stockxl_parent", $api_data->api_parent); } else { $product->delete_meta_data("_stockxl_parent"); } if (isset($api_data->unite)) { $product->update_meta_data("_stockxl_unit", $api_data->unite); } if (isset($api_data->kl)) { $product->update_meta_data("_stockxl_measure", $api_data->kl); } if (isset($api_data->vol)) { $product->set_weight($api_data->vol); } $product->update_meta_data("_alg_ean", (!empty($api_data->gencod) ? str_pad($api_data->gencod, 13, "0", STR_PAD_LEFT) : "")); $term_ids = array(); $c_meta_val = null; if (isset($this->categories_type)) { switch ($this->categories_type) { case "families": if (isset($api_data->nart)) $c_meta_val = substr($api_data->nart, 0, 2); break; case "groups": if (isset($api_data->groupe)) $c_meta_val = $api_data->groupe; break; } } if ($c_meta_val !== null) { $terms_query_args = [ "taxonomy" => "product_cat", "hide_empty" => false, "meta_query" => [[ "key" => "_stockxl_classnum", "value" => (string) $c_meta_val, ]] ]; $terms = get_terms($terms_query_args); if ($terms instanceof \WP_Error) { $this->logger->error("Erreur lors de la récupération des termes de catégorie pour " . $c_meta_val . ": " . $terms->get_error_message(), ["source" => "wc-stockxl"]); } elseif (!empty($terms)) { foreach ($terms as $term) { $term_ids[] = $term->term_id; $ancestors = \get_ancestors($term->term_id, "product_cat", "taxonomy"); $term_ids = array_merge($term_ids, $ancestors); } $term_ids = array_unique($term_ids); wp_set_object_terms($product->get_id(), $term_ids, "product_cat", false); } } if (class_exists("OS\WC_StockXL\API\UserFunctions") && method_exists("OS\WC_StockXL\API\UserFunctions", "product_custom_features")) { $product = \OS\WC_StockXL\API\UserFunctions::product_custom_features($product, $api_data); } if (method_exists($this, 'set_manufacturer')) { $product = $this->set_manufacturer($product, $api_data); } if (method_exists($this, 'set_attributes')) { $product = $this->set_attributes($product, $api_data); } return $product; } public function import_stocks() { /* ... Implementation ... */ return true;} public function import_prices() { /* ... Implementation ... */ return true;} public function import_customers() { /* ... Implementation ... */ return true;} public function import_sync_all() { /* ... Implementation ... */ } public function get_order($order_id) { /* ... Implementation ... */ } public function create_order($order_id) { /* ... Implementation ... */ } public function update_order($order_id) { /* ... Implementation ... */ } public function delete_order($order_id) { /* ... Implementation ... */ } public function get_order_statuses() { /* ... Implementation ... */ } public function get_payment_methods() { /* ... Implementation ... */ } public function get_shipping_methods() { /* ... Implementation ... */ } public function run_manual_image_import($args = []) { if (!$this->image_logger) { if (class_exists("OS\WC_StockXL\Includes\StockXL_Image_Logger")) { $this->image_logger = new StockXL_Image_Logger(); } else { if($this->logger) $this->logger->error("Le logger d'images n'est pas disponible pour run_manual_image_import.", ["source" => "wc-stockxl"]); return ["status" => "error", "message" => "Le logger d'images n'est pas disponible."]; } } if (!class_exists("OS\WC_StockXL\API\UserFunctions_Images")) { $this->image_logger->error("La classe UserFunctions_Images n'est pas disponible.", ["source" => "wc-stockxl"]); return ["status" => "error", "message" => "La classe UserFunctions_Images n'est pas disponible."]; } if (empty($this->photos) && method_exists($this, 'load_photos')) { $this->load_photos(); } $this->image_logger->info("Déclenchement manuel de l'importation des images.", ["source" => "wc-stockxl", "args" => $args]); $results = UserFunctions_Images::import_all_product_images($this, $args); $this->image_logger->info("Importation manuelle des images terminée.", ["source" => "wc-stockxl", "results" => $results]); return $results; } protected function is_visible($p) { return true; } protected function before_continue($p) { } protected function set_drive($product, $is_visible) { return $product; } protected function set_visibility($product, $p) { return $product; } protected function set_stock($product, $p) { return $product; } protected function set_price($product, $p) { return $product; } protected function set_manufacturer($product, $p) { return $product; } protected function set_attributes($product, $p) { return $product; } } } ?>