Magento API 'Exception' with message 'Item (Mage_Sales_Model_Order) with the same id "X" already exist' - magento

I'm trying to get a list of orders using the Magento REST API.
The REST request we use is pretty basic: http://www.example.com/api/rest/orders
The response shows the next error:
{
"messages": {
"error": [
{
"code": 0,
"message": "Item (Mage_Sales_Model_Order) with the same id \"54\" already exist"
}
]
}
}
Checking my exception log to see what's going on and got the next backtrace of the error:
2015-09-10T21:54:59+00:00 ERR (3):
exception 'Exception' with message 'Item (Mage_Sales_Model_Order) with the same id "54" already exist' in /path/to/site/lib/Varien/Data/Collection.php:373
Stack trace:
#0 /path/to/site/lib/Varien/Data/Collection/Db.php(576): Varien_Data_Collection->addItem(Object(Mage_Sales_Model_Order))
#1 /path/to/site/lib/Varien/Data/Collection.php(301): Varien_Data_Collection_Db->load()
#2 /path/to/site/app/code/core/Mage/Sales/Model/Api2/Order.php(302): Varien_Data_Collection->getItems()
#3 /path/to/site/app/code/core/Mage/Api2/Model/Resource.php(245): Mage_Sales_Model_Api2_Order->_retrieveCollection()
#4 /path/to/site/app/code/core/Mage/Api2/Model/Dispatcher.php(74): Mage_Api2_Model_Resource->dispatch()
#5 /path/to/site/app/code/core/Mage/Api2/Model/Server.php(239): Mage_Api2_Model_Dispatcher->dispatch(Object(Mage_Api2_Model_Request), Object(Mage_Api2_Model_Response))
#6 /path/to/site/app/code/core/Mage/Api2/Model/Server.php(107): Mage_Api2_Model_Server->_dispatch(Object(Mage_Api2_Model_Request), Object(Mage_Api2_Model_Response), Object(Mage_Api2_Model_Auth_User_Admin))
#7 /path/to/site/api.php(67): Mage_Api2_Model_Server->run()
#8 {main}
I modified the file app/code/core/Mage/Sales/Model/Api2/Order.php (function _retrieveCollection) and added a line to print some info on the logs:
Mage::log($collection->getSelect(),null,'mylog.log');
That's part of the output:
[_parts:protected] => Array
(
[straightjoin] =>
[distinct] =>
[columns] => Array
(
[0] => Array
(
[0] => main_table
[1] => *
[2] =>
)
[1] => Array
(
[0] => payment_method
[1] => method
[2] => payment_method
)
[2] => Array
(
[0] => gift_message
[1] => sender
[2] => gift_message_from
)
[3] => Array
(
[0] => gift_message
[1] => recipient
[2] => gift_message_to
)
[4] => Array
(
[0] => gift_message
[1] => message
[2] => gift_message_body
)
[5] => Array
(
[0] => order_tax
[1] => title
[2] => tax_name
)
[6] => Array
(
[0] => order_tax
[1] => percent
[2] => tax_rate
)
)
[union] => Array
(
)
[from] => Array
(
[main_table] => Array
(
[joinType] => from
[schema] =>
[tableName] => sales_flat_order
[joinCondition] =>
)
[payment_method] => Array
(
[joinType] => left join
[schema] =>
[tableName] => sales_flat_order_payment
[joinCondition] => main_table.entity_id = payment_method.parent_id
)
[gift_message] => Array
(
[joinType] => left join
[schema] =>
[tableName] => gift_message
[joinCondition] => main_table.gift_message_id = gift_message.gift_message_id
)
[order_tax] => Array
(
[joinType] => left join
[schema] =>
[tableName] => sales_order_tax
[joinCondition] => main_table.entity_id = order_tax.order_id
)
)
[where] => Array
(
)
[group] => Array
(
)
[having] => Array
(
)
[order] => Array
(
)
[limitcount] =>
[limitoffset] =>
[forupdate] =>
)
[_tableCols:protected] => Array
(
)
)
If I understood correctly that means that the SQL statement was something like:
SELECT
main_table.*,
payment_method.method AS method,
gift_message.sender AS gift_message_from,
gift_message.recipient AS gift_message_to,
gift_message.message AS gift_message_body,
order_tax.title AS tax_name,
order_tax.percent AS tax_rate
FROM
sales_flat_order AS main_table LEFT JOIN
sales_flat_order_payment AS payment_method ON main_table.entity_id = payment_method.parent_id LEFT JOIN
gift_message ON main_table.gift_message_id = gift_message.gift_message_id LEFT JOIN
sales_order_tax AS order_tax ON main_table.entity_id = order_tax.order_id
After manually running the previous query, it came up with more than one row with the same entity_id (sales_flat_order). These 'duplicated' entity_id rows seem to be the problem later on when using Varien_Data_Collection->addItem
The part of the query that is making the two rows with same entity_id to be on the resultset is the LEFT JOIN sales_order_tax. That table contains can contain N rows per each order placed, since every row contains a different tax rule applied.
For example in Canada we collect two different Tax Rules combined for
some areas. In British Columbia we collect GST 5% (country specific)
plus PST 7% (province specific).
Am I missing something obvious here, or did I run into a bug?
Any help is much appreciated, thanks for reading!
P.S. My issue is very close to the one described in here: Magento API V2 Sales Orders List Not Working

