Import Order Items by Product Code

Hi,



I am wandering if there is a way to import order items to a particular order by specifying the product code instead of item ID?



I understand the standard importing function is used only for exported items from CS-Cart, but I still require this functionality.



Is there an addon available, or some code someone has written?



Please let me know.



Thanks.



Nathan

For those interested, I have made some progress on this. Here is the base code I am using for this functionality. I understand that the code could be optimised and it is not 100% tested, but here it is. The best thing is there is no core modifications!



Hopefully it will help you.



Inside an addon (like my_changes) define these hooks:



fn_register_hooks(
'import_pre_moderation',
'import_complete'
);




In the func.php file of the addon add the following:



function fn_my_changes_import_pre_moderation($pattern, &$import_data, &$options, $processed_data) {

if($pattern["section"] == "orders" && $pattern["pattern_id"] == "order_items") {

foreach($import_data as &$row) {
if(empty($row["item_id"]) && !empty($row["product_code"])) {

// add an option to force order recalculation at the end
$options["force_recalculation"] = true;

// set additional fields
if(empty($row["amount"])) $row["amount"] = 1;

$params = array();
$params["pcode"] = $row["product_code"];

list($products, $search, $product_count) = fn_get_products($params, 1, DESCR_SL);

if(count($products)==1) {

$product = $products[0];

$product["amount"] = $row["amount"];

fn_gather_additional_products_data($product, array('get_icon' => false, 'get_detailed' => false, 'get_options' => true, 'get_discounts' => false));

// set product price
if(empty($row["price"])) $row["price"] = $product["price"];

if(empty($row["Extra"])) {

$e = array();

if(!empty($product["selected_options"])) $e["product_options"] = $product["selected_options"];
if(!empty($product["product"])) $e["product"] = $product["product"];
$e['stored_price'] = 'N';
$_data = db_get_row('SELECT is_edp, options_type, tracking, unlimited_download FROM ?:products WHERE product_id = ?i', $product["product_id"]);
if (isset($_data['is_edp'])) {
$e['is_edp'] = $_data['is_edp'];
}
if (isset($_data['unlimited_download'])) {
$e['unlimited_download'] = $_data['unlimited_download'];
}
$e["return_period"] = 1;
if(!empty($product["base_price"])) $e["base_price"] = $product["base_price"];

if(!empty($product["selected_options"])) {

$product_option_value = array();
foreach($product["selected_options"] as $option_id => $variant_id) {
$obj = array();

foreach($product["product_options"] as $product_option) {
if($option_id == $product_option["option_id"]) {

$obj["option_id"] = $product_option["option_id"];
$obj["product_id"] = $product_option["product_id"];
$obj["option_type"] = $product_option["option_type"];
$obj["inventory"] = $product_option["inventory"];
$obj["required"] = $product_option["required"];
$obj["multiupload"] = $product_option["multiupload"];
$obj["missing_variants_handling"] = $product_option["missing_variants_handling"];
$obj["status"] = $product_option["status"];
$obj["option_name"] = $product_option["option_name"];

if(isset($product_option["variants"][$variant_id])) {
$variant = $product_option["variants"][$variant_id];
$obj["modifier"] = $variant["modifier"];
$obj["modifier_type"] = $variant["modifier_type"];
$obj["variant_name"] = $variant["variant_name"];
$obj["value"] = $variant_id;
break;
}
}
}

if(!empty($obj)) $product_option_value[] = $obj;
break;
}

$e["product_options_value"] = $product_option_value;
}

$row["Extra"] = YAML_Parser::serialize($e);
}

// to get cart id
$extra = array(
"product_id" => $product["product_id"],
"product_options" => $product["selected_options"],
"amount" => $row["amount"]
);

$item_id = fn_generate_cart_id($product["product_id"], $extra, false);

$row["item_id"] = $item_id;
$row["product_id"] = $product["product_id"];

}
}
}

// Check if any product rows need to be merged
$data = array();
foreach($import_data as $data_row) {
$found = false;
for($i=0;$i $new_row = $data[$i];
if($new_row["order_id"] == $data_row["order_id"] && $new_row["product_code"] == $data_row["product_code"] && $new_row["item_id"] == $data_row["item_id"]) {
$found = true;
$data[$i]["amount"] += $data_row["amount"];
break;
}
}
if(!$found) {
$data[] = $data_row;
}
}
$import_data = $data;

my_ch_order_item_import_manager::$import_data = $import_data;
my_ch_order_item_import_manager::$import_options = $options;

}

}
function fn_my_changes_import_complete() {
if(!empty(my_ch_order_item_import_manager::$import_data) && !empty(my_ch_order_item_import_manager::$import_options)) {
if(!empty(my_ch_order_item_import_manager::$import_options["force_recalculation"])) {
// the previous import that was made was an order items import that requires some cost recalculation.

$data = my_ch_order_item_import_manager::$import_data;

$orders = array();
foreach($data as $row) {
$orders[$row["order_id"]] = (empty($orders[$row["order_id"]]) ? 0 : $orders[$row["order_id"]]);
$orders[$row["order_id"]] += ($row["price"]*$row["amount"]);

// reduce the quantity of this item in the inventory.
$extra = YAML_Parser::unserialize($row["Extra"]);
$po = (empty($extra["product_options"]) ? array() : $extra["product_options"]);

// if ($new_amount < 0 && Registry::get('settings.General.allow_negative_amount') != 'Y') {
fn_update_product_amount($row["product_id"], $row["amount"], $po, "-");

}

foreach($orders as $order_id=>$additional_price) {
db_query("UPDATE ?:orders SET total=total + ?d,subtotal=subtotal + ?d WHERE order_id = ?i", $additional_price, $additional_price, $order_id);
}

my_ch_order_item_import_manager::$import_data = null;
my_ch_order_item_import_manager::$import_options = null;
}
}
}

