Plugin Directory

Changeset 3382611


Ignore:
Timestamp:
10/22/2025 12:53:29 PM (5 months ago)
Author:
avitrop
Message:

Update to version 2.2.8 from GitHub

Location:
storeman
Files:
2 added
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • storeman/tags/2.2.8/README.txt

    r2912185 r3382611  
    33Tags: storeman , woocommerce , stock manager , export product , import product , stock, orders, bulk edit products
    44Requires at lest: 3.0.1
    5 Tested up to: 6.0
    6 Stable tag: 2.2.7
     5Tested up to: 6.8
     6Stable tag: 2.2.8
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    2828no. this plugin is just plug&play.
    2929
     30= Use upsert and stock endpoints? =
     31
     32stock body:
     33{
     34    "fromFile":false,
     35    "url":null, // if from file
     36    "mode":"set", // enums: add / set. def: set
     37    "data":[{"sku":"EG","quantity":1}]
     38}
     39upsert body:
     40{
     41   "create_if_missing": true, // def: true
     42   "products":[{"sku":"test","name":"test name"},{"sku":"test variation","name":"test variation name","parent_sku":"test"}]
     43}
     44
    3045== Screenshots ==
    3146
     
    3348
    3449== Changelog ==
     50= 2.2.8 =
     51add elementor pro new action storeman,  add woocommerce end point stock and upsert
    3552= 2.2.7 =
    3653add elementor pro form toDo field and storeman_webhook_response action on get response. and also storeman_webhooks_fields and storeman_webhooks_print_response filters
  • storeman/tags/2.2.8/admin/class-storeman-admin.php

    r2912177 r3382611  
    2323class Storeman_Admin
    2424{
    25 
    2625    /**
    2726     * The ID of this plugin.
     
    166165        return $c;
    167166    }
    168     public function register_action( $form_actions_registrar ) {
     167    public function register_action($form_actions_registrar)
     168    {
    169169
    170170
    171171        try {
    172             require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-webhook-storeman-action.php';
    173             $form_actions_registrar->register( new \StoremanElementorAction() );
     172            require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-webhook-storeman-action.php';
     173            $form_actions_registrar->register(new \StoremanElementorAction());
    174174        } catch (\Exception $th) {
    175            // cant add action to elementor pro form
    176         }
    177    
    178     }
     175            // cant add action to elementor pro form
     176        }
     177        try {
     178            require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-webhook-storeman-action-new.php';
     179            $form_actions_registrar->register(new \StoremanElementorActionNew());
     180        } catch (\Exception $th) {
     181            // cant add action to elementor pro form
     182        }
     183
     184    }
    179185    public function set_get_item_schema($c)
    180186    {
     
    301307            $attributes = array();
    302308            $parent     = wc_get_product($variation->get_parent_id());
    303    
     309
    304310            if (! $parent) {
    305311                return new WP_Error(
     
    310316                );
    311317            }
    312    
     318
    313319            $parent_attributes = $parent->get_attributes();
    314    
     320
    315321            foreach ($request['attributes'] as $attribute) {
    316322                $attribute_id   = 0;
    317323                $attribute_name = '';
    318    
     324
    319325                // Check ID for global attributes or name for product attributes.
    320326                if (! empty($attribute['id'])) {
     
    324330                    $attribute_name =  sanitize_title($attribute['name']);
    325331                }
    326    
     332
    327333                if (! $attribute_id && ! $attribute_name) {
    328334                    continue;
    329335                }
    330    
     336
    331337                if (! isset($parent_attributes[ $attribute_name ]) || ! $parent_attributes[ $attribute_name ]->get_variation()) {
    332338                    $attribute_name = strtolower($attribute_name);
    333                     if (! isset($parent_attributes[ $attribute_name ])|| ! $parent_attributes[ $attribute_name ]->get_variation()) {
     339                    if (! isset($parent_attributes[ $attribute_name ]) || ! $parent_attributes[ $attribute_name ]->get_variation()) {
    334340                        continue;
    335341                    }
    336342                }
    337    
     343
    338344                $attribute_key   = sanitize_title($parent_attributes[ $attribute_name ]->get_name());
    339345                $attribute_value = isset($attribute['option']) ? wc_clean(stripslashes($attribute['option'])) : '';
    340    
     346
    341347                if ($parent_attributes[ $attribute_name ]->is_taxonomy()) {
    342348                    // If dealing with a taxonomy, we need to get the slug from the name posted to the API.
    343349                    $term = get_term_by('name', $attribute_value, $attribute_name);
    344    
     350
    345351                    if ($term && ! is_wp_error($term)) {
    346352                        $attribute_value = $term->slug;
    347                     // $attribute_value = sanitize_title($attribute_value);
     353                        // $attribute_value = sanitize_title($attribute_value);
    348354                    } else {
    349355                        $attribute_value = sanitize_title($attribute_value);
    350356                    }
    351357                }
    352    
     358
    353359                $attributes[ $attribute_key ] = $attribute_value;
    354360            }
    355    
     361
    356362            $variation->set_attributes($attributes);
    357363        }
    358364
    359    
     365
    360366        return $variation;
    361367    }
     
    366372        }
    367373        $languages = pll_the_languages(array( 'raw' => 1 ));
    368        
    369    
     374
     375
    370376
    371377        $product = wc_get_product($parent_post_id);
     
    408414                ),
    409415            );
    410    
     416
    411417        return array_merge($topic_hooks, $new_hooks);
    412418    }
     
    419425            'update_variation',
    420426            );
    421    
     427
    422428        return array_merge($topic_events, $new_events);
    423429    }
     
    429435            'product.update_variation' => __('Product Variation Updated', 'woocommerce'),
    430436            );
     437
     438        return array_merge($topics, $new_topics);
     439    }
     440    public function storeman_update_stock_permission(\WP_REST_Request $request)
     441    {
     442        // אם WooCommerce לא פעיל – אין מה להמשיך
     443        if (!class_exists('WooCommerce')) {
     444            return false;
     445        }
     446
     447        list($ck, $cs) = $this->storeman_wc_get_basic_auth_pair($request);
     448
     449        // אם סופקו מפתחות – נוודא שהם קיימים ובעלי write
     450        if ($ck !== '' && $cs !== '') {
     451            $row = $this->storeman_wc_find_api_key_row_by_consumer_key($ck);
     452            if (!$row) {
     453                return false;
     454            }
     455
     456            // בדיקת התאמת secret
     457            if (!hash_equals((string)$row['consumer_secret'], (string)$cs)) {
     458                return false;
     459            }
     460
     461            // הרשאות צריכות להכיל write
     462            $perm = isset($row['permissions']) ? strtolower((string)$row['permissions']) : '';
     463            if (!in_array($perm, ['write','read_write'], true)) {
     464                return false;
     465            }
     466
     467            // נחשב שהבקשה אושרה – ה-user_id של המפתח יקבע ע"י מנגנון auth של WC (אם פועל),
     468            // אך מבחינתנו מספיק שהמפתח תקף ובעל write.
     469            return true;
     470        }
     471
     472        // ללא CK/CS – נאפשר למשתמשים שיש להם יכולות מתאימות במערכת (לדוגמה חיבור קיים)
     473        if (current_user_can('edit_products') || current_user_can('manage_woocommerce')) {
     474            return true;
     475        }
     476
     477        return false;
     478    }
     479    public function register_custom_routes()
     480    {
     481        register_rest_route('wc/v3', '/stock', [
     482        'methods'  => 'POST',
     483        'callback' =>  [$this,'storeman_update_stock_handler'],
     484        'permission_callback' => [$this, 'storeman_update_stock_permission'],
     485        'args' => [
     486            'fromFile' => [
     487                'description' => 'if true - send a url to load the json from file',
     488                'type'        => 'boolean',
     489                'required'    => false,
     490               
     491            ],
     492            'url' => [
     493                'description' => 'the url to load the json from if fromFile is true',
     494                'type'        => 'string',
     495                'required'    => false,
     496               
     497            ],
     498          'mode' => [
     499              'description' => 'set או add. ברירת מחדל set',
     500              'type'        => 'string',
     501              'required'    => false,
     502              'enum'        => ['set','add'],
     503          ],
     504        ],
     505        ]);
     506        register_rest_route('wc/v3', '/upsert', [
     507        'methods'  => 'POST',
     508        'callback' => [$this,'wc_upsert_by_sku_via_core'],
     509        'permission_callback' => [$this, 'storeman_update_stock_permission'],
     510        'args' => [
     511            'create_if_missing' => [
     512                'description' => 'אם המוצר/וריאציה לא קיימים, האם ליצור אותם אוטומטית',
     513                'type'        => 'boolean',
     514                'required'    => false,
     515               
     516            ],
     517            'products' => [
     518            'required' => true,
     519            'type'     => 'array',
     520            'items'    => [
     521                'type'       => 'object',
     522                'properties' => [
     523                'sku'               => [ 'required' => true,  'type' => 'string' ],
     524                'type'              => [ 'required' => false, 'type' => 'string', 'enum' => ['simple','variable','external','grouped','variation'] ],
     525                'create_if_missing' => [ 'required' => false, 'type' => 'boolean' ],
     526                'parent_sku'        => [ 'required' => false, 'type' => 'string' ],
     527                ],
     528            ],
     529            ],
     530        ],
     531        ]);
     532    }
     533    public function storeman_update_stock_handler(\WP_REST_Request $request)
     534    {
     535        if (!class_exists('WooCommerce')) {
     536            return new \WP_REST_Response(['success' => false,'message' => 'WooCommerce not active'], 400);
     537        }
     538
     539        $mode = $request->get_param('mode') ?: 'set';
     540        $fromFile = $request->get_param('fromFile') ?: false;
     541        if ($fromFile) {
     542            $url = $request->get_param('url') ?: '';
     543            if ($url === '') {
     544                return new \WP_REST_Response(['success' => false,'message' => 'Missing url parameter'], 400);
     545            }
     546            // schedule a single WP event to load/process the file in background
     547            $url = esc_url_raw($url);
     548            $args = array(
     549                'url'  => $url,
     550                'mode' => $mode,
     551            );
     552
     553            // avoid double-scheduling the exact same job
     554            if (! wp_next_scheduled('load_stock_from_file', $args)) {
     555                wp_schedule_single_event(time() + 5, 'load_stock_from_file', $args);
     556                $return = [
     557                    'status'    => true,
     558                    'message'   => 'File scheduled for background processing',
     559                    'scheduled' => true,
     560                    'url'       => $url,
     561                ];
     562            } else {
     563                $return = [
     564                    'status'    => false,
     565                    'message'   => 'A job for this file is already scheduled',
     566                    'scheduled' => false,
     567                    'url'       => $url,
     568                ];
     569            }
     570     
     571        }
     572        else
     573            {
     574                $raw = $request->get_param('data');
     575                if(is_string($raw)){
     576                    $data = json_decode($raw, true);
    431577   
    432         return array_merge($topics, $new_topics);
    433     }
     578                    // תמיכה במבנה "אובייקטים מופרדים בפסיקים" שנשלחו בלי סוגריים
     579                    if (!is_array($data)) {
     580                        $try = json_decode('['.$raw.']', true);
     581                        if (is_array($try)) {
     582                            $data = $try;
     583                        }
     584                    }
     585                    if (!is_array($data)) {
     586                        return new \WP_REST_Response(['success' => false,'message' => 'Invalid JSON array'], 400);
     587                    }
     588                }
     589                else{
     590                    $data = $raw;
     591                }
     592
     593                $results = [];
     594                $updated = 0;
     595                $return  = $this->update_batch_stock($data, $mode);
     596           
     597        }
     598        return new \WP_REST_Response($return, 200);
     599    }
     600    public function do_event_load_file_and_update_stock($url, $mode)
     601    {
     602        if (!class_exists('WooCommerce')) {
     603            // log error
     604            return;
     605        }
     606
     607        // wp_single_event('storeman_log_event', 'info', 'Fetching stock update file from URL: '.$url);
     608        $response = wp_remote_get($url);
     609        if (is_wp_error($response)) {
     610            // wp_single_event('storeman_log_event', 'error', 'Error fetching stock update file from URL: '.$url.' Error: '.$response->get_error_message());
     611            return;
     612        }
     613        $raw = wp_remote_retrieve_body($response);
     614        $data = json_decode($raw, true);
     615
     616        if (!is_array($data)) {
     617            // wp_single_event('storeman_log_event', 'error', 'Invalid JSON array in stock update file from URL: '.$url);
     618            return;
     619        }
     620
     621        $result = $this->update_batch_stock($data, $mode);
     622        // wp_single_event('storeman_log_event', 'info', 'Stock update from file completed. Updated items: '.$result['updated'].' Total items processed: '.count($data));
     623    }
     624    private function update_batch_stock(array $data, $mode)
     625    {
     626        $results = [];
     627        $updated = 0;
     628
     629        foreach ($data as $idx => $item) {
     630            $sku  = isset($item['sku']) ? trim((string)$item['sku']) : '';
     631            $qty  = isset($item['quantity']) ? $item['quantity'] : null;
     632
     633            // פר-פריט: האם לא לגעת ב-manage_stock אם הוא כבוי?
     634            // ברירת מחדל: false (כלומר כן להדליק ניהול מלאי אם כבוי)
     635            $dont_toggle_manage = !empty($item['dont_toggle_manage_stock']);
     636
     637            if ($sku === '' || !is_numeric($qty)) {
     638                $results[] = [
     639                    'index' => $idx,
     640                    'sku'   => $sku,
     641                    'status' => 'error',
     642                    'error' => 'Missing sku or invalid quantity'
     643                ];
     644                continue;
     645            }
     646
     647            // איתור מוצר/וריאציה לפי SKU
     648            $product_id = wc_get_product_id_by_sku($sku);
     649            if (!$product_id) {
     650                $results[] = [
     651                    'index' => $idx,
     652                    'sku'   => $sku,
     653                    'status' => 'not_found'
     654                ];
     655                continue;
     656            }
     657
     658            $product = wc_get_product($product_id);
     659            if (!$product) {
     660                $results[] = [
     661                    'index' => $idx,
     662                    'sku'   => $sku,
     663                    'status' => 'error',
     664                    'error' => 'Product load failed'
     665                ];
     666                continue;
     667            }
     668
     669            $was_managing = $product->managing_stock();
     670
     671            // אם ניהול מלאי כבוי:
     672            // - אם dont_toggle_manage_stock=true => לא משנים מצב ולא מעדכנים כמות (שזו המשמעות המתבקשת),
     673            //   ונחזיר status שמסביר את הדילוג.
     674            // - אחרת נדליק ניהול מלאי.
     675            if (!$was_managing) {
     676                if ($dont_toggle_manage) {
     677                    $results[] = [
     678                        'index' => $idx,
     679                        'sku'   => $sku,
     680                        'status' => 'skipped_manage_stock_off',
     681                        'reason' => 'Product is not set to manage stock and dont_toggle_manage_stock=true',
     682                        'product_id' => $product->get_id(),
     683                        'type'       => $product->get_type(),
     684                    ];
     685                    continue;
     686                } else {
     687                    $product->set_manage_stock(true);
     688                }
     689            }
     690
     691            try {
     692                $delta = (float)$qty;
     693                if ($mode === 'set') {
     694                    wc_update_product_stock($product, $delta, 'set');
     695                } else {
     696                    if ($delta > 0) {
     697                        wc_update_product_stock($product, $delta, 'increase');
     698                    } elseif ($delta < 0) {
     699                        wc_update_product_stock($product, abs($delta), 'decrease');
     700                    } // 0 => אין שינוי
     701                }
     702
     703                $product->save();
     704
     705                $results[] = [
     706                    'index'          => $idx,
     707                    'sku'            => $sku,
     708                    'status'         => 'updated',
     709                    'stock_quantity' => (float) $product->get_stock_quantity(),
     710                    'product_id'     => $product->get_id(),
     711                    'type'           => $product->get_type(),
     712                    'manage_stock'   => $product->managing_stock(),
     713                ];
     714                $updated++;
     715            } catch (\Throwable $e) {
     716                $results[] = [
     717                    'index' => $idx,
     718                    'sku'   => $sku,
     719                    'status' => 'error',
     720                    'error' => $e->getMessage()
     721                ];
     722            }
     723        }
     724       return [
     725            'success' => true,
     726            'mode'    => $mode,
     727            'updated' => $updated,
     728            'results' => $results
     729       ];
     730    }
     731    public function wc_upsert_by_sku_via_core(WP_REST_Request $request)
     732    {
     733        if (! class_exists('WooCommerce')) {
     734            return new WP_REST_Response(['error' => 'WooCommerce not active'], 400);
     735        }
     736
     737        // ודא שהבקרים טעונים
     738        if (! class_exists('WC_REST_Products_Controller') || ! class_exists('WC_REST_Product_Variations_Controller')) {
     739            return new WP_REST_Response(['error' => 'WooCommerce REST controllers unavailable'], 500);
     740        }
     741
     742        $data = $request->get_json_params() ?: [];
     743        $products = isset($data['products']) && is_array($data['products']) ? $data['products'] : [];
     744        $create_if_missing = isset($data['create_if_missing']) ? (bool)$data['create_if_missing'] : true;
     745        $results = [];
     746        foreach ($products as $k => $data) {
     747            $result = ['index' => $k];
     748            $sku  = trim((string)($data['sku'] ?? ''));
     749            if (!$sku) {
     750                $result['status'] = false;
     751                $result['error'] = 'Missing sku';
     752                $results[] = $result;
     753                continue;
     754            }
     755
     756            $type = $data['type'] ?? 'simple';
     757            $create_if_missing = array_key_exists('create_if_missing', $data) ? (bool)$data['create_if_missing'] : $create_if_missing;
     758
     759            try {
     760                if ($type === 'variation') {
     761                    // ----- וריאציה: צריך parent_sku, ונתמקד בבקר הוריאציות של ווקומרס -----
     762                    $parent_sku = trim((string)($data['parent_sku'] ?? ''));
     763                    if (!$parent_sku) {
     764
     765                        $result['status'] = false;
     766                        $result['error'] = 'Variation requires parent_sku';
     767                        $results[] = $result;
     768                        continue;
     769
     770                    }
     771
     772                    // מצא/צור הורה לפי SKU (באמצעות בקר המוצרים)
     773                    $parent_id = wc_get_product_id_by_sku($parent_sku);
     774                    $products_controller = new WC_REST_Products_Controller();
     775
     776                    if (!$parent_id) {
     777                        if (!$create_if_missing) {
     778                            $result['status'] = false;
     779                            $result['error'] = 'Parent not found by parent_sku and create_if_missing=false';
     780                            $results[] = $result;
     781                            continue;
     782                        }
     783                        // צור הורה מסוג variable (באמצעות create_item הרשמי)
     784                        $parent_create_req = new WP_REST_Request('POST', '/wc/v3/products');
     785                        $parent_payload = $data;
     786                        $parent_payload['sku']  = $parent_sku;
     787                        $parent_payload['type'] = 'variable';
     788                        $parent_create_req->set_body_params($parent_payload);
     789                        $parent_resp = $products_controller->create_item($parent_create_req);
     790                        if ($parent_resp instanceof WP_Error) {
     791
     792                            $result['status'] = false;
     793                            $result['error'] =  $parent_resp->get_error_message();
     794                            $results[] = $result;
     795                            continue;
     796                        }
     797                        $parent_arr = $parent_resp->get_data();
     798                        $parent_id  = (int)($parent_arr['id'] ?? 0);
     799                        if (!$parent_id) {
     800                            $result['status'] = false;
     801                            $result['error'] = 'Failed to create parent';
     802                            $results[] = $result;
     803                            continue;
     804                        }
     805                    } else {
     806                        // אם ההורה קיים אך אינו variable – עדכן דרך הבקר הרשמי
     807                        $parent = wc_get_product($parent_id);
     808                        if ($parent && $parent->get_type() !== 'variable') {
     809                            $parent_update_req = new WP_REST_Request('PUT', '/wc/v3/products/'.$parent_id);
     810                            $parent_update_req->set_url_params(['id' => $parent_id]);
     811                            $parent_update_req->set_body_params(['type' => 'variable']);
     812                            $u = $products_controller->update_item($parent_update_req);
     813                            if ($u instanceof WP_Error) {
     814                                $result['status'] = false;
     815                                $result['error'] = $u->get_error_message();
     816                                $results[] = $result;
     817                                continue;
     818                            }
     819                        }
     820                    }
     821
     822                    // כעת – וריאציה לפי SKU
     823                    $variation_id = wc_get_product_id_by_sku($sku);
     824                    $variations_controller = new WC_REST_Product_Variations_Controller();
     825
     826                    if ($variation_id) {
     827                        // UPDATE ע"י הבקר הרשמי: /products/{product_id}/variations/{id}
     828                        $upd_req = new WP_REST_Request('PUT', '/wc/v3/products/'.$parent_id.'/variations/'.$variation_id);
     829                        $upd_req->set_url_params(['product_id' => $parent_id, 'id' => $variation_id]);
     830                        $upd_req->set_body_params($data);
     831                        $resp = $variations_controller->update_item($upd_req);
     832                        if ($resp instanceof WP_Error) {
     833                            $result['status'] = false;
     834                            $result['error'] = $resp->get_error_message();
     835                            $results[] = $result;
     836                            continue;
     837                        }
     838                        return $resp; // מחזיר את המערך התקני של WooCommerce
     839                    } else {
     840                        if (!$create_if_missing) {
     841                            return new WP_REST_Response(['error' => 'Variation not found by sku and create_if_missing=false'], 404);
     842                        }
     843                        // CREATE ע"י הבקר הרשמי
     844                        $crt_req = new WP_REST_Request('POST', '/wc/v3/products/'.$parent_id.'/variations');
     845                        $crt_req->set_url_params(['product_id' => $parent_id]);
     846                        $crt_req->set_body_params($data);
     847                        $resp = $variations_controller->create_item($crt_req);
     848                        if ($resp instanceof WP_Error) {
     849                            return new WP_REST_Response(['error' => $resp->get_error_message()], 400);
     850                        }
     851                        $result['status'] = true;
     852                        $result['data'] = $resp->get_data();
     853                        $results[] = $result;
     854                        continue;
     855                        // return $resp;
     856                    }
     857
     858                } else {
     859                    // ----- מוצר (לא וריאציה): נשען על WC_REST_Products_Controller -----
     860                    $product_id = wc_get_product_id_by_sku($sku);
     861                    $products_controller = new WC_REST_Products_Controller();
     862
     863                    if ($product_id) {
     864                        // UPDATE ע"י הבקר הרשמי: /products/{id}
     865                        $upd_req = new WP_REST_Request('PUT', '/wc/v3/products/'.$product_id);
     866                        $upd_req->set_url_params(['id' => $product_id]);
     867                        $upd_req->set_body_params($data);
     868                        $resp = $products_controller->update_item($upd_req);
     869                        if ($resp instanceof WP_Error) {
     870
     871                            $result['status'] = false;
     872                            $result['error'] = $resp->get_error_message();
     873                            $results[] = $result;
     874                            continue;
     875                        }
     876                        $result['status'] = true;
     877                        $result['data'] = $resp->get_data();
     878                        $results[] = $result;
     879                        continue;
     880                    } else {
     881                        if (!$create_if_missing) {
     882                            $result['status'] = false;
     883                            $result['error'] = 'Product not found by sku and create_if_missing=false';
     884                            $results[] = $result;
     885                            continue;
     886                        }
     887                        // CREATE ע"י הבקר הרשמי
     888                        // ודא type אם לא נשלח
     889                        if (empty($data['type'])) {
     890                            $data['type'] = 'simple';
     891                        }
     892                        $crt_req = new WP_REST_Request('POST', '/wc/v3/products');
     893                        $crt_req->set_body_params($data);
     894                        $resp = $products_controller->create_item($crt_req);
     895                        if ($resp instanceof WP_Error) {
     896                            $result['status'] = false;
     897                            $result['error'] =  $resp->get_error_message();
     898                            $results[] = $result;
     899                            continue;
     900                        }
     901                        $result['status'] = true;
     902                        $result['data'] = $resp->get_data();
     903                        $results[] = $result;
     904                        continue;
     905                    }
     906                }
     907
     908            } catch (Throwable $e) {
     909                $result['status'] = false;
     910                $result['error'] =  $e->getMessage();
     911                $results[] = $result;
     912                continue;
     913                // return new WP_REST_Response(['error' => $e->getMessage()], 500);
     914            }
     915            // $result['status'] = true;
     916            // $results[] = $result;
     917        }
     918        return new WP_REST_Response(['results' => $results], 200);
     919    }
     920    public function storeman_wc_get_basic_auth_pair(\WP_REST_Request $request): array
     921    {
     922        // 1) Authorization: Basic ...
     923        $auth = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : '');
     924        if (!$auth && function_exists('apache_request_headers')) {
     925            $headers = apache_request_headers();
     926            if (!empty($headers['Authorization'])) {
     927                $auth = $headers['Authorization'];
     928            }
     929        }
     930        if ($auth && stripos($auth, 'Basic ') === 0) {
     931            $decoded = base64_decode(substr($auth, 6));
     932            if ($decoded !== false && strpos($decoded, ':') !== false) {
     933                list($user, $pass) = explode(':', $decoded, 2);
     934                return [trim($user), trim($pass)];
     935            }
     936        }
     937
     938        // 2) Query / Body params (WooCommerce REST API תומך גם בזה)
     939        $ck = $request->get_param('consumer_key') ?: '';
     940        $cs = $request->get_param('consumer_secret') ?: '';
     941        if ($ck !== '' && $cs !== '') {
     942            return [$ck, $cs];
     943        }
     944
     945        return ['', ''];
     946    }
     947
     948    public function storeman_wc_find_api_key_row_by_consumer_key($consumer_key)
     949    {
     950        global $wpdb;
     951
     952        if ($consumer_key === '') {
     953            return null;
     954        }
     955
     956        // WooCommerce שומר consumer_key בהאש SHA256 (לרוב דרך wc_api_hash אם קיים)
     957        if (function_exists('wc_api_hash')) {
     958            $hash = wc_api_hash($consumer_key);
     959        } else {
     960            $hash = hash('sha256', $consumer_key);
     961        }
     962
     963        $table = $wpdb->prefix . 'woocommerce_api_keys';
     964        // expected columns: key_id, user_id, permissions, consumer_key (hashed), consumer_secret, truncated_key, last_access, nonces, etc.
     965        $row = $wpdb->get_row(
     966            $wpdb->prepare("SELECT * FROM {$table} WHERE consumer_key = %s LIMIT 1", $hash),
     967            ARRAY_A
     968        );
     969
     970        return $row ?: null;
     971    }
     972
     973
     974
     975
    434976}
    435 // add_filter('storeman_stock_quantity_doubleval' , function($type){
    436 //     return true;
    437 //         });
  • storeman/tags/2.2.8/admin/class-webhook-storeman-action.php

    r2912185 r3382611  
    99    public function get_label()
    1010    {
    11         return 'Storeman';
     11        return 'Storeman (Legacy)';
    1212    }
    1313    // namespace Elementor\Core\Base;
     
    3030            'storeman_domain',
    3131            [
    32                 'label' => esc_html__('Domain', 'elementor'),
     32                'label' => esc_html__('Domain (without / at the end)', 'elementor'),
    3333                'type' => 'text',
    34                 'default' => get_site_url().".storeman.co.il",
     34                'default' => "",
    3535
    3636
     
    6060            'storeman_action',
    6161            [
    62                 'label' => esc_html__('ToDo', 'elementor'),
     62                'label' => esc_html__('toDo', 'elementor'),
    6363                'type' => 'text',
    6464                'default' => '',
  • storeman/tags/2.2.8/includes/class-storeman.php

    r2912177 r3382611  
    199199        $this->loader->add_action("woocommerce_rest_pre_insert_product_variation_object", $plugin_admin, 'remove_product_variation_webhook_action',1,3);
    200200        $this->loader->add_action("woocommerce_rest_insert_product_variation_object", $plugin_admin, 'remove_product_variation_webhook_action',999,3);
    201  
    202 
     201 
     202        $this->loader->add_action('rest_api_init', $plugin_admin, 'register_custom_routes' );
     203        $this->loader->add_action('load_stock_from_file', $plugin_admin, 'do_event_load_file_and_update_stock',999,2);
    203204    }
    204205
  • storeman/tags/2.2.8/public/js/storeman-public.js

    r2912177 r3382611  
    33    $(document).on('submit_success', function (e, form) {
    44   
     5
     6        if (form.data.storeman_webhook_response && form.data.storeman_webhook_response.redirect) {
     7            window.location.href = form.data.storeman_webhook_response.redirect;
     8            return false;
     9        }
    510        if (form.data.storeman_webhook_response) {
    611            $("#storeman_webhook_response").remove();
     
    914       
    1015        }
     16        // if (form.data.storeman_webhook_redirect) {
     17
     18        //  $("#storeman_webhook_response").remove();
     19        //  var div = $("<div id='storeman_webhook_response'>"+form.data.storeman_webhook_response+"</div>");
     20        //  $(e.target).append(div);
     21       
     22        // }
    1123   
    1224
  • storeman/tags/2.2.8/storeman.php

    r2912185 r3382611  
    1717 * Plugin URI:        https://storeman.co.il
    1818 * Description:       add storeman to your WordPress site.
    19  * Version:           2.2.7
     19 * Version:           2.2.8
    2020 * Author:            avitrop
    2121 * Author URI:        https://profiles.wordpress.org/avitrop/
     
    3636 * Rename this for your plugin and update it as you release new versions.
    3737 */
    38 define( 'STOREMAN_VERSION', '2.2.7' );
     38define( 'STOREMAN_VERSION', '2.2.8' );
    3939
    4040/**
  • storeman/trunk/README.txt

    r2912185 r3382611  
    33Tags: storeman , woocommerce , stock manager , export product , import product , stock, orders, bulk edit products
    44Requires at lest: 3.0.1
    5 Tested up to: 6.0
    6 Stable tag: 2.2.7
     5Tested up to: 6.8
     6Stable tag: 2.2.8
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    2828no. this plugin is just plug&play.
    2929
     30= Use upsert and stock endpoints? =
     31
     32stock body:
     33{
     34    "fromFile":false,
     35    "url":null, // if from file
     36    "mode":"set", // enums: add / set. def: set
     37    "data":[{"sku":"EG","quantity":1}]
     38}
     39upsert body:
     40{
     41   "create_if_missing": true, // def: true
     42   "products":[{"sku":"test","name":"test name"},{"sku":"test variation","name":"test variation name","parent_sku":"test"}]
     43}
     44
    3045== Screenshots ==
    3146
     
    3348
    3449== Changelog ==
     50= 2.2.8 =
     51add elementor pro new action storeman,  add woocommerce end point stock and upsert
    3552= 2.2.7 =
    3653add elementor pro form toDo field and storeman_webhook_response action on get response. and also storeman_webhooks_fields and storeman_webhooks_print_response filters
  • storeman/trunk/admin/class-storeman-admin.php

    r2912177 r3382611  
    2323class Storeman_Admin
    2424{
    25 
    2625    /**
    2726     * The ID of this plugin.
     
    166165        return $c;
    167166    }
    168     public function register_action( $form_actions_registrar ) {
     167    public function register_action($form_actions_registrar)
     168    {
    169169
    170170
    171171        try {
    172             require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-webhook-storeman-action.php';
    173             $form_actions_registrar->register( new \StoremanElementorAction() );
     172            require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-webhook-storeman-action.php';
     173            $form_actions_registrar->register(new \StoremanElementorAction());
    174174        } catch (\Exception $th) {
    175            // cant add action to elementor pro form
    176         }
    177    
    178     }
     175            // cant add action to elementor pro form
     176        }
     177        try {
     178            require_once plugin_dir_path(dirname(__FILE__)) . 'admin/class-webhook-storeman-action-new.php';
     179            $form_actions_registrar->register(new \StoremanElementorActionNew());
     180        } catch (\Exception $th) {
     181            // cant add action to elementor pro form
     182        }
     183
     184    }
    179185    public function set_get_item_schema($c)
    180186    {
     
    301307            $attributes = array();
    302308            $parent     = wc_get_product($variation->get_parent_id());
    303    
     309
    304310            if (! $parent) {
    305311                return new WP_Error(
     
    310316                );
    311317            }
    312    
     318
    313319            $parent_attributes = $parent->get_attributes();
    314    
     320
    315321            foreach ($request['attributes'] as $attribute) {
    316322                $attribute_id   = 0;
    317323                $attribute_name = '';
    318    
     324
    319325                // Check ID for global attributes or name for product attributes.
    320326                if (! empty($attribute['id'])) {
     
    324330                    $attribute_name =  sanitize_title($attribute['name']);
    325331                }
    326    
     332
    327333                if (! $attribute_id && ! $attribute_name) {
    328334                    continue;
    329335                }
    330    
     336
    331337                if (! isset($parent_attributes[ $attribute_name ]) || ! $parent_attributes[ $attribute_name ]->get_variation()) {
    332338                    $attribute_name = strtolower($attribute_name);
    333                     if (! isset($parent_attributes[ $attribute_name ])|| ! $parent_attributes[ $attribute_name ]->get_variation()) {
     339                    if (! isset($parent_attributes[ $attribute_name ]) || ! $parent_attributes[ $attribute_name ]->get_variation()) {
    334340                        continue;
    335341                    }
    336342                }
    337    
     343
    338344                $attribute_key   = sanitize_title($parent_attributes[ $attribute_name ]->get_name());
    339345                $attribute_value = isset($attribute['option']) ? wc_clean(stripslashes($attribute['option'])) : '';
    340    
     346
    341347                if ($parent_attributes[ $attribute_name ]->is_taxonomy()) {
    342348                    // If dealing with a taxonomy, we need to get the slug from the name posted to the API.
    343349                    $term = get_term_by('name', $attribute_value, $attribute_name);
    344    
     350
    345351                    if ($term && ! is_wp_error($term)) {
    346352                        $attribute_value = $term->slug;
    347                     // $attribute_value = sanitize_title($attribute_value);
     353                        // $attribute_value = sanitize_title($attribute_value);
    348354                    } else {
    349355                        $attribute_value = sanitize_title($attribute_value);
    350356                    }
    351357                }
    352    
     358
    353359                $attributes[ $attribute_key ] = $attribute_value;
    354360            }
    355    
     361
    356362            $variation->set_attributes($attributes);
    357363        }
    358364
    359    
     365
    360366        return $variation;
    361367    }
     
    366372        }
    367373        $languages = pll_the_languages(array( 'raw' => 1 ));
    368        
    369    
     374
     375
    370376
    371377        $product = wc_get_product($parent_post_id);
     
    408414                ),
    409415            );
    410    
     416
    411417        return array_merge($topic_hooks, $new_hooks);
    412418    }
     
    419425            'update_variation',
    420426            );
    421    
     427
    422428        return array_merge($topic_events, $new_events);
    423429    }
     
    429435            'product.update_variation' => __('Product Variation Updated', 'woocommerce'),
    430436            );
     437
     438        return array_merge($topics, $new_topics);
     439    }
     440    public function storeman_update_stock_permission(\WP_REST_Request $request)
     441    {
     442        // אם WooCommerce לא פעיל – אין מה להמשיך
     443        if (!class_exists('WooCommerce')) {
     444            return false;
     445        }
     446
     447        list($ck, $cs) = $this->storeman_wc_get_basic_auth_pair($request);
     448
     449        // אם סופקו מפתחות – נוודא שהם קיימים ובעלי write
     450        if ($ck !== '' && $cs !== '') {
     451            $row = $this->storeman_wc_find_api_key_row_by_consumer_key($ck);
     452            if (!$row) {
     453                return false;
     454            }
     455
     456            // בדיקת התאמת secret
     457            if (!hash_equals((string)$row['consumer_secret'], (string)$cs)) {
     458                return false;
     459            }
     460
     461            // הרשאות צריכות להכיל write
     462            $perm = isset($row['permissions']) ? strtolower((string)$row['permissions']) : '';
     463            if (!in_array($perm, ['write','read_write'], true)) {
     464                return false;
     465            }
     466
     467            // נחשב שהבקשה אושרה – ה-user_id של המפתח יקבע ע"י מנגנון auth של WC (אם פועל),
     468            // אך מבחינתנו מספיק שהמפתח תקף ובעל write.
     469            return true;
     470        }
     471
     472        // ללא CK/CS – נאפשר למשתמשים שיש להם יכולות מתאימות במערכת (לדוגמה חיבור קיים)
     473        if (current_user_can('edit_products') || current_user_can('manage_woocommerce')) {
     474            return true;
     475        }
     476
     477        return false;
     478    }
     479    public function register_custom_routes()
     480    {
     481        register_rest_route('wc/v3', '/stock', [
     482        'methods'  => 'POST',
     483        'callback' =>  [$this,'storeman_update_stock_handler'],
     484        'permission_callback' => [$this, 'storeman_update_stock_permission'],
     485        'args' => [
     486            'fromFile' => [
     487                'description' => 'if true - send a url to load the json from file',
     488                'type'        => 'boolean',
     489                'required'    => false,
     490               
     491            ],
     492            'url' => [
     493                'description' => 'the url to load the json from if fromFile is true',
     494                'type'        => 'string',
     495                'required'    => false,
     496               
     497            ],
     498          'mode' => [
     499              'description' => 'set או add. ברירת מחדל set',
     500              'type'        => 'string',
     501              'required'    => false,
     502              'enum'        => ['set','add'],
     503          ],
     504        ],
     505        ]);
     506        register_rest_route('wc/v3', '/upsert', [
     507        'methods'  => 'POST',
     508        'callback' => [$this,'wc_upsert_by_sku_via_core'],
     509        'permission_callback' => [$this, 'storeman_update_stock_permission'],
     510        'args' => [
     511            'create_if_missing' => [
     512                'description' => 'אם המוצר/וריאציה לא קיימים, האם ליצור אותם אוטומטית',
     513                'type'        => 'boolean',
     514                'required'    => false,
     515               
     516            ],
     517            'products' => [
     518            'required' => true,
     519            'type'     => 'array',
     520            'items'    => [
     521                'type'       => 'object',
     522                'properties' => [
     523                'sku'               => [ 'required' => true,  'type' => 'string' ],
     524                'type'              => [ 'required' => false, 'type' => 'string', 'enum' => ['simple','variable','external','grouped','variation'] ],
     525                'create_if_missing' => [ 'required' => false, 'type' => 'boolean' ],
     526                'parent_sku'        => [ 'required' => false, 'type' => 'string' ],
     527                ],
     528            ],
     529            ],
     530        ],
     531        ]);
     532    }
     533    public function storeman_update_stock_handler(\WP_REST_Request $request)
     534    {
     535        if (!class_exists('WooCommerce')) {
     536            return new \WP_REST_Response(['success' => false,'message' => 'WooCommerce not active'], 400);
     537        }
     538
     539        $mode = $request->get_param('mode') ?: 'set';
     540        $fromFile = $request->get_param('fromFile') ?: false;
     541        if ($fromFile) {
     542            $url = $request->get_param('url') ?: '';
     543            if ($url === '') {
     544                return new \WP_REST_Response(['success' => false,'message' => 'Missing url parameter'], 400);
     545            }
     546            // schedule a single WP event to load/process the file in background
     547            $url = esc_url_raw($url);
     548            $args = array(
     549                'url'  => $url,
     550                'mode' => $mode,
     551            );
     552
     553            // avoid double-scheduling the exact same job
     554            if (! wp_next_scheduled('load_stock_from_file', $args)) {
     555                wp_schedule_single_event(time() + 5, 'load_stock_from_file', $args);
     556                $return = [
     557                    'status'    => true,
     558                    'message'   => 'File scheduled for background processing',
     559                    'scheduled' => true,
     560                    'url'       => $url,
     561                ];
     562            } else {
     563                $return = [
     564                    'status'    => false,
     565                    'message'   => 'A job for this file is already scheduled',
     566                    'scheduled' => false,
     567                    'url'       => $url,
     568                ];
     569            }
     570     
     571        }
     572        else
     573            {
     574                $raw = $request->get_param('data');
     575                if(is_string($raw)){
     576                    $data = json_decode($raw, true);
    431577   
    432         return array_merge($topics, $new_topics);
    433     }
     578                    // תמיכה במבנה "אובייקטים מופרדים בפסיקים" שנשלחו בלי סוגריים
     579                    if (!is_array($data)) {
     580                        $try = json_decode('['.$raw.']', true);
     581                        if (is_array($try)) {
     582                            $data = $try;
     583                        }
     584                    }
     585                    if (!is_array($data)) {
     586                        return new \WP_REST_Response(['success' => false,'message' => 'Invalid JSON array'], 400);
     587                    }
     588                }
     589                else{
     590                    $data = $raw;
     591                }
     592
     593                $results = [];
     594                $updated = 0;
     595                $return  = $this->update_batch_stock($data, $mode);
     596           
     597        }
     598        return new \WP_REST_Response($return, 200);
     599    }
     600    public function do_event_load_file_and_update_stock($url, $mode)
     601    {
     602        if (!class_exists('WooCommerce')) {
     603            // log error
     604            return;
     605        }
     606
     607        // wp_single_event('storeman_log_event', 'info', 'Fetching stock update file from URL: '.$url);
     608        $response = wp_remote_get($url);
     609        if (is_wp_error($response)) {
     610            // wp_single_event('storeman_log_event', 'error', 'Error fetching stock update file from URL: '.$url.' Error: '.$response->get_error_message());
     611            return;
     612        }
     613        $raw = wp_remote_retrieve_body($response);
     614        $data = json_decode($raw, true);
     615
     616        if (!is_array($data)) {
     617            // wp_single_event('storeman_log_event', 'error', 'Invalid JSON array in stock update file from URL: '.$url);
     618            return;
     619        }
     620
     621        $result = $this->update_batch_stock($data, $mode);
     622        // wp_single_event('storeman_log_event', 'info', 'Stock update from file completed. Updated items: '.$result['updated'].' Total items processed: '.count($data));
     623    }
     624    private function update_batch_stock(array $data, $mode)
     625    {
     626        $results = [];
     627        $updated = 0;
     628
     629        foreach ($data as $idx => $item) {
     630            $sku  = isset($item['sku']) ? trim((string)$item['sku']) : '';
     631            $qty  = isset($item['quantity']) ? $item['quantity'] : null;
     632
     633            // פר-פריט: האם לא לגעת ב-manage_stock אם הוא כבוי?
     634            // ברירת מחדל: false (כלומר כן להדליק ניהול מלאי אם כבוי)
     635            $dont_toggle_manage = !empty($item['dont_toggle_manage_stock']);
     636
     637            if ($sku === '' || !is_numeric($qty)) {
     638                $results[] = [
     639                    'index' => $idx,
     640                    'sku'   => $sku,
     641                    'status' => 'error',
     642                    'error' => 'Missing sku or invalid quantity'
     643                ];
     644                continue;
     645            }
     646
     647            // איתור מוצר/וריאציה לפי SKU
     648            $product_id = wc_get_product_id_by_sku($sku);
     649            if (!$product_id) {
     650                $results[] = [
     651                    'index' => $idx,
     652                    'sku'   => $sku,
     653                    'status' => 'not_found'
     654                ];
     655                continue;
     656            }
     657
     658            $product = wc_get_product($product_id);
     659            if (!$product) {
     660                $results[] = [
     661                    'index' => $idx,
     662                    'sku'   => $sku,
     663                    'status' => 'error',
     664                    'error' => 'Product load failed'
     665                ];
     666                continue;
     667            }
     668
     669            $was_managing = $product->managing_stock();
     670
     671            // אם ניהול מלאי כבוי:
     672            // - אם dont_toggle_manage_stock=true => לא משנים מצב ולא מעדכנים כמות (שזו המשמעות המתבקשת),
     673            //   ונחזיר status שמסביר את הדילוג.
     674            // - אחרת נדליק ניהול מלאי.
     675            if (!$was_managing) {
     676                if ($dont_toggle_manage) {
     677                    $results[] = [
     678                        'index' => $idx,
     679                        'sku'   => $sku,
     680                        'status' => 'skipped_manage_stock_off',
     681                        'reason' => 'Product is not set to manage stock and dont_toggle_manage_stock=true',
     682                        'product_id' => $product->get_id(),
     683                        'type'       => $product->get_type(),
     684                    ];
     685                    continue;
     686                } else {
     687                    $product->set_manage_stock(true);
     688                }
     689            }
     690
     691            try {
     692                $delta = (float)$qty;
     693                if ($mode === 'set') {
     694                    wc_update_product_stock($product, $delta, 'set');
     695                } else {
     696                    if ($delta > 0) {
     697                        wc_update_product_stock($product, $delta, 'increase');
     698                    } elseif ($delta < 0) {
     699                        wc_update_product_stock($product, abs($delta), 'decrease');
     700                    } // 0 => אין שינוי
     701                }
     702
     703                $product->save();
     704
     705                $results[] = [
     706                    'index'          => $idx,
     707                    'sku'            => $sku,
     708                    'status'         => 'updated',
     709                    'stock_quantity' => (float) $product->get_stock_quantity(),
     710                    'product_id'     => $product->get_id(),
     711                    'type'           => $product->get_type(),
     712                    'manage_stock'   => $product->managing_stock(),
     713                ];
     714                $updated++;
     715            } catch (\Throwable $e) {
     716                $results[] = [
     717                    'index' => $idx,
     718                    'sku'   => $sku,
     719                    'status' => 'error',
     720                    'error' => $e->getMessage()
     721                ];
     722            }
     723        }
     724       return [
     725            'success' => true,
     726            'mode'    => $mode,
     727            'updated' => $updated,
     728            'results' => $results
     729       ];
     730    }
     731    public function wc_upsert_by_sku_via_core(WP_REST_Request $request)
     732    {
     733        if (! class_exists('WooCommerce')) {
     734            return new WP_REST_Response(['error' => 'WooCommerce not active'], 400);
     735        }
     736
     737        // ודא שהבקרים טעונים
     738        if (! class_exists('WC_REST_Products_Controller') || ! class_exists('WC_REST_Product_Variations_Controller')) {
     739            return new WP_REST_Response(['error' => 'WooCommerce REST controllers unavailable'], 500);
     740        }
     741
     742        $data = $request->get_json_params() ?: [];
     743        $products = isset($data['products']) && is_array($data['products']) ? $data['products'] : [];
     744        $create_if_missing = isset($data['create_if_missing']) ? (bool)$data['create_if_missing'] : true;
     745        $results = [];
     746        foreach ($products as $k => $data) {
     747            $result = ['index' => $k];
     748            $sku  = trim((string)($data['sku'] ?? ''));
     749            if (!$sku) {
     750                $result['status'] = false;
     751                $result['error'] = 'Missing sku';
     752                $results[] = $result;
     753                continue;
     754            }
     755
     756            $type = $data['type'] ?? 'simple';
     757            $create_if_missing = array_key_exists('create_if_missing', $data) ? (bool)$data['create_if_missing'] : $create_if_missing;
     758
     759            try {
     760                if ($type === 'variation') {
     761                    // ----- וריאציה: צריך parent_sku, ונתמקד בבקר הוריאציות של ווקומרס -----
     762                    $parent_sku = trim((string)($data['parent_sku'] ?? ''));
     763                    if (!$parent_sku) {
     764
     765                        $result['status'] = false;
     766                        $result['error'] = 'Variation requires parent_sku';
     767                        $results[] = $result;
     768                        continue;
     769
     770                    }
     771
     772                    // מצא/צור הורה לפי SKU (באמצעות בקר המוצרים)
     773                    $parent_id = wc_get_product_id_by_sku($parent_sku);
     774                    $products_controller = new WC_REST_Products_Controller();
     775
     776                    if (!$parent_id) {
     777                        if (!$create_if_missing) {
     778                            $result['status'] = false;
     779                            $result['error'] = 'Parent not found by parent_sku and create_if_missing=false';
     780                            $results[] = $result;
     781                            continue;
     782                        }
     783                        // צור הורה מסוג variable (באמצעות create_item הרשמי)
     784                        $parent_create_req = new WP_REST_Request('POST', '/wc/v3/products');
     785                        $parent_payload = $data;
     786                        $parent_payload['sku']  = $parent_sku;
     787                        $parent_payload['type'] = 'variable';
     788                        $parent_create_req->set_body_params($parent_payload);
     789                        $parent_resp = $products_controller->create_item($parent_create_req);
     790                        if ($parent_resp instanceof WP_Error) {
     791
     792                            $result['status'] = false;
     793                            $result['error'] =  $parent_resp->get_error_message();
     794                            $results[] = $result;
     795                            continue;
     796                        }
     797                        $parent_arr = $parent_resp->get_data();
     798                        $parent_id  = (int)($parent_arr['id'] ?? 0);
     799                        if (!$parent_id) {
     800                            $result['status'] = false;
     801                            $result['error'] = 'Failed to create parent';
     802                            $results[] = $result;
     803                            continue;
     804                        }
     805                    } else {
     806                        // אם ההורה קיים אך אינו variable – עדכן דרך הבקר הרשמי
     807                        $parent = wc_get_product($parent_id);
     808                        if ($parent && $parent->get_type() !== 'variable') {
     809                            $parent_update_req = new WP_REST_Request('PUT', '/wc/v3/products/'.$parent_id);
     810                            $parent_update_req->set_url_params(['id' => $parent_id]);
     811                            $parent_update_req->set_body_params(['type' => 'variable']);
     812                            $u = $products_controller->update_item($parent_update_req);
     813                            if ($u instanceof WP_Error) {
     814                                $result['status'] = false;
     815                                $result['error'] = $u->get_error_message();
     816                                $results[] = $result;
     817                                continue;
     818                            }
     819                        }
     820                    }
     821
     822                    // כעת – וריאציה לפי SKU
     823                    $variation_id = wc_get_product_id_by_sku($sku);
     824                    $variations_controller = new WC_REST_Product_Variations_Controller();
     825
     826                    if ($variation_id) {
     827                        // UPDATE ע"י הבקר הרשמי: /products/{product_id}/variations/{id}
     828                        $upd_req = new WP_REST_Request('PUT', '/wc/v3/products/'.$parent_id.'/variations/'.$variation_id);
     829                        $upd_req->set_url_params(['product_id' => $parent_id, 'id' => $variation_id]);
     830                        $upd_req->set_body_params($data);
     831                        $resp = $variations_controller->update_item($upd_req);
     832                        if ($resp instanceof WP_Error) {
     833                            $result['status'] = false;
     834                            $result['error'] = $resp->get_error_message();
     835                            $results[] = $result;
     836                            continue;
     837                        }
     838                        return $resp; // מחזיר את המערך התקני של WooCommerce
     839                    } else {
     840                        if (!$create_if_missing) {
     841                            return new WP_REST_Response(['error' => 'Variation not found by sku and create_if_missing=false'], 404);
     842                        }
     843                        // CREATE ע"י הבקר הרשמי
     844                        $crt_req = new WP_REST_Request('POST', '/wc/v3/products/'.$parent_id.'/variations');
     845                        $crt_req->set_url_params(['product_id' => $parent_id]);
     846                        $crt_req->set_body_params($data);
     847                        $resp = $variations_controller->create_item($crt_req);
     848                        if ($resp instanceof WP_Error) {
     849                            return new WP_REST_Response(['error' => $resp->get_error_message()], 400);
     850                        }
     851                        $result['status'] = true;
     852                        $result['data'] = $resp->get_data();
     853                        $results[] = $result;
     854                        continue;
     855                        // return $resp;
     856                    }
     857
     858                } else {
     859                    // ----- מוצר (לא וריאציה): נשען על WC_REST_Products_Controller -----
     860                    $product_id = wc_get_product_id_by_sku($sku);
     861                    $products_controller = new WC_REST_Products_Controller();
     862
     863                    if ($product_id) {
     864                        // UPDATE ע"י הבקר הרשמי: /products/{id}
     865                        $upd_req = new WP_REST_Request('PUT', '/wc/v3/products/'.$product_id);
     866                        $upd_req->set_url_params(['id' => $product_id]);
     867                        $upd_req->set_body_params($data);
     868                        $resp = $products_controller->update_item($upd_req);
     869                        if ($resp instanceof WP_Error) {
     870
     871                            $result['status'] = false;
     872                            $result['error'] = $resp->get_error_message();
     873                            $results[] = $result;
     874                            continue;
     875                        }
     876                        $result['status'] = true;
     877                        $result['data'] = $resp->get_data();
     878                        $results[] = $result;
     879                        continue;
     880                    } else {
     881                        if (!$create_if_missing) {
     882                            $result['status'] = false;
     883                            $result['error'] = 'Product not found by sku and create_if_missing=false';
     884                            $results[] = $result;
     885                            continue;
     886                        }
     887                        // CREATE ע"י הבקר הרשמי
     888                        // ודא type אם לא נשלח
     889                        if (empty($data['type'])) {
     890                            $data['type'] = 'simple';
     891                        }
     892                        $crt_req = new WP_REST_Request('POST', '/wc/v3/products');
     893                        $crt_req->set_body_params($data);
     894                        $resp = $products_controller->create_item($crt_req);
     895                        if ($resp instanceof WP_Error) {
     896                            $result['status'] = false;
     897                            $result['error'] =  $resp->get_error_message();
     898                            $results[] = $result;
     899                            continue;
     900                        }
     901                        $result['status'] = true;
     902                        $result['data'] = $resp->get_data();
     903                        $results[] = $result;
     904                        continue;
     905                    }
     906                }
     907
     908            } catch (Throwable $e) {
     909                $result['status'] = false;
     910                $result['error'] =  $e->getMessage();
     911                $results[] = $result;
     912                continue;
     913                // return new WP_REST_Response(['error' => $e->getMessage()], 500);
     914            }
     915            // $result['status'] = true;
     916            // $results[] = $result;
     917        }
     918        return new WP_REST_Response(['results' => $results], 200);
     919    }
     920    public function storeman_wc_get_basic_auth_pair(\WP_REST_Request $request): array
     921    {
     922        // 1) Authorization: Basic ...
     923        $auth = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : '');
     924        if (!$auth && function_exists('apache_request_headers')) {
     925            $headers = apache_request_headers();
     926            if (!empty($headers['Authorization'])) {
     927                $auth = $headers['Authorization'];
     928            }
     929        }
     930        if ($auth && stripos($auth, 'Basic ') === 0) {
     931            $decoded = base64_decode(substr($auth, 6));
     932            if ($decoded !== false && strpos($decoded, ':') !== false) {
     933                list($user, $pass) = explode(':', $decoded, 2);
     934                return [trim($user), trim($pass)];
     935            }
     936        }
     937
     938        // 2) Query / Body params (WooCommerce REST API תומך גם בזה)
     939        $ck = $request->get_param('consumer_key') ?: '';
     940        $cs = $request->get_param('consumer_secret') ?: '';
     941        if ($ck !== '' && $cs !== '') {
     942            return [$ck, $cs];
     943        }
     944
     945        return ['', ''];
     946    }
     947
     948    public function storeman_wc_find_api_key_row_by_consumer_key($consumer_key)
     949    {
     950        global $wpdb;
     951
     952        if ($consumer_key === '') {
     953            return null;
     954        }
     955
     956        // WooCommerce שומר consumer_key בהאש SHA256 (לרוב דרך wc_api_hash אם קיים)
     957        if (function_exists('wc_api_hash')) {
     958            $hash = wc_api_hash($consumer_key);
     959        } else {
     960            $hash = hash('sha256', $consumer_key);
     961        }
     962
     963        $table = $wpdb->prefix . 'woocommerce_api_keys';
     964        // expected columns: key_id, user_id, permissions, consumer_key (hashed), consumer_secret, truncated_key, last_access, nonces, etc.
     965        $row = $wpdb->get_row(
     966            $wpdb->prepare("SELECT * FROM {$table} WHERE consumer_key = %s LIMIT 1", $hash),
     967            ARRAY_A
     968        );
     969
     970        return $row ?: null;
     971    }
     972
     973
     974
     975
    434976}
    435 // add_filter('storeman_stock_quantity_doubleval' , function($type){
    436 //     return true;
    437 //         });
  • storeman/trunk/admin/class-webhook-storeman-action.php

    r2912185 r3382611  
    99    public function get_label()
    1010    {
    11         return 'Storeman';
     11        return 'Storeman (Legacy)';
    1212    }
    1313    // namespace Elementor\Core\Base;
     
    3030            'storeman_domain',
    3131            [
    32                 'label' => esc_html__('Domain', 'elementor'),
     32                'label' => esc_html__('Domain (without / at the end)', 'elementor'),
    3333                'type' => 'text',
    34                 'default' => get_site_url().".storeman.co.il",
     34                'default' => "",
    3535
    3636
     
    6060            'storeman_action',
    6161            [
    62                 'label' => esc_html__('ToDo', 'elementor'),
     62                'label' => esc_html__('toDo', 'elementor'),
    6363                'type' => 'text',
    6464                'default' => '',
  • storeman/trunk/includes/class-storeman.php

    r2912177 r3382611  
    199199        $this->loader->add_action("woocommerce_rest_pre_insert_product_variation_object", $plugin_admin, 'remove_product_variation_webhook_action',1,3);
    200200        $this->loader->add_action("woocommerce_rest_insert_product_variation_object", $plugin_admin, 'remove_product_variation_webhook_action',999,3);
    201  
    202 
     201 
     202        $this->loader->add_action('rest_api_init', $plugin_admin, 'register_custom_routes' );
     203        $this->loader->add_action('load_stock_from_file', $plugin_admin, 'do_event_load_file_and_update_stock',999,2);
    203204    }
    204205
  • storeman/trunk/public/js/storeman-public.js

    r2912177 r3382611  
    33    $(document).on('submit_success', function (e, form) {
    44   
     5
     6        if (form.data.storeman_webhook_response && form.data.storeman_webhook_response.redirect) {
     7            window.location.href = form.data.storeman_webhook_response.redirect;
     8            return false;
     9        }
    510        if (form.data.storeman_webhook_response) {
    611            $("#storeman_webhook_response").remove();
     
    914       
    1015        }
     16        // if (form.data.storeman_webhook_redirect) {
     17
     18        //  $("#storeman_webhook_response").remove();
     19        //  var div = $("<div id='storeman_webhook_response'>"+form.data.storeman_webhook_response+"</div>");
     20        //  $(e.target).append(div);
     21       
     22        // }
    1123   
    1224
  • storeman/trunk/storeman.php

    r2912185 r3382611  
    1717 * Plugin URI:        https://storeman.co.il
    1818 * Description:       add storeman to your WordPress site.
    19  * Version:           2.2.7
     19 * Version:           2.2.8
    2020 * Author:            avitrop
    2121 * Author URI:        https://profiles.wordpress.org/avitrop/
     
    3636 * Rename this for your plugin and update it as you release new versions.
    3737 */
    38 define( 'STOREMAN_VERSION', '2.2.7' );
     38define( 'STOREMAN_VERSION', '2.2.8' );
    3939
    4040/**
Note: See TracChangeset for help on using the changeset viewer.