|
@@ -0,0 +1,627 @@
|
|
|
|
|
+<?php
|
|
|
|
|
+/**
|
|
|
|
|
+ * Product import class
|
|
|
|
|
+ *
|
|
|
|
|
+ * Handles product import from CSV files
|
|
|
|
|
+ *
|
|
|
|
|
+ * @since 1.4.0
|
|
|
|
|
+ * @package Studiou_WC_Product_Cat_Manage
|
|
|
|
|
+ * @subpackage Studiou_WC_Product_Cat_Manage/includes
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+// If this file is called directly, abort.
|
|
|
|
|
+if (!defined('WPINC')) {
|
|
|
|
|
+ die;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class Studiou_WC_Product_Manage_Product_Import {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @var Studiou_WC_Product_Manage_Product_DB
|
|
|
|
|
+ */
|
|
|
|
|
+ private $db;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Constructor
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param Studiou_WC_Product_Manage_Product_DB $db Database instance
|
|
|
|
|
+ */
|
|
|
|
|
+ public function __construct($db) {
|
|
|
|
|
+ $this->db = $db;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Parse CSV file and store data for batch processing
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $file_path Path to CSV file
|
|
|
|
|
+ * @return array Result with total count and transient key
|
|
|
|
|
+ */
|
|
|
|
|
+ public function parse_csv_for_batch($file_path) {
|
|
|
|
|
+ if (!file_exists($file_path)) {
|
|
|
|
|
+ return array('error' => __('File not found', 'studiou-wc-product-cat-manage'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Open CSV file
|
|
|
|
|
+ $handle = fopen($file_path, 'r');
|
|
|
|
|
+ if (!$handle) {
|
|
|
|
|
+ return array('error' => __('Could not open file', 'studiou-wc-product-cat-manage'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Read header row
|
|
|
|
|
+ $header = fgetcsv($handle, 0, ',', '"');
|
|
|
|
|
+ if (!$header) {
|
|
|
|
|
+ fclose($handle);
|
|
|
|
|
+ return array('error' => __('Invalid CSV format', 'studiou-wc-product-cat-manage'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Read all data rows
|
|
|
|
|
+ $rows = array();
|
|
|
|
|
+ $row_number = 1;
|
|
|
|
|
+ while (($row = fgetcsv($handle, 0, ',', '"')) !== false) {
|
|
|
|
|
+ $row_number++;
|
|
|
|
|
+
|
|
|
|
|
+ // Skip empty rows
|
|
|
|
|
+ if (empty(array_filter($row))) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Combine header with row data
|
|
|
|
|
+ $data = array_combine($header, $row);
|
|
|
|
|
+ $rows[] = array(
|
|
|
|
|
+ 'row_number' => $row_number,
|
|
|
|
|
+ 'data' => $data
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fclose($handle);
|
|
|
|
|
+
|
|
|
|
|
+ // Store data in transient (expires in 1 hour)
|
|
|
|
|
+ $transient_key = 'studiou_wcpcm_import_' . uniqid();
|
|
|
|
|
+ set_transient($transient_key, $rows, 3600);
|
|
|
|
|
+
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'transient_key' => $transient_key,
|
|
|
|
|
+ 'total' => count($rows)
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Process batch of products
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $transient_key Transient key with CSV data
|
|
|
|
|
+ * @param int $offset Starting offset
|
|
|
|
|
+ * @param int $batch_size Number of items to process
|
|
|
|
|
+ * @return array Result with statistics
|
|
|
|
|
+ */
|
|
|
|
|
+ public function process_batch($transient_key, $offset = 0, $batch_size = 5) {
|
|
|
|
|
+ $rows = get_transient($transient_key);
|
|
|
|
|
+
|
|
|
|
|
+ if (!$rows) {
|
|
|
|
|
+ return array('error' => __('Import data not found or expired', 'studiou-wc-product-cat-manage'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $result = array(
|
|
|
|
|
+ 'success' => 0,
|
|
|
|
|
+ 'failed' => 0,
|
|
|
|
|
+ 'skipped' => 0,
|
|
|
|
|
+ 'failed_items' => array(),
|
|
|
|
|
+ 'messages' => array(),
|
|
|
|
|
+ 'processed' => 0,
|
|
|
|
|
+ 'total' => count($rows)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // Get batch of rows to process
|
|
|
|
|
+ $batch = array_slice($rows, $offset, $batch_size);
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($batch as $item) {
|
|
|
|
|
+ $row_number = $item['row_number'];
|
|
|
|
|
+ $data = $item['data'];
|
|
|
|
|
+
|
|
|
|
|
+ // Process the product
|
|
|
|
|
+ $process_result = $this->process_product_row($data, $row_number);
|
|
|
|
|
+
|
|
|
|
|
+ if ($process_result['status'] === 'success') {
|
|
|
|
|
+ $result['success']++;
|
|
|
|
|
+ // Don't add success messages to log
|
|
|
|
|
+ } elseif ($process_result['status'] === 'skipped') {
|
|
|
|
|
+ $result['skipped']++;
|
|
|
|
|
+ $result['messages'][] = sprintf(
|
|
|
|
|
+ __('Row %d: SKIPPED - %s', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $row_number,
|
|
|
|
|
+ $process_result['message']
|
|
|
|
|
+ );
|
|
|
|
|
+ // Add to failed items for re-processing
|
|
|
|
|
+ $result['failed_items'][] = $data;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $result['failed']++;
|
|
|
|
|
+ $result['messages'][] = sprintf(
|
|
|
|
|
+ __('Row %d: FAILED - %s', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $row_number,
|
|
|
|
|
+ $process_result['message']
|
|
|
|
|
+ );
|
|
|
|
|
+ // Add to failed items
|
|
|
|
|
+ $result['failed_items'][] = $data;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $result['processed'] = $offset + count($batch);
|
|
|
|
|
+
|
|
|
|
|
+ return $result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Process single product row
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array $data Product data
|
|
|
|
|
+ * @param int $row_number Row number for error reporting
|
|
|
|
|
+ * @return array Result with status and message
|
|
|
|
|
+ */
|
|
|
|
|
+ private function process_product_row($data, $row_number) {
|
|
|
|
|
+ // Validate required fields
|
|
|
|
|
+ if (!isset($data['Typ']) || empty($data['Typ'])) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => __('Missing product type', 'studiou-wc-product-cat-manage')
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Rule 3: Only allow "variable" or "variation" types
|
|
|
|
|
+ $type = trim($data['Typ']);
|
|
|
|
|
+ if (!in_array($type, array('variable', 'variation'))) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => sprintf(
|
|
|
|
|
+ __('Invalid product type: %s (only "variable" or "variation" allowed)', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $type
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Get product ID
|
|
|
|
|
+ $product_id = isset($data['ID']) ? intval($data['ID']) : 0;
|
|
|
|
|
+ $sku = isset($data['Katalogové číslo']) ? trim($data['Katalogové číslo']) : '';
|
|
|
|
|
+
|
|
|
|
|
+ // Rule 4: SKU must be unique
|
|
|
|
|
+ if (empty($sku)) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => __('Missing SKU (Katalogové číslo)', 'studiou-wc-product-cat-manage')
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!$this->db->is_sku_unique($sku, $product_id)) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => sprintf(
|
|
|
|
|
+ __('SKU "%s" already exists', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $sku
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Rule 5: Validate image URL if provided
|
|
|
|
|
+ $image_url = isset($data['Obrázky']) ? trim($data['Obrázky']) : '';
|
|
|
|
|
+ if (!empty($image_url) && !$this->db->validate_image_url($image_url)) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => sprintf(
|
|
|
|
|
+ __('Invalid image URL: %s', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $image_url
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Process based on product type
|
|
|
|
|
+ if ($type === 'variation') {
|
|
|
|
|
+ return $this->process_variation($data, $product_id);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return $this->process_variable_product($data, $product_id);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Process variable product
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array $data Product data
|
|
|
|
|
+ * @param int $product_id Product ID (0 for new product)
|
|
|
|
|
+ * @return array Result with status and message
|
|
|
|
|
+ */
|
|
|
|
|
+ private function process_variable_product($data, $product_id) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // Create or update product
|
|
|
|
|
+ if ($product_id == 0) {
|
|
|
|
|
+ $product = new WC_Product_Variable();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $product = wc_get_product($product_id);
|
|
|
|
|
+ if (!$product || !$product->is_type('variable')) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'error',
|
|
|
|
|
+ 'message' => sprintf(
|
|
|
|
|
+ __('Product ID %d is not a variable product', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $product_id
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Set basic data
|
|
|
|
|
+ $product->set_name(isset($data['Jméno']) ? $data['Jméno'] : '');
|
|
|
|
|
+ $product->set_sku(isset($data['Katalogové číslo']) ? $data['Katalogové číslo'] : '');
|
|
|
|
|
+ $product->set_short_description(isset($data['Krátký popis']) ? $data['Krátký popis'] : '');
|
|
|
|
|
+ $product->set_description(isset($data['Popis']) ? $data['Popis'] : '');
|
|
|
|
|
+
|
|
|
|
|
+ // Set categories
|
|
|
|
|
+ if (isset($data['Kategorie']) && !empty($data['Kategorie'])) {
|
|
|
|
|
+ $categories = array_map('trim', explode('|', $data['Kategorie']));
|
|
|
|
|
+ $category_ids = array();
|
|
|
|
|
+
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Processing categories for product: ' . $product->get_name());
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Categories string from CSV: ' . $data['Kategorie']);
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($categories as $category_name) {
|
|
|
|
|
+ if (empty($category_name)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $category_id = $this->find_category_by_name($category_name);
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Looking for category "' . $category_name . '" - Result: ' . ($category_id ? 'FOUND ID=' . $category_id : 'NOT FOUND'));
|
|
|
|
|
+
|
|
|
|
|
+ if ($category_id) {
|
|
|
|
|
+ $category_ids[] = $category_id;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Total category IDs found: ' . count($category_ids) . ' - IDs: ' . implode(',', $category_ids));
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($category_ids)) {
|
|
|
|
|
+ $product->set_category_ids($category_ids);
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Categories assigned to product via set_category_ids()');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: WARNING - No valid category IDs found for: ' . $data['Kategorie']);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: No categories in CSV for product: ' . $product->get_name());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Handle image
|
|
|
|
|
+ if (isset($data['Obrázky']) && !empty($data['Obrázky'])) {
|
|
|
|
|
+ $image_id = $this->upload_image_from_url($data['Obrázky']);
|
|
|
|
|
+ if ($image_id) {
|
|
|
|
|
+ $product->set_image_id($image_id);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Set reviews enabled
|
|
|
|
|
+ $reviews_enabled = isset($data['Povolit zákaznické recenze?']) && $data['Povolit zákaznické recenze?'] === '1';
|
|
|
|
|
+ $product->set_reviews_allowed($reviews_enabled);
|
|
|
|
|
+
|
|
|
|
|
+ // Handle product attribute
|
|
|
|
|
+ if (isset($data['Název 1 vlastnosti']) && !empty($data['Název 1 vlastnosti'])) {
|
|
|
|
|
+ $attribute_name = $data['Název 1 vlastnosti'];
|
|
|
|
|
+ $attribute_values = isset($data['Hodnota(y) 1 vlastnosti']) ? $data['Hodnota(y) 1 vlastnosti'] : '';
|
|
|
|
|
+ $is_visible = isset($data['Vlastnost 1 viditelnost']) && $data['Vlastnost 1 viditelnost'] === '1';
|
|
|
|
|
+ $is_variation = isset($data['Vlastnost 1 varianta']) && $data['Vlastnost 1 varianta'] === '1';
|
|
|
|
|
+
|
|
|
|
|
+ // Create attribute taxonomy if it doesn't exist
|
|
|
|
|
+ $this->db->create_attribute_if_not_exists($attribute_name);
|
|
|
|
|
+
|
|
|
|
|
+ $attribute = new WC_Product_Attribute();
|
|
|
|
|
+ $attribute->set_name(wc_attribute_taxonomy_name($attribute_name));
|
|
|
|
|
+ $attribute->set_visible($is_visible);
|
|
|
|
|
+ $attribute->set_variation($is_variation);
|
|
|
|
|
+
|
|
|
|
|
+ // Set attribute options (terms)
|
|
|
|
|
+ $values = array_map('trim', explode('|', $attribute_values));
|
|
|
|
|
+ $attribute->set_options($values);
|
|
|
|
|
+
|
|
|
|
|
+ $product->set_attributes(array($attribute));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Handle minimum/maximum quantity
|
|
|
|
|
+ if (isset($data['Minimum Quantity']) && !empty($data['Minimum Quantity'])) {
|
|
|
|
|
+ $product->update_meta_data('_minimum_quantity', intval($data['Minimum Quantity']));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isset($data['Maximum Quantity']) && !empty($data['Maximum Quantity'])) {
|
|
|
|
|
+ $product->update_meta_data('_maximum_quantity', intval($data['Maximum Quantity']));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Save product
|
|
|
|
|
+ $saved_id = $product->save();
|
|
|
|
|
+
|
|
|
|
|
+ $message = $product_id == 0 ?
|
|
|
|
|
+ sprintf(__('Created variable product: %s', 'studiou-wc-product-cat-manage'), $product->get_name()) :
|
|
|
|
|
+ sprintf(__('Updated variable product: %s', 'studiou-wc-product-cat-manage'), $product->get_name());
|
|
|
|
|
+
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'success',
|
|
|
|
|
+ 'message' => $message
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception $e) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'error',
|
|
|
|
|
+ 'message' => $e->getMessage()
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Process product variation
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array $data Product data
|
|
|
|
|
+ * @param int $variation_id Variation ID (0 for new variation)
|
|
|
|
|
+ * @return array Result with status and message
|
|
|
|
|
+ */
|
|
|
|
|
+ private function process_variation($data, $variation_id) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // Rule 6: Must have parent reference
|
|
|
|
|
+ if (!isset($data['Nadřazené']) || empty($data['Nadřazené'])) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => __('Missing parent product reference (Nadřazené)', 'studiou-wc-product-cat-manage')
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Rule 7: Must have regular price
|
|
|
|
|
+ if (!isset($data['Běžná cena']) || empty($data['Běžná cena'])) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => __('Missing regular price (Běžná cena)', 'studiou-wc-product-cat-manage')
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Find parent product by SKU
|
|
|
|
|
+ $parent_sku = trim($data['Nadřazené']);
|
|
|
|
|
+ $parent_product = $this->db->get_product_by_sku($parent_sku);
|
|
|
|
|
+
|
|
|
|
|
+ if (!$parent_product || !$parent_product->is_type('variable')) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'skipped',
|
|
|
|
|
+ 'message' => sprintf(
|
|
|
|
|
+ __('Parent product with SKU "%s" not found or not variable', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $parent_sku
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Create or update variation
|
|
|
|
|
+ if ($variation_id == 0) {
|
|
|
|
|
+ $variation = new WC_Product_Variation();
|
|
|
|
|
+ $variation->set_parent_id($parent_product->get_id());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $variation = wc_get_product($variation_id);
|
|
|
|
|
+ if (!$variation || !$variation->is_type('variation')) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'error',
|
|
|
|
|
+ 'message' => sprintf(
|
|
|
|
|
+ __('Product ID %d is not a variation', 'studiou-wc-product-cat-manage'),
|
|
|
|
|
+ $variation_id
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Set basic data
|
|
|
|
|
+ $variation->set_name(isset($data['Jméno']) ? $data['Jméno'] : '');
|
|
|
|
|
+ $variation->set_sku(isset($data['Katalogové číslo']) ? $data['Katalogové číslo'] : '');
|
|
|
|
|
+ $variation->set_description(isset($data['Popis']) ? $data['Popis'] : '');
|
|
|
|
|
+ $variation->set_regular_price($data['Běžná cena']);
|
|
|
|
|
+
|
|
|
|
|
+ // Handle image
|
|
|
|
|
+ if (isset($data['Obrázky']) && !empty($data['Obrázky'])) {
|
|
|
|
|
+ $image_id = $this->upload_image_from_url($data['Obrázky']);
|
|
|
|
|
+ if ($image_id) {
|
|
|
|
|
+ $variation->set_image_id($image_id);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Set variation attributes
|
|
|
|
|
+ if (isset($data['Název 1 vlastnosti']) && !empty($data['Název 1 vlastnosti']) &&
|
|
|
|
|
+ isset($data['Hodnota(y) 1 vlastnosti']) && !empty($data['Hodnota(y) 1 vlastnosti'])) {
|
|
|
|
|
+
|
|
|
|
|
+ $attribute_name = wc_attribute_taxonomy_name($data['Název 1 vlastnosti']);
|
|
|
|
|
+ $attribute_value = $data['Hodnota(y) 1 vlastnosti'];
|
|
|
|
|
+
|
|
|
|
|
+ $variation->set_attributes(array(
|
|
|
|
|
+ $attribute_name => $attribute_value
|
|
|
|
|
+ ));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Handle minimum/maximum quantity
|
|
|
|
|
+ if (isset($data['Minimum Quantity']) && !empty($data['Minimum Quantity'])) {
|
|
|
|
|
+ $variation->update_meta_data('_minimum_quantity', intval($data['Minimum Quantity']));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isset($data['Maximum Quantity']) && !empty($data['Maximum Quantity'])) {
|
|
|
|
|
+ $variation->update_meta_data('_maximum_quantity', intval($data['Maximum Quantity']));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Save variation
|
|
|
|
|
+ $saved_id = $variation->save();
|
|
|
|
|
+
|
|
|
|
|
+ $message = $variation_id == 0 ?
|
|
|
|
|
+ sprintf(__('Created variation: %s', 'studiou-wc-product-cat-manage'), $variation->get_name()) :
|
|
|
|
|
+ sprintf(__('Updated variation: %s', 'studiou-wc-product-cat-manage'), $variation->get_name());
|
|
|
|
|
+
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'success',
|
|
|
|
|
+ 'message' => $message
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception $e) {
|
|
|
|
|
+ return array(
|
|
|
|
|
+ 'status' => 'error',
|
|
|
|
|
+ 'message' => $e->getMessage()
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Upload image from URL
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $url Image URL
|
|
|
|
|
+ * @return int|null Attachment ID or null on failure
|
|
|
|
|
+ */
|
|
|
|
|
+ private function upload_image_from_url($url) {
|
|
|
|
|
+ require_once(ABSPATH . 'wp-admin/includes/media.php');
|
|
|
|
|
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
|
|
|
|
|
+ require_once(ABSPATH . 'wp-admin/includes/image.php');
|
|
|
|
|
+
|
|
|
|
|
+ // Set shorter timeout for image download (30 seconds)
|
|
|
|
|
+ add_filter('http_request_timeout', function() { return 30; });
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ $tmp = download_url($url, 30);
|
|
|
|
|
+
|
|
|
|
|
+ if (is_wp_error($tmp)) {
|
|
|
|
|
+ if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
|
|
|
+ error_log('STUDIOU WC: Image download failed for URL: ' . $url . ' - ' . $tmp->get_error_message());
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $file_array = array(
|
|
|
|
|
+ 'name' => basename($url),
|
|
|
|
|
+ 'tmp_name' => $tmp
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // Disable image processing to speed up import
|
|
|
|
|
+ add_filter('intermediate_image_sizes_advanced', function() { return array(); });
|
|
|
|
|
+
|
|
|
|
|
+ $id = media_handle_sideload($file_array, 0);
|
|
|
|
|
+
|
|
|
|
|
+ // Re-enable image processing
|
|
|
|
|
+ remove_filter('intermediate_image_sizes_advanced', function() { return array(); });
|
|
|
|
|
+
|
|
|
|
|
+ if (is_wp_error($id)) {
|
|
|
|
|
+ @unlink($file_array['tmp_name']);
|
|
|
|
|
+ if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
|
|
|
+ error_log('STUDIOU WC: Image upload failed for URL: ' . $url . ' - ' . $id->get_error_message());
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $id;
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception $e) {
|
|
|
|
|
+ if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
|
|
|
+ error_log('STUDIOU WC: Image upload exception for URL: ' . $url . ' - ' . $e->getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Find category by name (simple lookup, categories have unique names)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param string $category_name Category name
|
|
|
|
|
+ * @return int|null Category ID or null if not found
|
|
|
|
|
+ */
|
|
|
|
|
+ private function find_category_by_name($category_name) {
|
|
|
|
|
+ // Trim and normalize the category name
|
|
|
|
|
+ $category_name = trim($category_name);
|
|
|
|
|
+
|
|
|
|
|
+ if (empty($category_name)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Simple name lookup only (no hierarchy detection)
|
|
|
|
|
+ // Categories have unique names throughout the system
|
|
|
|
|
+
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: find_category_by_name() called for: "' . $category_name . '"');
|
|
|
|
|
+
|
|
|
|
|
+ // Method 1: Exact name match
|
|
|
|
|
+ $term = get_term_by('name', $category_name, 'product_cat');
|
|
|
|
|
+ if ($term) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Found by exact name match - ID: ' . $term->term_id);
|
|
|
|
|
+ return $term->term_id;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Method 2: Try by slug
|
|
|
|
|
+ $slug = sanitize_title($category_name);
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Exact name failed, trying slug: "' . $slug . '"');
|
|
|
|
|
+ $term = get_term_by('slug', $slug, 'product_cat');
|
|
|
|
|
+ if ($term) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Found by slug match - ID: ' . $term->term_id);
|
|
|
|
|
+ return $term->term_id;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Method 3: Search case-insensitively through all categories
|
|
|
|
|
+ $all_terms = get_terms(array(
|
|
|
|
|
+ 'taxonomy' => 'product_cat',
|
|
|
|
|
+ 'hide_empty' => false
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Slug failed, searching ' . count($all_terms) . ' categories case-insensitively');
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($all_terms as $t) {
|
|
|
|
|
+ if (strcasecmp($t->name, $category_name) === 0) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Found by case-insensitive match - ID: ' . $t->term_id . ', name: "' . $t->name . '"');
|
|
|
|
|
+ return $t->term_id;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Not found - try to create it
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Category NOT FOUND: "' . $category_name . '" - Attempting to create it');
|
|
|
|
|
+
|
|
|
|
|
+ $new_term = wp_insert_term(
|
|
|
|
|
+ $category_name,
|
|
|
|
|
+ 'product_cat',
|
|
|
|
|
+ array(
|
|
|
|
|
+ 'slug' => $slug
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (is_wp_error($new_term)) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Failed to create category "' . $category_name . '": ' . $new_term->get_error_message());
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Total available categories: ' . count($all_terms));
|
|
|
|
|
+
|
|
|
|
|
+ // Show first 20 categories for debugging
|
|
|
|
|
+ $sample_cats = array_slice($all_terms, 0, 20);
|
|
|
|
|
+ foreach ($sample_cats as $cat) {
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Available: "' . $cat->name . '" (ID: ' . $cat->term_id . ', slug: ' . $cat->slug . ')');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $term_id = $new_term['term_id'];
|
|
|
|
|
+ error_log('STUDIOU WC IMPORT: Successfully created category "' . $category_name . '" with ID: ' . $term_id);
|
|
|
|
|
+ return $term_id;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Generate CSV with failed items
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array $failed_items Array of failed product data
|
|
|
|
|
+ * @return string CSV content
|
|
|
|
|
+ */
|
|
|
|
|
+ public function generate_failed_csv($failed_items) {
|
|
|
|
|
+ if (empty($failed_items)) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Define CSV header
|
|
|
|
|
+ $header = array(
|
|
|
|
|
+ 'ID', 'Typ', 'Katalogové číslo', 'Jméno', 'Nadřazené', 'Krátký popis', 'Popis',
|
|
|
|
|
+ 'Název 1 vlastnosti', 'Hodnota(y) 1 vlastnosti', 'Kategorie', 'Obrázky', 'Běžná cena',
|
|
|
|
|
+ 'Vlastnost 1 viditelnost', 'Vlastnost 1 globální', 'Vlastnost 1 varianta',
|
|
|
|
|
+ 'Povolit zákaznické recenze?', 'Minimum Quantity', 'Maximum Quantity'
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // Start CSV output
|
|
|
|
|
+ $csv = '';
|
|
|
|
|
+ $csv .= '"' . implode('","', $header) . '"' . "\n";
|
|
|
|
|
+
|
|
|
|
|
+ // Add failed items
|
|
|
|
|
+ foreach ($failed_items as $item) {
|
|
|
|
|
+ $row = array();
|
|
|
|
|
+ foreach ($header as $column) {
|
|
|
|
|
+ $value = isset($item[$column]) ? $item[$column] : '';
|
|
|
|
|
+ $row[] = '"' . str_replace('"', '""', $value) . '"';
|
|
|
|
|
+ }
|
|
|
|
|
+ $csv .= implode(',', $row) . "\n";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $csv;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|