class my_ch_order_item_import_manager {
public static $import_data = array();
public static $import_options = array();
}

That is pure GENIUS Nathan! - CS-Cart please note !



This concept solves so many problems - let me elaborate:



The import add-on for Multistore does not import from Community to Multistore and the Upgrade from Community to Ultimate will only ever import one store at a time (when it works) and not combine multiple stores into one Ultimate installation.



This concept of importing by Product Code solves this problem and more. With multiple stores and the same product in some of the stores, using this idea to import by product code you will be linking the order items to the new single product. This way you can import the products using the standard import tool and all the order items will be linked correctly from each store to one product.



I was going down the route of having to modify the order items to change the ProductID within the order items (exported from Community) to match the new ProductID of Ultimate. I guess I could still do this - but by realising that the product code could be used to achieve a 1-1 relationship between source and target stores it is so much easier. Although it wouldn't work if you had used the same product code for different products (even on different stores) - but then product codes should be unique to the product anyway (even across stores).



I recommend that CS-Cart support this brilliant idea as it solves so many issues with converting from multiple stores to Ultimate and keeping order items linked correctly (ideally products and combinations). It also allows importing of orders from other shopping carts (which is I guess is what Nathan developed this for) and would allow users of competitor carts to import order items when they realise they should have been using CS-Cart all along and decide to switch (better late than never!).



Thank you for sharing - and would be glad to know of any problems / fixes that you encounter.



One question though - which version / package have you had this working on - I am hoping it would work for 3.0.2 Ultimate. Order items are not associated with stores (but with orders), so hoping it would work on Ultimate.

Hey! Thanks!



The reason behind this is for one of my clients who take stock to fairs, expos etc… I wrote a product barcode addon which generates barcode labels which they can simply scan for all the products they sell at the expo. They can then export a report of all the products sold and import them to an order in CS-Cart. (At the moment, an order has to be manually created and then the order items can be tied to that order)



This snippet updates order totals and also updates inventory counts too.



I have this working on 3.0.2 professional. I haven’t delved into ultimate as of yet, but I would assume that it should work ok, you may need to test it and let us know :)



Nathan

Hey Nathan,



Just wanted to say thanks. I'm not actually using your code but you have given me a good idea of how I should be using hooks. I've made a few changes to cscart to allow me to bulk upload a Text Box option and bulk upload all the attributes that are available through the admin page (regex, comment, description etc).



Your code has given me a good idea of how I can do it without having to hack around with the core!



Rikk