After a while playing around I think I found a solution, so I'm posting it here for future references.
Solution
We need to modify the query of the collection, to group by sales_flat_order.entity_id before it starts iterating the items in Mage_Sales_Model_Api2_Order::_retrieveCollection.
Since modifying core files it's not a good practice we can make a copy of the core class to the local code pool and modify it as desired. Magento will use the class under the local folder to override the core class.
Copy app/code/core/Mage/Sales/Model/Api2/Order.php to app/code/local/Mage/Sales/Model/Api2/Order.php
Modify the file under the local code pool folder (app/code/local/Mage/Sales/Model/Api2)
Look for the function _retrieveCollection (around line 288 in Magento 1.9.2)
After the line $this->_addTaxInfo($collection); you should add:
$collection->getSelect()->group('main_table.entity_id');
Cons
If we are updating Magento we want to compare our current version of the Mage_Sales_Model_Api2_Order class with the new version, and if we find differences we shall repeat the process of making a copy of the core file under the local code pool folder and perform the edition again.

Related

Square API - Object Update for Multiple Locations

I'm trying to update objects using the endpoint BatchRetrieveCatalogObjects (POST /v2/catalog/batch-upsert).
When I post the request, I get the error
[type] => bad_request
[message] => Multi-location support is unavailable for this endpoint. Parameter `merchant_id` must correspond to a single-location account, not a business account
How can I update objects if there are multiple locations and if items have location overrides?
Is there another endpoint that I can use?
UpsertCatalogObject (POST /v2/catalog/object) does not work either. Same error.
Below is a sample request payload:
Array
(
[idempotency_key] => 1122334455667788
[batches] => Array
(
[type] => ITEM
[id] => 5BLFIPNYBTDD5ARZYQU4K7SV
[item_data] => Array
(
[name] => Honey Mead
[description] => Mead is an adult beverage brewed exclusively with honey.
[category_id] => OIMGQF7DCO7W6SEXEMKGIJK5
)
[variations] => Array
(
[type] => ITEM_VARIATION
[id] => BEQZ6FXAJACGW55RRZBMXDEI
[item_variation_data] => Array
(
[item_id] => 5BLFIPNYBTDD5ARZYQU4K7SV
[name] => Case (24 bottles)
[pricing_type] => FIXED_PRICING
[ordinal] => 4
[price_money] => Array
(
[amount] => 11900
)
[location_overrides] => Array
(
[0] => Array
(
[location_id] => 81J6E4K21KGC9
[price_money] => Array
(
[amount] => 11900
)
[pricing_type] => FIXED_PRICING
[track_inventory] => 1
[inventory_alert_type] => LOW_QUANTITY
[inventory_alert_threshold] => 15
)
)
)
)
)
)
Additional Notes for Clarification:
I'm trying to edit the price at either the variation or location_overrides level. I get the error for any call to the endpoint. I think the Square API developers don't allow any calls to the endpoint for any multi-unit locations. I also cannot delete locations either on the website or API. They only allow you to hide locations, which I did. But the error still persists.
I tried the v1 endpoint and get strange results. V1 & V2 use different variation_id. So if I pass a v2 variation_id to the v1 endpoint, it creates a new variation rather than updating it. The new variation then displays a new field called catalog_v1_id. Then when I try to delete the variation on the website, it disappears from the website view but still exists as an active item when I call the v2 list variation endpoint. I'm thinking this API has a whole host of bugs and is not ready for prime time.

