RetailOps_Api - Version 1.0.3

Version Notes

* Fix for duplicate attribute options.
* Limited retry on image remote copy failure.
* Fix to update image attributes even if image itself already exists.
* Fix for inability to update or add more than one image to a sku.
* Fix for case insensitivity on attribute sets.
* Locate existing shipment before creating a new shipment.
* Don't add tracking if shipment already has tracking number.
* Fix for erroneous invoice captures.
* Added retailops_hold status.

Download this release

Release Info

Developer Daniel Norman
Extension RetailOps_Api
Version 1.0.3
Comparing to
See all releases


Version 1.0.3

Files changed (53) hide show
  1. app/code/community/RetailOps/Api/Block/Adminhtml/Sales/Order/View/Tab/Retailops.php +111 -0
  2. app/code/community/RetailOps/Api/Exception.php +30 -0
  3. app/code/community/RetailOps/Api/Helper/Data.php +139 -0
  4. app/code/community/RetailOps/Api/Model/Api.php +107 -0
  5. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Abstract.php +151 -0
  6. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Attribute.php +504 -0
  7. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Bundle.php +176 -0
  8. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Category.php +224 -0
  9. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Configurable.php +248 -0
  10. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Default.php +168 -0
  11. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Downloadable.php +160 -0
  12. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Link.php +164 -0
  13. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Media.php +399 -0
  14. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Option.php +266 -0
  15. app/code/community/RetailOps/Api/Model/Catalog/Adapter/Tag.php +72 -0
  16. app/code/community/RetailOps/Api/Model/Catalog/Api.php +91 -0
  17. app/code/community/RetailOps/Api/Model/Catalog/Exception.php +65 -0
  18. app/code/community/RetailOps/Api/Model/Catalog/Media/Item.php +50 -0
  19. app/code/community/RetailOps/Api/Model/Catalog/Pull/Api.php +98 -0
  20. app/code/community/RetailOps/Api/Model/Catalog/Push/Api.php +267 -0
  21. app/code/community/RetailOps/Api/Model/Catalog/Push/Downloadable/Validator.php +36 -0
  22. app/code/community/RetailOps/Api/Model/Inventory/Api.php +163 -0
  23. app/code/community/RetailOps/Api/Model/Observer.php +47 -0
  24. app/code/community/RetailOps/Api/Model/Observer2.php~ +47 -0
  25. app/code/community/RetailOps/Api/Model/Order/Api.php +216 -0
  26. app/code/community/RetailOps/Api/Model/Order/Status/History.php +122 -0
  27. app/code/community/RetailOps/Api/Model/Resource/Api.php +123 -0
  28. app/code/community/RetailOps/Api/Model/Resource/Catalog/Media/Item.php +39 -0
  29. app/code/community/RetailOps/Api/Model/Resource/Catalog/Media/Item/Collection.php +40 -0
  30. app/code/community/RetailOps/Api/Model/Resource/Order/Status/History.php +49 -0
  31. app/code/community/RetailOps/Api/Model/Resource/Order/Status/History/Collection.php +57 -0
  32. app/code/community/RetailOps/Api/Model/Return/Api.php +193 -0
  33. app/code/community/RetailOps/Api/Model/Shipment/Api.php +469 -0
  34. app/code/community/RetailOps/Api/Model/Shipment/Api.php.~80d30a79e43e602a0b5b5da0b2b1a8007dc0ada9~ +355 -0
  35. app/code/community/RetailOps/Api/Model/Shipment/Api.php.~84b644444a1b813b9b5956dbc9eeabb8577db950~ +355 -0
  36. app/code/community/RetailOps/Api/Model/System/Config/Source/Catalog/AttributeSet.php +41 -0
  37. app/code/community/RetailOps/Api/controllers/Adminhtml/Sales/OrderController.php +68 -0
  38. app/code/community/RetailOps/Api/etc/adminhtml.xml +45 -0
  39. app/code/community/RetailOps/Api/etc/api.xml +108 -0
  40. app/code/community/RetailOps/Api/etc/config.xml +117 -0
  41. app/code/community/RetailOps/Api/etc/config.xml~ +117 -0
  42. app/code/community/RetailOps/Api/etc/system.xml +74 -0
  43. app/code/community/RetailOps/Api/sql/retailops_api_setup/install-1.0.0.php +78 -0
  44. app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.0-1.0.1.php +44 -0
  45. app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.1-1.0.2.php +53 -0
  46. app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.2-1.0.3.php +42 -0
  47. app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.2-1.0.3.php~ +53 -0
  48. app/code/community/RetailOps/Model/Observer.php +18 -0
  49. app/code/community/RetailOps/Model/Observer.php~ +17 -0
  50. app/design/adminhtml/default/default/layout/retailops.xml +39 -0
  51. app/design/adminhtml/default/default/template/retailops/order/view/tab/retailops.phtml +60 -0
  52. app/etc/modules/RetailOps_Api.xml +35 -0
  53. package.xml +33 -0
