/** * Adds or updates product * * @param array $product_data Product data * @param int $product_id Product identifier * @param string $lang_code Two-letter language code (e.g. 'en', 'ru', etc.) * @return mixed ID of created/updated product or false in case of error */ function fn_update_product($product_data, $product_id = 0, $lang_code = CART_LANGUAGE) { $can_update = true; /** * Update product data (running before fn_update_product() function) * * @param array $product_data Product data * @param int $product_id Product identifier * @param string $lang_code Two-letter language code (e.g. 'en', 'ru', etc.) * @param boolean $can_update Flag, allows addon to forbid to create/update product */ fn_set_hook('update_product_pre', $product_data, $product_id, $lang_code, $can_update); if ($can_update == false) { return false; } if (fn_allowed_for('ULTIMATE')) { // check that product owner was not changed by store administrator if (Registry::get('runtime.company_id') || empty($product_data['company_id'])) { $product_company_id = db_get_field('SELECT company_id FROM ?:products WHERE product_id = ?i', $product_id); if (!empty($product_company_id)) { $product_data['company_id'] = $product_company_id; } else { if (Registry::get('runtime.company_id')) { $product_company_id = $product_data['company_id'] = Registry::get('runtime.company_id'); } else { $product_company_id = $product_data['company_id'] = fn_get_default_company_id(); } } } else { $product_company_id = $product_data['company_id']; } if (!empty($product_data['category_ids']) && !fn_check_owner_categories($product_company_id, $product_data['category_ids'])) { fn_set_notification('E', __('error'), __('product_must_have_owner_category')); return false; } if (fn_ult_is_shared_product($product_id) == 'Y') { $_product_id = fn_ult_update_shared_product($product_data, $product_id, Registry::ifGet('runtime.company_id', $product_company_id), $lang_code); } } if (fn_allowed_for('ULTIMATE') && Registry::get('runtime.company_id') && !empty($product_company_id) && Registry::get('runtime.company_id') != $product_company_id && !empty($_product_id)) { $product_id = $_product_id; $create = false; } else { $product_data['updated_timestamp'] = time(); $_data = $product_data; if (!empty($product_data['timestamp'])) { $_data['timestamp'] = fn_parse_date($product_data['timestamp']); // Minimal data for product record } elseif (empty($product_id) || isset($product_data['timestamp'])) { $_data['timestamp'] = time(); } if (empty($product_id) && Registry::get('runtime.company_id')) { $_data['company_id'] = Registry::get('runtime.company_id'); } if (!empty($product_data['avail_since'])) { $_data['avail_since'] = fn_parse_date($product_data['avail_since']); } if (isset($product_data['tax_ids'])) { $_data['tax_ids'] = empty($product_data['tax_ids']) ? '' : fn_create_set($product_data['tax_ids']); } if (isset($product_data['localization'])) { $_data['localization'] = empty($product_data['localization']) ? '' : fn_implode_localizations($_data['localization']); } if (isset($product_data['usergroup_ids'])) { $_data['usergroup_ids'] = empty($product_data['usergroup_ids']) ? '0' : implode(',', $_data['usergroup_ids']); } if (!empty($product_data['list_qty_count']) && $product_data['list_qty_count'] < 0) { $_data['list_qty_count'] = 0; } if (!empty($product_data['qty_step']) && $product_data['qty_step'] < 0) { $_data['qty_step'] = 0; } if (!empty($product_data['min_qty'])) { $_data['min_qty'] = fn_ceil_to_step(abs($product_data['min_qty']), $_data['qty_step']); } if (!empty($product_data['max_qty'])) { $_data['max_qty'] = fn_ceil_to_step(abs($product_data['max_qty']), $_data['qty_step']); } if (Registry::get('settings.General.inventory_tracking') == "N" && isset($_data['tracking'])) { unset($_data['tracking']); } if (Registry::get('settings.General.allow_negative_amount') == 'N' && isset($_data['amount'])) { $_data['amount'] = abs($_data['amount']); } $shipping_params = array(); if (!empty($product_id)) { $shipping_params = db_get_field('SELECT shipping_params FROM ?:products WHERE product_id = ?i', $product_id); if (!empty($shipping_params)) { $shipping_params = unserialize($shipping_params); } } // Save the product shipping params $_shipping_params = array('min_items_in_box' => isset($_data['min_items_in_box']) ? intval($_data['min_items_in_box']) : (!empty($shipping_params['min_items_in_box']) ? $shipping_params['min_items_in_box'] : 0), 'max_items_in_box' => isset($_data['max_items_in_box']) ? intval($_data['max_items_in_box']) : (!empty($shipping_params['max_items_in_box']) ? $shipping_params['max_items_in_box'] : 0), 'box_length' => isset($_data['box_length']) ? intval($_data['box_length']) : (!empty($shipping_params['box_length']) ? $shipping_params['box_length'] : 0), 'box_width' => isset($_data['box_width']) ? intval($_data['box_width']) : (!empty($shipping_params['box_width']) ? $shipping_params['box_width'] : 0), 'box_height' => isset($_data['box_height']) ? intval($_data['box_height']) : (!empty($shipping_params['box_height']) ? $shipping_params['box_height'] : 0)); $_data['shipping_params'] = serialize($_shipping_params); unset($_shipping_params); // add new product if (empty($product_id)) { $create = true; $product_data['create'] = true; // product title can't be empty and not set product_id if (empty($product_data['product']) || !empty($product_data['product_id'])) { fn_set_notification('E', __('error'), __('need_product_name')); return false; } $product_id = db_query("INSERT INTO ?:products ?e", $_data); if (empty($product_id)) { $product_id = false; } // // Adding same product descriptions for all cart languages // $_data = $product_data; $_data['product_id'] = $product_id; $_data['product'] = trim($_data['product'], " -"); foreach (fn_get_translation_languages() as $_data['lang_code'] => $_v) { db_query("INSERT INTO ?:product_descriptions ?e", $_data); } // update product } else { $create = false; if (isset($product_data['product']) && empty($product_data['product'])) { unset($product_data['product']); } $old_product_data = fn_get_product_data($product_id, $auth, $lang_code, '', false, false, false, false); if (isset($old_product_data['amount']) && isset($_data['amount']) && $old_product_data['amount'] <= 0 && $_data['amount'] > 0) { fn_send_product_notifications($product_id); } $arow = db_query("UPDATE ?:products SET ?u WHERE product_id = ?i", $_data, $product_id); $_data = $product_data; if (!empty($_data['product'])) { $_data['product'] = trim($_data['product'], " -"); } db_query("UPDATE ?:product_descriptions SET ?u WHERE product_id = ?i AND lang_code = ?s", $_data, $product_id, $lang_code); if ($arow === false) { fn_set_notification('E', __('error'), __('object_not_found', array('[object]' => __('product'))), '', '404'); $product_id = false; } } if ($product_id) { // Log product add/update fn_log_event('products', !empty($create) ? 'create' : 'update', array('product_id' => $product_id)); // Update product features value $product_data['product_features'] = !empty($product_data['product_features']) ? $product_data['product_features'] : array(); $product_data['add_new_variant'] = !empty($product_data['add_new_variant']) ? $product_data['add_new_variant'] : array(); fn_update_product_features_value($product_id, $product_data['product_features'], $product_data['add_new_variant'], $lang_code); fn_attach_image_pairs('p_feature_var_extra', 'p_feature_var_extra', 0, $lang_code); // Update product prices $product_data = fn_update_product_prices($product_id, $product_data); if (!empty($product_data['popularity'])) { $_data = array('product_id' => $product_id, 'total' => intval($product_data['popularity'])); db_query("INSERT INTO ?:product_popularity ?e ON DUPLICATE KEY UPDATE total = ?i", $_data, $product_data['popularity']); } fn_update_product_categories($product_id, $product_data); // Update main images pair fn_attach_image_pairs('product_main', 'product', $product_id, $lang_code); // Update additional images fn_attach_image_pairs('product_additional', 'product', $product_id, $lang_code); // Adding new additional images fn_attach_image_pairs('product_add_additional', 'product', $product_id, $lang_code); if (fn_allowed_for('ULTIMATE')) { fn_check_and_update_product_sharing($product_id); } } } /** * Update product data (running after fn_update_product() function) * * @param array $product_data Product data * @param int $product_id Product integer identifier * @param string $lang_code Two-letter language code (e.g. 'en', 'ru', etc.) * @param boolean $create Flag determines if product was created (true) or just updated (false). */ fn_set_hook('update_product_post', $product_data, $product_id, $lang_code, $create); return (int) $product_id; }
function fn_check_amount_in_stock($product_id, $amount, $product_options, $cart_id, $is_edp, $original_amount, &$cart, $update_id = 0) { fn_set_hook('check_amount_in_stock', $product_id, $amount, $product_options, $cart_id, $is_edp, $original_amount, $cart); // If the product is EDP don't track the inventory if ($is_edp == 'Y') { return 1; } $product = db_get_row("SELECT ?:products.tracking, ?:products.amount, ?:products.min_qty, ?:products.max_qty, ?:products.qty_step, ?:products.list_qty_count, ?:product_descriptions.product FROM ?:products LEFT JOIN ?:product_descriptions ON ?:product_descriptions.product_id = ?:products.product_id AND lang_code = ?s WHERE ?:products.product_id = ?i", CART_LANGUAGE, $product_id); if (isset($product['tracking']) && Registry::get('settings.General.inventory_tracking') == 'Y' && $product['tracking'] != ProductTracking::DO_NOT_TRACK) { // Track amount for ordinary product if ($product['tracking'] == ProductTracking::TRACK_WITHOUT_OPTIONS) { $current_amount = $product['amount']; // Track amount for product with options } elseif ($product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS) { $selectable_cart_id = fn_generate_cart_id($product_id, array('product_options' => $product_options), true); $current_amount = db_get_field("SELECT amount FROM ?:product_options_inventory WHERE combination_hash = ?i", $selectable_cart_id); $current_amount = intval($current_amount); } if (!empty($cart['products']) && is_array($cart['products'])) { $product_not_in_cart = true; foreach ($cart['products'] as $k => $v) { // Check if the product with the same selectable options already exists ( for tracking = O) if ($k != $cart_id) { if (isset($product['tracking']) && ($product['tracking'] == ProductTracking::TRACK_WITHOUT_OPTIONS && $v['product_id'] == $product_id) || $product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS && @$v['selectable_cart_id'] == $selectable_cart_id) { $current_amount -= $v['amount']; } } else { $product_not_in_cart = false; } } if ($product['tracking'] == ProductTracking::TRACK_WITHOUT_OPTIONS && !empty($update_id) && $product_not_in_cart && !empty($cart['products'][$update_id])) { $current_amount += $cart['products'][$update_id]['amount']; } if ($product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS) { // Store cart_id for selectable options in cart variable, so if the same product is added to // the cart with the same selectable options, but different text options, // the total amount will be tracked anyway as it is the one product if (!empty($selectable_cart_id) && isset($cart['products'][$cart_id])) { $cart['products'][$cart_id]['selectable_cart_id'] = $selectable_cart_id; } } } } $min_qty = 1; if (!empty($product['min_qty']) && $product['min_qty'] > $min_qty) { $min_qty = fn_ceil_to_step($product['min_qty'], $product['qty_step']); } if (!empty($product['qty_step']) && $product['qty_step'] > $min_qty) { $min_qty = $product['qty_step']; } $cart_amount_changed = false; // Step parity check if (!empty($product['qty_step']) && $amount % $product['qty_step']) { $amount = fn_ceil_to_step($amount, $product['qty_step']); $cart_amount_changed = true; } if (isset($current_amount) && $current_amount >= 0 && $current_amount - $amount < 0 && Registry::get('settings.General.allow_negative_amount') != 'Y') { // For order edit: add original amount to existent amount $current_amount += $original_amount; if ($current_amount > 0 && $current_amount - $amount < 0 && Registry::get('settings.General.allow_negative_amount') != 'Y') { if (!defined('ORDER_MANAGEMENT')) { fn_set_notification('W', __('important'), __('text_cart_amount_corrected', array('[product]' => $product['product']))); $amount = fn_ceil_to_step($current_amount, $product['qty_step']); } else { if ($product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS) { fn_set_notification('E', __('warning'), __('text_combination_out_of_stock')); } else { fn_set_notification('W', __('warning'), __('text_cart_not_enough_inventory')); } } } elseif ($current_amount - $amount < 0 && Registry::get('settings.General.allow_negative_amount') != 'Y') { if ($product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS) { fn_set_notification('E', __('notice'), __('text_combination_out_of_stock')); } else { fn_set_notification('E', __('notice'), __('text_cart_zero_inventory', array('[product]' => $product['product']))); } return false; } elseif ($current_amount <= 0 && $amount <= 0 && Registry::get('settings.General.allow_negative_amount') != 'Y') { fn_set_notification('E', __('notice'), __('text_cart_zero_inventory_and_removed', array('[product]' => $product['product']))); return false; } } if ($amount < $min_qty || isset($current_amount) && $amount > $current_amount && Registry::get('settings.General.allow_negative_amount') != 'Y' && Registry::get('settings.General.inventory_tracking') == 'Y' && isset($product_not_in_cart) && !$product_not_in_cart) { if (($current_amount < $min_qty || $current_amount == 0) && Registry::get('settings.General.allow_negative_amount') != 'Y' && Registry::get('settings.General.inventory_tracking') == 'Y') { if ($product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS) { fn_set_notification('E', __('warning'), __('text_combination_out_of_stock')); } else { fn_set_notification('W', __('warning'), __('text_cart_not_enough_inventory')); } if (!defined('ORDER_MANAGEMENT')) { $amount = false; } } elseif ($amount > $current_amount && Registry::get('settings.General.allow_negative_amount') != 'Y' && Registry::get('settings.General.inventory_tracking') == 'Y') { if ($product['tracking'] == ProductTracking::TRACK_WITH_OPTIONS) { fn_set_notification('E', __('warning'), __('text_combination_out_of_stock')); } else { fn_set_notification('W', __('warning'), __('text_cart_not_enough_inventory')); } if (!defined('ORDER_MANAGEMENT')) { $amount = fn_floor_to_step($current_amount, $product['qty_step']); } } elseif ($amount < $min_qty) { fn_set_notification('W', __('notice'), __('text_cart_min_qty', array('[product]' => $product['product'], '[quantity]' => $min_qty))); $cart_amount_changed = false; if (!defined('ORDER_MANAGEMENT')) { $amount = $min_qty; } } } $max_qty = fn_floor_to_step($product['max_qty'], $product['qty_step']); if (!empty($max_qty) && $amount > $max_qty) { fn_set_notification('W', __('notice'), __('text_cart_max_qty', array('[product]' => $product['product'], '[quantity]' => $max_qty))); $cart_amount_changed = false; if (!defined('ORDER_MANAGEMENT')) { $amount = $max_qty; } } if ($cart_amount_changed) { fn_set_notification('W', __('important'), __('text_cart_amount_changed', array('[product]' => $product['product']))); } fn_set_hook('post_check_amount_in_stock', $product_id, $amount, $product_options, $cart_id, $is_edp, $original_amount, $cart); return empty($amount) ? false : $amount; }