How can I sort an array by specific value containing class object inside it?

I want to sort this array by product price in ascending order. How can I do this?
Array
(
[0] => stdClass Object
(
[product_id] => 16
[product_name] => Ferrari 4802
[product_details] => I know this car is good. Because I have driven this car 10 year.
[product_quantity] => 1
[product_price] => 2560
[shipping_cost] => 1000
[product_image] => ./admin_assets/image/uploads/product_image/ea28444f93d1fea413c95861bf306fbf.jpg
[product_details_image] => ./admin_assets/image/uploads/product_details/ea28444f93d1fea413c95861bf306fbf.jpg
[thumbnails_image] => ./admin_assets/image/uploads/thumbnails/ea28444f93d1fea413c95861bf306fbf.jpg
[slidder_image] => ./admin_assets/image/uploads/slidders/ea28444f93d1fea413c95861bf306fbf.jpg
[status] => 1
[slidder] =>
[category_id] => 4
)
[1] => stdClass Object
(
[product_id] => 15
[product_name] => Laborghini urus
[product_details] => I know this car is good. Because I have driven this car 10 year.
[product_quantity] => 2
[product_price] => 500
[shipping_cost] => 1000
[product_image] => ./admin_assets/image/uploads/product_image/68a7eb1c6bf5b3b43f0c3fd4ed143c68.jpg
[product_details_image] => ./admin_assets/image/uploads/product_details/68a7eb1c6bf5b3b43f0c3fd4ed143c68.jpg
[thumbnails_image] => ./admin_assets/image/uploads/thumbnails/68a7eb1c6bf5b3b43f0c3fd4ed143c68.jpg
[slidder_image] => ./admin_assets/image/uploads/slidders/68a7eb1c6bf5b3b43f0c3fd4ed143c68.jpg
[status] => 1
[slidder] =>
[category_id] => 4
)
[2] => stdClass Object
(
[product_id] => 14
[product_name] => Laborghini hurican023
[product_details] => I know this car is good. Because I have driven this car 10 year.
[product_quantity] => 4
[product_price] => 50
[shipping_cost] => 1000
[product_image] => ./admin_assets/image/uploads/product_image/65e660ef6e3ebcb0773d87c048c93e1b.png
[product_details_image] => ./admin_assets/image/uploads/product_details/65e660ef6e3ebcb0773d87c048c93e1b.png
[thumbnails_image] => ./admin_assets/image/uploads/thumbnails/65e660ef6e3ebcb0773d87c048c93e1b.png
[slidder_image] => ./admin_assets/image/uploads/slidders/65e660ef6e3ebcb0773d87c048c93e1b.png
[status] => 1
[slidder] =>
[category_id] => 4
)
)
Because the array is generated by a database query you can easily have the query do the sorting for you.
You don't show your model code but assuming you are using Query Builder (a.k.a. Active Record) you need the following in your model before you execute $this->db->get().
$this->db->order_by('product_price', 'ASC');
Here is some example code showing a full query. It assumes the table is named 'products', you want to select all the table's fields, and you want only a particular 'category_id'.
In the Model file:
public function product_by_category($category)
{
$query = $this->db
->where('category_id', $category)
->order_by('product_price', 'ASC')
->get('products');
//return all records or NULL if no records are found
return $query->num_rows() > 0 ? $query->result() : NULL;
}

Get the ordered product size and color in order items admin grid in Magento 1.9.2

