function cw_rebuild_variants($product_id, $force_rebuild = false, $tick = 1)
{
    global $tables;
    if (!$force_rebuild) {
        # Check variant's matrix
        $options_count = cw_query_first_cell("SELECT COUNT(*) FROM {$tables['product_options']}, {$tables['product_options_values']} WHERE {$tables['product_options']}.product_option_id = {$tables['product_options_values']}.product_option_id AND {$tables['product_options']}.product_id = '{$product_id}' AND {$tables['product_options']}.type = '' AND {$tables['product_options']}.avail = 1 AND {$tables['product_options_values']}.avail = 1");
        $variants_count = count(cw_query_column("SELECT COUNT(*) FROM {$tables['product_variant_items']}, {$tables['product_variants']} WHERE {$tables['product_variants']}.product_id = '{$product_id}' AND {$tables['product_variants']}.variant_id = {$tables['product_variant_items']}.variant_id GROUP BY {$tables['product_variant_items']}.option_id"));
        if ($options_count == $variants_count && $options_count > 0) {
            return true;
        }
    }
    if ($tick > 0) {
        cw_display_service_header("lbl_rebuild_variants");
    }
    $ids = cw_query_column("SELECT variant_id FROM {$tables['product_variants']} WHERE product_id = '{$product_id}'");
    if (!empty($ids)) {
        # Save old data
        $vars = cw_query_hash("SELECT pv.*, pwa.avail  FROM {$tables['product_variants']} as pv LEFT JOIN {$tables['products_warehouses_amount']} as pwa ON pv.variant_id = pwa.variant_id WHERE pv.product_id = '{$product_id}'", "variant_id", false);
        $prices = db_query("select pp.* from {$tables['products_prices']} as pp where pp.product_id = '{$product_id}' AND variant_id != 0");
        if ($prices) {
            while ($v = db_fetch_array($prices)) {
                if (!isset($vars[$v['variant_id']])) {
                    continue;
                }
                $key = $v['quantity'] . "|" . $v['membership_id'];
                if (!isset($vars[$v['variant_id']]['prices'])) {
                    $vars[$v['variant_id']]['prices'] = array();
                }
                if (!isset($vars[$v['variant_id']]['prices'][$key]) || $vars[$v['variant_id']]['prices'][$key]['price'] > $v['price']) {
                    $vars[$v['variant_id']]['prices'][$key] = $v;
                }
            }
            db_free_result($prices);
        }
        unset($prices);
        $items = cw_query_hash("SELECT {$tables['product_variant_items']}.*, {$tables['product_options_values']}.product_option_id FROM {$tables['product_variant_items']}, {$tables['product_options_values']}, {$tables['product_variants']} WHERE {$tables['product_variant_items']}.option_id = {$tables['product_options_values']}.option_id AND {$tables['product_variant_items']}.variant_id = {$tables['product_variants']}.variant_id AND {$tables['product_variants']}.product_id = '{$product_id}'", array('product_option_id', "option_id"), true, true);
        # Delete old variants
        /*
                $tmp = cw_query_first("SELECT MIN(avail) as avail, MIN(weight) as weight FROM $tables[product_variants] WHERE product_id = '$product_id'");
                db_query("UPDATE $tables[products] SET avail = '$tmp[avail]', weight = '$tmp[weight]' WHERE product_id = '$product_id'");
                unset($tmp);
        */
        db_query("DELETE FROM {$tables['products_prices']} WHERE product_id = '{$product_id}' AND variant_id != 0");
        db_query("DELETE FROM {$tables['product_variant_items']} WHERE variant_id IN ('" . implode("','", $ids) . "')");
        db_query("DELETE FROM {$tables['products_warehouses_amount']} WHERE product_id = '{$product_id}' AND variant_id != 0");
    }
    unset($ids);
    db_query("DELETE FROM {$tables['product_variants']} WHERE product_id = '{$product_id}'");
    # Get modifier-classes
    $classes = cw_query($sql = "SELECT product_option_id FROM {$tables['product_options']} WHERE product_id = '{$product_id}' AND type = '' AND avail = 1 ORDER BY orderby");
    if (empty($classes)) {
        return false;
    }
    foreach ($classes as $k => $v) {
        $classes[$k]['cnt'] = 0;
        $classes[$k]['options'] = cw_query_column("SELECT option_id FROM {$tables['product_options_values']} WHERE product_option_id = '{$v['product_option_id']}' AND avail = 1 ORDER BY orderby, option_id ASC ");
        if (!@count($classes[$k]['options']) || !is_array($classes[$k]['options'])) {
            unset($classes[$k]);
        }
    }
    if (empty($classes)) {
        return false;
    }
    $classes = array_values($classes);
    $classes[0]['cnt'] = -1;
    # Build variant's matrix
    $variants = array();
    # Write variants to DB
    $product = cw_query_first("SELECT {$tables['products']}.eancode, {$tables['products']}.productcode, {$tables['products']}.weight,  {$tables['products']}.cost, {$tables['products_prices']}.price FROM {$tables['products']}, {$tables['products_prices']}  WHERE  {$tables['products_prices']}.variant_id = 0 AND {$tables['products_prices']}.quantity = '1' AND {$tables['products_prices']}.membership_id = 0 AND {$tables['products']}.product_id = '{$product_id}' GROUP BY {$tables['products']}.product_id");
    $cnt_row = $cnt = $cnd_ean = 0;
    do {
        $is_end = false;
        $options = array();
        $old_variants = array();
        foreach ($classes as $k => $c) {
            $option_id = 0;
            if (!$is_end) {
                if ($c['cnt'] >= count($c['options']) - 1) {
                    $c['cnt'] = 0;
                } else {
                    $c['cnt']++;
                    $is_end = true;
                }
                $classes[$k] = $c;
            }
            $option_id = $c['options'][$c['cnt']];
            if (empty($option_id)) {
                continue;
            }
            $options[] = $option_id;
            if (isset($items[$c['product_option_id']][$option_id])) {
                if (empty($old_variants)) {
                    $old_variants = $items[$c['product_option_id']][$option_id];
                } else {
                    $old_variants = array_intersect($old_variants, $items[$c['product_option_id']][$option_id]);
                }
            }
        }
        if (!$is_end || empty($options)) {
            break;
        }
        $_product = $product;
        # Restore old data
        $old_variant_id = false;
        if (is_array($old_variants) && !empty($old_variants)) {
            $old_variant_id = array_shift($old_variants);
            if (isset($vars[$old_variant_id])) {
                $_product = cw_array_merge($_product, $vars[$old_variant_id]);
            }
        }
        unset($old_variants);
        # Get unique SKU
        $sku = $_product['productcode'];
        while (cw_query_first_cell("SELECT COUNT(*) FROM {$tables['product_variants']} WHERE productcode = '{$sku}'") > 0) {
            $sku = $_product['productcode'] . ++$cnt;
        }
        $eancode = $_product['eancode'];
        while (cw_query_first_cell("SELECT COUNT(*) FROM {$tables['product_variants']} WHERE eancode = '{$eancode}'") > 0) {
            $eancode = $_product['eancode'] . ++$cnd_ean;
        }
        $data = array("product_id" => $product_id, "weight" => $_product['weight'], "cost" => $_product['cost'], "productcode" => $sku, 'eancode' => $eancode);
        # Check variant_id
        if (!empty($old_variant_id) && cw_query_first_cell("SELECT COUNT(*) FROM {$tables['product_variants']} WHERE variant_id = '{$old_variant_id}'") == 0) {
            $data['variant_id'] = $old_variant_id;
        }
        # Insert variant info
        $variant_id = cw_array2insert('product_variants', $data);
        if (empty($variant_id)) {
            continue;
        }
        if ($_product['avail'] == NULL) {
            $_product['avail'] = 0;
        }
        if (cw_query_first_cell("SELECT COUNT(*) FROM {$tables['products_warehouses_amount']} WHERE variant_id = '{$variant_id}'") == 0) {
            cw_array2insert('products_warehouses_amount', array('product_id' => $product_id, 'avail' => $_product['avail'], 'avail_ordered' => 0, 'avail_sold' => 0, 'avail_reserved' => '0', 'variant_id' => $variant_id, 'warehouse_customer_id' => '0'), false);
        }
        # Write products_prices
        if (empty($_product['prices'])) {
            cw_price_lists_replace_price($product_id, $_product['price'], $variant_id, true, true);
        } else {
            foreach ($_product['prices'] as $p) {
                cw_price_lists_replace_price($product_id, $p['price'], $variant_id, true, $p['is_manual']);
            }
        }
        # Restore image
        if (!empty($old_variant_id) && $variant_id != $old_variant_id) {
            cw_image_delete($variant_id, "W");
            db_query("UPDATE {$tables['products_images_var']} SET id = '{$variant_id}' WHERE id = '{$old_variant_id}'");
        }
        # Write matrix
        foreach ($options as $i) {
            db_query("INSERT INTO {$tables['product_variant_items']} (variant_id, option_id) VALUES ('{$variant_id}','{$i}')");
        }
        if ($tick > 0 && $cnt_row++ % $tick == 0) {
            cw_flush(". ");
        }
    } while ($is_end);
    # Clean old variants images
    $images = cw_query_column("SELECT {$tables['products_images_var']}.id FROM {$tables['product_variants']} LEFT JOIN {$tables['products_images_var']} ON {$tables['product_variants']}.variant_id = {$tables['products_images_var']}.id WHERE {$tables['products_images_var']}.id IS NULL");
    if (!empty($images)) {
        cw_image_delete($images, "W");
    }
    return true;
}
         cw_array2update('product_variants', $query_data, "variant_id = '{$k}'");
         cw_price_lists_replace_price($product_id, $v['price'], $k, false, $v['is_manual_price']);
         $v['variant_id'] = $k;
         $v['product_id'] = $product_id;
         $v['warehouse_customer_id'] = 0;
         cw_array2insert('products_warehouses_amount', $v, 1, array('product_id', 'avail', 'avail_ordered', 'avail_sold', 'avail_reserved', 'variant_id', 'warehouse_customer_id'));
         cw_call('cw_warehouse_recalculate', array($product_id));
         if ($ge_id && !$fields['variants'][$k]) {
             cw_unset($query_data, 'productcode');
             while ($pid = cw_group_edit_each($ge_id, 1, $product_id)) {
                 $vid = cw_variants_get_same($k, $pid);
                 if (empty($vid)) {
                     continue;
                 }
                 cw_array2update('product_variants', $query_data, "variant_id = '{$vid}'");
                 cw_price_lists_replace_price($pid, $v['price'], $vid, false, $v['is_manual_price']);
                 if ($def_variant == $k) {
                     cw_array2update('product_variants', array('def' => ''), "product_id = '{$pid}'");
                     cw_array2update('product_variants', array('def' => 'Y'), "product_id = '{$pid}' and variant_id='{$vid}'");
                 }
             }
         }
     }
 }
 if (!empty($def_variant)) {
     cw_array2update('product_variants', array('def' => ''), "product_id = '{$product_id}'");
     cw_array2update('product_variants', array("def" => 'Y'), "product_id = '{$product_id}' and variant_id='{$def_variant}'");
 }
 if (is_array($vids) && cw_image_check_posted($file_upload_data['products_images_var'])) {
     $vids = array_keys($vids);
     $vid = array_shift($vids);
 if ($is_new_product) {
     cw_add_top_message(cw_get_langvar_by_name('msg_adm_product_add'));
 } else {
     cw_add_top_message(cw_get_langvar_by_name("msg_adm_product_upd"));
 }
 if (!$is_variant) {
     //cw_price_lists_replace_price($product_id, $product_data['price'], 0, $is_new_product, $product_data['is_manual_price']);
     if (isset($product_data['price']) && isset($product_data['list_price'])) {
         cw_product_update_price($product_id, 0, 0, 0, 1, 1, $product_data['price'], $product_data['list_price']);
     }
 }
 if ($fields['price'] && !$is_variant) {
     if ($ge_id) {
         while ($pid = cw_group_edit_each($ge_id, 1, $product_id)) {
             if ($pid != $product_id) {
                 cw_price_lists_replace_price($pid, $product_data['price'], 0);
             }
         }
     }
 }
 cw_func_call('cw_items_attribute_classes_save', array('item_id' => $product_id, 'attribute_class_ids' => $product_data['attribute_class_ids'], 'item_type' => 'P'));
 # kornev, it have to be product_data here - because we change the attributes in the error_check function
 cw_call('cw_attributes_save', array('item_id' => $product_id, 'item_type' => 'P', 'attributes' => $product_data['attributes'], 'language' => $edited_language, array('update_posted_only' => true, 'is_default' => false)));
 cw_attributes_group_update($ge_id, $product_id, 'P', $fields);
 cw_func_call('cw_product_build_flat', array('product_id' => $product_id));
 cw_group_edit_end($product_id);
 cw_product_update_system_info($product_id, array('supplier_customer_id' => $product_data['supplier']));
 cw_group_edit_copy_system_info($product_id, array('supplier_customer_id' => $product_data['supplier']));
 cw_warehouse_recalculate($product_id);
 cw_product_filter_recalculate_price_ranges();
 // tags