app/code/community/RetailOps/Api/Block/Adminhtml/Sales/Order/View/Tab/Retailops.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Block_Adminhtml_Sales_Order_View_Tab_Retailops
27
+ extends Mage_Adminhtml_Block_Sales_Order_Abstract
28
+ implements Mage_Adminhtml_Block_Widget_Tab_Interface
29
+ {
30
+ protected function _construct()
31
+ {
32
+ parent::_construct();
33
+ $this->setTemplate('retailops/order/view/tab/retailops.phtml');
34
+ }
35
+
36
+ protected function _prepareLayout()
37
+ {
38
+ $onclick = "submitAndReloadArea($('retops-info').parentNode, '" . $this->getSubmitUrl() . "')";
39
+ $button = $this->getLayout()->createBlock('adminhtml/widget_button')
40
+ ->setData(array(
41
+ 'label' => Mage::helper('sales')->__('Submit'),
42
+ 'class' => 'save',
43
+ 'onclick' => $onclick
44
+ ));
45
+ $this->setChild('submit_button', $button);
46
+ return parent::_prepareLayout();
47
+ }
48
+
49
+ /**
50
+ * Gets submit url for retail ops order status
51
+ *
52
+ * @return string
53
+ */
54
+ public function getSubmitUrl()
55
+ {
56
+ return $this->getUrl('*/*/saveRetailOpsInfo', array('order_id' => $this->getOrder()->getId()));
57
+ }
58
+
59
+ /**
60
+ * Gets Retail Ops order statuses
61
+ *
62
+ * @return mixed
63
+ */
64
+ public function getStatuses()
65
+ {
66
+ return Mage::helper('retailops_api')->getRetOpsStatuses();
67
+ }
68
+
69
+ /**
70
+ * Gets Current Order
71
+ *
72
+ * @return Mage_Sales_Model_Order|mixed
73
+ */
74
+ public function getOrder()
75
+ {
76
+ return Mage::registry('current_order');
77
+ }
78
+
79
+ /**
80
+ * Gets Retail Ops Order Status History
81
+ *
82
+ * @return mixed
83
+ */
84
+ public function getRetailOpsStatusHistory()
85
+ {
86
+ return Mage::getModel('retailops_api/order_status_history')->getRetailOpsStatusHistory($this->getOrder());
87
+ }
88
+
89
+ /**
90
+ * ######################## TAB settings #################################
91
+ */
92
+ public function getTabLabel()
93
+ {
94
+ return Mage::helper('retailops_api')->__('RetailOps');
95
+ }
96
+
97
+ public function getTabTitle()
98
+ {
99
+ return Mage::helper('retailops_api')->__('RetailOps');
100
+ }
101
+
102
+ public function canShowTab()
103
+ {
104
+ return true;
105
+ }
106
+
107
+ public function isHidden()
108
+ {
109
+ return false;
110
+ }
111
+ }
app/code/community/RetailOps/Api/Exception.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Exception
27
+ extends Mage_Core_Exception
28
+ {
29
+
30
+ }
app/code/community/RetailOps/Api/Helper/Data.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Helper_Data extends Mage_Api_Helper_Data
27
+ {
28
+ const API_STATUS_SUCCESS = 'success';
29
+ const API_STATUS_FAIL = 'fail';
30
+
31
+ const RETAILOPS_ORDER_PROCESSING = 'retailops_processing';
32
+ const RETAILOPS_ORDER_COMPLETE = 'retailops_complete';
33
+ const RETAILOPS_ORDER_READY = 'retailops_ready';
34
+ const RETAILOPS_ORDER_HOLD = 'retailops_hold';
35
+
36
+ const DEFAULT_LIMIT = 10;
37
+
38
+ const XML_CONFIG_DEFAULT_GROUP = 'catalog';
39
+
40
+ public function getRetOpsStatuses()
41
+ {
42
+ return array(
43
+ self::RETAILOPS_ORDER_PROCESSING => 'Processing',
44
+ self::RETAILOPS_ORDER_COMPLETE => 'Complete',
45
+ self::RETAILOPS_ORDER_READY => 'Ready',
46
+ self::RETAILOPS_ORDER_HOLD => 'Hold'
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Apply pager to collection
52
+ *
53
+ * @param $collection
54
+ * @param $filters
55
+ * @return array
56
+ */
57
+ public function applyPager($collection, $filters)
58
+ {
59
+ $start = 0;
60
+ if (isset($filters['start'])) {
61
+ $start = $filters['start'];
62
+ unset($filters['start']);
63
+ }
64
+ $limit = self::DEFAULT_LIMIT;
65
+ if (isset($filters['limit'])) {
66
+ $limit = $filters['limit'];
67
+ unset($filters['limit']);
68
+ }
69
+
70
+ $collection->getSelect()->limit($limit, $start);
71
+
72
+ return $filters;
73
+ }
74
+
75
+ /**
76
+ * @param array $array
77
+ * @param $keyField
78
+ * @param $valueField
79
+ * @return array
80
+ */
81
+ public function arrayToOptionHash(array $array, $keyField, $valueField)
82
+ {
83
+ $result = array();
84
+ foreach ($array as $item) {
85
+ /** @var $item Varien_Object */
86
+ $result[$item[$keyField]] = $item[$valueField];
87
+ }
88
+
89
+ return $result;
90
+ }
91
+
92
+ /**
93
+ * Get config value
94
+ *
95
+ * @param $path
96
+ * @return mixed
97
+ */
98
+ public function getConfig($path)
99
+ {
100
+ if (strpos($path, '/') === false) {
101
+ $path = self::XML_CONFIG_DEFAULT_GROUP . '/' . $path;
102
+ }
103
+
104
+ return Mage::getStoreConfig('retailops_settings/' . $path);
105
+ }
106
+
107
+ /**
108
+ * Reindex stock and price data for products
109
+ *
110
+ * @param $idsToReindex
111
+ * @param null $type
112
+ */
113
+ public function reindexProducts($idsToReindex, $type = null)
114
+ {
115
+ $indexerStock = Mage::getModel('cataloginventory/stock_status');
116
+ foreach ($idsToReindex as $id) {
117
+ $indexerStock->updateStatus($id, $type);
118
+ }
119
+ $indexerPrice = Mage::getResourceModel('catalog/product_indexer_price');
120
+ $indexerPrice->reindexProductIds($idsToReindex);
121
+ }
122
+
123
+ /**
124
+ * Remove objects from result array
125
+ *
126
+ * @param $data
127
+ * @return array
128
+ */
129
+ public function removeObjectsFromResult($data)
130
+ {
131
+ foreach ($data as $key => $value) {
132
+ if (is_object($value)) {
133
+ unset($data[$key]);
134
+ }
135
+ }
136
+
137
+ return $data;
138
+ }
139
+ }
app/code/community/RetailOps/Api/Model/Api.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Api extends Mage_Api_Model_Resource_Abstract
27
+ {
28
+ /**
29
+ * Get Products
30
+ *
31
+ * @param mixed $filters
32
+ * @return array
33
+ */
34
+ public function catalogPull($filters = null){
35
+ return Mage::getModel('retailops_api/catalog_pull_api')->catalogPull($filters);
36
+ }
37
+
38
+ /**
39
+ * Create/update Products
40
+ *
41
+ * @param mixed $productsData
42
+ * @return array
43
+ */
44
+ public function catalogPush($productsData){
45
+ return Mage::getModel('retailops_api/catalog_push_api')->catalogPush($productsData);
46
+ }
47
+
48
+ /**
49
+ * Product Inventory Update
50
+ *
51
+ * @param array $itemData
52
+ * @return array
53
+ */
54
+ public function inventoryPush($itemData){
55
+ return Mage::getModel('retailops_api/inventory_api')->inventoryPush($itemData);
56
+ }
57
+
58
+ /**
59
+ * create Credit Memo
60
+ *
61
+ * @param mixed $returns
62
+ * @return array
63
+ */
64
+ public function returnPush($returns){
65
+ return Mage::getModel('retailops_api/return_api')->returnPush($returns);
66
+ }
67
+
68
+ /**
69
+ * Get orders
70
+ *
71
+ * @param mixed $filters
72
+ * @return array
73
+ */
74
+ public function orderPull($filters = null){
75
+ return Mage::getModel('retailops_api/order_api')->orderPull($filters);
76
+ }
77
+
78
+ /**
79
+ * Update retailops order status
80
+ *
81
+ * @param $ordersData
82
+ * @return mixed
83
+ */
84
+ public function orderStatusUpdate($ordersData){
85
+ return Mage::getModel('retailops_api/order_api')->orderStatusUpdate($ordersData);
86
+ }
87
+
88
+ /**
89
+ * Create shipments
90
+ *
91
+ * @param mixed $shipments
92
+ * @return array
93
+ */
94
+ public function shipmentPush($shipments){
95
+ return Mage::getModel('retailops_api/shipment_api')->shipmentPush($shipments);
96
+ }
97
+
98
+ /**
99
+ * Close order
100
+ *
101
+ * @param $ordersData
102
+ * @return mixed
103
+ */
104
+ public function orderClose($ordersData){
105
+ return Mage::getModel('retailops_api/shipment_api')->orderClose($ordersData);
106
+ }
107
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Abstract.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ abstract class RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'general';
29
+ protected $_errorCodes = array();
30
+ /** @var RetailOps_Api_Model_Catalog_Api */
31
+ protected $_api;
32
+
33
+ public function __construct($api = null)
34
+ {
35
+ $this->_api = $api;
36
+ $this->_construct();
37
+ }
38
+
39
+ /**
40
+ * @return RetailOps_Api_Model_Resource_Api
41
+ */
42
+ protected function _getResource()
43
+ {
44
+ return Mage::getResourceModel('retailops_api/api');
45
+ }
46
+
47
+ /**
48
+ * @return RetailOps_Api_Helper_Data
49
+ */
50
+ public function getHelper()
51
+ {
52
+ return Mage::helper('retailops_api');
53
+ }
54
+
55
+ /**
56
+ * @return $this
57
+ */
58
+ protected function _construct()
59
+ {
60
+ return $this;
61
+ }
62
+
63
+ /**
64
+ * @param $message
65
+ * @param string $code
66
+ * @param null $sku
67
+ * @throws RetailOps_Api_Model_Catalog_Exception
68
+ */
69
+ protected function _throwException($message, $code, $sku = null)
70
+ {
71
+ if (isset($this->_errorCodes[$code])) {
72
+ $code = $this->_errorCodes[$code];
73
+ }
74
+ throw new RetailOps_Api_Model_Catalog_Exception($message, $code, $sku, $this->_section);
75
+ }
76
+
77
+ /**
78
+ * Will be called before preparing
79
+ *
80
+ * @return $this
81
+ */
82
+ public function beforeDataPrepare()
83
+ {
84
+ return $this;
85
+ }
86
+
87
+ /**
88
+ * @param array $data
89
+ * @return $this
90
+ */
91
+ public function prepareData(array &$data)
92
+ {
93
+ return $this;
94
+ }
95
+
96
+ /**
97
+ * Will be called when all rows prepared
98
+ *
99
+ * @return $this
100
+ */
101
+ public function afterDataPrepare()
102
+ {
103
+ return $this;
104
+ }
105
+
106
+ /**
107
+ * @return $this
108
+ */
109
+ public function beforeDataProcess()
110
+ {
111
+ return $this;
112
+ }
113
+
114
+ /**
115
+ * @param array $productData
116
+ * @param $product
117
+ * @return mixed
118
+ */
119
+ abstract public function processData(array &$productData, $product);
120
+
121
+ /**
122
+ * @param array $skuToIdMap
123
+ * @return $this
124
+ */
125
+ public function afterDataProcess(array &$skuToIdMap)
126
+ {
127
+ return $this;
128
+ }
129
+
130
+ /**
131
+ * Prepare data for pull api
132
+ *
133
+ * @param $productCollection
134
+ * @return $this
135
+ */
136
+ public function prepareOutputData($productCollection)
137
+ {
138
+ return $this;
139
+ }
140
+
141
+ /**
142
+ * Output data for pull api
143
+ *
144
+ * @param Mage_Catalog_Model_Product $product
145
+ * @return array
146
+ */
147
+ public function outputData($product)
148
+ {
149
+ return array();
150
+ }
151
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Attribute.php ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Attribute extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'attribute';
29
+
30
+ protected $_attributeSets;
31
+ protected $_attributeSetGroups;
32
+ protected $_attributes;
33
+ protected $_simpleAttributes;
34
+ protected $_sourceAttributes;
35
+ protected $_multiSelectAttributes = array();
36
+ protected $_attributeOptionCache;
37
+ protected $_entityTypeId;
38
+ protected $_newOptions = array();
39
+ /**
40
+ * Array of already processed attribute codes to avoid double save
41
+ * @var array
42
+ */
43
+ protected $_wereProcessed = array();
44
+
45
+
46
+ /*
47
+ * Attributes to skip while unsetting missing attributes
48
+ */
49
+ protected $_systemAttributes = array('has_options', 'required_options', 'media_gallery');
50
+
51
+ protected function _construct()
52
+ {
53
+ $this->_entityTypeId = Mage::getModel('eav/entity')->setType(Mage_Catalog_Model_Product::ENTITY)->getTypeId();
54
+ $this->_attributeSets = $this->_getAttributeSets();
55
+ $this->_attributeSetGroups = $this->_getAttributeSetGroups();
56
+ $this->_initAttributes();
57
+ $this->_errorCodes = array(
58
+ 'cant_create_attribute_set' => 101,
59
+ 'missing_attribute_set' => 102,
60
+ 'default_attribute_set_not_set' => 103,
61
+ 'error_processing_attribute' => 104,
62
+ 'error_adding_attribute_options' => 105
63
+ );
64
+
65
+ parent::_construct();
66
+ }
67
+
68
+ /**
69
+ * @param array $data
70
+ * @return $this
71
+ */
72
+ public function prepareData(array &$data)
73
+ {
74
+ $this->_prepareAttributeSet($data);
75
+ $this->_prepareAttributes($data);
76
+ $this->_processAttributes($data, true);
77
+
78
+ return $this;
79
+ }
80
+
81
+ /**
82
+ * @return $this
83
+ */
84
+ public function afterDataPrepare()
85
+ {
86
+ $this->_addAttributeOptions($this->_newOptions);
87
+
88
+ return $this;
89
+ }
90
+
91
+ /**
92
+ * @param array $productData
93
+ * @param Mage_Catalog_Model_Product $product
94
+ * @return mixed|void
95
+ */
96
+ public function processData(array &$productData, $product)
97
+ {
98
+ $productData['attribute_set_id'] = $this->_getAttributeSetIdByName($productData['attribute_set']);
99
+ $this->_processStaticAttributes($productData);
100
+ $this->_processAttributes($productData);
101
+ if ($product->getId() &&
102
+ (!isset($productData['unset_other_attribute']) || $productData['unset_other_attribute'])) {
103
+ $this->_unsetOldData($productData, $product);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * @return mixed
109
+ */
110
+ public function getSourceAttributes()
111
+ {
112
+ return $this->_sourceAttributes;
113
+ }
114
+
115
+ /**
116
+ * @return mixed
117
+ */
118
+ public function getAttributeOptions()
119
+ {
120
+ return $this->_attributeOptionCache;
121
+ }
122
+
123
+ /**
124
+ * @param Mage_Catalog_Model_Product $product
125
+ * @return array
126
+ */
127
+ public function outputData($product)
128
+ {
129
+ $data = array();
130
+ $data['attribute_set'] = $this->_attributeSets[$product->getDefaultAttributeSetId()];
131
+ foreach ($this->_simpleAttributes as $code) {
132
+ $data[$code] = $product->getData($code);
133
+ }
134
+
135
+ foreach ($this->_sourceAttributes as $code) {
136
+ $values = $product->getData($code);
137
+ if (array_search($code, $this->_multiSelectAttributes) !== false) {
138
+ $values = explode(',', $values);
139
+ }
140
+ $values = (array) $values;
141
+ $data[$code] = array();
142
+ /**
143
+ * Return array for multiselect options and string for select options
144
+ */
145
+ foreach ($values as $value) {
146
+ $optionLabel = array_search($value, $this->_attributeOptionCache[$code]);
147
+ if ($optionLabel) {
148
+ $data[$code][] = $optionLabel;
149
+ } else {
150
+ $data[$code][] = $value;
151
+ }
152
+ }
153
+ if (count($data[$code]) == 1) {
154
+ $data[$code] = $data[$code][0];
155
+ }
156
+ }
157
+
158
+ return $data;
159
+ }
160
+
161
+ /**
162
+ * @return Mage_Catalog_Model_Product_Attribute_Api
163
+ */
164
+ protected function _getProductAttributeApi()
165
+ {
166
+ return Mage::getModel('catalog/product_attribute_api');
167
+ }
168
+
169
+ /**
170
+ * @return Mage_Catalog_Model_Product_Attribute_Set_Api
171
+ */
172
+ protected function _getProductAttributeSetApi()
173
+ {
174
+ return Mage::getModel('catalog/product_attribute_set_api');
175
+ }
176
+
177
+ /**
178
+ * @return array
179
+ */
180
+ protected function _getAttributeSets()
181
+ {
182
+ /** @var $attributeSetCollection Mage_Eav_Model_Resource_Entity_Attribute_Set_Collection */
183
+ $attributeSetCollection = Mage::getResourceModel('eav/entity_attribute_set_collection');
184
+ $attributeSetCollection->setEntityTypeFilter($this->_entityTypeId);
185
+
186
+ return $attributeSetCollection->toOptionHash();
187
+ }
188
+
189
+ /**
190
+ * @return array
191
+ */
192
+ protected function _getAttributeSetGroups()
193
+ {
194
+ /** @var $attributeSetCollection Mage_Eav_Model_Resource_Entity_Attribute_Group_Collection */
195
+ $groupsCollection = Mage::getResourceModel('eav/entity_attribute_group_collection');
196
+ $groups = array();
197
+ foreach ($groupsCollection as $group) {
198
+ if (!isset($groups[$group->getAttributeSetId()])) {
199
+ $groups[$group->getAttributeSetId()] = array();
200
+ }
201
+ $groups[$group->getAttributeSetId()][$group->getAttributeGroupName()] = $group->getId();
202
+ }
203
+
204
+ return $groups;
205
+ }
206
+
207
+ /**
208
+ * Init product attributes and attirubtes options
209
+ */
210
+ protected function _initAttributes()
211
+ {
212
+ /** @var $attributeCollection Mage_Catalog_Model_Resource_Product_Attribute_Collection */
213
+ $attributeCollection = Mage::getResourceModel('catalog/product_attribute_collection');
214
+ $attributeCollection->setItemObjectClass('catalog/resource_eav_attribute');
215
+ $attributeCollection->addFieldToSelect('*');
216
+
217
+ $this->_sourceAttributes = array();
218
+ $this->_simpleAttributes = array();
219
+ $this->_multiSelectAttributes = array();
220
+ foreach ($attributeCollection as $attribute) {
221
+ $attributeCode = $attribute->getAttributeCode();
222
+ if ($this->_usesSource($attribute)) {
223
+ /** @var Mage_Eav_Model_Entity_Attribute $attribute */
224
+ $this->_attributeOptionCache[$attributeCode] =
225
+ $this->getHelper()->arrayToOptionHash(
226
+ $attribute->getSource()->getAllOptions(),
227
+ 'label',
228
+ 'value',
229
+ false
230
+ );
231
+ $this->_sourceAttributes[$attribute->getId()] = $attributeCode;
232
+ if ($attribute->getFrontendInput() === 'multiselect') {
233
+ $this->_multiSelectAttributes[] = $attribute->getAttributeCode();
234
+ }
235
+ } else {
236
+ $this->_simpleAttributes[$attribute->getId()] = $attributeCode;
237
+ }
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Check if attribute uses source
243
+ *
244
+ * @param $attribute
245
+ * @return bool
246
+ */
247
+ protected function _usesSource($attribute)
248
+ {
249
+ return $attribute->getFrontendInput() === 'select' || $attribute->getFrontendInput() === 'multiselect'
250
+ || $attribute->getData('source_model') != '';
251
+ }
252
+
253
+ /**
254
+ * @param $data
255
+ * @return mixed
256
+ */
257
+ protected function _prepareAttributeSet(array &$data)
258
+ {
259
+ if (!empty($data['attribute_set'])) {
260
+ $attributeSet = $data['attribute_set'];
261
+ $attributeSetId = $this->_getAttributeSetIdByName($attributeSet);
262
+ if ($attributeSetId === false) {
263
+ try {
264
+ $attributeSetId = $this->_createAttributeSet($attributeSet, $data['sku']);
265
+ } catch (Mage_Api_Exception $e) {
266
+ $this->_throwException($e->getCustomMessage(), 'cant_create_attribute_set', $data['sku']);
267
+ }
268
+ $this->_attributeSets[$attributeSetId] = $attributeSet;
269
+ }
270
+ } else {
271
+ $this->_throwException('Attribute set not provided', 'missing_attribute_set', $data['sku']);
272
+ }
273
+
274
+ $data['attribute_set_id'] = $attributeSetId;
275
+ }
276
+
277
+ /**
278
+ * @param $name
279
+ * @return mixed
280
+ */
281
+ protected function _createAttributeSet($name, $sku)
282
+ {
283
+ $defaultAttributeSetId = $this->getHelper()->getConfig('catalog/default_attribute_set');
284
+ if (!$defaultAttributeSetId) {
285
+ $this->_throwException('Default attribute set is not set', 'default_attribute_set_not_set', $sku);
286
+ }
287
+
288
+ $attributeSetId = $this->_getProductAttributeSetApi()->create($name, $defaultAttributeSetId);
289
+
290
+ return $attributeSetId;
291
+ }
292
+
293
+ /**
294
+ * @param $set
295
+ * @return mixed
296
+ */
297
+ protected function _getAttributeSetIdByName($set)
298
+ {
299
+ $tolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
300
+
301
+ return array_search($tolower($set), array_map($tolower, $this->_attributeSets));
302
+ }
303
+
304
+ /**
305
+ * Create/update attributes. Assign them to the attribute set
306
+ *
307
+ * @param $data
308
+ */
309
+ protected function _prepareAttributes(array &$data)
310
+ {
311
+ $attributeSetId = $data['attribute_set_id'];
312
+ $attributeApi = $this->_getProductAttributeApi();
313
+ $attributeSetApi = $this->_getProductAttributeSetApi();
314
+ if (isset($data['attributes'])) {
315
+ foreach ($data['attributes'] as $attributeData) {
316
+ try {
317
+ $attribute = new Varien_Object($attributeData);
318
+ $attributeId = $this->findAttribute($attributeData['attribute_code']);
319
+ if (!in_array($attributeData['attribute_code'], $this->_wereProcessed)) {
320
+ if ($attributeId !== false) {
321
+ if (!$attributeData['no_update_if_exists']) {
322
+ Mage::dispatchEvent('retailops_catalog_attribute_update_before',
323
+ array('attribute_data' => $attribute));
324
+ $attributeApi->update($attributeData['attribute_code'], $attribute->getData());
325
+ Mage::dispatchEvent('retailops_catalog_attribute_update_after',
326
+ array('attribute_data' => $attribute));
327
+ }
328
+ } else {
329
+ Mage::dispatchEvent('retailops_catalog_attribute_create_before',
330
+ array('attribute_data' => $attribute));
331
+ $attributeId = $attributeApi->create($attribute->getData());
332
+ Mage::dispatchEvent('retailops_catalog_attribute_create_after',
333
+ array('attribute_id' => $attributeId, 'attribute_data' => $attribute));
334
+ if ($this->_usesSource($attribute)) {
335
+ $this->_sourceAttributes[$attributeId] = $attribute->getData('attribute_code');
336
+ } else {
337
+ $this->_simpleAttributes[$attributeId] = $attribute->getData('attribute_code');
338
+ }
339
+ }
340
+ $this->_wereProcessed[] = $attributeData['attribute_code'];
341
+ }
342
+ $attributeGroup = $attributeData['group_name'];
343
+ $attributeGroupId = null;
344
+ if (isset($this->_attributeSetGroups[$attributeSetId][$attributeGroup])) {
345
+ $attributeGroupId = $this->_attributeSetGroups[$attributeSetId][$attributeGroup];
346
+ }
347
+ /**
348
+ * Try to add attribute set group, use the default group if failed
349
+ */
350
+ try {
351
+ $attributeGroupId = $attributeSetApi->groupAdd($attributeSetId, $attributeGroup);
352
+ } catch (Mage_Api_Exception $e) { }
353
+ $sortOrder = isset($attributeData['sort_order']) ? $attributeData['sort_order'] : 0;
354
+ try {
355
+ $this->_getProductAttributeSetApi()
356
+ ->attributeAdd($attributeId, $attributeSetId, $attributeGroupId, $sortOrder);
357
+ } catch (Mage_Api_Exception $e) {
358
+ if ($e->getMessage() !== 'attribute_is_already_in_set') {
359
+ throw new Mage_Api_Exception($e->getMessage(), $e->getCustomMessage());
360
+ }
361
+ }
362
+ } catch (Mage_Api_Exception $e) {
363
+ $this->_throwException(
364
+ sprintf('Error while saving attribute %s, error message: %s', $attributeData['attribute_code'], $e->getMessage()),
365
+ 'error_processing_attribute', $data['sku']);
366
+ }
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * @param $attributeCode
373
+ * @return bool
374
+ */
375
+ public function findAttribute($attributeCode)
376
+ {
377
+ return array_search($attributeCode, $this->_getAttributes());
378
+ }
379
+
380
+ /**
381
+ * @return mixed
382
+ */
383
+ protected function _getAttributes()
384
+ {
385
+ return $this->_simpleAttributes + $this->_sourceAttributes;
386
+ }
387
+
388
+ /**
389
+ * @param $productData
390
+ * @param bool $collectOptions
391
+ */
392
+ protected function _processAttributes(&$productData, $collectOptions = false)
393
+ {
394
+ if (isset($productData['attributes'])) {
395
+ foreach ($productData['attributes'] as $attributeData) {
396
+ $code = $attributeData['attribute_code'];
397
+ $attributeId = array_search($code, $this->_sourceAttributes);
398
+ if ($attributeId !== false && isset($attributeData['value'])) {
399
+ $values = (array) $attributeData['value'];
400
+ if ($collectOptions) {
401
+ /**
402
+ * Collect missing options to add
403
+ */
404
+ foreach ($values as $value) {
405
+ if (!isset($this->_attributeOptionCache[$code][$value])) {
406
+ $this->_newOptions[$attributeId][] = $value;
407
+ $this->_attributeOptionCache[$code][$value] = true;
408
+ }
409
+ }
410
+ } else {
411
+ /**
412
+ * Collect attribute values
413
+ */
414
+ $valuesIds = array();
415
+ foreach ($values as $value) {
416
+ if (isset($this->_attributeOptionCache[$code][$value])) {
417
+ $valuesIds[] = $this->_attributeOptionCache[$code][$value];
418
+ }
419
+ }
420
+ if (count($valuesIds) == 1) {
421
+ $valuesIds = current($valuesIds);
422
+ }
423
+ $productData[$code] = $valuesIds;
424
+ }
425
+ } elseif (isset($attributeData['value'])) {
426
+ $productData[$code] = $attributeData['value'];
427
+ }
428
+ }
429
+ }
430
+ }
431
+
432
+ /**
433
+ * @param $productData
434
+ */
435
+ protected function _processStaticAttributes(&$productData)
436
+ {
437
+ foreach ($productData as $code => $value) {
438
+ $attributeId = array_search($code, $this->_sourceAttributes);
439
+ if ($attributeId !== false) {
440
+ if (isset($this->_attributeOptionCache[$code][$value])) {
441
+ $realValue = $this->_attributeOptionCache[$code][$value];
442
+ $productData[$code] = $realValue;
443
+ }
444
+ }
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Add missing attribute options
450
+ *
451
+ * @param $newOptions
452
+ */
453
+ protected function _addAttributeOptions($newOptions)
454
+ {
455
+ foreach ($newOptions as $attributeId => $options) {
456
+ $attribute = Mage::getResourceModel('catalog/eav_attribute')
457
+ ->setEntityTypeId($this->_entityTypeId);
458
+ $attribute->load($attributeId);
459
+ $optionsData = array();
460
+ foreach ($options as $key => $option) {
461
+ $optionsData['value']['option_' . $key][0] = $option;
462
+ $optionsData['order']['option_' . $key] = 0;
463
+ }
464
+ $attribute->setData('option', $optionsData);
465
+ try {
466
+ $attribute->save();
467
+ $this->_attributeOptionCache[$attribute->getAttributeCode()] =
468
+ $this->getHelper()->arrayToOptionHash(
469
+ $attribute->getSource()->getAllOptions(),
470
+ 'label',
471
+ 'value',
472
+ false
473
+ );
474
+ } catch (Exception $e) {
475
+ $this->_throwException(sprintf('Error saving attribute "%s" options, %s', $attribute->getAttributeCode(),
476
+ $e->getMessage()), 'error_adding_attribute_options');
477
+ }
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Unset attributes which are not passed in the API call
483
+ *
484
+ * @param array $productData
485
+ * @param Mage_Catalog_Model_Product $product
486
+ */
487
+ protected function _unsetOldData(array &$productData, $product)
488
+ {
489
+ $usedAttributes = array();
490
+ if (isset($data['attributes'])) {
491
+ foreach ($data['attributes'] as $attributeData) {
492
+ $usedAttributes[] = $attributeData['attribute_code'];
493
+ }
494
+ }
495
+ $usedAttributes = array_merge(array_keys($productData), $usedAttributes);
496
+ $origDataKeys = array_keys($product->getOrigData());
497
+ $origDataAttributeKeys = array_intersect($origDataKeys, $this->_getAttributes());
498
+ $keysToUnset = array_diff($origDataAttributeKeys, $usedAttributes, $this->_systemAttributes);
499
+
500
+ foreach ($keysToUnset as $key) {
501
+ $product->setData($key, null);
502
+ }
503
+ }
504
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Bundle.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Bundle extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'bundle';
29
+
30
+ protected $_bundleOptions = array();
31
+
32
+ protected function _construct()
33
+ {
34
+ $this->_errorCodes = array(
35
+ 'cant_save_bundle_data' => '801'
36
+ );
37
+ parent::_construct();
38
+ }
39
+
40
+ /**
41
+ * @param array $productData
42
+ * @param Mage_Catalog_Model_Product $product
43
+ * @return mixed|void
44
+ */
45
+ public function processData(array &$productData, $product)
46
+ {
47
+ $sku = $productData['sku'];
48
+
49
+ if (isset($productData['bundle_options'])) {
50
+ $this->_bundleOptions[$sku] = $productData['bundle_options'];
51
+ }
52
+ }
53
+
54
+ /**
55
+ * @param array $skuToIdMap
56
+ * @return $this|void
57
+ */
58
+ public function afterDataProcess(array &$skuToIdMap)
59
+ {
60
+ $failedSkus = array();
61
+ $idsToReindex = array();
62
+ if (isset($this->_bundleOptions)) {
63
+
64
+ foreach ($this->_bundleOptions as $sku => $bundleOptions) {
65
+ try {
66
+ $productId = $skuToIdMap[$sku];
67
+ /** @var Mage_Catalog_Model_Product $bundle */
68
+ $bundle = Mage::getModel('catalog/product')->load($productId);
69
+ $options = array();
70
+ $selections = array();
71
+ foreach ($bundleOptions as $optionKey => $bundleOption) {
72
+ $option = array(
73
+ 'type' => $bundleOption['type'],
74
+ 'title' => $bundleOption['title'],
75
+ 'required' => isset($bundleOption['required']) ? $bundleOption['required'] : 0,
76
+ 'position' => isset($bundleOption['position']) ? $bundleOption['position'] : 0,
77
+ 'delete' => 0,
78
+ );
79
+ $options[] = $option;
80
+ if (!empty($bundleOption['bundle_selections'])) {
81
+ $selections[$optionKey] = array();
82
+ foreach ($bundleOption['bundle_selections'] as $selectionData) {
83
+ if (isset($skuToIdMap[$selectionData['sku']])) {
84
+ $productId = $skuToIdMap[$selectionData['sku']];
85
+ $priceType = 0;
86
+ $price = isset($selectionData['price']) ? $selectionData['price'] : 0;
87
+ if (false !== strpos($price, '%')) {
88
+ $priceType = 1;
89
+ }
90
+ $selection = array(
91
+ 'product_id' => $productId,
92
+ 'delete' => 0,
93
+ 'selection_price_value' => $price,
94
+ 'selection_price_type' => $priceType,
95
+ 'selection_qty' => isset($selectionData['default_qty']) ? $selectionData['default_qty'] : 1,
96
+ 'selection_can_change_qty' => isset($selectionData['can_change_qty']) ? $selectionData['can_change_qty'] : 1,
97
+ 'is_default' => isset($selectionData['is_default']) ? $selectionData['is_default'] : 0,
98
+ 'position' => isset($selectionData['position']) ? $selectionData['position'] : 0,
99
+ );
100
+ $selections[$optionKey][] = $selection;
101
+ }
102
+ }
103
+
104
+ }
105
+ }
106
+ Mage::register('product', $bundle); //required by selection object
107
+ $currentOptions = $bundle->getTypeInstance()->getOptionsCollection();
108
+ foreach ($currentOptions as $option) {
109
+ $option->delete();
110
+ }
111
+ $bundle->setBundleOptionsData($options);
112
+ $bundle->setBundleSelectionsData($selections);
113
+ $bundle->setCanSaveCustomOptions(true);
114
+ $bundle->setCanSaveBundleSelections(true);
115
+ $bundle->save();
116
+ $idsToReindex[] = $bundle->getId();
117
+ $bundle->clearInstance();
118
+ Mage::unregister('product');
119
+ } catch (Exception $e) {
120
+ $failedSkus[] = $sku;
121
+ }
122
+ }
123
+ }
124
+ if ($idsToReindex) {
125
+ $this->getHelper()->reindexProducts(array_unique($idsToReindex), 'bundle');
126
+ }
127
+ if ($failedSkus) {
128
+ $this->_throwException('Bundle data is not saved for ' . implode(',', array_unique($failedSkus)),
129
+ 'cant_save_bundle_data');
130
+ }
131
+ }
132
+
133
+ /**
134
+ * @param Mage_Catalog_Model_Product $product
135
+ * @return array
136
+ */
137
+ public function outputData($product)
138
+ {
139
+ $data = array();
140
+
141
+ if ($product->getTypeId() !== 'bundle') {
142
+ return array();
143
+ }
144
+ $options = $product->getTypeInstance()->getOptionsCollection();
145
+ $selectionsCollection = $product->getTypeInstance()->getSelectionsCollection($options->getAllIds());
146
+ $options->appendSelections($selectionsCollection, false, true);
147
+ $optionsData = array();
148
+ /** @var $option Mage_Bundle_Model_Option */
149
+ foreach ($options as $option) {
150
+ $selectionsData = array();
151
+ $optionSelections = $option->getSelections();
152
+ /** @var $selection Mage_Bundle_Model_Selection */
153
+ foreach ($optionSelections as $selection) {
154
+ $selectionsData[] = array(
155
+ 'sku' => $selection->getSku(),
156
+ 'price' => $selection->getSelectionPriceValue() . ($selection->getSelectionPriceType() ? '%' : ''),
157
+ 'default_qty' => $selection->getSelectionQty(),
158
+ 'selection_can_change_qty' => $selection->getSelectionCanChangeQty(),
159
+ 'is_default' => $selection->getIsDefault(),
160
+ 'position' => $selection->getPosition(),
161
+ );
162
+ }
163
+ $optionsData[] = array(
164
+ 'type' => $option->getType(),
165
+ 'title' => $option->getDefaultTitle(),
166
+ 'required' => $option->getRequired(),
167
+ 'position' => $option->getPosition(),
168
+ 'bundle_selections' => $selectionsData
169
+ );
170
+ }
171
+
172
+ $data['bundle_options'] = $optionsData;
173
+
174
+ return $data;
175
+ }
176
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Category.php ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Category extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_categories;
29
+ /**
30
+ * Array of already processed category indexes to avoid double save
31
+ * @var array
32
+ */
33
+ protected $_wereProcessed = array();
34
+
35
+ protected $_section = 'category';
36
+
37
+ protected function _construct()
38
+ {
39
+ $this->_initCategories();
40
+ $this->_errorCodes = array(
41
+ 'cant_save_category' => 201,
42
+ );
43
+ parent::_construct();
44
+ }
45
+
46
+ /**
47
+ * @param array $productData
48
+ * @param Mage_Catalog_Model_Product $product
49
+ * @return mixed|void
50
+ */
51
+ public function processData(array &$productData, $product)
52
+ {
53
+ $assignedCategories = array();
54
+ $categoryApi = $this->_getCategoryApi();
55
+ if (isset($productData['categories'])) {
56
+ foreach ($productData['categories'] as $categoriesData) {
57
+ $categoryPath = array();
58
+ foreach ($categoriesData as $categoryData) {
59
+ try {
60
+ $data = new Varien_Object($categoryData);
61
+ $parentCategoryPath = $categoryPath;
62
+ $categoryPath[] = $categoryData['name'];
63
+ $index = $this->_getCategoryPathIndex($categoryPath);
64
+ if (!in_array($index, $this->_wereProcessed)) {
65
+ if (!isset($this->_categories[$index])) {
66
+ $parentIndex = $this->_getCategoryPathIndex($parentCategoryPath);
67
+ $parentId = isset($this->_categories[$parentIndex]) ? $this->_categories[$parentIndex] : 1;
68
+
69
+ Mage::dispatchEvent('retailops_catalog_category_create_before',
70
+ array('category_data' => $data));
71
+
72
+ $categoryId = $categoryApi->create($parentId, $data->getData());
73
+ $this->_categories[$index] = $categoryId;
74
+
75
+ Mage::dispatchEvent('retailops_catalog_category_create_after',
76
+ array('category_id' => $categoryId, 'category_data' => $data));
77
+
78
+ } else {
79
+ $categoryId = $this->_categories[$index];
80
+
81
+ Mage::dispatchEvent('retailops_catalog_category_update_before',
82
+ array('category_id' => $categoryId, 'category_data' => $data));
83
+
84
+ $categoryApi->update($categoryId, $data->getData());
85
+
86
+ Mage::dispatchEvent('retailops_catalog_category_update_after',
87
+ array('category_id' => $categoryId, 'category_data' => $data));
88
+ }
89
+ $this->_wereProcessed[] = $index;
90
+ }
91
+ if (!empty($categoryData['link'])) {
92
+ $categoryId = $this->_categories[$index];
93
+ $assignedCategories[] = $categoryId;
94
+ }
95
+ } catch (Mage_Api_Exception $e) {
96
+ $this->_throwException(
97
+ sprintf('Error while saving category %s, message: %s', $categoryData['name'], $e->getCustomMessage()),
98
+ 'cant_save_category'
99
+ );
100
+ }
101
+ }
102
+ }
103
+ if (empty($productData['unset_other_categories']) || !$productData['unset_other_categories']) {
104
+ $assignedCategories = array_merge($assignedCategories, $product->getCategoryIds());
105
+ }
106
+ $productData['category_ids'] = $assignedCategories;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @param Mage_Catalog_Model_Resource_Product_Collection $productCollection
112
+ * @return $this
113
+ */
114
+ public function prepareOutputData($productCollection)
115
+ {
116
+ $usedCategoryIds = array();
117
+ /** @var $product Mage_Catalog_Model_Product */
118
+ foreach ($productCollection as $product) {
119
+ $usedCategoryIds = array_merge($usedCategoryIds, $product->getCategoryIds());
120
+ }
121
+ $usedCategoryIds = array_unique($usedCategoryIds);
122
+ /** @var $collection Mage_Catalog_Model_Resource_Category_Collection */
123
+ $collection = Mage::getModel('catalog/category')->getCollection();
124
+ $collection->addAttributeToSelect('path');
125
+ $collection->addIdFilter($usedCategoryIds);
126
+ $categoryToLoad = array();
127
+ /** @var $category Mage_Catalog_Model_Category */
128
+ foreach ($collection as $category) {
129
+ $categoryToLoad = array_merge($categoryToLoad, $category->getPathIds());
130
+ }
131
+ $categoryToLoad = array_unique($categoryToLoad);
132
+ /** @var $fullCollection Mage_Catalog_Model_Resource_Category_Collection */
133
+ $fullCollection = Mage::getModel('catalog/category')->getCollection();
134
+ $fullCollection->addAttributeToSelect('*');
135
+ $fullCollection->addIdFilter($categoryToLoad);
136
+ foreach ($fullCollection as $category) {
137
+ $this->_categories[$category->getId()] = $category;
138
+ }
139
+
140
+ return $this;
141
+ }
142
+
143
+ /**
144
+ * @param Mage_Catalog_Model_Product $product
145
+ * @return array
146
+ */
147
+ public function outputData($product)
148
+ {
149
+ $data = array();
150
+ $categoryIds = $product->getCategoryIds();
151
+ $categoryPaths = array();
152
+ foreach ($categoryIds as $categoryId) {
153
+ $categoryPath = $this->_getCategoryData($categoryId);
154
+ $categoryPaths[]['path'] = $categoryPath;
155
+ }
156
+
157
+ $data['categories'] = $categoryPaths;
158
+
159
+ return $data;
160
+ }
161
+
162
+ /**
163
+ * Get category data
164
+ *
165
+ * @param $categoryId
166
+ * @return array
167
+ */
168
+ protected function _getCategoryData($categoryId)
169
+ {
170
+ $data = array();
171
+ if (isset($this->_categories[$categoryId])) {
172
+ $category = $this->_categories[$categoryId];
173
+ $path = array_slice($category->getPathIds(), 1);
174
+ foreach ($path as $catId) {
175
+ if (isset($this->_categories[$catId])) {
176
+ $parentCategory = $this->_categories[$catId];
177
+ $data[] = $parentCategory->getData();
178
+ }
179
+ }
180
+ }
181
+
182
+ return $data;
183
+ }
184
+
185
+ /**
186
+ * @return $this
187
+ */
188
+ protected function _initCategories()
189
+ {
190
+ $collection = Mage::getResourceModel('catalog/category_collection')->addNameToResult();
191
+ /* @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Collection */
192
+ foreach ($collection as $category) {
193
+ $structure = explode('/', $category->getPath());
194
+ $pathSize = count($structure);
195
+ if ($pathSize > 1) {
196
+ $path = array();
197
+ for ($i = 1; $i < $pathSize; $i++) {
198
+ $path[] = $collection->getItemById($structure[$i])->getName();
199
+ }
200
+ $index = $this->_getCategoryPathIndex($path);
201
+ $this->_categories[$index] = $category->getId();
202
+ }
203
+ }
204
+
205
+ return $this;
206
+ }
207
+
208
+ /**
209
+ * @return Mage_Catalog_Model_Category_Api
210
+ */
211
+ protected function _getCategoryApi()
212
+ {
213
+ return Mage::getModel('catalog/category_api');
214
+ }
215
+
216
+ /**
217
+ * @param $path
218
+ * @return string
219
+ */
220
+ protected function _getCategoryPathIndex($path)
221
+ {
222
+ return implode('/', $path);
223
+ }
224
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Configurable.php ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Configurable extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'configurable';
29
+
30
+ protected $_associations = array();
31
+ protected $_configurableOptions = array();
32
+
33
+ protected function _construct()
34
+ {
35
+ $this->_errorCodes = array(
36
+ 'cant_save_configurable_data' => 301,
37
+ );
38
+ parent::_construct();
39
+ }
40
+
41
+ /**
42
+ * @param array $productData
43
+ * @param Mage_Catalog_Model_Product $product
44
+ * @return mixed|void
45
+ */
46
+ public function processData(array &$productData, $product)
47
+ {
48
+ $sku = $productData['sku'];
49
+ if (isset($productData['configurable_sku'])) {
50
+ $this->_associations[$sku] = $productData['configurable_sku'];
51
+ }
52
+
53
+ if (isset($productData['price_changes'])) {
54
+ $this->_configurableOptions[$sku] = $productData['price_changes'];
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @param array $skuToIdMap
60
+ * @return $this|void
61
+ */
62
+ public function afterDataProcess(array &$skuToIdMap)
63
+ {
64
+ $failedSkus = array();
65
+ $idsToReindex = array();
66
+ if (!empty($this->_associations)) {
67
+ $parentChildIds = array();
68
+ $disassociateIds = array();
69
+ foreach ((array) $this->_associations as $sku => $parentSkus) {
70
+ $childProductId = $skuToIdMap[$sku];
71
+ if ($parentSkus) {
72
+ foreach ($parentSkus as $parentSku) {
73
+ if (isset($skuToIdMap[$parentSku])) {
74
+ $parentProductId = $skuToIdMap[$parentSku];
75
+ if (!isset($parentChildIds[$parentProductId])) {
76
+ $parentChildIds[$parentProductId]['add'] = array();
77
+ }
78
+ $parentChildIds[$parentProductId]['add'][] = $childProductId;
79
+ } else {
80
+ $failedSkus[$sku] = sprintf('Parent product "%s" not found', $parentSku);
81
+ }
82
+ }
83
+ } else {
84
+ $disassociateIds[] = $childProductId;
85
+ }
86
+ }
87
+ foreach ($disassociateIds as $disassociateId) {
88
+ $parents = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($disassociateId);
89
+ foreach ($parents as $parentId) {
90
+ if (!isset($parentChildIds[$parentId])) {
91
+ $parentChildIds[$parentId]['remove'] = array();
92
+ }
93
+ $parentChildIds[$parentId]['remove'][] = $disassociateId;
94
+ }
95
+ }
96
+ foreach ($parentChildIds as $parentId => $childIds) {
97
+ try {
98
+ /** @var Mage_Catalog_Model_Product $configurable */
99
+ $configurable = Mage::getModel('catalog/product')->load($parentId);
100
+ if ($configurable->getTypeId() !== Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
101
+ Mage::throwException('Product is not configurable');
102
+ }
103
+ $assignedProducts = $configurable->getTypeInstance()->getUsedProductIds($configurable);
104
+ if (!empty($childIds['add'])) {
105
+ $assignedProducts = array_merge($assignedProducts, $childIds['add']);
106
+ }
107
+ if (!empty($childIds['remove'])) {
108
+ $assignedProducts = array_diff($assignedProducts, $childIds['remove']);
109
+ }
110
+ Mage::getResourceModel('catalog/product_type_configurable')
111
+ ->saveProducts($configurable, $assignedProducts);
112
+ $idsToReindex[] = $parentId;
113
+ } catch (Exception $e) {
114
+ $failedSkus[$configurable->getSku()] = $e->getMessage();
115
+ }
116
+ }
117
+ }
118
+ if (isset($this->_configurableOptions)) {
119
+ /** @var RetailOps_Api_Model_Catalog_Adapter_Attribute $attributesAdapter */
120
+ $attributesAdapter = $this->_api->getAdapter('attributes');
121
+ $allOptions = $attributesAdapter->getAttributeOptions();
122
+ foreach ($this->_configurableOptions as $sku => $configurableAttributes) {
123
+ try {
124
+ $productId = $skuToIdMap[$sku];
125
+ $configurable = Mage::getModel('catalog/product')->load($productId);
126
+ if ($configurable->getTypeId() !== Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
127
+ Mage::throwException('Product is not configurable');
128
+ }
129
+ $productType = $configurable->getTypeInstance();
130
+ $productType->setProduct($configurable);
131
+ $usedAttributes = array();
132
+ foreach ($configurableAttributes as $attributeCode => $attribute) {
133
+ $attributeId = $attributesAdapter->findAttribute($attributeCode);
134
+ if ($attributeId === false) {
135
+ Mage::throwException(sprintf('Attribute "%s" not found', $attributeCode));
136
+ }
137
+ /** @var Mage_Catalog_Model_Resource_Eav_Attribute $attribute */
138
+ $attribute = Mage::getResourceModel('catalog/eav_attribute')->load($attributeId);
139
+ $isInSet = Mage::getResourceModel('eav/entity_attribute_set')->getSetInfo(array($attributeId),
140
+ $configurable->getAttributeSetId());
141
+ if (!$isInSet[$attributeId]
142
+ || !$productType->canUseAttribute($attribute)) {
143
+ Mage::throwException(sprintf('Attribute "%s" is not assigned to attribute set or cannot be used for configurable products', $attributeCode));
144
+ }
145
+ $usedAttributes[] = $attributeId;
146
+ }
147
+ $configurableAttributesData = $productType->getConfigurableAttributesAsArray();
148
+ if (!$configurableAttributesData) {
149
+ $productType->setUsedProductAttributeIds($usedAttributes);
150
+ $configurableAttributesData = $productType->getConfigurableAttributesAsArray();
151
+ } else {
152
+ foreach ($configurableAttributesData as $key => $attributeData) {
153
+ if (!in_array($attributeData['attribute_id'], $usedAttributes)) {
154
+ unset($configurableAttributesData[$key]);
155
+ }
156
+ }
157
+ }
158
+ foreach ($configurableAttributesData as &$attributeData) {
159
+ if (isset($configurableAttributes[$attributeData['attribute_code']])) {
160
+ $attribute = $configurableAttributes[$attributeData['attribute_code']];
161
+ } else {
162
+ $attribute = array();
163
+ }
164
+ $attributeData['label'] = isset($attribute['label']) ? $attribute['label'] : $attributeData['frontend_label'];
165
+ $attributeData['position'] = isset($attribute['position']) ? $attribute['position'] : 0;
166
+ if (isset($attribute['options'])) {
167
+ foreach ($attribute['options'] as $option => $priceChange) {
168
+ if (isset($allOptions[$attributeCode][$option])) {
169
+ $optionId = $allOptions[$attributeCode][$option];
170
+ $isPercent = 0;
171
+ if (false !== strpos($priceChange, '%')) {
172
+ $isPercent = 1;
173
+ }
174
+ $priceChange = preg_replace('/[^0-9\.,-]/', '', $priceChange);
175
+ $priceChange = (float) str_replace(',', '.', $priceChange);
176
+ $attributeData['values'][$optionId] = array(
177
+ 'value_index' => $optionId,
178
+ 'is_percent' => $isPercent,
179
+ 'pricing_value' => $priceChange,
180
+ );
181
+ }
182
+ }
183
+ }
184
+ }
185
+ $configurable->setConfigurableAttributesData($configurableAttributesData);
186
+ $configurable->save();
187
+ $idsToReindex[] = $configurable->getId();
188
+ $configurable->clearInstance();
189
+ } catch (Exception $e) {
190
+ $failedSkus[$configurable->getSku()] = $e->getMessage();
191
+ }
192
+ }
193
+ }
194
+ if ($idsToReindex) {
195
+ $this->getHelper()->reindexProducts(array_unique($idsToReindex),
196
+ Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE);
197
+ }
198
+ if ($failedSkus) {
199
+ $finalMessage = array();
200
+ foreach ($failedSkus as $sku => $message) {
201
+ $finalMessage[] = sprintf('sku: "%s", error: "%s"', $sku, $message);
202
+ }
203
+ $finalMessage = implode('; ', $finalMessage);
204
+ $this->_throwException('Configurable data is not saved for: ' . $finalMessage,
205
+ 'cant_save_configurable_data');
206
+ }
207
+ }
208
+
209
+ /**
210
+ * @param Mage_Catalog_Model_Product $product
211
+ * @return array
212
+ */
213
+ public function outputData($product)
214
+ {
215
+ $data = array();
216
+
217
+ if ($product->getTypeId() !== Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
218
+ return array();
219
+ }
220
+ $productType = $product->getTypeInstance(true);
221
+ $productType->setProduct($product);
222
+ $children = $productType->getChildrenIds($product->getId());
223
+ if ($children) {
224
+ $children = $children[0];
225
+ }
226
+ $associations = $this->_getResource()->getSkuByProductIds($children);
227
+
228
+ $data['associations'] = $associations;
229
+
230
+ $attributesData = $productType->getConfigurableAttributesAsArray();
231
+ $priceChanges = array();
232
+ foreach ($attributesData as $attributeData) {
233
+ $options = array();
234
+ foreach ($attributeData['values'] as $value) {
235
+ $options[$value['label']] = floatval($value['pricing_value']) . ($value['is_percent'] ? '%' : '');
236
+ }
237
+ $priceChanges[$attributeData['attribute_code']] = array(
238
+ 'label' => $attributeData['label'],
239
+ 'position' => $attributeData['position'],
240
+ 'options' => $options
241
+ );
242
+ }
243
+
244
+ $data['price_changes'] = $priceChanges;
245
+
246
+ return $data;
247
+ }
248
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Default.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Default extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'general';
29
+
30
+ /** @var array */
31
+ protected $_productTypes;
32
+
33
+ protected function _construct()
34
+ {
35
+ $this->_productTypes = array_keys(Mage::getModel('catalog/product_type')->getOptionArray());
36
+ $this->_errorCodes = array(
37
+ 'product_type_not_passed' => 401,
38
+ 'invalid_product_data' => 402,
39
+ 'error_saving_product' => 403,
40
+ 'product_type_not_exists' => 404
41
+ );
42
+ }
43
+
44
+ /**
45
+ * @param array $productData
46
+ * @param Mage_Catalog_Model_Product $product
47
+ * @return mixed
48
+ */
49
+ public function processData(array &$productData, $product)
50
+ {
51
+ $attributeSetId = $productData['attribute_set_id'];
52
+ $sku = $productData['sku'];
53
+
54
+ $product->setAttributeSetId($attributeSetId);
55
+
56
+ if (!$product->getId()) {
57
+ if (empty($productData['type_id'])) {
58
+ $this->_throwException('Product type is not specified', 'product_type_not_passed');
59
+ }
60
+ $type = $productData['type_id'];
61
+ $this->_checkProductTypeExists($type);
62
+ $product->setTypeId($type)->setSku($sku);
63
+ if (!isset($productData['stock_data']) || !is_array($productData['stock_data'])) {
64
+ //Set default stock_data if not exist in product data
65
+ $product->setStockData(array('use_config_manage_stock' => 0));
66
+ }
67
+ }
68
+ $this->_prepareDataForSave($product, $productData);
69
+
70
+ try {
71
+ if (is_array($errors = $product->validate())) {
72
+ $strErrors = array();
73
+ foreach($errors as $code => $error) {
74
+ if ($error === true) {
75
+ $error = Mage::helper('catalog')->__('Attribute "%s" is invalid.', $code);
76
+ }
77
+ $strErrors[] = $error;
78
+ }
79
+ $this->_throwException(implode("\n", $strErrors), 'invalid_product_data');
80
+ }
81
+
82
+ $product->save();
83
+ } catch (Mage_Core_Exception $e) {
84
+ $this->_throwException($e->getMessage(), 'error_saving_product');
85
+ }
86
+
87
+ $productData['product_id'] = $product->getId();
88
+
89
+ return $product->getId();
90
+ }
91
+
92
+ /**
93
+ * @param Mage_Catalog_Model_Product $product
94
+ * @return array
95
+ */
96
+ public function outputData($product)
97
+ {
98
+ $data = array();
99
+
100
+ $data['parents'] = array();
101
+
102
+ if ($product->isComposite()) {
103
+ return $data;
104
+ }
105
+ $parents = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
106
+ $parents = array_merge($parents, Mage::getModel('bundle/product_type')->getParentIdsByChild($product->getId()));
107
+ $parents = $this->_getResource()->getSkuByProductIds($parents);
108
+
109
+ $data['parents'] = $parents;
110
+
111
+ return $data;
112
+ }
113
+
114
+ /**
115
+ * Set additional data before product saved
116
+ *
117
+ * @param Mage_Catalog_Model_Product $product
118
+ * @param array $productData
119
+ * @return object
120
+ */
121
+ protected function _prepareDataForSave($product, $productData)
122
+ {
123
+ $product->addData($productData);
124
+
125
+ if (isset($productData['website_ids']) && is_array($productData['website_ids'])) {
126
+ $product->setWebsiteIds($productData['website_ids']);
127
+ }
128
+
129
+ if (isset($productData['websites']) && is_array($productData['websites'])) {
130
+ foreach ($productData['websites'] as &$website) {
131
+ if (is_string($website)) {
132
+ try {
133
+ $website = Mage::app()->getWebsite($website)->getId();
134
+ } catch (Exception $e) { }
135
+ }
136
+ }
137
+ $product->setWebsiteIds($productData['websites']);
138
+ }
139
+
140
+ if (Mage::app()->isSingleStoreMode()) {
141
+ $product->setWebsiteIds(array(Mage::app()->getStore(true)->getWebsite()->getId()));
142
+ }
143
+
144
+ if (isset($productData['stock_data']) && is_array($productData['stock_data'])) {
145
+ $product->setStockData($productData['stock_data']);
146
+ }
147
+
148
+ if (isset($productData['tier_price']) && is_array($productData['tier_price'])) {
149
+ $tierPrices = Mage::getModel('catalog/product_attribute_tierprice_api')
150
+ ->prepareTierPrices($product, $productData['tier_price']);
151
+ $product->setData(Mage_Catalog_Model_Product_Attribute_Tierprice_Api::ATTRIBUTE_CODE, $tierPrices);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Check if product type exists
157
+ *
158
+ * @param $productType
159
+ * @throw Mage_Api_Exception
160
+ * @return void
161
+ */
162
+ protected function _checkProductTypeExists($productType)
163
+ {
164
+ if (!in_array($productType, $this->_productTypes)) {
165
+ $this->_throwException('Product type not exists', 'product_type_not_exists');
166
+ }
167
+ }
168
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Downloadable.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Downloadable extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'downloadable';
29
+
30
+ protected $_downloadableData = array();
31
+
32
+ protected function _construct()
33
+ {
34
+ $this->_errorCodes = array(
35
+ 'cant_save_downloadable_data' => '901'
36
+ );
37
+ parent::_construct();
38
+ }
39
+
40
+ /**
41
+ * @param array $productData
42
+ * @param Mage_Catalog_Model_Product $product
43
+ * @return mixed|void
44
+ */
45
+ public function processData(array &$productData, $product)
46
+ {
47
+ $sku = $productData['sku'];
48
+
49
+ if (isset($productData['downloadable_links'])) {
50
+ $this->_downloadableData[$sku] = $productData['downloadable_links'];
51
+ }
52
+ }
53
+
54
+ /**
55
+ * @param array $skuToIdMap
56
+ * @return $this|void
57
+ */
58
+ public function afterDataProcess(array &$skuToIdMap)
59
+ {
60
+ $failedSkus = array();
61
+ $idsToReindex = array();
62
+ if (isset($this->_downloadableData)) {
63
+
64
+ foreach ($this->_downloadableData as $sku => $downloadableData) {
65
+ try {
66
+ $productId = $skuToIdMap[$sku];
67
+ /** @var Mage_Catalog_Model_Product $downloadable */
68
+ $downloadable = Mage::getModel('catalog/product')->load($productId);
69
+ $data = array();
70
+ foreach ($downloadableData as $resource) {
71
+ $resourceType = $resource['link_type'];
72
+ if (!isset($data[$resourceType])) {
73
+ $data[$resourceType] = array();
74
+ }
75
+ $this->_getValidator()->validateType($resourceType);
76
+ $this->_getValidator()->validateAttributes($resource, $resourceType);
77
+ $resource['is_delete'] = 0;
78
+ if ($resourceType == 'link') {
79
+ $resource['link_id'] = 0;
80
+ } elseif ($resourceType == 'sample') {
81
+ $resource['sample_id'] = 0;
82
+ }
83
+
84
+ if ($resource['type'] == 'file') {
85
+ if (isset($resource['file'])) {
86
+ $resource['file'] = $this->_uploadFile($resource['file'], $resourceType);
87
+ }
88
+ } elseif ($resource['type'] == 'url') {
89
+ unset($resource['file']);
90
+ }
91
+
92
+ if ($resourceType == 'link' && $resource['sample']['type'] == 'file') {
93
+ if (isset($resource['sample']['file'])) {
94
+ $resource['sample']['file'] = $this->_uploadFile($resource['sample']['file'], 'link_samples');
95
+ }
96
+ unset($resource['sample']['url']);
97
+ } elseif ($resourceType == 'link' && $resource['sample']['type'] == 'url') {
98
+ $resource['sample']['file'] = null;
99
+ }
100
+ $data[$resourceType][] = $resource;
101
+ }
102
+ $downloadable->setDownloadableData($data);
103
+ $downloadable->save();
104
+ $downloadable->clearInstance();
105
+ } catch (Exception $e) {
106
+ echo $e->getMessage();
107
+ $failedSkus[] = $sku;
108
+ }
109
+ }
110
+ }
111
+ if ($failedSkus) {
112
+ $this->_throwException('Downloadable data is not saved for ' . implode(',', array_unique($failedSkus)),
113
+ 'cant_save_downloadable_data');
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Return validator instance
119
+ *
120
+ * @return RetailOps_Api_Model_Catalog_Push_Downloadable_Validator
121
+ */
122
+ protected function _getValidator()
123
+ {
124
+ return Mage::getSingleton('retailops_api/catalog_push_downloadable_validator');
125
+ }
126
+
127
+ /**
128
+ * Decode file from base64 and upload it to donwloadable 'tmp' folder
129
+ *
130
+ * @param array $fileInfo
131
+ * @param string $type
132
+ * @return string
133
+ */
134
+ protected function _uploadFile($fileInfo, $type)
135
+ {
136
+ $tmpPath = '';
137
+ if ($type == 'sample') {
138
+ $tmpPath = Mage_Downloadable_Model_Sample::getBaseTmpPath();
139
+ } elseif ($type == 'link') {
140
+ $tmpPath = Mage_Downloadable_Model_Link::getBaseTmpPath();
141
+ } elseif ($type == 'link_samples') {
142
+ $tmpPath = Mage_Downloadable_Model_Link::getBaseSampleTmpPath();
143
+ }
144
+
145
+ $result = array();
146
+ $url = $fileInfo['url'];
147
+ $remoteFileName = $fileInfo['name'];
148
+ $ioAdapter = new Varien_Io_File();
149
+ $ioAdapter->checkAndCreateFolder($tmpPath);
150
+ $ioAdapter->open(array('path' => $tmpPath));
151
+ $fileName = $tmpPath . DS . Varien_File_Uploader::getCorrectFileName($remoteFileName);
152
+ if ($ioAdapter->cp($url, $fileName)) {
153
+ Mage::helper('core/file_storage_database')->saveFile($fileName);
154
+ }
155
+ $result['file'] = $remoteFileName;
156
+ $result['status'] = 'new';
157
+ $result['name'] = $remoteFileName;
158
+ return Mage::helper('core')->jsonEncode(array($result));
159
+ }
160
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Link.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Link extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'links';
29
+
30
+ protected $_linksData = array();
31
+ protected $_linkTypes = array();
32
+
33
+ protected function _construct()
34
+ {
35
+ $this->_linkTypes = $this->_getResource()->getLinkTypes();
36
+ $this->_errorCodes = array(
37
+ 'cant_save_links_data' => 501,
38
+ );
39
+ parent::_construct();
40
+ }
41
+
42
+ /**
43
+ * @param array $productData
44
+ * @param Mage_Catalog_Model_Product $product
45
+ * @return mixed|void
46
+ */
47
+ public function processData(array &$productData, $product)
48
+ {
49
+ if (isset($productData['links'])) {
50
+ foreach ($this->_linkTypes as $linkTypeId => $linkTypeCode) {
51
+ if (isset($productData['links'][$linkTypeCode])) {
52
+ $this->_linksData[$linkTypeId][$productData['sku']] = $productData['links'][$linkTypeCode];
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @param array $skuToIdMap
60
+ * @return $this|void
61
+ */
62
+ public function afterDataProcess(array &$skuToIdMap)
63
+ {
64
+ $failedSkus = array();
65
+ if (!empty($this->_linksData)) {
66
+ foreach ($this->_linksData as $typeId => $linksData) {
67
+ foreach ($linksData as $sku => $productLinksData) {
68
+ $productId = $skuToIdMap[$sku];
69
+ /** @var Mage_Catalog_Model_Product $product */
70
+ $product = Mage::getModel('catalog/product')->setId($productId);
71
+ $link = $product->getLinkInstance()->setLinkTypeId($typeId);
72
+ $links = array();
73
+ foreach ($productLinksData as $productLinkData) {
74
+ if (!isset($skuToIdMap[$productLinkData['sku']])) {
75
+ continue;
76
+ }
77
+ $linkedProductId = $skuToIdMap[$productLinkData['sku']];
78
+ $links[$linkedProductId] = array();
79
+ foreach ($link->getAttributes() as $attribute) {
80
+ if (isset($productLinkData[$attribute['code']])) {
81
+ $links[$linkedProductId][$attribute['code']] = $productLinkData[$attribute['code']];
82
+ }
83
+ }
84
+ }
85
+ try {
86
+ if ($typeId == Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED) {
87
+ $link->getResource()->saveGroupedLinks($product, $links, $typeId);
88
+ $this->getHelper()->reindexProducts(array($product->getId()),
89
+ Mage_Catalog_Model_Product_Type_Grouped::TYPE_CODE);
90
+ } else {
91
+ $link->getResource()->saveProductLinks($product, $links, $typeId);
92
+ }
93
+ } catch (Exception $e) {
94
+ $failedSkus[] = $sku;
95
+ }
96
+ }
97
+ }
98
+ }
99
+ if ($failedSkus) {
100
+ $this->_throwException('Links data is not saved for ' . implode(',', array_unique($failedSkus)),
101
+ 'cant_save_links_data');
102
+ }
103
+ }
104
+
105
+ /**
106
+ * @param Mage_Catalog_Model_Product $product
107
+ * @return array
108
+ */
109
+ public function outputData($product)
110
+ {
111
+ $data = array();
112
+ $data['links'] = $this->_getLinksData($product);
113
+
114
+ return $data;
115
+ }
116
+
117
+ /**
118
+ * Collect product links data
119
+ *
120
+ * @param Mage_Catalog_Model_Product $product
121
+ * @return array
122
+ */
123
+ protected function _getLinksData(Mage_Catalog_Model_Product $product)
124
+ {
125
+ $linksData = array();
126
+ $link = $product->getLinkInstance();
127
+ foreach ($this->_linkTypes as $linkTypeId => $linkType) {
128
+ $link->setLinkTypeId($linkTypeId);
129
+ $linkedProducts = $this->_initLinksCollection($link, $product);
130
+ foreach ($linkedProducts as $linkedProduct) {
131
+ $row = array(
132
+ 'product_id' => $linkedProduct->getId(),
133
+ 'type' => $linkedProduct->getTypeId(),
134
+ 'sku' => $linkedProduct->getSku()
135
+ );
136
+
137
+ foreach ($link->getAttributes() as $attribute) {
138
+ $row[$attribute['code']] = $linkedProduct->getData($attribute['code']);
139
+ }
140
+
141
+ $linksData[$linkType][] = $row;
142
+ }
143
+ }
144
+
145
+ return $linksData;
146
+ }
147
+
148
+ /**
149
+ * Initialize and return linked products collection
150
+ *
151
+ * @param Mage_Catalog_Model_Product_Link $link
152
+ * @param Mage_Catalog_Model_Product $product
153
+ * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection
154
+ */
155
+ protected function _initLinksCollection($link, $product)
156
+ {
157
+ $collection = $link
158
+ ->getProductCollection()
159
+ ->setIsStrongMode()
160
+ ->setProduct($product);
161
+
162
+ return $collection;
163
+ }
164
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Media.php ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Media extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ /**
29
+ * Attribute code for media gallery
30
+ */
31
+ const ATTRIBUTE_CODE = 'media_gallery';
32
+
33
+ const CRON_DOWNLOAD_LIMIT = 10;
34
+
35
+ protected $_section = 'media';
36
+
37
+ protected $_mediaDataToSave = array();
38
+ protected $_straightMediaProcessing = false;
39
+
40
+ protected function _construct()
41
+ {
42
+ $this->_errorCodes = array(
43
+ 'no_media_attribute' => 801,
44
+ );
45
+ parent::_construct();
46
+ }
47
+
48
+ /**
49
+ * @param array $productData
50
+ * @param Mage_Catalog_Model_Product $product
51
+ * @return mixed|void
52
+ */
53
+ public function processData(array &$productData, $product)
54
+ {
55
+ if ($product->getId() && (!isset($productData['unset_other_media']) || $productData['unset_other_media'])) {
56
+ $this->clearProductGallery($product);
57
+ }
58
+
59
+ if (isset($productData['media'])) {
60
+ $allMediaData = array();
61
+ foreach ($productData['media'] as $mediaData) {
62
+ $mediaData = new Varien_Object($mediaData);
63
+
64
+ Mage::dispatchEvent('retailops_catalog_media_process_before',
65
+ array('media_data' => $mediaData));
66
+
67
+ $allMediaData[] = $mediaData->getData();
68
+ }
69
+ $this->_mediaDataToSave[$productData['sku']] = json_encode($allMediaData);
70
+ }
71
+ if (isset($productData['straight_media_process']) && $productData['straight_media_process']) {
72
+ $this->_straightMediaProcessing = true;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @param array $skuToIdMap
78
+ * @return $this|void
79
+ */
80
+ public function afterDataProcess(array &$skuToIdMap)
81
+ {
82
+ if ($this->_mediaDataToSave) {
83
+ foreach ($this->_mediaDataToSave as $sku => $data) {
84
+ $productId = $skuToIdMap[$sku];
85
+ $dataToSave['media_data'] = $data;
86
+ $dataToSave['product_id'] = $productId;
87
+ $item = Mage::getModel('retailops_api/catalog_media_item')->setData($dataToSave);
88
+ if (!$this->_straightMediaProcessing) {
89
+ $item->save();
90
+ } else {
91
+ $this->downloadProductImages($item);
92
+ }
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Unset product image gallery
99
+ *
100
+ * @param $product
101
+ */
102
+ public function clearProductGallery($product)
103
+ {
104
+ $galleryData = $this->_prepareGallery($product);
105
+
106
+ if (isset($galleryData['images']) && is_array($galleryData['images'])) {
107
+ foreach ($galleryData['images'] as &$image) {
108
+ $image['removed'] = 1;
109
+ }
110
+ }
111
+
112
+ $product->setData(self::ATTRIBUTE_CODE, $galleryData);
113
+ }
114
+
115
+ /**
116
+ * Download products media
117
+ *
118
+ * @param Varien_Object|null $item
119
+ * @return array
120
+ */
121
+ public function downloadProductImages($item = null)
122
+ {
123
+ $ioAdapter = new Varien_Io_File();
124
+ $tmpDirectory = Mage::getBaseDir('var') . DS . 'api' . DS . uniqid();
125
+ $ioAdapter->checkAndCreateFolder($tmpDirectory);
126
+ $ioAdapter->open(array('path' => $tmpDirectory));
127
+ $remoteCopyRetryLimit = 3;
128
+ $errorLogPath = '/tmp/retailops_magento_image_error.log';
129
+ if (!$item) {
130
+ $items = Mage::getModel('retailops_api/catalog_media_item')->getCollection();
131
+ $limit = $this->getHelper()->getConfig('media_processing_products_limit');
132
+ if (!is_numeric($limit)) {
133
+ $limit = self::CRON_DOWNLOAD_LIMIT;
134
+ }
135
+ $items->getSelect()->limit($limit);
136
+ } else {
137
+ $items = array($item);
138
+ }
139
+ $result = array();
140
+ /** @var $item RetailOps_Api_Model_Catalog_Media_Item */
141
+ foreach ($items as $item) {
142
+ $productId = $item->getProductId();
143
+ $product = Mage::getModel('catalog/product')->load($productId);
144
+ $product->setStoreId(0); //using default store for images import
145
+ $gallery = $this->_getGalleryAttribute($product);
146
+ $data = json_decode($item->getMediaData(), true);
147
+ $allImages = $this->_getResource()->getProductMedia($productId);
148
+ $existingImageMap = array();
149
+ foreach ($allImages as $image) {
150
+ $existingImageMap[$image['value_id']]
151
+ = array( 'mediakey' => $image['retailops_mediakey'], 'filename' => $image['value'] );
152
+ }
153
+ $sku = $product->getSku();
154
+ $result[$sku] = array();
155
+ try {
156
+ $imageResult = array();
157
+ $newImages = array();
158
+ foreach ($data as $newImage) {
159
+ try {
160
+ $file = $this->_existingImage($existingImageMap, $newImage);
161
+
162
+ if (!$file) {
163
+ $url = $newImage['download_url'];
164
+ if (!$this->_httpFileExists($url)) {
165
+ Mage::throwException('Image does not exist.');
166
+ }
167
+ $fileName = $this->_getFileName($url, $newImage['mediakey']);
168
+ $fileName = $tmpDirectory . DS . $fileName;
169
+ $ioAdapter->cp($url, $fileName);
170
+
171
+ $retry = 0;
172
+ $remoteCopySuccess = false;
173
+ while ($retry++ < $remoteCopyRetryLimit && !$remoteCopySuccess) {
174
+ $remoteCopySuccess = $ioAdapter->cp($url, $fileName);
175
+ }
176
+
177
+ if (!$remoteCopySuccess) {
178
+ $remoteCopyError = error_get_last();
179
+
180
+ throw new Exception($remoteCopyError['message']);
181
+ }
182
+
183
+ // Adding image to gallery
184
+ $file = $gallery->getBackend()->addImage(
185
+ $product,
186
+ $fileName,
187
+ null,
188
+ true
189
+ );
190
+
191
+ $newImages[$file] = $newImage['mediakey'];
192
+ }
193
+
194
+ $gallery->getBackend()->updateImage($product, $file, $newImage);
195
+ if (isset($newImage['types'])) {
196
+ $gallery->getBackend()->setMediaAttribute($product, $newImage['types'], $file);
197
+ }
198
+ } catch (Exception $e) {
199
+ $message = sprintf("Could not process image %s, error message: %s", $newImage['download_url'], $e->getMessage());
200
+ $imageResult[] = $message;
201
+ file_put_contents($errorLogPath, "$message\n", FILE_APPEND);
202
+ }
203
+ }
204
+ if ($imageResult) {
205
+ $result[$sku]['images'] = $imageResult;
206
+ }
207
+ $product->save();
208
+ $this->_updateMediaKeys($product->getId(), $newImages);
209
+ if ($item->getId()) {
210
+ $item->delete();
211
+ }
212
+ $product->clearInstance();
213
+ } catch (Exception $e) {
214
+ $result[$sku]['general'] = $e->getMessage();
215
+ file_put_contents($errorLogPath, "{$e->getMessage()}\n", FILE_APPEND);
216
+ }
217
+ }
218
+
219
+ // Remove temporary directory
220
+ $ioAdapter->rmdir($tmpDirectory, true);
221
+
222
+ return $result;
223
+ }
224
+
225
+ /**
226
+ * @param Mage_Catalog_Model_Product $product
227
+ * @return array
228
+ */
229
+ public function outputData($product)
230
+ {
231
+ $galleryData = $this->_prepareGallery($product);
232
+ $data = array();
233
+ $data['media'] = array();
234
+
235
+ if (!isset($galleryData['images']) || !is_array($galleryData['images'])) {
236
+ return $data;
237
+ }
238
+
239
+ $result = array();
240
+ $mediaWithMediaKey = $this->_getResource()->getProductMedia($product->getId());
241
+ $valueIdToMediaKey = array();
242
+ foreach ($mediaWithMediaKey as $media) {
243
+ $valueIdToMediaKey[$media['value_id']] = $media['retailops_mediakey'];
244
+ }
245
+ foreach ($galleryData['images'] as &$image) {
246
+ if (!empty($valueIdToMediaKey[$image['value_id']])) {
247
+ $image['mediakey'] = $valueIdToMediaKey[$image['value_id']];
248
+ }
249
+ $result[] = $this->_imageToArray($image, $product);
250
+ }
251
+
252
+ $data['media'] = $result;
253
+
254
+ return $data;
255
+ }
256
+
257
+ /**
258
+ * Converts image to api array data
259
+ *
260
+ * @param array $image
261
+ * @param Mage_Catalog_Model_Product $product
262
+ * @return array
263
+ */
264
+ protected function _imageToArray(&$image, $product)
265
+ {
266
+ $result = array(
267
+ 'file' => $image['file'],
268
+ 'label' => $image['label'],
269
+ 'position' => $image['position'],
270
+ 'exclude' => $image['disabled'],
271
+ 'mediakey' => $image['mediakey'],
272
+ 'url' => $this->_getMediaConfig()->getMediaUrl($image['file']),
273
+ 'types' => array()
274
+ );
275
+
276
+
277
+ foreach ($product->getMediaAttributes() as $attribute) {
278
+ if ($product->getData($attribute->getAttributeCode()) == $image['file']) {
279
+ $result['types'][] = $attribute->getAttributeCode();
280
+ }
281
+ }
282
+
283
+ return $result;
284
+ }
285
+
286
+ /**
287
+ * Retrieve media config
288
+ *
289
+ * @return Mage_Catalog_Model_Product_Media_Config
290
+ */
291
+ protected function _getMediaConfig()
292
+ {
293
+ return Mage::getSingleton('catalog/product_media_config');
294
+ }
295
+
296
+ /**
297
+ * Prepare product's gallery data
298
+ *
299
+ * @param $product
300
+ * @return mixed
301
+ */
302
+ protected function _prepareGallery($product)
303
+ {
304
+ $gallery = $this->_getGalleryAttribute($product);
305
+ $gallery->getBackend()->afterLoad($product);
306
+ $galleryData = $product->getData(self::ATTRIBUTE_CODE);
307
+
308
+ return $galleryData;
309
+ }
310
+
311
+ /**
312
+ * Check if image download url is valid
313
+ *
314
+ * @param $url
315
+ * @return bool
316
+ */
317
+ protected function _httpFileExists($url)
318
+ {
319
+ $headers = @get_headers($url);
320
+
321
+ return !(strpos($headers[0], '200') === false);
322
+ }
323
+
324
+ /**
325
+ * Get existing image filename, if any, based on mediakey and filename
326
+ *
327
+ * @param $existingImageMap
328
+ * @param $imageData
329
+ * @return mixed
330
+ */
331
+ protected function _existingImage($existingImageMap, $imageData)
332
+ {
333
+ // Prioritize mediakeys. Search all existing images for mediakey before considering filename_match.
334
+ foreach ($existingImageMap as $existingImageData) {
335
+ if ($imageData['mediakey'] == $existingImageData['mediakey']) {
336
+ return $existingImageData['filename'];
337
+ }
338
+ }
339
+
340
+ foreach ($existingImageMap as $existingImageData) {
341
+ $fileNameMatch = preg_quote($data['filename_match'], '~');
342
+
343
+ if (strlen($fileNameMatch) && preg_grep('~' . $fileNameMatch . '~', $existingImageData['filename'])) {
344
+ return $existingImageData['filename'];
345
+ }
346
+ }
347
+
348
+ return false;
349
+ }
350
+
351
+ protected function _getFileName($url, $mediakey)
352
+ {
353
+ $fileName = Varien_File_Uploader::getCorrectFileName(basename($url));
354
+ $fileName = trim($fileName, '_');
355
+
356
+ return $fileName;
357
+ }
358
+
359
+ /**
360
+ * Update product's gallery with mediakeys
361
+ *
362
+ * @param $productId
363
+ * @param $newImages
364
+ */
365
+ protected function _updateMediaKeys($productId, $newImages)
366
+ {
367
+ $allImages = Mage::getResourceModel('retailops_api/api')->getProductMedia($productId);
368
+ $dataToUpdate = array();
369
+ foreach ($allImages as $image) {
370
+ if (isset($newImages[$image['value']])) {
371
+ $dataToUpdate[] = array(
372
+ 'value_id' => $image['value_id'],
373
+ 'retailops_mediakey' => $newImages[$image['value']]
374
+ );
375
+ }
376
+ }
377
+ if ($dataToUpdate) {
378
+ Mage::getResourceModel('retailops_api/api')->updateMediaKeys($dataToUpdate);
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Retrieve gallery attribute from product
384
+ *
385
+ * @param Mage_Catalog_Model_Product $product
386
+ * @param Mage_Catalog_Model_Resource_Eav_Mysql4_Attribute|boolean
387
+ */
388
+ protected function _getGalleryAttribute($product)
389
+ {
390
+ $attributes = $product->getTypeInstance(true)
391
+ ->getSetAttributes($product);
392
+
393
+ if (!isset($attributes[self::ATTRIBUTE_CODE])) {
394
+ $this->_throwException('Product has no media attribute', 'no_media_attribute');
395
+ }
396
+
397
+ return $attributes[self::ATTRIBUTE_CODE];
398
+ }
399
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Option.php ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Option extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'custom_options';
29
+ protected $_optionTypes = array();
30
+
31
+ protected function _construct()
32
+ {
33
+ $this->_optionTypes = $this->_getOptionTypes();
34
+ $this->_errorCodes = array(
35
+ 'invalid_options_data' => 701,
36
+ 'invalid_options_type' => 702,
37
+ );
38
+ parent::_construct();
39
+ }
40
+
41
+ /**
42
+ * @param array $productData
43
+ * @param Mage_Catalog_Model_Product $product
44
+ * @return mixed|void
45
+ */
46
+ public function processData(array &$productData, $product)
47
+ {
48
+ if (isset($productData['custom_options'])) {
49
+ if ($product->getId()) {
50
+ $currentOptions = $product->getOptions();
51
+ foreach ($currentOptions as $option) {
52
+ $option->delete();
53
+ }
54
+ }
55
+ $optionsData = $productData['custom_options'];
56
+ if (!empty($optionsData)) {
57
+ foreach ($optionsData as $optionData) {
58
+ $this->_addOption($product, $optionData);
59
+ }
60
+ $product->setHasOptions(true);
61
+ } else {
62
+ $product->setHasOptions(false);
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * @param Mage_Catalog_Model_Product $product
69
+ * @return array
70
+ */
71
+ public function outputData($product)
72
+ {
73
+ $data = array();
74
+ $data['custom_options'] = $this->_getCustomOptions($product);
75
+
76
+ return $data;
77
+ }
78
+
79
+ /**
80
+ * Get product custom options data
81
+ *
82
+ * @param Mage_Catalog_Model_Product $product
83
+ * @return array
84
+ */
85
+ protected function _getCustomOptions($product)
86
+ {
87
+ $data = array();
88
+ foreach ($product->getProductOptionsCollection() as $option) {
89
+ $result = array(
90
+ 'title' => $option->getTitle(),
91
+ 'type' => $option->getType(),
92
+ 'is_require' => $option->getIsRequire(),
93
+ 'sort_order' => $option->getSortOrder(),
94
+ // additional_fields should be two-dimensional array for all option types
95
+ 'additional_fields' => array(
96
+ array(
97
+ 'price' => $option->getPrice(),
98
+ 'price_type' => $option->getPriceType(),
99
+ 'sku' => $option->getSku()
100
+ )
101
+ )
102
+ );
103
+ // Set additional fields to each type group
104
+ switch ($option->getGroupByType()) {
105
+ case Mage_Catalog_Model_Product_Option::OPTION_GROUP_TEXT:
106
+ $result['additional_fields'][0]['max_characters'] = $option->getMaxCharacters();
107
+ break;
108
+ case Mage_Catalog_Model_Product_Option::OPTION_GROUP_FILE:
109
+ $result['additional_fields'][0]['file_extension'] = $option->getFileExtension();
110
+ $result['additional_fields'][0]['image_size_x'] = $option->getImageSizeX();
111
+ $result['additional_fields'][0]['image_size_y'] = $option->getImageSizeY();
112
+ break;
113
+ case Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT:
114
+ $result['additional_fields'] = array();
115
+ foreach ($option->getValuesCollection() as $value) {
116
+ $result['additional_fields'][] = array(
117
+ 'value_id' => $value->getId(),
118
+ 'title' => $value->getTitle(),
119
+ 'price' => $value->getPrice(),
120
+ 'price_type' => $value->getPriceType(),
121
+ 'sku' => $value->getSku(),
122
+ 'sort_order' => $value->getSortOrder()
123
+ );
124
+ }
125
+ break;
126
+ default:
127
+ break;
128
+ }
129
+
130
+ $data[] = $result;
131
+ }
132
+
133
+ return $data;
134
+ }
135
+
136
+ /**
137
+ * Add custom option to product
138
+ *
139
+ * @param Mage_Catalog_Model_Product $product
140
+ * @param array $data
141
+ * @param int|string|null $store
142
+ * @return bool $isAdded
143
+ */
144
+ protected function _addOption($product, $data, $store = null)
145
+ {
146
+ if (!(is_array($data['additional_fields']) and count($data['additional_fields']))) {
147
+ $this->_throwException('Invalid custom options data', 'invalid_options_data');
148
+ }
149
+ if (!$this->_isTypeAllowed($data['type'])) {
150
+ $this->_throwException('Invalid custom options type', 'invalid_options_type');
151
+ }
152
+ $this->_prepareAdditionalFields(
153
+ $data,
154
+ $product->getOptionInstance()->getGroupByType($data['type'])
155
+ );
156
+ $this->_addProductCustomOption($product, $data);
157
+
158
+ return true;
159
+ }
160
+
161
+ /**
162
+ * @return Mage_Catalog_Model_Product_Option_Api
163
+ */
164
+ protected function _getOptionApi()
165
+ {
166
+ return Mage::getModel('catalog/product_option_api');
167
+ }
168
+
169
+ /**
170
+ * Add product custom option data.
171
+ *
172
+ * @param Mage_Catalog_Model_Product $product
173
+ * @param array $data
174
+ * @return void
175
+ */
176
+ protected function _addProductCustomOption($product, $data)
177
+ {
178
+ foreach ($data as $key => $value) {
179
+ if (is_string($value)) {
180
+ $data[$key] = Mage::helper('catalog')->stripTags($value);
181
+ }
182
+ }
183
+
184
+ if (!$product->getOptionsReadonly()) {
185
+ $product
186
+ ->getOptionInstance()
187
+ ->addOption($data);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Prepare custom option data for saving by model.
193
+ *
194
+ * @param array $data
195
+ * @param string $groupType
196
+ * @return void
197
+ */
198
+ protected function _prepareAdditionalFields(&$data, $groupType)
199
+ {
200
+ if (is_array($data['additional_fields'])) {
201
+ if ($groupType != Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) {
202
+ // reset can be used as there should be the only
203
+ // element in 'additional_fields' for options of all types except those from Select group
204
+ $field = reset($data['additional_fields']);
205
+ if (!(is_array($field) and count($field))) {
206
+ $this->_throwException('Invalid custom options data', 'invalid_options_data');
207
+ } else {
208
+ foreach ($field as $key => $value) {
209
+ $data[$key] = $value;
210
+ }
211
+ }
212
+ } else {
213
+ // convert Select rows array to appropriate format for saving in the model
214
+ foreach ($data['additional_fields'] as $row) {
215
+ if (!(is_array($row) and count($row))) {
216
+ $this->_throwException('Invalid custom options data', 'invalid_options_data');
217
+ } else {
218
+ foreach ($row as $key => $value) {
219
+ $row[$key] = Mage::helper('catalog')->stripTags($value);
220
+ }
221
+ if (!empty($row['value_id'])) {
222
+ // map 'value_id' to 'option_type_id'
223
+ $row['option_type_id'] = $row['value_id'];
224
+ unset($row['value_id']);
225
+ $data['values'][$row['option_type_id']] = $row;
226
+ } else {
227
+ $data['values'][] = $row;
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ unset($data['additional_fields']);
234
+ }
235
+
236
+ /**
237
+ * Get allowed option types
238
+ *
239
+ * @return array
240
+ */
241
+ protected function _getOptionTypes()
242
+ {
243
+ $types = $this->_getOptionApi()->types();
244
+ $allowedTypes = array();
245
+ foreach($types as $optionType){
246
+ $allowedTypes[] = $optionType['value'];
247
+ }
248
+
249
+ return $allowedTypes;
250
+ }
251
+
252
+ /**
253
+ * Check is type in allowed set
254
+ *
255
+ * @param string $type
256
+ * @return bool
257
+ */
258
+ protected function _isTypeAllowed($type)
259
+ {
260
+ if (!in_array($type, $this->_optionTypes)) {
261
+ return false;
262
+ }
263
+
264
+ return true;
265
+ }
266
+ }
app/code/community/RetailOps/Api/Model/Catalog/Adapter/Tag.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Adapter_Tag extends RetailOps_Api_Model_Catalog_Adapter_Abstract
27
+ {
28
+ protected $_section = 'tags';
29
+
30
+ /**
31
+ * @param array $productData
32
+ * @param Mage_Catalog_Model_Product $product
33
+ * @return mixed|void
34
+ */
35
+ public function processData(array &$productData, $product)
36
+ {
37
+ }
38
+
39
+ /**
40
+ * @param Mage_Catalog_Model_Product $product
41
+ * @return array
42
+ */
43
+ public function outputData($product)
44
+ {
45
+ $data = array();
46
+ $data['tags'] = $this->_getTagsData($product->getId());
47
+
48
+ return $data;
49
+ }
50
+
51
+ /**
52
+ * Get products tags data
53
+ *
54
+ * @param $productId
55
+ * @return array
56
+ */
57
+ protected function _getTagsData($productId)
58
+ {
59
+ $data = array();
60
+ $tags = Mage::getModel('tag/tag')->getCollection()->addProductFilter($productId)->addPopularity();
61
+ /** @var $tag Mage_Tag_Model_Tag */
62
+ foreach ($tags as $tag) {
63
+ $result = array();
64
+ $result['status'] = $tag->getStatus();
65
+ $result['name'] = $tag->getName();
66
+ $result['base_popularity'] = (is_numeric($tag->getBasePopularity())) ? $tag->getBasePopularity() : 0;
67
+ $data[] = $result;
68
+ }
69
+
70
+ return $data;
71
+ }
72
+ }
app/code/community/RetailOps/Api/Model/Catalog/Api.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Api extends Mage_Catalog_Model_Product_Api
27
+ {
28
+ /** @var array */
29
+ protected $_dataAdapters;
30
+
31
+ /**
32
+ * Constructor. Initializes default values.
33
+ */
34
+ public function __construct()
35
+ {
36
+ $this->_dataAdapters = array(
37
+ 'attributes' => new RetailOps_Api_Model_Catalog_Adapter_Attribute($this),
38
+ 'category' => new RetailOps_Api_Model_Catalog_Adapter_Category($this),
39
+ 'media' => new RetailOps_Api_Model_Catalog_Adapter_Media($this),
40
+ 'option' => new RetailOps_Api_Model_Catalog_Adapter_Option($this),
41
+ 'configurable' => new RetailOps_Api_Model_Catalog_Adapter_Configurable($this),
42
+ 'bundle' => new RetailOps_Api_Model_Catalog_Adapter_Bundle($this),
43
+ 'downloadable' => new RetailOps_Api_Model_Catalog_Adapter_Downloadable($this),
44
+ 'tag' => new RetailOps_Api_Model_Catalog_Adapter_Tag($this),
45
+ 'default' => new RetailOps_Api_Model_Catalog_Adapter_Default($this),
46
+ 'link' => new RetailOps_Api_Model_Catalog_Adapter_Link($this),
47
+ );
48
+ Mage::dispatchEvent('retailops_catalog_adapter_init_after', array('api' => $this));
49
+ }
50
+
51
+ /**
52
+ * @param $code
53
+ * @return bool
54
+ */
55
+ public function getAdapter($code)
56
+ {
57
+ if (isset($this->_dataAdapters[$code])) {
58
+ return $this->_dataAdapters[$code];
59
+ }
60
+
61
+ return false;
62
+ }
63
+
64
+ /**
65
+ * @param $code
66
+ * @param $adapter
67
+ */
68
+ public function addAdapter($code, $adapter)
69
+ {
70
+ if (!($adapter instanceof RetailOps_Api_Model_Catalog_Adapter_Abstract)) {
71
+ $this->_fault('wrong_data_adapter', 'Wrong data adapter class');
72
+ }
73
+ $this->_dataAdapters[$code] = $adapter;
74
+ }
75
+
76
+ /**
77
+ * @return RetailOps_Api_Helper_Data
78
+ */
79
+ public function getHelper()
80
+ {
81
+ return Mage::helper('retailops_api');
82
+ }
83
+
84
+ /**
85
+ * @return RetailOps_Api_Model_Resource_Api
86
+ */
87
+ protected function _getResource()
88
+ {
89
+ return Mage::getResourceModel('retailops_api/api');
90
+ }
91
+ }
app/code/community/RetailOps/Api/Model/Catalog/Exception.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Exception
27
+ extends RetailOps_Api_Exception
28
+ {
29
+ /**
30
+ * API section with exception
31
+ */
32
+ protected $_section = null;
33
+
34
+ /**
35
+ * sku of product caused the excpetion
36
+ */
37
+ protected $_sku = null;
38
+
39
+ public function __construct($message, $code = 0, $sku = null, $section = null)
40
+ {
41
+ parent::__construct($message, $code);
42
+ $this->_section = $section;
43
+ $this->_sku = $sku;
44
+ }
45
+
46
+ /**
47
+ * Area of code with exception
48
+ *
49
+ * @return string
50
+ */
51
+ public function getSku()
52
+ {
53
+ return $this->_sku;
54
+ }
55
+
56
+ /**
57
+ * Area of code with exception
58
+ *
59
+ * @return string
60
+ */
61
+ public function getSection()
62
+ {
63
+ return $this->_section;
64
+ }
65
+ }
app/code/community/RetailOps/Api/Model/Catalog/Media/Item.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ /**
27
+ * RetailOps media update item
28
+ *
29
+ * @method RetailOps_Api_Model_Resource_Catalog_Media_Item _getResource()
30
+ * @method RetailOps_Api_Model_Resource_Catalog_Media_Item getResource()
31
+ * @method int getProductId()
32
+ * @method RetailOps_Api_Model_Catalog_Media_Item setProductId()
33
+ * @method int getUnsetOtherMedia()
34
+ * @method RetailOps_Api_Model_Catalog_Media_Item setUnsetOtherMedia()
35
+ * @method string getMediaData()
36
+ * @method RetailOps_Api_Model_Catalog_Media_Item setMediaData()
37
+ *
38
+ * @category RetailOps
39
+ * @package RetailOps_Api
40
+ */
41
+ class RetailOps_Api_Model_Catalog_Media_Item extends Mage_Core_Model_Abstract
42
+ {
43
+ /**
44
+ * Initialize resource model
45
+ */
46
+ protected function _construct()
47
+ {
48
+ $this->_init('retailops_api/catalog_media_item');
49
+ }
50
+ }
app/code/community/RetailOps/Api/Model/Catalog/Pull/Api.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Pull_Api extends RetailOps_Api_Model_Catalog_Api
27
+ {
28
+ /**
29
+ * Get Products
30
+ *
31
+ * @param mixed $filters
32
+ * @return array
33
+ */
34
+ public function catalogPull($filters = null)
35
+ {
36
+ /** @var $collection Mage_Catalog_Model_Resource_Product_Collection */
37
+ $collection = Mage::getModel('catalog/product')->getCollection()
38
+ ->addStoreFilter($this->_getStoreId())
39
+ ->addAttributeToSelect('*');
40
+
41
+ $apiHelper = $this->getHelper();
42
+ $filters = $apiHelper->applyPager($collection, $filters);
43
+ $filters = $apiHelper->parseFilters($filters, $this->_filtersMap);
44
+ try {
45
+ foreach ($filters as $field => $value) {
46
+ $collection->addFieldToFilter($field, $value);
47
+ }
48
+ } catch (Mage_Core_Exception $e) {
49
+ $this->_fault('filters_invalid', $e->getMessage());
50
+ }
51
+
52
+ $result = array();
53
+ try {
54
+
55
+ Mage::dispatchEvent(
56
+ 'retailops_catalog_pull_collection_prepare',
57
+ array('collection' => $collection)
58
+ );
59
+
60
+ $result['totalCount'] = $collection->getSize();
61
+
62
+ foreach ($this->_dataAdapters as $adapter) {
63
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
64
+ $adapter->prepareOutputData($collection);
65
+ }
66
+
67
+
68
+ $result['count'] = count($collection);
69
+ $records = array();
70
+ /** @var $product Mage_Catalog_Model_Product */
71
+ foreach ($collection as $product) {
72
+ $record = array(
73
+ 'product_id' => $product->getId(),
74
+ 'type' => $product->getTypeId(),
75
+ );
76
+ foreach ($this->_dataAdapters as $adapter) {
77
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
78
+ $record = array_merge($record, $adapter->outputData($product));
79
+ }
80
+
81
+ $recordObj = new Varien_Object($record);
82
+
83
+ Mage::dispatchEvent(
84
+ 'retailops_catalog_pull_record',
85
+ array('record' => $recordObj)
86
+ );
87
+
88
+ $records[] = $recordObj->getData();
89
+ }
90
+
91
+ $result['records'] = $records;
92
+ } catch (Mage_Core_Exception $e) {
93
+ $this->_fault('catalog_pull_error', $e->getMessage());
94
+ }
95
+
96
+ return $result;
97
+ }
98
+ }
app/code/community/RetailOps/Api/Model/Catalog/Push/Api.php ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Push_Api extends RetailOps_Api_Model_Catalog_Api
27
+ {
28
+ /** @var array */
29
+ protected $_skuToIdMap;
30
+
31
+ /** @var Mage_Index_Model_Resource_Process_Collection */
32
+ protected $_indexers;
33
+
34
+ protected $_errors = array();
35
+
36
+ public function __construct()
37
+ {
38
+ //$this->_indexers = Mage::getSingleton('index/indexer')->getProcessesCollection();
39
+ parent::__construct();
40
+ }
41
+
42
+ /**
43
+ * @return $this
44
+ */
45
+ public function beforeDataPrepare()
46
+ {
47
+ try {
48
+ foreach ($this->_dataAdapters as $adapter) {
49
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
50
+ $adapter->beforeDataPrepare();
51
+ }
52
+ } catch (RetailOps_Api_Model_Catalog_Exception $e) {
53
+ $this->_addError($e);
54
+ }
55
+
56
+ return $this;
57
+ }
58
+
59
+ /**
60
+ * @param array $data
61
+ * @return $this
62
+ */
63
+ public function prepareData(array &$data)
64
+ {
65
+ $this->_validateData($data);
66
+ foreach ($this->_dataAdapters as $adapter) {
67
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
68
+ $adapter->prepareData($data);
69
+ }
70
+
71
+ return $this;
72
+ }
73
+
74
+ /**
75
+ * @return $this
76
+ */
77
+ public function afterDataPrepare()
78
+ {
79
+ try {
80
+ foreach ($this->_dataAdapters as $adapter) {
81
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
82
+ $adapter->afterDataPrepare();
83
+ }
84
+ } catch (RetailOps_Api_Model_Catalog_Exception $e) {
85
+ $this->_addError($e);
86
+ }
87
+
88
+ return $this;
89
+ }
90
+
91
+ /**
92
+ * @return $this
93
+ */
94
+ public function beforeDataProcess()
95
+ {
96
+ try {
97
+ foreach ($this->_dataAdapters as $adapter) {
98
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
99
+ $adapter->beforeDataProcess();
100
+ }
101
+ } catch (RetailOps_Api_Model_Catalog_Exception $e) {
102
+ $this->_addError($e);
103
+ }
104
+ $this->_skuToIdMap = $this->_getResource()->getIdsByProductSkus();
105
+
106
+ return $this;
107
+ }
108
+
109
+ /**
110
+ * @param array $data
111
+ * @return bool
112
+ * @throws Exception
113
+ */
114
+ public function processData(array &$data)
115
+ {
116
+ /** @var Mage_Catalog_Model_Product $product */
117
+ $product = Mage::getModel('catalog/product');
118
+ if (isset($this->_skuToIdMap[$data['sku']])) {
119
+ $data['product_id'] = $this->_skuToIdMap[$data['sku']];
120
+ $product->load($data['product_id']);
121
+ }
122
+
123
+ foreach ($this->_dataAdapters as $adapter) {
124
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
125
+ $adapter->processData($data, $product);
126
+ }
127
+
128
+ $this->_skuToIdMap[$data['sku']] = $product->getId();
129
+ if ($product->getHasOptions()) {
130
+ /**
131
+ * Clear custom options singleton
132
+ */
133
+ $product->getOptionInstance()->unsetOptions();
134
+ }
135
+ $product->clearInstance();
136
+
137
+ return true;
138
+ }
139
+
140
+ /**
141
+ * @return $this
142
+ */
143
+ public function afterDataProcess()
144
+ {
145
+ foreach ($this->_dataAdapters as $adapter) {
146
+ try {
147
+ /** @var $adapter RetailOps_Api_Model_Catalog_Adapter_Abstract */
148
+ $adapter->afterDataProcess($this->_skuToIdMap);
149
+ } catch (RetailOps_Api_Model_Catalog_Exception $e) {
150
+ $this->_addError($e);
151
+ }
152
+ }
153
+
154
+ return $this;
155
+ }
156
+
157
+ /**
158
+ * Create products
159
+ *
160
+ * @param $productsData
161
+ * @return array
162
+ */
163
+ public function catalogPush($productsData)
164
+ {
165
+ file_put_contents('/tmp/magento_debug', 'catalogPush data ' . var_export($productsData, true) . "\n", FILE_APPEND);
166
+ if (isset($productsData['records'])) {
167
+ $productsData = $productsData['records'];
168
+ }
169
+ $result = array();
170
+ $result['records'] = array();
171
+ $processedSkus = array();
172
+ try {
173
+ //$this->_stopReindex();
174
+ $this->beforeDataPrepare();
175
+ foreach ($productsData as $key => $data) {
176
+ try {
177
+ $dataObj = new Varien_Object($data);
178
+ Mage::dispatchEvent('retailops_catalog_push_data_prepare_before', array('data' => $dataObj));
179
+ $data = $dataObj->getData();
180
+ $processedSkus[] = $data['sku'];
181
+ $this->prepareData($data);
182
+ } catch (RetailOps_Api_Model_Catalog_Exception $e) {
183
+ unset($productsData[$key]);
184
+ $this->_addError($e);
185
+ }
186
+ }
187
+ $this->afterDataPrepare();
188
+
189
+ $this->beforeDataProcess();
190
+ foreach ($productsData as $data) {
191
+ try {
192
+ $dataObj = new Varien_Object($data);
193
+ Mage::dispatchEvent('retailops_catalog_push_data_process_before', array('data' => $dataObj));
194
+ $data = $dataObj->getData();
195
+ $this->processData($data);
196
+ } catch (RetailOps_Api_Model_Catalog_Exception $e) {
197
+ $this->_addError($e);
198
+ }
199
+ }
200
+ $this->afterDataProcess();
201
+ //$this->_startReindex();
202
+ } catch (Exception $e) {
203
+ $this->_addError(new RetailOps_Api_Model_Catalog_Exception($e->getMessage()));
204
+ }
205
+ foreach ($processedSkus as $sku) {
206
+ $r = array();
207
+ $r['sku'] = $sku;
208
+ $r['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
209
+ if (!empty($this->_errors[$sku])) {
210
+ $r['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
211
+ $r['errors'] = $this->_errors[$sku];
212
+ }
213
+ $result['records'][] = $r;
214
+ }
215
+ if (isset($this->_errors['global'])) {
216
+ $result['global_errors'] = $this->_errors['global'];
217
+ }
218
+
219
+ return $result;
220
+ }
221
+
222
+ /**
223
+ * @param array $data
224
+ * @throws RetailOps_Api_Model_Catalog_Exception
225
+ */
226
+ protected function _validateData(array &$data)
227
+ {
228
+ if (empty($data['sku'])) {
229
+ throw new RetailOps_Api_Model_Catalog_Exception('Sku is missing', 100, null, 'general');
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Add error message for api results
235
+ *
236
+ * @param RetailOps_Api_Model_Catalog_Exception $e
237
+ */
238
+ protected function _addError(RetailOps_Api_Model_Catalog_Exception $e)
239
+ {
240
+ if ($e->getSku()) {
241
+ $errorKey = $e->getSku();
242
+ } else {
243
+ $errorKey = 'global';
244
+ }
245
+ if (!isset($this->_errors[$errorKey])) {
246
+ $this->_errors[$errorKey] = array();
247
+ }
248
+ $this->_errors[$errorKey][] = array(
249
+ 'message' => $e->getMessage(),
250
+ 'code' => $e->getCode(),
251
+ 'section' => $e->getSection(),
252
+ );
253
+ }
254
+
255
+ // protected function _stopReindex()
256
+ // {
257
+ // $this->_indexers->walk('setMode', array(Mage_Index_Model_Process::MODE_MANUAL));
258
+ // $this->_indexers->walk('save');
259
+ // }
260
+ //
261
+ // protected function _startReindex()
262
+ // {
263
+ // $this->_indexers->walk('reindexAll');
264
+ // $this->_indexers->walk('setMode', array(Mage_Index_Model_Process::MODE_REAL_TIME));
265
+ // $this->_indexers->walk('save');
266
+ // }
267
+ }
app/code/community/RetailOps/Api/Model/Catalog/Push/Downloadable/Validator.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Catalog_Push_Downloadable_Validator extends Mage_Downloadable_Model_Link_Api_Validator
27
+ {
28
+ /**
29
+ * Remove base64 file contents validation
30
+ *
31
+ * @param mixed $var
32
+ */
33
+ public function validateFileDetails(&$var)
34
+ {
35
+ }
36
+ }
app/code/community/RetailOps/Api/Model/Inventory/Api.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Inventory_Api extends Mage_CatalogInventory_Model_Stock_Item_Api
27
+ {
28
+ /**
29
+ * Update stock data of multiple products at once
30
+ *
31
+ * @param array $itemData
32
+ * @return array
33
+ */
34
+ public function inventoryPush($itemData)
35
+ {
36
+ if (isset($itemData['records'])) {
37
+ $itemData = $itemData['records'];
38
+ }
39
+ $response = array();
40
+ $response['records'] = array();
41
+ $orderItemsCollection = Mage::getResourceModel('retailops_api/api')->getRetailopsNonretrievedOrderItems();
42
+ $orderItems = $this->filterOrderItems($orderItemsCollection);
43
+ $productIds = $this->getProductIds($itemData);
44
+
45
+ foreach ($itemData as $item) {
46
+ try {
47
+ $itemObj = new Varien_Object($item);
48
+
49
+ Mage::dispatchEvent(
50
+ 'retailops_inventory_push_record',
51
+ array('record' => $itemObj)
52
+ );
53
+
54
+ $result = array();
55
+ $result['sku'] = $itemObj->getSku();
56
+
57
+ $itemObj->setQty($itemObj->getQuantity()); // api update accepts qty not quantity parameter
58
+
59
+ $qty = $itemObj->getQty();
60
+ if (isset($orderItems[$itemObj->getSku()])) {
61
+ $qty = $itemObj->getQty() - $orderItems[$itemObj->getSku()];
62
+ }
63
+ $itemObj->setQty($qty);
64
+
65
+ Mage::dispatchEvent(
66
+ 'retailops_inventory_push_record_qty_processed',
67
+ array('record' => $itemObj)
68
+ );
69
+
70
+ $this->update($productIds[$itemObj->getSku()], $itemObj->getData());
71
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
72
+ } catch (Mage_Core_Exception $e) {
73
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
74
+ $result['error'] = array(
75
+ 'code' => $e->getCode(),
76
+ 'message' => $e->getMessage()
77
+ );
78
+ }
79
+ $response['records'][] = $result;
80
+ }
81
+
82
+ return $response;
83
+ }
84
+
85
+ /**
86
+ * Removes parent order items from collection
87
+ *
88
+ * @param $collection Mage_Sales_Model_Resource_Order_Item_Collection
89
+ * @return array
90
+ */
91
+ public function filterOrderItems(Mage_Sales_Model_Resource_Order_Item_Collection $collection)
92
+ {
93
+ $result = array();
94
+
95
+ /* remove parent items */
96
+ foreach ($collection as $item) {
97
+ $collection->removeItemByKey($item->getParentItemId());
98
+ }
99
+
100
+ /* calculate total ordered quantity per item */
101
+ foreach ($collection as $item) {
102
+ if (isset($result[$item->getSku()])){
103
+ $result[$item->getSku()] += $item->getQtyOrdered();
104
+ } else {
105
+ $result[$item->getSku()] = $item->getQtyOrdered();
106
+ }
107
+ }
108
+
109
+ return $result;
110
+ }
111
+
112
+ /**
113
+ * Update product stock data
114
+ *
115
+ * @param int $productId
116
+ * @param array $data
117
+ * @return bool
118
+ */
119
+ public function update($productId, $data)
120
+ {
121
+ /** @var $product Mage_Catalog_Model_Product */
122
+ $product = Mage::getModel('catalog/product');
123
+
124
+ $product->setStoreId($this->_getStoreId())
125
+ ->load($productId);
126
+
127
+ if (!$product->getId()) {
128
+ $this->_fault('not_exists');
129
+ }
130
+
131
+ /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */
132
+ $stockItem = $product->getStockItem();
133
+ $stockData = array_replace($stockItem->getData(), (array)$data);
134
+ $stockItem->setData($stockData);
135
+
136
+ try {
137
+ $stockItem->save();
138
+ } catch (Mage_Core_Exception $e) {
139
+ $this->_fault('not_updated', $e->getMessage());
140
+ }
141
+
142
+ return true;
143
+ }
144
+
145
+ /**
146
+ *
147
+ *
148
+ * @param $data array
149
+ * @return array
150
+ */
151
+ public function getProductIds($data)
152
+ {
153
+ $skus = array();
154
+
155
+ foreach ($data as $item) {
156
+ $skus[] = $item['sku'];
157
+ }
158
+
159
+ $result = Mage::getResourceModel('retailops_api/api')->getIdsByProductSkus($skus);
160
+
161
+ return $result;
162
+ }
163
+ }
app/code/community/RetailOps/Api/Model/Observer.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Observer
27
+ {
28
+ /**
29
+ * Update retailops order status when payment is made
30
+ *
31
+ * @param $observer
32
+ */
33
+ public function updateRetailopsStatus($observer)
34
+ {
35
+ $order = $observer->getEvent()->getPayment()->getOrder();
36
+ $order->setRetailopsStatus(RetailOps_Api_Helper_Data::RETAILOPS_ORDER_READY);
37
+ }
38
+
39
+ /**
40
+ * Download product images
41
+ */
42
+ public function downloadProductImages()
43
+ {
44
+ $mediaAdapter = new RetailOps_Api_Model_Catalog_Adapter_Media();
45
+ $mediaAdapter->downloadProductImages();
46
+ }
47
+ }
app/code/community/RetailOps/Api/Model/Observer2.php~ ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Observer
27
+ {
28
+ /**
29
+ * Update retailops order status when payment is made
30
+ *
31
+ * @param $observer
32
+ */
33
+ public function updateRetailopsStatus($observer)
34
+ {
35
+ $order = $observer->getEvent()->getPayment()->getOrder();
36
+ $order->setRetailopsStatus(RetailOps_Api_Helper_Data::RETAILOPS_ORDER_READY);
37
+ }
38
+
39
+ /**
40
+ * Download product images
41
+ */
42
+ public function downloadProductImages()
43
+ {
44
+ $mediaAdapter = new RetailOps_Api_Model_Catalog_Adapter_Media();
45
+ $mediaAdapter->downloadProductImages();
46
+ }
47
+ }
app/code/community/RetailOps/Api/Model/Order/Api.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Order_Api extends Mage_Sales_Model_Order_Api
27
+ {
28
+ /**
29
+ * Initialize attributes map
30
+ */
31
+ public function __construct()
32
+ {
33
+ $this->_attributesMap = array(
34
+ 'order' => array(
35
+ 'order_id' => 'entity_id',
36
+ 'order_increment_id' => 'increment_id'
37
+ ),
38
+ 'order_address' => array('address_id' => 'entity_id'),
39
+ 'order_payment' => array('payment_id' => 'entity_id')
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Retrieve list of orders. Filtration could be applied
45
+ *
46
+ * @param null|object|array $filters
47
+ * @return array
48
+ */
49
+ public function orderPull($filters = null)
50
+ {
51
+ $result = array();
52
+ //TODO: add full name logic
53
+ $billingAliasName = 'billing_o_a';
54
+ $shippingAliasName = 'shipping_o_a';
55
+
56
+ /** @var $orderCollection Mage_Sales_Model_Mysql4_Order_Collection */
57
+ $orderCollection = Mage::getModel("sales/order")->getCollection();
58
+ $billingFirstnameField = "$billingAliasName.firstname";
59
+ $billingLastnameField = "$billingAliasName.lastname";
60
+ $shippingFirstnameField = "$shippingAliasName.firstname";
61
+ $shippingLastnameField = "$shippingAliasName.lastname";
62
+ $orderCollection->addAttributeToSelect('*')
63
+ ->addAddressFields()
64
+ ->addExpressionFieldToSelect('billing_firstname', "{{billing_firstname}}",
65
+ array('billing_firstname' => $billingFirstnameField))
66
+ ->addExpressionFieldToSelect('billing_lastname', "{{billing_lastname}}",
67
+ array('billing_lastname' => $billingLastnameField))
68
+ ->addExpressionFieldToSelect('shipping_firstname', "{{shipping_firstname}}",
69
+ array('shipping_firstname' => $shippingFirstnameField))
70
+ ->addExpressionFieldToSelect('shipping_lastname', "{{shipping_lastname}}",
71
+ array('shipping_lastname' => $shippingLastnameField))
72
+ ->addExpressionFieldToSelect('billing_name', "CONCAT({{billing_firstname}}, ' ', {{billing_lastname}})",
73
+ array('billing_firstname' => $billingFirstnameField, 'billing_lastname' => $billingLastnameField))
74
+ ->addExpressionFieldToSelect('shipping_name', 'CONCAT({{shipping_firstname}}, " ", {{shipping_lastname}})',
75
+ array('shipping_firstname' => $shippingFirstnameField, 'shipping_lastname' => $shippingLastnameField)
76
+ );
77
+
78
+ Mage::dispatchEvent(
79
+ 'retailops_order_collection_prepare',
80
+ array('collection' => $orderCollection)
81
+ );
82
+
83
+
84
+
85
+ /** @var $apiHelper Retailops_Api_Helper_Data */
86
+ $apiHelper = Mage::helper('retailops_api');
87
+ $filters = $apiHelper->applyPager($orderCollection, $filters);
88
+ $filters = $apiHelper->parseFilters($filters, $this->_attributesMap['order']);
89
+
90
+ try {
91
+ foreach ($filters as $field => $value) {
92
+ $orderCollection->addFieldToFilter($field, $value);
93
+ }
94
+ } catch (Mage_Core_Exception $e) {
95
+ $this->_fault('filters_invalid', $e->getMessage());
96
+ }
97
+
98
+ $result['totalCount'] = $orderCollection->getSize();
99
+ $result['count'] = count($orderCollection);
100
+
101
+ try {
102
+ foreach ($orderCollection as $order) {
103
+ $record = $this->orderInfo($order);
104
+ $recordObj = new Varien_Object($record);
105
+
106
+ Mage::dispatchEvent(
107
+ 'retailops_order_pull_record',
108
+ array('record' => $recordObj)
109
+ );
110
+
111
+ $orders[] = $recordObj->getData();
112
+ }
113
+
114
+ $result['records'] = $orders;
115
+ } catch (Mage_Core_Exception $e) {
116
+ $this->_fault('order_pull_error', $e->getMessage());
117
+ }
118
+
119
+ return $result;
120
+ }
121
+
122
+ /**
123
+ * Retrieve full order information
124
+ *
125
+ * @param Mage_Core_Model_Abstract $order
126
+ * @return array
127
+ */
128
+ public function orderInfo($order)
129
+ {
130
+ if ($order->getGiftMessageId() > 0) {
131
+ $order->setGiftMessage(
132
+ Mage::getSingleton('giftmessage/message')->load($order->getGiftMessageId())->getMessage()
133
+ );
134
+ } else {
135
+ $order->setGiftMessage(null);
136
+ }
137
+
138
+ $result['order_info'] = $this->_getAttributes($order, 'order');
139
+
140
+ $result['shipping_address'] = $this->_getAttributes($order->getShippingAddress(), 'order_address');
141
+ $result['billing_address'] = $this->_getAttributes($order->getBillingAddress(), 'order_address');
142
+ $result['items'] = array();
143
+
144
+ foreach ($order->getAllItems() as $item) {
145
+ if ($item->getGiftMessageId() > 0) {
146
+ $item->setGiftMessage(
147
+ Mage::getSingleton('giftmessage/message')->load($item->getGiftMessageId())->getMessage()
148
+ );
149
+ }
150
+
151
+ $result['items'][] = $this->_getAttributes($item, 'order_item');
152
+ }
153
+
154
+ $result['payment'] = $this->_getAttributes($order->getPayment(), 'order_payment');
155
+
156
+ $result['status_history'] = array();
157
+
158
+ foreach ($order->getAllStatusHistory() as $history) {
159
+ $result['status_history'][] = $this->_getAttributes($history, 'order_status_history');
160
+ }
161
+
162
+ $retailops_status_history = Mage::getModel('retailops_api/order_status_history')->getRetailOpsStatusHistory($order);
163
+ $result['retailops_status_history'] = array();
164
+
165
+ foreach ($retailops_status_history as $history) {
166
+ $result['retailops_status_history'][] = $this->_getAttributes($history, 'retailops_order_status_history');
167
+ }
168
+
169
+ return $result;
170
+ }
171
+
172
+ /**
173
+ * @param array $ordersData
174
+ * @return array
175
+ */
176
+ public function orderStatusUpdate($ordersData)
177
+ {
178
+ if (isset($ordersData['records'])) {
179
+ $ordersData = $ordersData['records'];
180
+ }
181
+ $fullResult = array();
182
+ $fullResult['records'] = array();
183
+ foreach ($ordersData as $orderData) {
184
+ if (isset($orderData['order_increment_id'])) {
185
+ try {
186
+ $result = array(
187
+ 'order_increment_id' => $orderData['order_increment_id']
188
+ );
189
+ /** @var Mage_Sales_Model_Order $order */
190
+ $order = Mage::getModel('sales/order');
191
+ $order->loadByIncrementId($orderData['order_increment_id']);
192
+ if (!$order->getId()) {
193
+ throw new Exception('Order is not found');
194
+ }
195
+ $order->setRetailopsStatus($orderData['retailops_status']);
196
+ $order->save();
197
+ /** @var RetailOps_Api_Model_Order_Status_History $history */
198
+ $history = Mage::getModel('retailops_api/order_status_history');
199
+ $history->setOrder($order);
200
+ $history->setStatus($orderData['retailops_status']);
201
+ if (isset($orderData['comment'])) {
202
+ $history->setComment($orderData['comment']);
203
+ }
204
+ $history->save();
205
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
206
+ } catch (Exception $e) {
207
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
208
+ $result['message'] = $e->getMessage();
209
+ }
210
+ $fullResult['records'][] = $result;
211
+ }
212
+ }
213
+
214
+ return $fullResult;
215
+ }
216
+ }
app/code/community/RetailOps/Api/Model/Order/Status/History.php ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ /**
27
+ * Order status history comments
28
+ *
29
+ * @method RetailOps_Api_Model_Resource_Order_Status_History _getResource()
30
+ * @method RetailOps_Api_Model_Resource_Order_Status_History getResource()
31
+ * @method int getParentId()
32
+ * @method RetailOps_Api_Model_Order_Status_History setParentId(int $value)
33
+ * @method string getComment()
34
+ * @method RetailOps_Api_Model_Order_Status_History setComment(string $value)
35
+ * @method string getStatus()
36
+ * @method RetailOps_Api_Model_Order_Status_History setStatus(string $value)
37
+ * @method string getCreatedAt()
38
+ * @method RetailOps_Api_Model_Order_Status_History setCreatedAt(string $value)
39
+ *
40
+ * @category RetailOps
41
+ * @package RetailOps_Api
42
+ */
43
+ class RetailOps_Api_Model_Order_Status_History extends Mage_Core_Model_Abstract
44
+ {
45
+
46
+ /**
47
+ * Order instance
48
+ *
49
+ * @var Mage_Sales_Model_Order
50
+ */
51
+ protected $_order;
52
+
53
+ protected $_eventPrefix = 'retailops_order_status_history';
54
+ protected $_eventObject = 'retailops_status_history';
55
+
56
+ /**
57
+ * Initialize resource model
58
+ */
59
+ protected function _construct()
60
+ {
61
+ $this->_init('retailops_api/order_status_history');
62
+ }
63
+
64
+ /**
65
+ * Set order object and grab some metadata from it
66
+ *
67
+ * @param Mage_Sales_Model_Order $order
68
+ * @return RetailOps_Api_Model_Order_Status_History
69
+ */
70
+ public function setOrder(Mage_Sales_Model_Order $order)
71
+ {
72
+ $this->_order = $order;
73
+ return $this;
74
+ }
75
+
76
+ /**
77
+ * Retrieve order instance
78
+ *
79
+ * @return Mage_Sales_Model_Order
80
+ */
81
+ public function getOrder()
82
+ {
83
+ return $this->_order;
84
+ }
85
+
86
+ /**
87
+ * Set order again if required
88
+ *
89
+ * @return RetailOps_Api_Model_Order_Status_History
90
+ */
91
+ protected function _beforeSave()
92
+ {
93
+ parent::_beforeSave();
94
+ if (!$this->getParentId() && $this->getOrder()) {
95
+ $this->setParentId($this->getOrder()->getId());
96
+ $this->setStatus($this->getOrder()->getRetailopsStatus());
97
+ }
98
+
99
+ return $this;
100
+ }
101
+
102
+ /**
103
+ * @param Mage_Sales_Model_Order $order
104
+ *
105
+ * @return RetailOps_Api_Model_Resource_Order_Status_History_Collection
106
+ */
107
+ public function getRetailOpsStatusHistory(Mage_Sales_Model_Order $order)
108
+ {
109
+ $statusHistory = Mage::getResourceModel('retailops_api/order_status_history_collection')
110
+ ->setOrderFilter($order)
111
+ ->setOrder('created_at', 'desc')
112
+ ->setOrder('entity_id', 'desc');
113
+
114
+ if ($order->getId()) {
115
+ foreach ($statusHistory as $status) {
116
+ $status->setOrder($order);
117
+ }
118
+ }
119
+
120
+ return $statusHistory;
121
+ }
122
+ }
app/code/community/RetailOps/Api/Model/Resource/Api.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Resource_Api extends Mage_Core_Model_Resource_Db_Abstract
27
+ {
28
+ protected function _construct()
29
+ {
30
+ $this->_setResource('catalog');
31
+ }
32
+
33
+ /**
34
+ * Get product link types
35
+ *
36
+ * @return array
37
+ */
38
+ public function getLinkTypes()
39
+ {
40
+ $select = $this->_getReadAdapter()->select()->from($this->getTable('catalog/product_link_type'));
41
+
42
+ return $this->_getReadAdapter()->fetchPairs($select);
43
+ }
44
+
45
+ /**
46
+ * Get products skus
47
+ *
48
+ * @param array $produtIds
49
+ * @return array
50
+ */
51
+ public function getSkuByProductIds($produtIds)
52
+ {
53
+ if (!$produtIds) {
54
+ return array();
55
+ }
56
+ $select = $this->_getReadAdapter()->select()->from($this->getTable('catalog/product'), array('sku'));
57
+ $where = sprintf('entity_id IN (%s)', implode(',', $produtIds));
58
+ $select->where($where);
59
+
60
+ return $this->_getReadAdapter()->fetchCol($select);
61
+ }
62
+
63
+ /**
64
+ * Get products ids
65
+ *
66
+ * @param array $productSkus
67
+ * @return array
68
+ */
69
+ public function getIdsByProductSkus($productSkus = null)
70
+ {
71
+ $select = $this->_getReadAdapter()->select()->from($this->getTable('catalog/product'), array('sku', 'entity_id'));
72
+ if ($productSkus) {
73
+ $select->where("sku IN (?)", $productSkus);
74
+ }
75
+
76
+ return $this->_getReadAdapter()->fetchPairs($select);
77
+ }
78
+
79
+ /**
80
+ * Gets Order Items For Orders with retailops_ready status
81
+ *
82
+ * @return Mage_Sales_Order_Item_Collection
83
+ */
84
+ public function getRetailopsNonretrievedOrderItems()
85
+ {
86
+ $collection = Mage::getModel('sales/order_item')->getCollection();
87
+
88
+ $collection->getSelect()
89
+ ->join(
90
+ array('orders' => $this->getTable('sales/order')),
91
+ 'orders.entity_id = main_table.order_id',
92
+ array('orders.retailops_status')
93
+ )
94
+ ->where('orders.retailops_status IN (?)', array( RetailOps_Api_Helper_Data::RETAILOPS_ORDER_HOLD, RetailOps_Api_Helper_Data::RETAILOPS_ORDER_READY ));
95
+
96
+ return $collection;
97
+ }
98
+
99
+ /**
100
+ * Get product's media gallery records
101
+ *
102
+ * @param $productId
103
+ * @return array
104
+ */
105
+ public function getProductMedia($productId)
106
+ {
107
+ $select = $this->_getReadAdapter()->select()->from($this->getTable('catalog/product_attribute_media_gallery'))
108
+ ->where('entity_id = ?', $productId);
109
+
110
+ return $this->_getReadAdapter()->fetchAll($select);
111
+ }
112
+
113
+ /**
114
+ * Update gallery table with media keys
115
+ *
116
+ * @param $data
117
+ */
118
+ public function updateMediaKeys($data)
119
+ {
120
+ $adapter = $this->_getWriteAdapter();
121
+ $adapter->insertOnDuplicate($this->getTable('catalog/product_attribute_media_gallery'), $data);
122
+ }
123
+ }
app/code/community/RetailOps/Api/Model/Resource/Catalog/Media/Item.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ /**
27
+ * @category RetailOps
28
+ * @package RetailOps_Api
29
+ */
30
+ class RetailOps_Api_Model_Resource_Catalog_Media_Item extends Mage_Core_Model_Resource_Db_Abstract
31
+ {
32
+ /**
33
+ * Model initialization
34
+ */
35
+ protected function _construct()
36
+ {
37
+ $this->_init('retailops_api/media_import', 'entity_id');
38
+ }
39
+ }
app/code/community/RetailOps/Api/Model/Resource/Catalog/Media/Item/Collection.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ /**
27
+ * @category RetailOps
28
+ * @package RetailOps_Api
29
+ */
30
+ class RetailOps_Api_Model_Resource_Catalog_Media_Item_Collection
31
+ extends Mage_Core_Model_Resource_Db_Collection_Abstract
32
+ {
33
+ /**
34
+ * Model initialization
35
+ */
36
+ protected function _construct()
37
+ {
38
+ $this->_init('retailops_api/catalog_media_item');
39
+ }
40
+ }
app/code/community/RetailOps/Api/Model/Resource/Order/Status/History.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ /**
27
+ * Flat RetailOps_Api order status history resource
28
+ *
29
+ * @category RetailOps
30
+ * @package RetailOps_Api
31
+ */
32
+ class RetailOps_Api_Model_Resource_Order_Status_History extends Mage_Sales_Model_Resource_Order_Abstract
33
+ {
34
+ /**
35
+ * Event prefix
36
+ *
37
+ * @var string
38
+ */
39
+ protected $_eventPrefix = 'retailops_api_order_status_history_resource';
40
+
41
+ /**
42
+ * Model initialization
43
+ *
44
+ */
45
+ protected function _construct()
46
+ {
47
+ $this->_init('retailops_api/order_status_history', 'entity_id');
48
+ }
49
+ }
app/code/community/RetailOps/Api/Model/Resource/Order/Status/History/Collection.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ /**
27
+ * Flat retailops order status history collection
28
+ *
29
+ * @category RetailOps
30
+ * @package RetailOps_Api
31
+ */
32
+ class RetailOps_Api_Model_Resource_Order_Status_History_Collection
33
+ extends Mage_Sales_Model_Resource_Order_Collection_Abstract
34
+ {
35
+ /**
36
+ * Event prefix
37
+ *
38
+ * @var string
39
+ */
40
+ protected $_eventPrefix = 'retailops_order_status_history_collection';
41
+
42
+ /**
43
+ * Event object
44
+ *
45
+ * @var string
46
+ */
47
+ protected $_eventObject = 'order_retailops_status_history_collection';
48
+
49
+ /**
50
+ * Model initialization
51
+ *
52
+ */
53
+ protected function _construct()
54
+ {
55
+ $this->_init('retailops_api/order_status_history');
56
+ }
57
+ }
app/code/community/RetailOps/Api/Model/Return/Api.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Return_Api extends Mage_Sales_Model_Order_Creditmemo_Api
27
+ {
28
+ protected $_requestParams;
29
+
30
+ /**
31
+ * Initialize attributes mapping
32
+ */
33
+ public function __construct()
34
+ {
35
+ parent::__construct();
36
+ $this->_ignoredAttributeCodes['creditmemo'] = array('invoice');
37
+ }
38
+
39
+ /**
40
+ * Creates Credit Memo
41
+ *
42
+ * @param mixed $returns
43
+ * @return array
44
+ */
45
+ public function returnPush($returns)
46
+ {
47
+ $this->_requestParams = $returns;
48
+
49
+ if (isset($returns['records'])) {
50
+ $returns = $returns['records'];
51
+ }
52
+ $fullResult = array();
53
+ $fullResult['records'] = array();
54
+ foreach ($returns as $return) {
55
+ try {
56
+ $result = array();
57
+ $returnObj = new Varien_Object($return);
58
+ Mage::dispatchEvent(
59
+ 'retailops_return_push_record',
60
+ array('record' => $returnObj)
61
+ );
62
+ $order = Mage::getModel('sales/order')->loadByIncrementId($returnObj->getOrderIncrementId());
63
+ $result = $this->create($order, $returnObj->getCreditmemoData(),
64
+ $returnObj->getComment(), $returnObj->getNotifyCustomer(), $returnObj->getIncludeComment(),
65
+ $returnObj->getRefundToStoreCredit());
66
+ } catch (Exception $e) {
67
+ $result['message'] = $e->getMessage();
68
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
69
+ $result['stack_trace'] = $e->getTraceAsString();
70
+ $result['request_params'] = $this->_requestParams;
71
+ }
72
+ $fullResult['records'][] = $result;
73
+ }
74
+
75
+ return $fullResult;
76
+ }
77
+
78
+ /**
79
+ * Create new credit memo for order
80
+ *
81
+ * @param Mage_Sales_Model_Order $order
82
+ * @param array $creditmemoData array('qtys' => array('itemId1' => qty1, ... , 'itemIdN' => qtyN),
83
+ * 'shipping_amount' => value, 'adjustment_positive' => value, 'adjustment_negative' => value)
84
+ * @param string|null $comment
85
+ * @param bool $notifyCustomer
86
+ * @param bool $includeComment
87
+ * @param string $refundToStoreCreditAmount
88
+ * @return string $creditmemoIncrementId
89
+ */
90
+ public function create($order, $creditmemoData = null, $comment = null, $notifyCustomer = false,
91
+ $includeComment = false, $refundToStoreCreditAmount = null)
92
+ {
93
+ /** @var $helper RetailOps_Api_Helper_Data */
94
+ $helper = Mage::helper('retailops_api');
95
+ try {
96
+ $result = array();
97
+ if (!$order->getId()) {
98
+ $this->_fault('order_not_exists');
99
+ }
100
+ if (!$order->canCreditmemo()) {
101
+ $this->_fault('cannot_create_creditmemo');
102
+ }
103
+ $creditmemoData['order'] = $order;
104
+ $creditmemoData = $this->_prepareCreateData($creditmemoData);
105
+
106
+ /** @var $service Mage_Sales_Model_Service_Order */
107
+ $service = Mage::getModel('sales/service_order', $order);
108
+ /** @var $creditmemo Mage_Sales_Model_Order_Creditmemo */
109
+ $creditmemo = $service->prepareCreditmemo($creditmemoData, $order);
110
+ $invoice = $order->getInvoiceCollection()->getFirstItem();
111
+ if ($invoice) {
112
+ $creditmemo->setInvoice($invoice);
113
+ }
114
+ // refund to Store Credit
115
+ if ($refundToStoreCreditAmount) {
116
+ // check if refund to Store Credit is available
117
+ if ($order->getCustomerIsGuest()) {
118
+ $this->_fault('cannot_refund_to_storecredit');
119
+ }
120
+ $refundToStoreCreditAmount = max(
121
+ 0,
122
+ min($creditmemo->getBaseCustomerBalanceReturnMax(), $refundToStoreCreditAmount)
123
+ );
124
+ if ($refundToStoreCreditAmount) {
125
+ $refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice($refundToStoreCreditAmount);
126
+ $creditmemo->setBaseCustomerBalanceTotalRefunded($refundToStoreCreditAmount);
127
+ $refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice(
128
+ $refundToStoreCreditAmount*$order->getStoreToOrderRate()
129
+ );
130
+ // this field can be used by customer balance observer
131
+ $creditmemo->setBsCustomerBalTotalRefunded($refundToStoreCreditAmount);
132
+ // setting flag to make actual refund to customer balance after credit memo save
133
+ $creditmemo->setCustomerBalanceRefundFlag(true);
134
+ }
135
+ $creditmemo->setPaymentRefundDisallowed(true);
136
+ }
137
+ $creditmemo->register();
138
+ // add comment to creditmemo
139
+ if (!empty($comment)) {
140
+ $creditmemo->addComment($comment, $notifyCustomer);
141
+ }
142
+
143
+ Mage::getModel('core/resource_transaction')
144
+ ->addObject($creditmemo)
145
+ ->addObject($order)
146
+ ->save();
147
+ // send email notification
148
+ $creditmemo->sendEmail($notifyCustomer, ($includeComment ? $comment : ''));
149
+ $result['credit_memo'] = $helper->removeObjectsFromResult($this->_getAttributes($creditmemo, 'creditmemo'));
150
+ $result['items'] = array();
151
+ foreach ($creditmemo->getAllItems() as $item) {
152
+ $result['items'][] = $helper->removeObjectsFromResult($this->_getAttributes($item, 'creditmemo_item'));
153
+ }
154
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
155
+ } catch (Mage_Core_Exception $e) {
156
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
157
+ $result['message'] = $e->getMessage();
158
+ $result['code'] = $e->getCode();
159
+ $result['stack_trace'] = $e->getTraceAsString();
160
+ $result['request_params'] = $this->_requestParams;
161
+ }
162
+
163
+ return $result;
164
+ }
165
+
166
+ /**
167
+ * Set shipping amount to refund to 0 in case it's not passed
168
+ * Set qtys to refund to 0 by default if they are not passed
169
+ *
170
+ * @param array $data
171
+ * @return array
172
+ */
173
+ protected function _prepareCreateData($data)
174
+ {
175
+ $data = parent::_prepareCreateData($data);
176
+ $qtys = array();
177
+ if (isset($data['qtys'])) {
178
+ $qtys = $data['qtys'];
179
+ }
180
+ $order = $data['order'];
181
+ $qtysArray = array();
182
+ foreach ($order->getAllItems() as $orderItem) {
183
+ $qtysArray[$orderItem->getId()] = isset($qtys[$orderItem->getId()]) ? $qtys[$orderItem->getId()] : 0;
184
+ }
185
+ $data['qtys'] = $qtysArray;
186
+
187
+ if (empty($data['shipping_amount'])) {
188
+ $data['shipping_amount'] = 0;
189
+ }
190
+
191
+ return $data;
192
+ }
193
+ }
app/code/community/RetailOps/Api/Model/Shipment/Api.php ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Shipment_Api extends Mage_Sales_Model_Order_Shipment_Api
27
+ {
28
+ protected $_requestParams;
29
+
30
+ /**
31
+ * Create new shipment for order
32
+ *
33
+ * @param string $orderIncrementId
34
+ * @param array $itemsQty
35
+ * @param string $comment
36
+ * @param booleam $email
37
+ * @param boolean $includeComment
38
+ * @param int $retailopsShipmentId
39
+ * @return string
40
+ */
41
+ public function create($orderIncrementId, $itemsQty = array(), $comment = null, $email = false,
42
+ $includeComment = false, $retailopsShipmentId = null
43
+ ) {
44
+ $shipmentIncrementId = parent::create($orderIncrementId, $itemsQty, $comment, $email, $includeComment);
45
+
46
+ if ($retailopsShipmentId && $shipmentIncrementId) {
47
+ $shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($shipmentIncrementId);
48
+
49
+ if (!$shipment->getId()) {
50
+ $this->_fault('shipment_not_exists');
51
+ }
52
+
53
+ $shipment->setRetailopsShipmentId($retailopsShipmentId);
54
+ $shipment->save();
55
+ }
56
+
57
+ return $shipmentIncrementId;
58
+ }
59
+
60
+ /**
61
+ * Creates shipment, adds track numbers, creates new invoices
62
+ *
63
+ * @param $shipments array
64
+ * @return array
65
+ */
66
+ public function shipmentPush($shipments)
67
+ {
68
+ $this->_requestParams = $shipments;
69
+
70
+ $fullResult = array();
71
+ $fullResult['records'] = array();
72
+ if (isset($shipments['records'])) {
73
+ $shipments = $shipments['records'];
74
+ }
75
+ foreach ($shipments as $shipmentData) {
76
+ $result = array();
77
+ try{
78
+ $shipment = new Varien_Object($shipmentData);
79
+
80
+ Mage::dispatchEvent(
81
+ 'retailops_shipment_process_before',
82
+ array('record' => $shipment)
83
+ );
84
+
85
+ $result['order_increment_id'] = $shipment->getOrderIncrementId();
86
+
87
+ $orderIncrementId = $shipment->getOrderIncrementId();
88
+ $shipmentInfo = $shipment->getShipment();
89
+ $trackInfo = isset($shipmentInfo['track']) ? $shipmentInfo['track'] : array();
90
+ $invoiceInfo = isset($shipmentInfo['invoice']) ? $shipmentInfo['invoice'] : array();
91
+ $shipmentResult = array();
92
+ $shipmentIncrementId = null;
93
+
94
+ /** @var Mage_Sales_Model_Order $order */
95
+ $order = Mage::getModel('sales/order');
96
+ $order->loadByIncrementId($orderIncrementId);
97
+ if (!$order->getId()) {
98
+ throw new Exception('Order is not found');
99
+ }
100
+
101
+ // create shipment
102
+ try {
103
+ // Try to locate existing shipment
104
+ $orderShipments = $order->getShipmentsCollection();
105
+ if (count($orderShipments)) {
106
+ foreach ($orderShipments as $orderShipment) {
107
+ $retailopsShipmentId = $orderShipment->getRetailopsShipmentId();
108
+ if ($retailopsShipmentId && $shipmentInfo['retailops_shipment_id']
109
+ && $retailopsShipmentId == $shipmentInfo['retailops_shipment_id']) {
110
+ $shipmentIncrementId = $orderShipment->getIncrementId();
111
+
112
+ break;
113
+ }
114
+
115
+ $shipmentComments = $orderShipment->getCommentsCollection();
116
+ foreach ($shipmentComments as $shipmentComment) {
117
+ if ($shipmentComment->getComment() == $shipmentInfo['comment']) {
118
+ $shipmentIncrementId = $orderShipment->getIncrementId();
119
+
120
+ break;
121
+ }
122
+ }
123
+
124
+ if ($shipmentIncrementId) {
125
+ break;
126
+ }
127
+ }
128
+ }
129
+
130
+ // Create a new shipment if we didn't find an existing one
131
+ // Only adding shipment to result if it was created
132
+ if (!$shipmentIncrementId && $order->canShip()) {
133
+ $shipmentIncrementId = $this->create($orderIncrementId,
134
+ $shipmentInfo['qtys'],
135
+ $shipmentInfo['comment'],
136
+ $shipmentInfo['email'],
137
+ $shipmentInfo['include_comment'],
138
+ $shipmentInfo['retailops_shipment_id']
139
+ );
140
+
141
+ if ($shipmentIncrementId) {
142
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
143
+ $shipmentResult['shipment_increment_id'] = $shipmentIncrementId;
144
+ } else {
145
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
146
+ $shipmentResult['message'] = Mage::helper('retailops_api')->__('Can not create shipment');
147
+ }
148
+ }
149
+
150
+ // We should have found or created a shipment by now
151
+ if (!$shipmentIncrementId) {
152
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
153
+ $shipmentResult['message'] = Mage::helper('retailops_api')->__('Shipment not found');
154
+ }
155
+ } catch (Mage_Core_Exception $e) {
156
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
157
+ $shipmentResult['message'] = $e->getCustomMessage() ? $e->getCustomMessage() : $e->getMessage();
158
+ $shipmentResult['stack_trace'] = $e->getTraceAsString();
159
+ $shipmentResult['request_params'] = $this->_requestParams;
160
+ }
161
+ $result['shipment_result'] = $shipmentResult ? array($shipmentResult) : array();
162
+
163
+ if ($shipmentIncrementId) {
164
+ if ($trackInfo) {
165
+ $existingShipmentInfo = Mage::getModel('sales/order_shipment_api')->info($shipmentIncrementId);
166
+
167
+ $result['track_result'] = array();
168
+ foreach ($trackInfo as $track) {
169
+ // add shipment track
170
+ try {
171
+ $trackResult = array();
172
+ $track = new Varien_Object($track);
173
+
174
+ $trackId = null;
175
+ foreach ($existingShipmentInfo['tracks'] as $existingTrack) {
176
+ if ($existingTrack['track_number'] == $track->getData('track_number')) {
177
+ $trackId = $existingTrack['track_id'];
178
+
179
+ break;
180
+ }
181
+ }
182
+
183
+ if ($trackId) {
184
+ continue;
185
+ }
186
+
187
+ Mage::dispatchEvent(
188
+ 'retailops_shipment_add_track_before',
189
+ array('record' => $track)
190
+ );
191
+ $trackResult['track_number'] = $track->getData('track_number');
192
+
193
+ $trackId = $this->addTrack($shipmentIncrementId,
194
+ $track->getData('carrier'),
195
+ $track->getData('title'),
196
+ $track->getData('track_number')
197
+ );
198
+
199
+ $trackResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
200
+ $trackResult['track_id'] = $trackId;
201
+ } catch (Mage_Core_Exception $e) {
202
+ $trackResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
203
+ $trackResult['message'] = $e->getMessage();
204
+ $trackResult['stack_trace'] = $e->getTraceAsString();
205
+ $trackResult['request_params'] = $this->_requestParams;
206
+ }
207
+ $result['track_result'][] = $trackResult;
208
+ }
209
+ }
210
+
211
+ // create invoice
212
+ /** @var Mage_Sales_Model_Order $order */
213
+ // Note that this does need to be loaded again. The item data may be stale if a shipment
214
+ // was submitted above.
215
+ $order = Mage::getModel('sales/order');
216
+ $order->loadByIncrementId($orderIncrementId);
217
+ $isFullyShipped = $this->_checkAllItemsShipped($order);
218
+ $invoices = $order->getInvoiceCollection();
219
+ $invoiceResult = array();
220
+ if ($order->canInvoice()) {
221
+ $itemsToInvoice = array();
222
+ if ($order->getPayment()->canCapturePartial()) {
223
+ /**
224
+ * If payment allows partial capture, trying to create invoice with shipped items only and capture it
225
+ */
226
+ $itemsToInvoice = $shipmentInfo['qtys'];
227
+ }
228
+ if (($itemsToInvoice || $isFullyShipped) && $invoiceInfo) {
229
+
230
+ $invoice = new Varien_Object($invoiceInfo);
231
+ $invoice->setData('items_to_invoice', $itemsToInvoice);
232
+
233
+ Mage::dispatchEvent(
234
+ 'retailops_shipment_invoice_before',
235
+ array('record' => $invoice)
236
+ );
237
+
238
+ $invoiceResult = $this->_createInvoiceAndCapture(
239
+ $order,
240
+ $invoice->getItemsToInvoice(),
241
+ $invoice->getComment(),
242
+ $invoice->getEmail(),
243
+ $invoice->getIncludeComment()
244
+ );
245
+
246
+ $invoiceResult = array($invoiceResult);
247
+ }
248
+ } else {
249
+ if ($isFullyShipped) {
250
+ /**
251
+ * Capturing all available invoices if all order items are shipped
252
+ */
253
+ $invoiceResult = $this->_captureInvoices($invoices);
254
+ }
255
+ }
256
+ $result['invoice_result'] = $invoiceResult;
257
+ }
258
+ } catch (Exception $e) {
259
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
260
+ $result['message'] = $e->getMessage();
261
+ $result['stack_trace'] = $e->getTraceAsString();
262
+ $result['request_params'] = $this->_requestParams;
263
+ }
264
+ $fullResult['records'][] = $result;
265
+ }
266
+
267
+ return $fullResult;
268
+ }
269
+
270
+ /**
271
+ * @param array $ordersData
272
+ * @return array
273
+ */
274
+ public function orderClose($ordersData)
275
+ {
276
+ $this->_requestParams = $ordersData;
277
+
278
+ $fullResult = array();
279
+ $fullResult['records'] = array();
280
+ if (isset($ordersData['records'])) {
281
+ $ordersData = $ordersData['records'];
282
+ }
283
+ foreach ($ordersData as $orderData) {
284
+ try {
285
+ if (!empty($orderData['order_increment_id'])) {
286
+ $result = array(
287
+ 'order_increment_id' => $orderData['order_increment_id']
288
+ );
289
+ /** @var Mage_Sales_Model_Order $order */
290
+ $order = Mage::getModel('sales/order');
291
+ $order->loadByIncrementId($orderData['order_increment_id']);
292
+ if (!$order->getId()) {
293
+ throw new Exception('Order is not found');
294
+ }
295
+ Mage::dispatchEvent(
296
+ 'retailops_order_close_before',
297
+ array('order' => $order)
298
+ );
299
+ $items = $order->getAllItems();
300
+ $itemsToReturn = array();
301
+ $itemsToCancel = array();
302
+ $itemsToCapture = array();
303
+ /** @var $item Mage_Sales_Model_Order_Item */
304
+ foreach ($items as $item) {
305
+ $qtyToShip = $item->getQtyToShip();
306
+ $qtyToInvoice = $item->getQtyToInvoice();
307
+ if ($qtyToShip + $qtyToInvoice > 0) {
308
+ if ($qtyToShip < $qtyToInvoice) {
309
+ /**
310
+ * If we have shipped more items than invoiced, capture the difference
311
+ */
312
+ $itemsToCapture[$item->getId()] = $qtyToInvoice - $qtyToShip;
313
+ } elseif ($qtyToShip > $qtyToInvoice) {
314
+ /**
315
+ * If we have invoiced more items than shipped, return the difference
316
+ */
317
+ $itemsToReturn[$item->getId()] = $qtyToShip - $qtyToInvoice;
318
+ }
319
+ }
320
+ }
321
+ if ($itemsToCapture) {
322
+ $result['invoice_result'] = $this->_createInvoiceAndCapture($order, $itemsToCapture);
323
+ }
324
+ if ($itemsToReturn) {
325
+ $result['creditmemo_result'] = $this->_getCreditMemoApi()->create($order, array('qtys' => $itemsToReturn));
326
+ }
327
+ /**
328
+ * Cancel the rest items if any
329
+ */
330
+ $order->registerCancellation(Mage::helper('retailops_api')->__('No more items will be shipped'));
331
+ if (isset($orderData['retailops_status'])) {
332
+ $order->setRetailopsStatus($orderData['retailops_status']);
333
+ }
334
+ $order->save();
335
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
336
+ }
337
+ } catch (Exception $e) {
338
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
339
+ $result['message'] = $e->getMessage();
340
+ $result['stack_trace'] = $e->getTraceAsString();
341
+ $result['request_params'] = $this->_requestParams;
342
+ }
343
+ $fullResult['records'][] = $result;
344
+ }
345
+
346
+ return $fullResult;
347
+ }
348
+
349
+ /**
350
+ * @return RetailOps_Api_Model_Return_Api
351
+ */
352
+ protected function _getCreditMemoApi()
353
+ {
354
+ return Mage::getModel('retailops_api/return_api');
355
+ }
356
+
357
+ /**
358
+ * @param Mage_Sales_Model_Order $order
359
+ * @param array $itemsQty
360
+ * @param string $comment
361
+ * @param bool $email
362
+ * @param bool $includeComment
363
+ * @return string
364
+ */
365
+ protected function _createInvoiceAndCapture($order, $itemsQty, $comment = null, $email = false, $includeComment = false)
366
+ {
367
+ /** @var $helper RetailOps_Api_Helper_Data */
368
+ $helper = Mage::helper('retailops_api');
369
+ try {
370
+ $itemsQtyFomratted = array();
371
+ if ($itemsQty) {
372
+ foreach ($order->getAllItems() as $item) {
373
+ $itemsQtyFomratted[$item->getId()] = isset($itemsQty[$item->getId()]) ? $itemsQty[$item->getId()] : 0;
374
+ }
375
+ }
376
+ $invoice = $order->prepareInvoice($itemsQtyFomratted);
377
+ $invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
378
+ $invoice->register();
379
+
380
+ if ($comment !== null) {
381
+ $invoice->addComment($comment, $email);
382
+ }
383
+
384
+ if ($email) {
385
+ $invoice->setEmailSent(true);
386
+ }
387
+
388
+ $invoice->getOrder()->setIsInProcess(true);
389
+
390
+
391
+ $transactionSave = Mage::getModel('core/resource_transaction')
392
+ ->addObject($invoice)
393
+ ->addObject($invoice->getOrder())
394
+ ->save();
395
+
396
+ $invoice->sendEmail($email, ($includeComment ? $comment : ''));
397
+
398
+ $result['invoice'] = $helper->removeObjectsFromResult($this->_getAttributes($invoice, 'invoice'));
399
+ $result['items'] = array();
400
+ foreach ($invoice->getAllItems() as $item) {
401
+ $result['items'][] = $helper->removeObjectsFromResult($this->_getAttributes($item, 'invoice_item'));
402
+ }
403
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
404
+ } catch (Exception $e) {
405
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
406
+ $result['message'] = $e->getMessage();
407
+ $result['stack_trace'] = $e->getTraceAsString();
408
+ $result['request_params'] = $this->_requestParams;
409
+ }
410
+
411
+ return $result;
412
+ }
413
+
414
+ /**
415
+ * Check that there are no items left for shipping
416
+ *
417
+ * @param Mage_Sales_Model_Order $order $order
418
+ * @return bool
419
+ */
420
+ protected function _checkAllItemsShipped($order)
421
+ {
422
+ $items = $order->getAllItems();
423
+ /** @var $item Mage_Sales_Model_Order_Item */
424
+ foreach ($items as $item) {
425
+ if ($item->getQtyToShip()) {
426
+ return false;
427
+ }
428
+ }
429
+
430
+ return true;
431
+ }
432
+
433
+ /**
434
+ * @param Mage_Sales_Model_Resource_Order_Invoice_Collection $invoices
435
+ * @return array
436
+ */
437
+ protected function _captureInvoices($invoices)
438
+ {
439
+ $result = array();
440
+ /** @var $invoice Mage_Sales_Model_Order_Invoice */
441
+ foreach ($invoices as $invoice) {
442
+ try {
443
+ if ($invoice->getState() == Mage_Sales_Model_Order_Invoice::STATE_PAID) {
444
+ continue;
445
+ }
446
+ $invoiceResult = array();
447
+ $invoiceResult['invoice_increment_id'] = $invoice->getIncrementId();
448
+ if (!$invoice->canCapture()) {
449
+ throw new Exception('Invoice cannot be captured.');
450
+ }
451
+ $invoice->capture();
452
+ $invoice->getOrder()->setIsInProcess(true);
453
+ $transactionSave = Mage::getModel('core/resource_transaction')
454
+ ->addObject($invoice)
455
+ ->addObject($invoice->getOrder())
456
+ ->save();
457
+ $invoiceResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
458
+ } catch (Exception $e) {
459
+ $invoiceResult['message'] = $e->getMessage();
460
+ $invoiceResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
461
+ $invoiceResult['stack_trace'] = $e->getTraceAsString();
462
+ $invoiceResult['request_params'] = $this->_requestParams;
463
+ }
464
+ $result[] = $invoiceResult;
465
+ }
466
+
467
+ return $result;
468
+ }
469
+ }
app/code/community/RetailOps/Api/Model/Shipment/Api.php.~80d30a79e43e602a0b5b5da0b2b1a8007dc0ada9~ ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Shipment_Api extends Mage_Sales_Model_Order_Shipment_Api
27
+ {
28
+ /**
29
+ * Creates shipment, adds track numbers, creates new invoices
30
+ *
31
+ * @param $shipments array
32
+ * @return array
33
+ */
34
+ public function shipmentPush($shipments)
35
+ {
36
+ $fullResult = array();
37
+ $fullResult['records'] = array();
38
+ if (isset($shipments['records'])) {
39
+ $shipments = $shipments['records'];
40
+ }
41
+ foreach ($shipments as $shipmentData) {
42
+ $result = array();
43
+ try{
44
+ $shipment = new Varien_Object($shipmentData);
45
+
46
+ Mage::dispatchEvent(
47
+ 'retailops_shipment_process_before',
48
+ array('record' => $shipment)
49
+ );
50
+
51
+ $result['order_increment_id'] = $shipment->getOrderIncrementId();
52
+
53
+ $orderIncrementId = $shipment->getOrderIncrementId();
54
+ $shipmentInfo = $shipment->getShipment();
55
+ $trackInfo = isset($shipmentInfo['track']) ? $shipmentInfo['track'] : array();
56
+ $invoiceInfo = isset($shipmentInfo['invoice']) ? $shipmentInfo['invoice'] : array();
57
+ $shipmentIncrementId = null;
58
+ // create shipment
59
+ try {
60
+ $shipmentResult = array();
61
+ $shipmentIncrementId = $this->create($orderIncrementId,
62
+ $shipmentInfo['qtys'],
63
+ $shipmentInfo['comment'],
64
+ $shipmentInfo['email'],
65
+ $shipmentInfo['include_comment']
66
+ );
67
+
68
+ if ($shipmentIncrementId) {
69
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
70
+ $shipmentResult['shipment_increment_id'] = $shipmentIncrementId;
71
+ } else {
72
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
73
+ $shipmentResult['message'] = Mage::helper('retailops_api')->__('Can not create shipment');
74
+ }
75
+ } catch (Mage_Core_Exception $e) {
76
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
77
+ $shipmentResult['message'] = $e->getCustomMessage() ? $e->getCustomMessage() : $e->getMessage();
78
+ }
79
+
80
+ $result['shipment_result'] = $shipmentResult;
81
+ if ($shipmentIncrementId) {
82
+ if ($trackInfo) {
83
+ $result['track_result'] = array();
84
+ foreach ($trackInfo as $track) {
85
+ // add shipment track
86
+ try {
87
+ $trackResult = array();
88
+ $track = new Varien_Object($track);
89
+
90
+ Mage::dispatchEvent(
91
+ 'retailops_shipment_add_track_before',
92
+ array('record' => $track)
93
+ );
94
+ $trackResult['track_number'] = $track->getData('track_number');
95
+
96
+ $trackId = $this->addTrack($shipmentIncrementId,
97
+ $track->getData('carrier'),
98
+ $track->getData('title'),
99
+ $track->getData('track_number')
100
+ );
101
+
102
+ $trackResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
103
+ $trackResult['track_id'] = $trackId;
104
+ } catch (Mage_Core_Exception $e) {
105
+ $trackResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
106
+ $trackResult['message'] = $e->getMessage();
107
+ }
108
+ $result['track_result'][] = $trackResult;
109
+ }
110
+ }
111
+
112
+ // create invoice
113
+ /** @var Mage_Sales_Model_Order $order */
114
+ $order = Mage::getModel('sales/order');
115
+ $order->loadByIncrementId($orderIncrementId);
116
+ $isFullyShipped = $this->_checkAllItemsShipped($order);
117
+ $invoices = $order->getInvoiceCollection();
118
+ $invoiceResult = array();
119
+ if ($order->canInvoice()) {
120
+ $itemsToInvoice = array();
121
+ if ($order->getPayment()->canCapturePartial()) {
122
+ /**
123
+ * If payment allows partial capture, trying to create invoice with shipped items only and capture it
124
+ */
125
+ $itemsToInvoice = $shipmentInfo['qtys'];
126
+ }
127
+ if (($itemsToInvoice || $isFullyShipped) && $invoiceInfo) {
128
+
129
+ $invoice = new Varien_Object($invoiceInfo);
130
+ $invoice->setData('items_to_invoice', $itemsToInvoice);
131
+
132
+ Mage::dispatchEvent(
133
+ 'retailops_shipment_invoice_before',
134
+ array('record' => $invoice)
135
+ );
136
+
137
+ $invoiceResult = $this->_createInvoiceAndCapture(
138
+ $order,
139
+ $invoice->getItemsToInvoice(),
140
+ $invoice->getComment(),
141
+ $invoice->getEmail(),
142
+ $invoice->getIncludeComment()
143
+ );
144
+
145
+ $invoiceResult = array($invoiceResult);
146
+ }
147
+ } else {
148
+ if ($isFullyShipped) {
149
+ /**
150
+ * Capturing all available invoices if all order items are shipped
151
+ */
152
+ $invoiceResult = $this->_captureInvoices($invoices);
153
+ }
154
+ }
155
+ $result['invoice_result'] = $invoiceResult;
156
+ }
157
+ } catch (Exception $e) {
158
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
159
+ $result['message'] = $e->getMessage();
160
+ }
161
+ $fullResult['records'][] = $result;
162
+ }
163
+
164
+ return $fullResult;
165
+ }
166
+
167
+ /**
168
+ * @param array $ordersData
169
+ * @return array
170
+ */
171
+ public function orderClose($ordersData)
172
+ {
173
+ $fullResult = array();
174
+ $fullResult['records'] = array();
175
+ if (isset($ordersData['records'])) {
176
+ $ordersData = $ordersData['records'];
177
+ }
178
+ foreach ($ordersData as $orderData) {
179
+ try {
180
+ if (!empty($orderData['order_increment_id'])) {
181
+ $result = array(
182
+ 'order_increment_id' => $orderData['order_increment_id']
183
+ );
184
+ /** @var Mage_Sales_Model_Order $order */
185
+ $order = Mage::getModel('sales/order');
186
+ $order->loadByIncrementId($orderData['order_increment_id']);
187
+ if (!$order->getId()) {
188
+ throw new Exception('Order is not found');
189
+ }
190
+ Mage::dispatchEvent(
191
+ 'retailops_order_close_before',
192
+ array('order' => $order)
193
+ );
194
+ $items = $order->getAllItems();
195
+ $itemsToReturn = array();
196
+ $itemsToCancel = array();
197
+ $itemsToCapture = array();
198
+ /** @var $item Mage_Sales_Model_Order_Item */
199
+ foreach ($items as $item) {
200
+ $qtyToShip = $item->getQtyToShip();
201
+ $qtyToInvoice = $item->getQtyToInvoice();
202
+ if ($qtyToShip + $qtyToInvoice > 0) {
203
+ if ($qtyToShip < $qtyToInvoice) {
204
+ /**
205
+ * If we have shipped more items than invoiced, capture the difference
206
+ */
207
+ $itemsToCapture[$item->getId()] = $qtyToInvoice - $qtyToShip;
208
+ } elseif ($qtyToShip > $qtyToInvoice) {
209
+ /**
210
+ * If we have invoiced more items than shipped, return the difference
211
+ */
212
+ $itemsToReturn[$item->getId()] = $qtyToShip - $qtyToInvoice;
213
+ }
214
+ }
215
+ }
216
+ if ($itemsToCapture) {
217
+ $result['invoice_result'] = $this->_createInvoiceAndCapture($order, $itemsToCapture);
218
+ }
219
+ if ($itemsToReturn) {
220
+ $result['creditmemo_result'] = $this->_getCreditMemoApi()->create($order, array('qtys' => $itemsToReturn));
221
+ }
222
+ /**
223
+ * Cancel the rest items if any
224
+ */
225
+ $order->registerCancellation(Mage::helper('retailops_api')->__('No more items will be shipped'));
226
+ if (isset($orderData['retailops_status'])) {
227
+ $order->setRetailopsStatus($orderData['retailops_status']);
228
+ }
229
+ $order->save();
230
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
231
+ }
232
+ } catch (Exception $e) {
233
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
234
+ $result['message'] = $e->getMessage();
235
+ }
236
+ $fullResult['records'][] = $result;
237
+ }
238
+
239
+ return $fullResult;
240
+ }
241
+
242
+ /**
243
+ * @return RetailOps_Api_Model_Return_Api
244
+ */
245
+ protected function _getCreditMemoApi()
246
+ {
247
+ return Mage::getModel('retailops_api/return_api');
248
+ }
249
+
250
+ /**
251
+ * @param Mage_Sales_Model_Order $order
252
+ * @param array $itemsQty
253
+ * @param string $comment
254
+ * @param bool $email
255
+ * @param bool $includeComment
256
+ * @return string
257
+ */
258
+ protected function _createInvoiceAndCapture($order, $itemsQty, $comment = null, $email = false, $includeComment = false)
259
+ {
260
+ /** @var $helper RetailOps_Api_Helper_Data */
261
+ $helper = Mage::helper('retailops_api');
262
+ try {
263
+ $itemsQtyFomratted = array();
264
+ if ($itemsQty) {
265
+ foreach ($order->getAllItems() as $item) {
266
+ $itemsQtyFomratted[$item->getId()] = isset($itemsQty[$item->getId()]) ? $itemsQty[$item->getId()] : 0;
267
+ }
268
+ }
269
+ $invoice = $order->prepareInvoice($itemsQtyFomratted);
270
+ $invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
271
+ $invoice->register();
272
+
273
+ if ($comment !== null) {
274
+ $invoice->addComment($comment, $email);
275
+ }
276
+
277
+ if ($email) {
278
+ $invoice->setEmailSent(true);
279
+ }
280
+
281
+ $invoice->getOrder()->setIsInProcess(true);
282
+
283
+
284
+ $transactionSave = Mage::getModel('core/resource_transaction')
285
+ ->addObject($invoice)
286
+ ->addObject($invoice->getOrder())
287
+ ->save();
288
+
289
+ $invoice->sendEmail($email, ($includeComment ? $comment : ''));
290
+
291
+ $result['invoice'] = $helper->removeObjectsFromResult($this->_getAttributes($invoice, 'invoice'));
292
+ $result['items'] = array();
293
+ foreach ($invoice->getAllItems() as $item) {
294
+ $result['items'][] = $helper->removeObjectsFromResult($this->_getAttributes($item, 'invoice_item'));
295
+ }
296
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
297
+ } catch (Exception $e) {
298
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
299
+ $result['message'] = $e->getMessage();
300
+ }
301
+
302
+ return $result;
303
+ }
304
+
305
+ /**
306
+ * Check that there are no items left for shipping
307
+ *
308
+ * @param Mage_Sales_Model_Order $order $order
309
+ * @return bool
310
+ */
311
+ protected function _checkAllItemsShipped($order)
312
+ {
313
+ $items = $order->getAllItems();
314
+ /** @var $item Mage_Sales_Model_Order_Item */
315
+ foreach ($items as $item) {
316
+ if ($item->getQtyToShip()) {
317
+ return false;
318
+ }
319
+ }
320
+
321
+ return true;
322
+ }
323
+
324
+ /**
325
+ * @param Mage_Sales_Model_Resource_Order_Invoice_Collection $invoices
326
+ * @return array
327
+ */
328
+ protected function _captureInvoices($invoices)
329
+ {
330
+ $result = array();
331
+ /** @var $invoice Mage_Sales_Model_Order_Invoice */
332
+ foreach ($invoices as $invoice) {
333
+ try {
334
+ $invoiceResult = array();
335
+ $invoiceResult['invoice_increment_id'] = $invoice->getIncrementId();
336
+ if (!$invoice->canCapture()) {
337
+ throw new Exception('Invoice cannot be captured.');
338
+ }
339
+ $invoice->capture();
340
+ $invoice->getOrder()->setIsInProcess(true);
341
+ $transactionSave = Mage::getModel('core/resource_transaction')
342
+ ->addObject($invoice)
343
+ ->addObject($invoice->getOrder())
344
+ ->save();
345
+ $invoiceResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
346
+ } catch (Exception $e) {
347
+ $invoiceResult['message'] = $e->getMessage();
348
+ $invoiceResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
349
+ }
350
+ $result[] = $invoiceResult;
351
+ }
352
+
353
+ return $result;
354
+ }
355
+ }
app/code/community/RetailOps/Api/Model/Shipment/Api.php.~84b644444a1b813b9b5956dbc9eeabb8577db950~ ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_Shipment_Api extends Mage_Sales_Model_Order_Shipment_Api
27
+ {
28
+ /**
29
+ * Creates shipment, adds track numbers, creates new invoices
30
+ *
31
+ * @param $shipments array
32
+ * @return array
33
+ */
34
+ public function shipmentPush($shipments)
35
+ {
36
+ $fullResult = array();
37
+ $fullResult['records'] = array();
38
+ if (isset($shipments['records'])) {
39
+ $shipments = $shipments['records'];
40
+ }
41
+ foreach ($shipments as $shipmentData) {
42
+ $result = array();
43
+ try{
44
+ $shipment = new Varien_Object($shipmentData);
45
+
46
+ Mage::dispatchEvent(
47
+ 'retailops_shipment_process_before',
48
+ array('record' => $shipment)
49
+ );
50
+
51
+ $result['order_increment_id'] = $shipment->getOrderIncrementId();
52
+
53
+ $orderIncrementId = $shipment->getOrderIncrementId();
54
+ $shipmentInfo = $shipment->getShipment();
55
+ $trackInfo = isset($shipmentInfo['track']) ? $shipmentInfo['track'] : array();
56
+ $invoiceInfo = isset($shipmentInfo['invoice']) ? $shipmentInfo['invoice'] : array();
57
+ $shipmentIncrementId = null;
58
+ // create shipment
59
+ try {
60
+ $shipmentResult = array();
61
+ $shipmentIncrementId = $this->create($orderIncrementId,
62
+ $shipmentInfo['qtys'],
63
+ $shipmentInfo['comment'],
64
+ $shipmentInfo['email'],
65
+ $shipmentInfo['include_comment']
66
+ );
67
+
68
+ if ($shipmentIncrementId) {
69
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
70
+ $shipmentResult['shipment_increment_id'] = $shipmentIncrementId;
71
+ } else {
72
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
73
+ $shipmentResult['message'] = Mage::helper('retailops_api')->__('Can not create shipment');
74
+ }
75
+ } catch (Mage_Core_Exception $e) {
76
+ $shipmentResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
77
+ $shipmentResult['message'] = $e->getCustomMessage() ? $e->getCustomMessage() : $e->getMessage();
78
+ }
79
+
80
+ $result['shipment_result'] = $shipmentResult;
81
+ if ($shipmentIncrementId) {
82
+ if ($trackInfo) {
83
+ $result['track_result'] = array();
84
+ foreach ($trackInfo as $track) {
85
+ // add shipment track
86
+ try {
87
+ $trackResult = array();
88
+ $track = new Varien_Object($track);
89
+
90
+ Mage::dispatchEvent(
91
+ 'retailops_shipment_add_track_before',
92
+ array('record' => $track)
93
+ );
94
+ $trackResult['track_number'] = $track->getData('track_number');
95
+
96
+ $trackId = $this->addTrack($shipmentIncrementId,
97
+ $track->getData('carrier'),
98
+ $track->getData('title'),
99
+ $track->getData('track_number')
100
+ );
101
+
102
+ $trackResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
103
+ $trackResult['track_id'] = $trackId;
104
+ } catch (Mage_Core_Exception $e) {
105
+ $trackResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
106
+ $trackResult['message'] = $e->getMessage();
107
+ }
108
+ $result['track_result'][] = $trackResult;
109
+ }
110
+ }
111
+
112
+ // create invoice
113
+ /** @var Mage_Sales_Model_Order $order */
114
+ $order = Mage::getModel('sales/order');
115
+ $order->loadByIncrementId($orderIncrementId);
116
+ $isFullyShipped = $this->_checkAllItemsShipped($order);
117
+ $invoices = $order->getInvoiceCollection();
118
+ $invoiceResult = array();
119
+ if ($order->canInvoice()) {
120
+ $itemsToInvoice = array();
121
+ if ($order->getPayment()->canCapturePartial()) {
122
+ /**
123
+ * If payment allows partial capture, trying to create invoice with shipped items only and capture it
124
+ */
125
+ $itemsToInvoice = $shipmentInfo['qtys'];
126
+ }
127
+ if (($itemsToInvoice || $isFullyShipped) && $invoiceInfo) {
128
+
129
+ $invoice = new Varien_Object($invoiceInfo);
130
+ $invoice->setData('items_to_invoice', $itemsToInvoice);
131
+
132
+ Mage::dispatchEvent(
133
+ 'retailops_shipment_invoice_before',
134
+ array('record' => $invoice)
135
+ );
136
+
137
+ $invoiceResult = $this->_createInvoiceAndCapture(
138
+ $order,
139
+ $invoice->getItemsToInvoice(),
140
+ $invoice->getComment(),
141
+ $invoice->getEmail(),
142
+ $invoice->getIncludeComment()
143
+ );
144
+
145
+ $invoiceResult = array($invoiceResult);
146
+ }
147
+ } else {
148
+ if ($isFullyShipped) {
149
+ /**
150
+ * Capturing all available invoices if all order items are shipped
151
+ */
152
+ $invoiceResult = $this->_captureInvoices($invoices);
153
+ }
154
+ }
155
+ $result['invoice_result'] = $invoiceResult;
156
+ }
157
+ } catch (Exception $e) {
158
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
159
+ $result['message'] = $e->getMessage();
160
+ }
161
+ $fullResult['records'][] = $result;
162
+ }
163
+
164
+ return $fullResult;
165
+ }
166
+
167
+ /**
168
+ * @param array $ordersData
169
+ * @return array
170
+ */
171
+ public function orderClose($ordersData)
172
+ {
173
+ $fullResult = array();
174
+ $fullResult['records'] = array();
175
+ if (isset($ordersData['records'])) {
176
+ $ordersData = $ordersData['records'];
177
+ }
178
+ foreach ($ordersData as $orderData) {
179
+ try {
180
+ if (!empty($orderData['order_increment_id'])) {
181
+ $result = array(
182
+ 'order_increment_id' => $orderData['order_increment_id']
183
+ );
184
+ /** @var Mage_Sales_Model_Order $order */
185
+ $order = Mage::getModel('sales/order');
186
+ $order->loadByIncrementId($orderData['order_increment_id']);
187
+ if (!$order->getId()) {
188
+ throw new Exception('Order is not found');
189
+ }
190
+ Mage::dispatchEvent(
191
+ 'retailops_order_close_before',
192
+ array('order' => $order)
193
+ );
194
+ $items = $order->getAllItems();
195
+ $itemsToReturn = array();
196
+ $itemsToCancel = array();
197
+ $itemsToCapture = array();
198
+ /** @var $item Mage_Sales_Model_Order_Item */
199
+ foreach ($items as $item) {
200
+ $qtyToShip = $item->getQtyToShip();
201
+ $qtyToInvoice = $item->getQtyToInvoice();
202
+ if ($qtyToShip + $qtyToInvoice > 0) {
203
+ if ($qtyToShip < $qtyToInvoice) {
204
+ /**
205
+ * If we have shipped more items than invoiced, capture the difference
206
+ */
207
+ $itemsToCapture[$item->getId()] = $qtyToInvoice - $qtyToShip;
208
+ } elseif ($qtyToShip > $qtyToInvoice) {
209
+ /**
210
+ * If we have invoiced more items than shipped, return the difference
211
+ */
212
+ $itemsToReturn[$item->getId()] = $qtyToShip - $qtyToInvoice;
213
+ }
214
+ }
215
+ }
216
+ if ($itemsToCapture) {
217
+ $result['invoice_result'] = $this->_createInvoiceAndCapture($order, $itemsToCapture);
218
+ }
219
+ if ($itemsToReturn) {
220
+ $result['creditmemo_result'] = $this->_getCreditMemoApi()->create($order, array('qtys' => $itemsToReturn));
221
+ }
222
+ /**
223
+ * Cancel the rest items if any
224
+ */
225
+ $order->registerCancellation(Mage::helper('retailops_api')->__('No more items will be shipped'));
226
+ if (isset($orderData['retailops_status'])) {
227
+ $order->setRetailopsStatus($orderData['retailops_status']);
228
+ }
229
+ $order->save();
230
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
231
+ }
232
+ } catch (Exception $e) {
233
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
234
+ $result['message'] = $e->getMessage();
235
+ }
236
+ $fullResult['records'][] = $result;
237
+ }
238
+
239
+ return $fullResult;
240
+ }
241
+
242
+ /**
243
+ * @return RetailOps_Api_Model_Return_Api
244
+ */
245
+ protected function _getCreditMemoApi()
246
+ {
247
+ return Mage::getModel('retailops_api/return_api');
248
+ }
249
+
250
+ /**
251
+ * @param Mage_Sales_Model_Order $order
252
+ * @param array $itemsQty
253
+ * @param string $comment
254
+ * @param bool $email
255
+ * @param bool $includeComment
256
+ * @return string
257
+ */
258
+ protected function _createInvoiceAndCapture($order, $itemsQty, $comment = null, $email = false, $includeComment = false)
259
+ {
260
+ /** @var $helper RetailOps_Api_Helper_Data */
261
+ $helper = Mage::helper('retailops_api');
262
+ try {
263
+ $itemsQtyFomratted = array();
264
+ if ($itemsQty) {
265
+ foreach ($order->getAllItems() as $item) {
266
+ $itemsQtyFomratted[$item->getId()] = isset($itemsQty[$item->getId()]) ? $itemsQty[$item->getId()] : 0;
267
+ }
268
+ }
269
+ $invoice = $order->prepareInvoice($itemsQtyFomratted);
270
+ $invoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
271
+ $invoice->register();
272
+
273
+ if ($comment !== null) {
274
+ $invoice->addComment($comment, $email);
275
+ }
276
+
277
+ if ($email) {
278
+ $invoice->setEmailSent(true);
279
+ }
280
+
281
+ $invoice->getOrder()->setIsInProcess(true);
282
+
283
+
284
+ $transactionSave = Mage::getModel('core/resource_transaction')
285
+ ->addObject($invoice)
286
+ ->addObject($invoice->getOrder())
287
+ ->save();
288
+
289
+ $invoice->sendEmail($email, ($includeComment ? $comment : ''));
290
+
291
+ $result['invoice'] = $helper->removeObjectsFromResult($this->_getAttributes($invoice, 'invoice'));
292
+ $result['items'] = array();
293
+ foreach ($invoice->getAllItems() as $item) {
294
+ $result['items'][] = $helper->removeObjectsFromResult($this->_getAttributes($item, 'invoice_item'));
295
+ }
296
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
297
+ } catch (Exception $e) {
298
+ $result['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
299
+ $result['message'] = $e->getMessage();
300
+ }
301
+
302
+ return $result;
303
+ }
304
+
305
+ /**
306
+ * Check that there are no items left for shipping
307
+ *
308
+ * @param Mage_Sales_Model_Order $order $order
309
+ * @return bool
310
+ */
311
+ protected function _checkAllItemsShipped($order)
312
+ {
313
+ $items = $order->getAllItems();
314
+ /** @var $item Mage_Sales_Model_Order_Item */
315
+ foreach ($items as $item) {
316
+ if ($item->getQtyToShip()) {
317
+ return false;
318
+ }
319
+ }
320
+
321
+ return true;
322
+ }
323
+
324
+ /**
325
+ * @param Mage_Sales_Model_Resource_Order_Invoice_Collection $invoices
326
+ * @return array
327
+ */
328
+ protected function _captureInvoices($invoices)
329
+ {
330
+ $result = array();
331
+ /** @var $invoice Mage_Sales_Model_Order_Invoice */
332
+ foreach ($invoices as $invoice) {
333
+ try {
334
+ $invoiceResult = array();
335
+ $invoiceResult['invoice_increment_id'] = $invoice->getIncrementId();
336
+ if ($invoice->canCapture()) {
337
+ throw new Exception('Invoice cannot be captured.');
338
+ }
339
+ $invoice->capture();
340
+ $invoice->getOrder()->setIsInProcess(true);
341
+ $transactionSave = Mage::getModel('core/resource_transaction')
342
+ ->addObject($invoice)
343
+ ->addObject($invoice->getOrder())
344
+ ->save();
345
+ $invoiceResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_SUCCESS;
346
+ } catch (Exception $e) {
347
+ $invoiceResult['message'] = $e->getMessage();
348
+ $invoiceResult['status'] = RetailOps_Api_Helper_Data::API_STATUS_FAIL;
349
+ }
350
+ $result[] = $invoiceResult;
351
+ }
352
+
353
+ return $result;
354
+ }
355
+ }
app/code/community/RetailOps/Api/Model/System/Config/Source/Catalog/AttributeSet.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Model_System_Config_Source_Catalog_AttributeSet
27
+ {
28
+ protected $_options;
29
+
30
+ public function toOptionArray()
31
+ {
32
+ if (!$this->_options) {
33
+ $entityType = Mage::getModel('eav/entity')->setType(Mage_Catalog_Model_Product::ENTITY)->getTypeId();
34
+ $collection = Mage::getResourceModel('eav/entity_attribute_set_collection')
35
+ ->setEntityTypeFilter($entityType);
36
+ $this->_options = $collection->load()->toOptionArray();
37
+ }
38
+
39
+ return $this->_options;
40
+ }
41
+ }
app/code/community/RetailOps/Api/controllers/Adminhtml/Sales/OrderController.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ class RetailOps_Api_Adminhtml_Sales_OrderController extends Mage_Adminhtml_Sales_OrderController
27
+ {
28
+
29
+ /**
30
+ * Add order comment action
31
+ */
32
+ public function saveRetailOpsInfoAction()
33
+ {
34
+ if ($order = $this->_initOrder()) {
35
+ try {
36
+ $response = false;
37
+ $data = $this->getRequest()->getPost('retops');
38
+
39
+ $order->setRetailopsStatus($data['status']);
40
+ $order->save();
41
+
42
+ $history = Mage::getModel('retailops_api/order_status_history')
43
+ ->setOrder($order)
44
+ ->setComment($data['comment'])
45
+ ->save();
46
+
47
+ $this->loadLayout('empty');
48
+ $this->renderLayout();
49
+ }
50
+ catch (Mage_Core_Exception $e) {
51
+ $response = array(
52
+ 'error' => true,
53
+ 'message' => $e->getMessage(),
54
+ );
55
+ }
56
+ catch (Exception $e) {
57
+ $response = array(
58
+ 'error' => true,
59
+ 'message' => $this->__('Cannot add order history.')
60
+ );
61
+ }
62
+ if (is_array($response)) {
63
+ $response = Mage::helper('core')->jsonEncode($response);
64
+ $this->getResponse()->setBody($response);
65
+ }
66
+ }
67
+ }
68
+ }
app/code/community/RetailOps/Api/etc/adminhtml.xml ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <config>
26
+ <acl>
27
+ <resources>
28
+ <admin>
29
+ <children>
30
+ <system>
31
+ <children>
32
+ <config>
33
+ <children>
34
+ <retailops_settings translate="title" module="retailops_api">
35
+ <title>RetailOps Api Settings</title>
36
+ </retailops_settings>
37
+ </children>
38
+ </config>
39
+ </children>
40
+ </system>
41
+ </children>
42
+ </admin>
43
+ </resources>
44
+ </acl>
45
+ </config>
app/code/community/RetailOps/Api/etc/api.xml ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <config>
26
+ <api>
27
+ <resources>
28
+ <retailops translate="title" module="retailops_api">
29
+ <title>RetailOps API</title>
30
+ <model>retailops_api/api</model>
31
+ <acl>retailops</acl><!-- acl resource alias -->
32
+ <methods><!-- definne the methods -->
33
+ <catalog_pull translate="title" module="retailops_api">
34
+ <title>Retrieve list of products</title>
35
+ <method>catalogPull</method>
36
+ <acl>retailops/catalog_pull</acl>
37
+ </catalog_pull>
38
+ <catalog_push translate="title" module="retailops_api">
39
+ <title>Create/update products</title>
40
+ <method>catalogPush</method>
41
+ <acl>retailops/catalog_push</acl>
42
+ </catalog_push>
43
+ <return_push translate="title" module="retailops_api">
44
+ <title>Create Credit Memo</title>
45
+ <method>returnPush</method>
46
+ <acl>retailops/return_push</acl>
47
+ </return_push>
48
+ <order_pull translate="title" module="retailops_api">
49
+ <title>Retrieve list of orders</title>
50
+ <method>orderPull</method>
51
+ <acl>retailops/order_pull</acl>
52
+ </order_pull>
53
+ <inventory_push translate="title" module="retailops_api">
54
+ <title>Update product inventory</title>
55
+ <method>inventoryPush</method>
56
+ <acl>retailops/inventory_push</acl>
57
+ </inventory_push>
58
+ <shipment_push translate="title" module="retailops_api">
59
+ <title>Create Shipments</title>
60
+ <method>shipmentPush</method>
61
+ <acl>retailops/shipment_push</acl>
62
+ </shipment_push>
63
+ <order_close translate="title" module="retailops_api">
64
+ <title>Order Close</title>
65
+ <method>orderClose</method>
66
+ <acl>retailops/shipment_push</acl>
67
+ </order_close>
68
+ <order_status_update translate="title" module="retailops_api">
69
+ <title>Order Status Update</title>
70
+ <method>orderStatusUpdate</method>
71
+ <acl>retailops/order_pull</acl>
72
+ </order_status_update>
73
+ </methods>
74
+ <faults module="retailops_api"><!-- errors that might appear-->
75
+ </faults>
76
+ </retailops>
77
+ </resources>
78
+ <acl><!-- acl definition -->
79
+ <resources>
80
+ <retailops translate="title" module="retailops_api">
81
+ <title>Retailops</title>
82
+ <sort_order>120</sort_order>
83
+ <catalog_pull translate="title" module="retailops_api">
84
+ <title>Catalog Pull</title>
85
+ </catalog_pull>
86
+ <catalog_push translate="title" module="retailops_api">
87
+ <title>Catalog Push</title>
88
+ </catalog_push>
89
+ <inventory_push translate="title" module="retailops_api">
90
+ <title>Inventory Push</title>
91
+ </inventory_push>
92
+ <order_pull translate="title" module="retailops_api">
93
+ <title>Order Pull</title>
94
+ </order_pull>
95
+ <order_update translate="title" module="retailops_api">
96
+ <title>Order Update</title>
97
+ </order_update>
98
+ <return_push translate="title" module="retailops_api">
99
+ <title>Return Push</title>
100
+ </return_push>
101
+ <shipment_push translate="title" module="retailops_api">
102
+ <title>Shipment Push</title>
103
+ </shipment_push>
104
+ </retailops>
105
+ </resources>
106
+ </acl>
107
+ </api>
108
+ </config>
app/code/community/RetailOps/Api/etc/config.xml ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <config>
26
+ <modules>
27
+ <RetailOps_Api>
28
+ <version>1.0.3</version>
29
+ </RetailOps_Api>
30
+ </modules>
31
+ <global>
32
+ <helpers>
33
+ <retailops_api>
34
+ <class>RetailOps_Api_Helper</class>
35
+ </retailops_api>
36
+ </helpers>
37
+ <models>
38
+ <retailops_api>
39
+ <class>RetailOps_Api_Model</class>
40
+ <resourceModel>retailops_api_resource</resourceModel>
41
+ </retailops_api>
42
+ <retailops_api_resource>
43
+ <class>RetailOps_Api_Model_Resource</class>
44
+ <entities>
45
+ <order_status_history>
46
+ <table>retailops_order_status_history</table>
47
+ </order_status_history>
48
+ <media_import>
49
+ <table>retailops_media_import</table>
50
+ </media_import>
51
+ </entities>
52
+ </retailops_api_resource>
53
+ </models>
54
+ <blocks>
55
+ <retailops_api>
56
+ <class>RetailOps_Api_Block</class>
57
+ </retailops_api>
58
+ </blocks>
59
+ <resources>
60
+ <retailops_api_setup>
61
+ <setup>
62
+ <module>RetailOps_Api</module>
63
+ <class>Mage_Core_Model_Resource_Setup</class>
64
+ </setup>
65
+ </retailops_api_setup>
66
+ </resources>
67
+ <events>
68
+ <sales_order_payment_place_end>
69
+ <observers>
70
+ <update_retailops_status>
71
+ <class>retailops_api/observer</class>
72
+ <method>updateRetailopsStatus</method>
73
+ </update_retailops_status>
74
+ </observers>
75
+ </sales_order_payment_place_end>
76
+ </events>
77
+ </global>
78
+ <admin>
79
+ <routers>
80
+ <adminhtml>
81
+ <args>
82
+ <modules>
83
+ <RetailOps_Api after="Mage_Adminhtml">RetailOps_Api_Adminhtml</RetailOps_Api>
84
+ </modules>
85
+ </args>
86
+ </adminhtml>
87
+ </routers>
88
+ </admin>
89
+ <adminhtml>
90
+ <layout>
91
+ <updates>
92
+ <retailops_api>
93
+ <file>retailops.xml</file>
94
+ </retailops_api>
95
+ </updates>
96
+ </layout>
97
+ </adminhtml>
98
+ <frontend>
99
+ </frontend>
100
+ <crontab>
101
+ <jobs>
102
+ <retailops_media_download>
103
+ <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
104
+ <run>
105
+ <model>retailops_api/observer::downloadProductImages</model>
106
+ </run>
107
+ </retailops_media_download>
108
+ </jobs>
109
+ </crontab>
110
+ <default>
111
+ <retailops_settings>
112
+ <catalog>
113
+ <media_processing_products_limit>10</media_processing_products_limit>
114
+ </catalog>
115
+ </retailops_settings>
116
+ </default>
117
+ </config>
app/code/community/RetailOps/Api/etc/config.xml~ ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <config>
26
+ <modules>
27
+ <RetailOps_Api>
28
+ <version>1.0.3</version>
29
+ </RetailOps_Api>
30
+ </modules>
31
+ <global>
32
+ <helpers>
33
+ <retailops_api>
34
+ <class>RetailOps_Api_Helper</class>
35
+ </retailops_api>
36
+ </helpers>
37
+ <models>
38
+ <retailops_api>
39
+ <class>RetailOps_Api_Model</class>
40
+ <resourceModel>retailops_api_resource</resourceModel>
41
+ </retailops_api>
42
+ <retailops_api_resource>
43
+ <class>RetailOps_Api_Model_Resource</class>
44
+ <entities>
45
+ <order_status_history>
46
+ <table>retailops_order_status_history</table>
47
+ </order_status_history>
48
+ <media_import>
49
+ <table>retailops_media_import</table>
50
+ </media_import>
51
+ </entities>
52
+ </retailops_api_resource>
53
+ </models>
54
+ <blocks>
55
+ <retailops_api>
56
+ <class>RetailOps_Api_Block</class>
57
+ </retailops_api>
58
+ </blocks>
59
+ <resources>
60
+ <retailops_api_setup>
61
+ <setup>
62
+ <module>RetailOps_Api</module>
63
+ <class>Mage_Core_Model_Resource_Setup</class>
64
+ </setup>
65
+ </retailops_api_setup>
66
+ </resources>
67
+ <events>
68
+ <sales_order_payment_place_end>
69
+ <observers>
70
+ <update_retailops_status>
71
+ <class>retailops_api/observer</class>
72
+ <method>updateRetailopsStatus</method>
73
+ </update_retailops_status>
74
+ </observers>
75
+ </sales_order_payment_place_end>
76
+ </events>
77
+ </global>
78
+ <admin>
79
+ <routers>
80
+ <adminhtml>
81
+ <args>
82
+ <modules>
83
+ <RetailOps_Api after="Mage_Adminhtml">RetailOps_Api_Adminhtml</RetailOps_Api>
84
+ </modules>
85
+ </args>
86
+ </adminhtml>
87
+ </routers>
88
+ </admin>
89
+ <adminhtml>
90
+ <layout>
91
+ <updates>
92
+ <retailops_api>
93
+ <file>retailops.xml</file>
94
+ </retailops_api>
95
+ </updates>
96
+ </layout>
97
+ </adminhtml>
98
+ <frontend>
99
+ </frontend>
100
+ <crontab>
101
+ <jobs>
102
+ <retailops_media_download>
103
+ <schedule><cron_expr>*/5 * * * *</cron_expr></schedule>
104
+ <run>
105
+ <model>retailops_api/observer::downloadProductImages</model>
106
+ </run>
107
+ </retailops_media_download>
108
+ </jobs>
109
+ </crontab>
110
+ <default>
111
+ <retailops_settings>
112
+ <catalog>
113
+ <media_processing_products_limit>10</media_processing_products_limit>
114
+ </catalog>
115
+ </retailops_settings>
116
+ </default>
117
+ </config>
app/code/community/RetailOps/Api/etc/system.xml ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <config>
26
+ <tabs>
27
+ <retailops translate="label" module="retailops_api">
28
+ <label>RetailOps</label>
29
+ <sort_order>250</sort_order>
30
+ </retailops>
31
+ </tabs>
32
+ <sections>
33
+ <retailops_settings translate="label" module="retailops_api">
34
+ <class>separator-top</class>
35
+ <label>Settings</label>
36
+ <tab>retailops</tab>
37
+ <frontend_type>text</frontend_type>
38
+ <sort_order>40</sort_order>
39
+ <show_in_default>1</show_in_default>
40
+ <show_in_website>1</show_in_website>
41
+ <show_in_store>1</show_in_store>
42
+ <groups>
43
+ <catalog translate="label">
44
+ <label>Catalog</label>
45
+ <frontend_type>text</frontend_type>
46
+ <sort_order>100</sort_order>
47
+ <show_in_default>1</show_in_default>
48
+ <show_in_website>0</show_in_website>
49
+ <show_in_store>0</show_in_store>
50
+ <fields>
51
+ <default_attribute_set translate="label">
52
+ <label>Default Attribute Set To Extend</label>
53
+ <frontend_type>select</frontend_type>
54
+ <source_model>retailops_api/system_config_source_catalog_attributeSet</source_model>
55
+ <sort_order>1</sort_order>
56
+ <show_in_default>1</show_in_default>
57
+ <show_in_website>0</show_in_website>
58
+ <show_in_store>0</show_in_store>
59
+ </default_attribute_set>
60
+ <media_processing_products_limit>
61
+ <label>Limit to process media data for products by a cron job</label>
62
+ <frontend_type>text</frontend_type>
63
+ <sort_order>1</sort_order>
64
+ <show_in_default>1</show_in_default>
65
+ <show_in_website>0</show_in_website>
66
+ <show_in_store>0</show_in_store>
67
+ </media_processing_products_limit>
68
+ </fields>
69
+ </catalog>
70
+ </groups>
71
+ </retailops_settings>
72
+ </sections>
73
+ </config>
74
+
app/code/community/RetailOps/Api/sql/retailops_api_setup/install-1.0.0.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ $installer = $this;
27
+
28
+ $installer->startSetup();
29
+
30
+ $installer->getConnection()
31
+ ->addColumn($installer->getTable('sales/order'),
32
+ 'retailops_status',
33
+ array(
34
+ 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
35
+ 'length' => 255,
36
+ 'comment' => 'RetailOps Order Status'
37
+ ));
38
+ $installer->getConnection()
39
+ ->addIndex($installer->getTable('sales/order'),
40
+ $installer->getIdxName('sales/order', array('retailops_status')),
41
+ array('retailops_status')
42
+ );
43
+
44
+ /**
45
+ * Create table 'retail_ops/order_status_history'
46
+ */
47
+ $table = $installer->getConnection()
48
+ ->newTable($installer->getTable('retailops_api/order_status_history'))
49
+ ->addColumn('entity_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
50
+ 'identity' => true,
51
+ 'unsigned' => true,
52
+ 'nullable' => false,
53
+ 'primary' => true,
54
+ ), 'Entity Id')
55
+ ->addColumn('parent_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
56
+ 'unsigned' => true,
57
+ 'nullable' => false,
58
+ ), 'Parent Id')
59
+ ->addColumn('comment', Varien_Db_Ddl_Table::TYPE_TEXT, '64k', array(
60
+ ), 'Comment')
61
+ ->addColumn('status', Varien_Db_Ddl_Table::TYPE_TEXT, 32, array(
62
+ ), 'Status')
63
+ ->addColumn('created_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array(
64
+ ), 'Created At')
65
+ ->addIndex($installer->getIdxName('retailops_api/order_status_history', array('parent_id')),
66
+ array('parent_id'))
67
+ ->addIndex($installer->getIdxName('retailops_api/order_status_history', array('created_at')),
68
+ array('created_at'))
69
+ ->addIndex($installer->getIdxName('retailops_api/order_status_history', array('status')),
70
+ array('status'))
71
+ ->addForeignKey($installer->getFkName('retailops_api/order_status_history', 'parent_id', 'sales/order', 'entity_id'),
72
+ 'parent_id', $installer->getTable('sales/order'), 'entity_id',
73
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
74
+ ->setComment('Sales Flat Order Status History');
75
+
76
+ $installer->getConnection()->createTable($table);
77
+
78
+ $installer->endSetup();
app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.0-1.0.1.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ $installer = $this;
27
+
28
+ $installer->startSetup();
29
+
30
+ $installer->getConnection()
31
+ ->addColumn($installer->getTable('catalog/product_attribute_media_gallery'),
32
+ 'retailops_mediakey',
33
+ array(
34
+ 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
35
+ 'length' => 255,
36
+ 'comment' => 'RetailOps Mediakey'
37
+ ));
38
+ $installer->getConnection()
39
+ ->addIndex($installer->getTable('catalog/product_attribute_media_gallery'),
40
+ $installer->getIdxName('catalog/product_attribute_media_gallery', array('retailops_mediakey')),
41
+ array('retailops_mediakey')
42
+ );
43
+
44
+ $installer->endSetup();
app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.1-1.0.2.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ $installer = $this;
27
+
28
+ $installer->startSetup();
29
+
30
+ $table = $installer->getConnection()
31
+ ->newTable($installer->getTable('retailops_api/media_import'))
32
+ ->addColumn('entity_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
33
+ 'identity' => true,
34
+ 'unsigned' => true,
35
+ 'nullable' => false,
36
+ 'primary' => true,
37
+ ), 'Entity Id')
38
+ ->addColumn('product_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
39
+ 'unsigned' => true,
40
+ 'nullable' => false,
41
+ ), 'Product Id')
42
+ ->addColumn('unset_other_media', Varien_Db_Ddl_Table::TYPE_TINYINT, null, array(
43
+ ), 'Unset other media flag')
44
+ ->addColumn('media_data', Varien_Db_Ddl_Table::TYPE_TEXT, '64k', array(
45
+ ), 'Full Media Data')
46
+ ->addForeignKey($installer->getFkName('retailops_api/media_import', 'product_id', 'catalog/product', 'entity_id'),
47
+ 'product_id', $installer->getTable('catalog/product'), 'entity_id',
48
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
49
+ ->setComment('RetailOps Media Update Table');
50
+
51
+ $installer->getConnection()->createTable($table);
52
+
53
+ $installer->endSetup();
app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.2-1.0.3.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ $installer = $this;
27
+
28
+ $installer->startSetup();
29
+
30
+ $installer->getConnection()
31
+ ->addColumn($installer->getTable('sales/shipment'),
32
+ 'retailops_shipment_id',
33
+ array(
34
+ 'type' => Varien_Db_Ddl_Table::TYPE_INTEGER,
35
+ 'length' => 10,
36
+ 'unsigned' => true,
37
+ 'comment' => 'RetailOps Shipment ID',
38
+ 'nullable' => true,
39
+ ));
40
+
41
+ $installer->endSetup();
42
+
app/code/community/RetailOps/Api/sql/retailops_api_setup/upgrade-1.0.2-1.0.3.php~ ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ */
25
+
26
+ $installer = $this;
27
+
28
+ $installer->startSetup();
29
+
30
+ $table = $installer->getConnection()
31
+ ->newTable($installer->getTable('retailops_api/media_import'))
32
+ ->addColumn('entity_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
33
+ 'identity' => true,
34
+ 'unsigned' => true,
35
+ 'nullable' => false,
36
+ 'primary' => true,
37
+ ), 'Entity Id')
38
+ ->addColumn('product_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
39
+ 'unsigned' => true,
40
+ 'nullable' => false,
41
+ ), 'Product Id')
42
+ ->addColumn('unset_other_media', Varien_Db_Ddl_Table::TYPE_TINYINT, null, array(
43
+ ), 'Unset other media flag')
44
+ ->addColumn('media_data', Varien_Db_Ddl_Table::TYPE_TEXT, '64k', array(
45
+ ), 'Full Media Data')
46
+ ->addForeignKey($installer->getFkName('retailops_api/media_import', 'product_id', 'catalog/product', 'entity_id'),
47
+ 'product_id', $installer->getTable('catalog/product'), 'entity_id',
48
+ Varien_Db_Ddl_Table::ACTION_CASCADE, Varien_Db_Ddl_Table::ACTION_CASCADE)
49
+ ->setComment('RetailOps Media Update Table');
50
+
51
+ $installer->getConnection()->createTable($table);
52
+
53
+ $installer->endSetup();
app/code/community/RetailOps/Model/Observer.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class RetailOps_Model_Observer
4
+ {
5
+ /*
6
+ * Transfer MyGiftCardPlugin amount to 'gift_cards_amount' for RetailOps Orders
7
+ */
8
+ public function transferMyGiftCardValue(Varien_Event_Observer $observer) {
9
+ file_put_contents('/tmp/magento_gift_card_test', "transferMyGiftCardValue observer " . var_export($observer, TRUE) . "\n", FILE_APPEND);
10
+ $record = $observer->getEvent()->getRecord();
11
+ $orderInfo = $record->getOrderInfo();
12
+ // $orderInfo->setGiftCardsAmount($orderInfo->getMyGiftCardAmount());
13
+ $orderInfo->setGiftCardsAmount('100');
14
+ $record->setOrderInfo($orderInfo);
15
+ }
16
+ }
17
+
18
+ ?>
app/code/community/RetailOps/Model/Observer.php~ ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Acme_Model_Observer
4
+ {
5
+ /*
6
+ * Transfer MyGiftCardPlugin amount to 'gift_cards_amount' for RetailOps Orders
7
+ */
8
+ public function transferMyGiftCardValue(Varien_Event_Observer $observer) {
9
+ $record = $observer->getEvent()->getRecord();
10
+ $orderInfo = $record->getOrderInfo();
11
+ // $orderInfo->setGiftCardsAmount($orderInfo->getMyGiftCardAmount());
12
+ $orderInfo->setGiftCardsAmount('100');
13
+ $record->setOrderInfo($orderInfo);
14
+ }
15
+ }
16
+
17
+ ?>
app/design/adminhtml/default/default/layout/retailops.xml ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <layout>
26
+ <adminhtml_sales_order_view>
27
+ <reference name="sales_order_tabs">
28
+ <action method="addTab"><name>retailops_info</name><block>retailops_api/adminhtml_sales_order_view_tab_retailops</block></action>
29
+ </reference>
30
+ </adminhtml_sales_order_view>
31
+
32
+ <adminhtml_sales_order_saveretailopsinfo>
33
+ <block type="retailops_api/adminhtml_sales_order_view_tab_retailops"
34
+ name="retailops_info"
35
+ template="retailops/order/view/tab/retailops.phtml"
36
+ output="toHtml"
37
+ />
38
+ </adminhtml_sales_order_saveretailopsinfo>
39
+ </layout>
app/design/adminhtml/default/default/template/retailops/order/view/tab/retailops.phtml ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!--
2
+ The MIT License (MIT)
3
+
4
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
23
+ -->
24
+ <div class="entry-edit" id="retops-info">
25
+ <div class="entry-edit-head">
26
+ <h4><?php echo $this->__('RetailOps'); ?></h4>
27
+ </div>
28
+ <div class="fieldset fieldset-wide">
29
+ <div id="status_form" class="order-history-form">
30
+ <span class="field-row">
31
+ <label class="normal" for="history_status"><?php echo Mage::helper('sales')->__('Status') ?></label><br/>
32
+ <select name="retops[status]" class="select" id="retops_status">
33
+ <?php foreach ($this->getStatuses() as $_code=>$_label): ?>
34
+ <option value="<?php echo $_code ?>"<?php if($_code==$this->getOrder()->getRetailopsStatus()): ?> selected="selected"<?php endif; ?>><?php echo $_label ?></option>
35
+ <?php endforeach; ?>
36
+ </select>
37
+ </span>
38
+ <span class="field-row">
39
+ <label class="normal" for="status_comment"><?php echo Mage::helper('sales')->__('Comment') ?></label>
40
+ <textarea name="retops[comment]" rows="3" cols="5" style="height:6em; width:99%;" id="retops_comment"><?php echo $this->getOrder()->getRetailopsStatusComment() ?></textarea>
41
+ </span>
42
+ <div class="f-right">
43
+ <?php echo $this->getChildHtml('submit_button') ?>
44
+ </div>
45
+ <div class="clear"></div>
46
+ </div>
47
+ </div>
48
+ <div class="divider"></div>
49
+ <ul class="note-list">
50
+ <?php foreach ($this->getRetailOpsStatusHistory() as $_item): ?>
51
+ <li>
52
+ <strong><?php echo $this->helper('core')->formatDate($_item->getCreatedAt(), 'medium') ?></strong>
53
+ <?php echo $this->helper('core')->formatTime($_item->getCreatedAt(), 'medium') ?><span class="separator">|</span><strong><?php echo $_item->getStatus() ?></strong>
54
+ <?php if ($_item->getComment()): ?>
55
+ <br/><?php echo $this->escapeHtml($_item->getComment(), array('b', 'br', 'strong', 'i', 'u', 'a')) ?>
56
+ <?php endif; ?>
57
+ </li>
58
+ <?php endforeach; ?>
59
+ </ul>
60
+ </div>
app/etc/modules/RetailOps_Api.xml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2015 Gud Technologies Incorporated (RetailOps by GüdTech)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
24
+ -->
25
+ <config>
26
+ <modules>
27
+ <RetailOps_Api>
28
+ <active>true</active>
29
+ <codePool>community</codePool>
30
+ <depends>
31
+ <Mage_Core/>
32
+ </depends>
33
+ </RetailOps_Api>
34
+ </modules>
35
+ </config>
package.xml ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>RetailOps_Api</name>
4
+ <version>1.0.3</version>
5
+ <stability>stable</stability>
6
+ <license uri="https://opensource.org/licenses/MIT">MIT</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Seamless integration with the RetailOps distributed order management platform</summary>
10
+ <description>RetailOps Magento Extension - The Ultimate Tools for Retail&#xD;
11
+ &#xD;
12
+ When it comes to modern retail, best-of-breed is broken. Retailers today are forced to string together disjointed, legacy software to run their businesses because nothing better exists. RetailOps&#x2019; cloud-based platform addresses the unique challenges facing eCommerce and Omni-Channel retailers today. Our philosophy is simple; create software that doesn't get in the way and makes the modern retailer's life easier and more profitable.&#xD;
13
+ &#xD;
14
+ The RetailOps Magento extension allows for a seamless integration with RetailOps, offering unparalleled performance and flexibility.&#xD;
15
+ &#xD;
16
+ For more details or to sign up for a demo, visit: http://www.retailops.com</description>
17
+ <notes>* Fix for duplicate attribute options.&#xD;
18
+ * Limited retry on image remote copy failure.&#xD;
19
+ * Fix to update image attributes even if image itself already exists.&#xD;
20
+ * Fix for inability to update or add more than one image to a sku.&#xD;
21
+ * Fix for case insensitivity on attribute sets.&#xD;
22
+ * Locate existing shipment before creating a new shipment.&#xD;
23
+ * Don't add tracking if shipment already has tracking number.&#xD;
24
+ * Fix for erroneous invoice captures.&#xD;
25
+ * Added retailops_hold status.&#xD;
26
+ </notes>
27
+ <authors><author><name>RetailOps</name><user>RetailOps</user><email>daniel@retailops.com</email></author></authors>
28
+ <date>2015-09-14</date>
29
+ <time>23:48:56</time>
30
+ <contents><target name="mageetc"><dir name="modules"><file name="RetailOps_Api.xml" hash="4efaf09b352f1f122f0af83342bdef07"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="retailops"><dir name="order"><dir name="view"><dir name="tab"><file name="retailops.phtml" hash="51d17085f74fb4ad1e09b987d7a0e733"/></dir></dir></dir></dir></dir><dir name="layout"><file name="retailops.xml" hash="8879a617964a66e0e02731e262234637"/></dir></dir></dir></dir></target><target name="magecommunity"><dir name="RetailOps"><dir name="Api"><dir name="Block"><dir name="Adminhtml"><dir name="Sales"><dir name="Order"><dir name="View"><dir name="Tab"><file name="Retailops.php" hash="27e0a91fcf5c0aaac5a545c16574465e"/></dir></dir></dir></dir></dir></dir><file name="Exception.php" hash="921703ce05b3e8d630fc8a36baa25d12"/><dir name="Helper"><file name="Data.php" hash="cb10d1f57ba9b7f96bd77b106c950a83"/></dir><dir name="Model"><file name="Api.php" hash="65e9e16cb8ee9c75c746d689f7f9e03e"/><dir name="Catalog"><dir name="Adapter"><file name="Abstract.php" hash="61a561a95bb40071d4bb7c2e02e63c46"/><file name="Attribute.php" hash="6fa97e2e8af011979c3600c877e81dea"/><file name="Bundle.php" hash="6777aa2ba12fdec543713cae97d50fe3"/><file name="Category.php" hash="4bf0053cd77d2f0a4a0094b67414169c"/><file name="Configurable.php" hash="91a5984f2e59a062f39f118aaa8a3bd5"/><file name="Default.php" hash="fcb2b32e6e4d008292d84caee9bdf240"/><file name="Downloadable.php" hash="dd2d4cf7650268861175c5cecf6ed1f2"/><file name="Link.php" hash="887104e69a274352867286c194172a8f"/><file name="Media.php" hash="4ba7f7e4f18cd055d86b73120c2b96a5"/><file name="Option.php" hash="d33d2a018f3ba2aa9460ec208df7fb95"/><file name="Tag.php" hash="615eff9ad88ab0c33122cb423f51fa67"/></dir><file name="Api.php" hash="b0b25c3897d67bc3bb6967d569a9ebb6"/><file name="Exception.php" hash="9a1ea92e48522ddaaa24e42dde94d0ef"/><dir name="Media"><file name="Item.php" hash="cf68048a5a9284fa2e3a50749b41235d"/></dir><dir name="Pull"><file name="Api.php" hash="b183269002499dfa29ad692904f41949"/></dir><dir name="Push"><file name="Api.php" hash="cc71f946efdd5d8e96cb68980e011262"/><dir name="Downloadable"><file name="Validator.php" hash="a2b4eb49b62e212eae06a0f55dfa393e"/></dir></dir></dir><dir name="Inventory"><file name="Api.php" hash="176ab5a377d1ca993791ca1f48af5f90"/></dir><file name="Observer.php" hash="7ef2c195010ae4c81b676d3c177e23e5"/><file name="Observer2.php~" hash="7ef2c195010ae4c81b676d3c177e23e5"/><dir name="Order"><file name="Api.php" hash="69beaf68afc724619a7dffbf923f0d08"/><dir name="Status"><file name="History.php" hash="39aae107a2029de67ecb01477508f271"/></dir></dir><dir name="Resource"><file name="Api.php" hash="168233c0b41251b67a20d0c8a32396ea"/><dir name="Catalog"><dir name="Media"><dir name="Item"><file name="Collection.php" hash="5938f25540bd59e75538eda614637179"/></dir><file name="Item.php" hash="eb3292a63811de285d2f44a6cb0dd630"/></dir></dir><dir name="Order"><dir name="Status"><dir name="History"><file name="Collection.php" hash="7427e972c92a5a435b772779a5548d29"/></dir><file name="History.php" hash="eab86f946bd58e5ab86d1cca21eee435"/></dir></dir></dir><dir name="Return"><file name="Api.php" hash="30ca168255518611669ac2e79067af5c"/></dir><dir name="Shipment"><file name="Api.php" hash="c0e471e66f14d29757b91479cdb6dc60"/><file name="Api.php.~80d30a79e43e602a0b5b5da0b2b1a8007dc0ada9~" hash="e1475ba8ce8216e0da57d45ae167246f"/><file name="Api.php.~84b644444a1b813b9b5956dbc9eeabb8577db950~" hash="8704e365691335f82eac3d3636539d35"/></dir><dir name="System"><dir name="Config"><dir name="Source"><dir name="Catalog"><file name="AttributeSet.php" hash="a158e6ee2971b6464a184f6f07d2a5dd"/></dir></dir></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><dir name="Sales"><file name="OrderController.php" hash="9b620414e2f98a6764f803bdaa7d01d3"/></dir></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="60053ceed8f5e0d8d175331fb239c7f5"/><file name="api.xml" hash="32f6e0212a770054748aefd338bb2c85"/><file name="config.xml" hash="1336116e03068593f3d971fb49d619d4"/><file name="config.xml~" hash="1336116e03068593f3d971fb49d619d4"/><file name="system.xml" hash="5840692fb14c6d5d7848753bce79aa53"/></dir><dir name="sql"><dir name="retailops_api_setup"><file name="install-1.0.0.php" hash="3edc90b95f4219c9508be3de6a86dda1"/><file name="upgrade-1.0.0-1.0.1.php" hash="63724e6209e0ef59696ffdd6a14dc738"/><file name="upgrade-1.0.1-1.0.2.php" hash="a5c7795cd35f9af984c729bd3d3cd18a"/><file name="upgrade-1.0.2-1.0.3.php" hash="3987d4fda58f7545469700df599b050a"/><file name="upgrade-1.0.2-1.0.3.php~" hash="a5c7795cd35f9af984c729bd3d3cd18a"/></dir></dir></dir><dir name="Model"><file name="Observer.php" hash="5d53ef3a3901d287ea7368606ce081f4"/><file name="Observer.php~" hash="ac545a977d4b202444e5af526188f9b3"/></dir></dir></target></contents>
31
+ <compatible/>
32
+ <dependencies><required><php><min>5.4.0</min><max>5.6.13</max></php></required></dependencies>
33
+ </package>