I am showing a list of items ordered from all the orders from 'sales_flat_order_item table' in Magneto 1.9.2. I get the name, SKU, and all other values from the following collection.
protected function _prepareCollection() {
$collection = Mage::getModel('sales/order_item')->getCollection();
$collection->getSelect()->join(array('o' => 'sales_flat_order'), 'main_table.order_id = o.entity_id', array('increment_id','created_at'));
$this->setCollection($collection);
parent::_prepareCollection();
return $this;
}
Now I want to get the size and color of the item ordered. I am new to Magento, so I have no clue where I can get these values from.
You can access them in sales_flat_order_item table collection.
$data = $collection->getData();
$optionsData = unserialize($data['product_options']);
echo "<pre>";print_r($optionsData);
// check the [attributes_info] for the custom options
The data in product_options is serialized, so if you unserialize it you end up with something like below :
Array
(
[info_buyRequest] => Array
(
[uenc] => aHR0cDovLzUwLjU2LjIxMy4xNTcvbWVuL3RlZXMta25pdHMtYW5kLXBvbG9zL2NoZWxzZWEtdGVlLTUxNC5odG1sP29wdGlvbnM9Y2FydA,,
[product] => 409
[related_product] =>
[super_attribute] => Array
(
[92] => 20
[180] => 80
)
[qty] => 6
[return_url] =>
)
[attributes_info] => Array
(
[0] => Array
(
[label] => Color
[value] => Black
)
[1] => Array
(
[label] => Size
[value] => S
)
)
[simple_name] => Chelsea Tee
[simple_sku] => mtk004
[product_calculations] => 1
[shipment_type] => 0
[giftcard_lifetime] =>
[giftcard_is_redeemable] => 0
[giftcard_email_template] =>
[giftcard_type] =>
)

Magento custom shippment method

I wrote shippment module with checking a lot of resources/tutorials etc. I am using magento 1.9. Source of module is:
https://github.com/aleextra/magentoshipping
I lost full day to find why it doesn't works fine. Magento see my module, when I am adding at Model/Carrier.php at line 52 code:
die($result);
it shows objec dump:
Mage_Shipping_Model_Rate_Result Object
(
[_rates:protected] => Array
(
[0] => Mage_Shipping_Model_Rate_Result_Method Object
(
[_data:protected] => Array
(
[carrier] => mikshipping
[carrier_title] => Mik Carrier
[method] => fixed
[method_title] => Fixed price 10
[price] => 10
[cost] => 10.00
)
[_hasDataChanges:protected] => 1
[_origData:protected] =>
[_idFieldName:protected] =>
[_isDeleted:protected] =>
[_oldFieldsMap:protected] => Array
(
)
[_syncFieldsMap:protected] => Array
(
)
)
)
[_error:protected] =>
)
What I am doing wrong?
As a quick guess I say that your carriers name in the config.xml (mshipping) does not match the code used in Carrier.php (mikshipping). I have them always being the same so Iḿ not absolutely sure if this might cause the issue, but it is easy to check.

Magento - How to set media gallery images programmatically?

I try to set a new magento product image e.g. che_3.png with:
$visibility = array (
'thumbnail',
'small_image',
'image'
);
$product->addImageToMediaGallery( $file, $visibility, true, false);
The product image is moved from the
temporary directory: /media/tmp/catalog/product/upload/sessionid/original/che_3.png
to the
destination directory: /media/catalog/product/c/h/che_3.png
Now, if i want to call the image filepath - that is saved by magento - is wrong. The media_gallery object looks like this:
[media_gallery] => Array
(
[images] => Array
(
[0] => Array
(
[value_id] => 89
[file] => /c/h/che_3_2_1.png //che_3.svg is already in here
[label] =>
[position] => 1
[disabled] => 0
[label_default] =>
[position_default] => 1
[disabled_default] => 0
)
[1] => Array // why this second item?
(
[value_id] => 88
[file] => /c/h/che_3_2.png
[label] =>
[position] => 1
[disabled] => 0
[label_default] =>
[position_default] => 1
[disabled_default] => 0
)
)
[values] => Array
(
)
)
As you can see the filename is extended by magento and also two items are added to the media_gallery.
My question is how can I reset the product respectively the internal counter so magento is using the original file as it is?
Thanks in advance.
Problem solved: I've saved the product twice with $product->save() and it seems $product->addImageToMediaGallery() is also executed twice.

Resources