Fruugo_Integration - Version 1.1.2

Version Notes

- Fixed: use parent product's attributes when skus' are empty, including: Brand, Manufacturer, Description and Short Description

Download this release

Release Info

Developer inoutput.io
Extension Fruugo_Integration
Version 1.1.2
Comparing to
See all releases


Code changes from version 1.0.9 to 1.1.2

app/code/community/Fruugo/Integration/.DS_Store CHANGED
Binary file
app/code/community/Fruugo/Integration/Block/AttributeMapping.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ class Fruugo_Integration_Block_AttributeMapping
24
+ extends Mage_Adminhtml_Block_System_Config_Form_Field_Array_Abstract
25
+ {
26
+ protected $_itemRenderer;
27
+ protected $_fieldLayout = 'Fruugo_Integration_Block_AttributesDropdown';
28
+
29
+ public function _prepareToRender()
30
+ {
31
+ $this->addColumn('attribute', array(
32
+ 'label' => 'Attribute',
33
+ 'renderer' => $this->_getRenderer()
34
+ ));
35
+
36
+ $this->addColumn('priority', array(
37
+ 'label' => 'Priority',
38
+ 'style' => 'width: 50px',
39
+ 'type' => 'number'
40
+ ));
41
+
42
+ $this->_addAfter = false;
43
+ $this->_addButtonLabel = 'Add';
44
+ }
45
+
46
+ protected function _getRenderer()
47
+ {
48
+ if (!$this->_itemRenderer) {
49
+ $this->_itemRenderer = $this->getLayout()->createBlock(
50
+ $this->_fieldLayout,
51
+ '',
52
+ array('is_render_to_js_template' => true)
53
+ );
54
+ }
55
+
56
+ return $this->_itemRenderer;
57
+ }
58
+
59
+ protected function _prepareArrayRow(Varien_Object $row)
60
+ {
61
+ $row->setData(
62
+ 'option_extra_attr_' . $this->_getRenderer()
63
+ ->calcOptionHash($row->getData('attribute')),
64
+ 'selected="selected"'
65
+ );
66
+ }
67
+ }
app/code/community/Fruugo/Integration/Block/AttributeMappingFiltered.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ class Fruugo_Integration_Block_AttributeMappingFiltered
24
+ extends Fruugo_Integration_Block_AttributeMapping
25
+ {
26
+ public function __construct()
27
+ {
28
+ $this->_fieldLayout = 'Fruugo_Integration_Block_AttributesDropdownFiltered';
29
+
30
+ parent::__construct();
31
+ }
32
+ }
app/code/community/Fruugo/Integration/Block/AttributesDropdown.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ class Fruugo_Integration_Block_AttributesDropdown
24
+ extends Mage_Core_Block_Html_Select
25
+ {
26
+ protected $_optionsSource = 'Fruugo_Integration_Model_Adminhtml_System_Config_Source_Attributes';
27
+
28
+ public function _toHtml()
29
+ {
30
+ if (!$this->getOptions()) {
31
+ $options = Mage::getSingleton($this->_optionsSource)
32
+ ->toOptionArray();
33
+
34
+ foreach ($options as $option) {
35
+ $this->addOption($option['value'], $option['label']);
36
+ }
37
+ }
38
+
39
+ return parent::_toHtml();
40
+ }
41
+
42
+ public function setInputName($value)
43
+ {
44
+ return $this->setName($value);
45
+ }
46
+ }
app/code/community/Fruugo/Integration/Block/AttributesDropdownFiltered.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ class Fruugo_Integration_Block_AttributesDropdownFiltered
24
+ extends Fruugo_Integration_Block_AttributesDropdown
25
+ {
26
+ public function __construct()
27
+ {
28
+ $this->_optionsSource = 'Fruugo_Integration_Model_Adminhtml_System_Config_Source_AttributesFiltered';
29
+
30
+ parent::__construct();
31
+ }
32
+ }
app/code/community/Fruugo/Integration/Helper/OrdersFeedProcessor.php CHANGED
@@ -30,6 +30,12 @@ use \DOMXpath as DOMXpath;
30
 
31
  class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
32
  {
 
 
 
 
 
 
33
  public function processOrders($from = null)
34
  {
35
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
@@ -50,19 +56,26 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
50
  )
51
  ));
52
 
53
- if (empty($from) || $from === null) {
54
  $from = Fruugo_Integration_Helper_ConfigLoader::load('integration_options/orders_options/orders_endpoint_last_checked');
55
- if (empty($from) || $from === null) {
56
  $from = new DateTime('NOW');
57
- $from = $from->format(DateTime::ISO8601);
 
 
 
 
 
 
58
  }
 
 
59
  }
60
 
61
  $apiUrl .= ("?from=".urlencode($from));
62
 
63
  try {
64
- Fruugo_Integration_Helper_Logger::log("Getting new orders from Integration. From: " . $from);
65
-
66
  $data = @file_get_contents($apiUrl, false, $context);
67
 
68
  if ($data === false) {
@@ -82,12 +95,13 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
82
  $orders = $xpath->query('//o:order');
83
 
84
  if ($orders->length == 0) {
85
- Fruugo_Integration_Helper_Logger::log('No new Fruugo orders to process.');
86
  } else {
87
  foreach ($orders as $orderXml) {
88
  $this->_mapOrderFromXml($orderXml);
89
  }
90
- Fruugo_Integration_Helper_Logger::log("Processing new orders finished.");
 
91
  }
92
  } catch (Exception $ex) {
93
  Mage::logException($ex);
@@ -100,7 +114,7 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
100
  $fruugoId = $orderArray['orderId'];
101
 
102
  if ($orderArray['orderStatus'] !== 'PENDING') {
103
- Fruugo_Integration_Helper_Logger::log('Fruugo order ' . $fruugoId . ' status is not PENDING. Order skipped.');
104
  return false;
105
  }
106
 
@@ -108,7 +122,8 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
108
  $existingCount = $salesModel->getCollection()->addAttributeToFilter('fruugo_order_id', $fruugoId)->count();
109
 
110
  if ($existingCount > 0) {
111
- Fruugo_Integration_Helper_Logger::log("The Fruugo order $fruugoId already exists and is being skipped");
 
112
  return false;
113
  }
114
 
@@ -168,6 +183,7 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
168
  $quantity = $orderLine['totalNumberOfItems'];
169
 
170
  $quoteItem = $this->_mapLineItem($orderLine);
 
171
  if (!$quoteItem) {
172
  // product does not exist any more, cancel this item
173
  $orderItemsInfo .= '&item=' . $fruugoProductId.','
@@ -241,30 +257,172 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
241
  ->setIsVisibleOnFront(false)
242
  ->setIsCustomerNotified(false);
243
 
244
- // if you don't save each order item, the fruugo_product_id and fruugo_sku_id will not be saved
245
  foreach ($order->getAllItems() as $orderItem) {
 
 
 
246
  $orderItem->save();
 
 
 
247
  }
248
 
 
 
249
  $order->save();
250
 
251
- // remove items from shopping cart
252
  $quote->removeAllItems();
253
  $quote->save();
254
 
255
  if (count($order->getAllItems()) == 0) {
256
- $nonExistProductInfo = '';
257
  foreach ($orderArray['orderLines'] as $orderLine) {
258
  $productId = $orderLine['productId'];
259
  $productSku = $orderLine['skuId'];
260
  $nonExistProductInfo .= "ProductId:{$productId}, Sku:{$productSku}";
 
 
 
 
261
  }
262
- $this->cancelItemsOnProductNotExist($order, $fruugoId, $nonExistProductInfo);
263
  } elseif (!empty($orderItemsInfo)) {
264
  $this->cancelItemsOnProductNotExist($order, $fruugoId, $nonExistProductInfo, $orderItemsInfo);
265
  }
266
  }
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  private function _mapLineItem($orderLine)
269
  {
270
  $sku = $orderLine['skuId'];
@@ -345,7 +503,7 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
345
  return implode($pass);
346
  }
347
 
348
- private function cancelItemsOnProductNotExist($order, $fruugoId, $nonExistProductInfo, $orderItemsInfo = null)
349
  {
350
  $observer = new Fruugo_Integration_Model_Observer;
351
  $data = array();
@@ -357,6 +515,12 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
357
  if (strpos($apiUrl, '127.0.0.1')) {
358
  $data['mock_api_operation'] = 'cancel';
359
  $data['orderId'] = $fruugoId;
 
 
 
 
 
 
360
  }
361
  }
362
 
@@ -377,4 +541,9 @@ class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
377
  $observer->_saveHistoryComment($order, "Failed to send notification to Fruugo of cancellation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
378
  }
379
  }
 
 
 
 
 
380
  }
30
 
31
  class Fruugo_Integration_OrdersFeedProcessor extends Mage_Core_Helper_Abstract
32
  {
33
+ protected static $ALWAYS = Fruugo_Integration_Helper_Logger::ALWAYS;
34
+ protected static $ERROR = Fruugo_Integration_Helper_Logger::ERROR;
35
+ protected static $WARNING = Fruugo_Integration_Helper_Logger::WARNING;
36
+ protected static $INFO = Fruugo_Integration_Helper_Logger::INFO;
37
+ protected static $DEBUG = Fruugo_Integration_Helper_Logger::DEBUG;
38
+
39
  public function processOrders($from = null)
40
  {
41
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
56
  )
57
  ));
58
 
59
+ if (empty($from)) {
60
  $from = Fruugo_Integration_Helper_ConfigLoader::load('integration_options/orders_options/orders_endpoint_last_checked');
61
+ if (empty($from)) {
62
  $from = new DateTime('NOW');
63
+ } else {
64
+ try {
65
+ $from = new DateTime($from);
66
+ } catch (Exception $ex) {
67
+ $this->_writeLog('Invalid $from paramter format. Value: ' . $from, self::$ERROR);
68
+ $from = new DateTime('NOW');
69
+ }
70
  }
71
+
72
+ $from = $from->format(DateTime::ISO8601);
73
  }
74
 
75
  $apiUrl .= ("?from=".urlencode($from));
76
 
77
  try {
78
+ $this->_writeLog("Getting new orders from Integration. From: " . $from, self::$ALWAYS);
 
79
  $data = @file_get_contents($apiUrl, false, $context);
80
 
81
  if ($data === false) {
95
  $orders = $xpath->query('//o:order');
96
 
97
  if ($orders->length == 0) {
98
+ $this->_writeLog('No new Fruugo orders to process.', self::$WARNING);
99
  } else {
100
  foreach ($orders as $orderXml) {
101
  $this->_mapOrderFromXml($orderXml);
102
  }
103
+
104
+ $this->_writeLog('Processing new orders finished.', self::$ALWAYS);
105
  }
106
  } catch (Exception $ex) {
107
  Mage::logException($ex);
114
  $fruugoId = $orderArray['orderId'];
115
 
116
  if ($orderArray['orderStatus'] !== 'PENDING') {
117
+ $this->_writeLog('Fruugo order ' . $fruugoId . ' status is not PENDING. Order skipped.', self::$WARNING);
118
  return false;
119
  }
120
 
122
  $existingCount = $salesModel->getCollection()->addAttributeToFilter('fruugo_order_id', $fruugoId)->count();
123
 
124
  if ($existingCount > 0) {
125
+ // Fruugo_Integration_Helper_Logger::log();
126
+ $this->_writeLog("The Fruugo order $fruugoId already exists and is being skipped", self::$WARNING);
127
  return false;
128
  }
129
 
183
  $quantity = $orderLine['totalNumberOfItems'];
184
 
185
  $quoteItem = $this->_mapLineItem($orderLine);
186
+
187
  if (!$quoteItem) {
188
  // product does not exist any more, cancel this item
189
  $orderItemsInfo .= '&item=' . $fruugoProductId.','
257
  ->setIsVisibleOnFront(false)
258
  ->setIsCustomerNotified(false);
259
 
 
260
  foreach ($order->getAllItems() as $orderItem) {
261
+ $orderItem->setBaseTaxBeforeDiscount(null);
262
+
263
+ // Save the order item so fruugo_product_id and fruugo_sku_id are persisted
264
  $orderItem->save();
265
+
266
+ // Override the order item's prices based on data from Fruugo
267
+ $this->_setOrderItemPrices($orderArray['orderLines'], $orderItem);
268
  }
269
 
270
+ // Override the order's prices based on data from Fruugo
271
+ $this->_setOrderPrices($orderArray['orderLines'], $order, $orderArray['shippingCostInclVAT']);
272
  $order->save();
273
 
274
+ // Remove items from shopping cart
275
  $quote->removeAllItems();
276
  $quote->save();
277
 
278
  if (count($order->getAllItems()) == 0) {
 
279
  foreach ($orderArray['orderLines'] as $orderLine) {
280
  $productId = $orderLine['productId'];
281
  $productSku = $orderLine['skuId'];
282
  $nonExistProductInfo .= "ProductId:{$productId}, Sku:{$productSku}";
283
+ $orderItemsInfo .= '&item=' . $orderLine['fruugoProductId'].','
284
+ .$orderLine['fruugoSkuId'].','
285
+ .(int)$orderLine['totalNumberOfItems'];
286
+ $this->cancelItemsOnProductNotExist($order, $fruugoId, $nonExistProductInfo, $orderItemsInfo, $orderLine['fruugoProductId']);
287
  }
 
288
  } elseif (!empty($orderItemsInfo)) {
289
  $this->cancelItemsOnProductNotExist($order, $fruugoId, $nonExistProductInfo, $orderItemsInfo);
290
  }
291
  }
292
 
293
+ protected function _setOrderPrices($orderLines, $order, $shippingCost)
294
+ {
295
+ // Set tax percentage to null, since we don't know it
296
+ $order->setTaxPercent(null);
297
+ $order->setBaseTaxPercent(null);
298
+
299
+ // Set subtotal
300
+ $subtotal = $this->_getOrderTotal($orderLines);
301
+
302
+ $order->setBaseSubtotal($subtotal);
303
+ $order->setSubtotal($subtotal);
304
+
305
+ // Set tax amount
306
+ $tax = $this->_getOrderTax($orderLines);
307
+
308
+ $order->setTaxAmount($tax);
309
+ $order->setBaseTaxAmount($tax);
310
+
311
+ // Set total
312
+ $total = $this->_getOrderTotal(
313
+ $orderLines,
314
+ $withTax = true,
315
+ $withShipping = $shippingCost
316
+ );
317
+
318
+ $order->setBaseGrandTotal($total);
319
+ $order->setGrandTotal($total);
320
+ $order->setBaseTotalDue($total);
321
+ $order->setTotalDue($total);
322
+ }
323
+
324
+ protected function _getOrderTax($orderLines)
325
+ {
326
+ return array_sum(array_map(function ($item) {
327
+ return $item['totalVat'];
328
+ }, $orderLines));
329
+ }
330
+
331
+ protected function _getOrderTotal($orderLines, $withTax = false, $withShipping = false)
332
+ {
333
+ $price = array_sum(array_map(function ($item) {
334
+ return $item['totalPriceInclVat'];
335
+ }, $orderLines));
336
+
337
+ if (!$withTax) {
338
+ $vat = array_sum(array_map(function ($item) {
339
+ return $item['totalVat'];
340
+ }, $orderLines));
341
+
342
+ $price = $price - $vat;
343
+ }
344
+
345
+ if ($withShipping) {
346
+ $price = $price + $withShipping;
347
+ }
348
+
349
+ return $price;
350
+ }
351
+
352
+ protected function _setOrderItemPrices($orderLines, $orderItem)
353
+ {
354
+ $orderLine = $this->_getOrderItemByFruugoSku($orderLines, $orderItem->getData('fruugo_sku_id'));
355
+
356
+ // Set unit price
357
+ $price = $this->_getOrderLinePrice($orderLine);
358
+
359
+ $orderItem->setBasePrice($price);
360
+ $orderItem->setPrice($price);
361
+ $orderItem->setBaseOriginalPrice($price);
362
+ $orderItem->setOriginalPrice($price);
363
+
364
+ // Set tax-inclusive unit price
365
+ $priceInclTax = $this->_getOrderLinePrice($orderLine, true);
366
+
367
+ $orderItem->setPriceInclTax($priceInclTax);
368
+ $orderItem->setBasePriceInclTax($priceInclTax);
369
+
370
+ // Set tax percentage
371
+ $orderItem->setTaxPercent($orderLine['vatPercentage']);
372
+ $orderItem->setBaseTaxPercent($orderLine['vatPercentage']);
373
+
374
+ // Set unit tax amount
375
+ $orderItem->setTaxAmount($orderLine['totalVat']);
376
+ $orderItem->setBaseTaxAmount($orderLine['totalVat']);
377
+
378
+ // Set total
379
+ $total = $this->_getOrderLineTotal($orderLine);
380
+
381
+ $orderItem->setBaseRowTotal($total);
382
+ $orderItem->setRowTotal($total);
383
+
384
+ // Set tax-inclusive total
385
+ $totalInclTax = $this->_getOrderLineTotal($orderLine, true);
386
+
387
+ $orderItem->setBaseRowTotalInclTax($totalInclTax);
388
+ $orderItem->setRowTotalInclTax($totalInclTax);
389
+ }
390
+
391
+ protected function _getOrderLinePrice($orderLine, $withTax = false)
392
+ {
393
+ $price = $orderLine['itemPriceInclVat'];
394
+
395
+ if (!$withTax) {
396
+ $price = $price - $orderLine['itemVat'];
397
+ }
398
+
399
+ return $price;
400
+ }
401
+
402
+ protected function _getOrderLineTotal($orderLine, $withTax = false)
403
+ {
404
+ $price = $orderLine['totalPriceInclVat'];
405
+
406
+ if (!$withTax) {
407
+ $price = $price - $orderLine['totalVat'];
408
+ }
409
+
410
+ return $price;
411
+ }
412
+
413
+ protected function _getOrderItemByFruugoSku($orderLines, $fruugoSku)
414
+ {
415
+ $orderItem;
416
+
417
+ foreach ($orderLines as $orderLine) {
418
+ if ($orderLine['fruugoSkuId'] == $fruugoSku) {
419
+ $orderItem = $orderLine;
420
+ };
421
+ }
422
+
423
+ return $orderItem;
424
+ }
425
+
426
  private function _mapLineItem($orderLine)
427
  {
428
  $sku = $orderLine['skuId'];
503
  return implode($pass);
504
  }
505
 
506
+ private function cancelItemsOnProductNotExist($order, $fruugoId, $nonExistProductInfo, $orderItemsInfo = null, $fruugoProductId = null)
507
  {
508
  $observer = new Fruugo_Integration_Model_Observer;
509
  $data = array();
515
  if (strpos($apiUrl, '127.0.0.1')) {
516
  $data['mock_api_operation'] = 'cancel';
517
  $data['orderId'] = $fruugoId;
518
+
519
+ if ($fruugoProductId) {
520
+ $data['mock_api_operation'] = 'cancel_item';
521
+ $data['orderId'] = $fruugoId;
522
+ $data['fruugoProductId'] = $fruugoProductId;
523
+ }
524
  }
525
  }
526
 
541
  $observer->_saveHistoryComment($order, "Failed to send notification to Fruugo of cancellation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
542
  }
543
  }
544
+
545
+ protected function _writeLog($message, $level = Fruugo_Integration_Helper_Logger::DEBUG)
546
+ {
547
+ Fruugo_Integration_Helper_Logger::log($message, $level);
548
+ }
549
  }
app/code/community/Fruugo/Integration/Helper/ProductsFeedGenerator.php CHANGED
@@ -24,16 +24,29 @@ require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/Logger.php'
24
 
25
  class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
26
  {
27
- protected $MONITOR_RESOURCES = true; // Should the script check resource usage while running
28
- protected $MAX_RESOURCES = 0.5; // Maximum load average allowed in the last minute
29
- protected $SLEEP_TIME_SEC = 20; // Time to sleep for if over load limit
30
-
31
- protected $MAX_ERRORS = 30; // The number of errors after which the script will abort, set to -1 to disable
32
- protected $PAGE_SIZE = 100; // The number of products to process per batch
33
- // whether or not to track the last id processed to avoid a double process if items are deleted
34
- // in between selecting batches, incurs a small performance cost.
 
 
 
 
 
 
 
 
 
 
35
  protected $TRACK_LAST_ID = true;
 
36
 
 
 
37
  protected $stores = null;
38
  protected $taxHelper = null;
39
  protected $taxCalculation = null;
@@ -46,6 +59,7 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
46
  protected $currencyConverter = null;
47
  protected $storeBaseCurrencies = null;
48
  protected $shouldConvertCurrency = true;
 
49
 
50
  protected static $ALWAYS = Fruugo_Integration_Helper_Logger::ALWAYS;
51
  protected static $ERROR = Fruugo_Integration_Helper_Logger::ERROR;
@@ -53,7 +67,6 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
53
  protected static $INFO = Fruugo_Integration_Helper_Logger::INFO;
54
  protected static $DEBUG = Fruugo_Integration_Helper_Logger::DEBUG;
55
 
56
-
57
  public function generateProdcutsFeed($cached = false)
58
  {
59
  $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
@@ -74,12 +87,12 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
74
  $f = fopen($lockFile, 'w');
75
  if ($f === false) {
76
  $this->_writeLog('Did not start Fruugo products export because the script is already running.', self::$WARNING);
77
- die('Cannot create lock file');
78
  }
79
 
80
  if (!flock($f, LOCK_EX | LOCK_NB)) {
81
  $this->_writeLog('Did not start Fruugo products export because the script is already running.', self::$WARNING);
82
- die('Cannot create lock file');
83
  } else {
84
  $this->_writeLog('Beginning export of products feed...', self::$ALWAYS);
85
  }
@@ -161,9 +174,8 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
161
 
162
  if ($productXml) {
163
  $xmlBuffer .= $productXml;
 
164
  }
165
-
166
- $numOfProds++;
167
  } catch (Exception $ex) {
168
  $errorsCount += 1;
169
  Mage::logException($ex);
@@ -263,26 +275,65 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
263
  }
264
 
265
  // SkuId *M
266
- $productXml->addChild('SkuId', htmlspecialchars($product->getSku()));
 
 
 
 
 
267
 
268
- // EAN *R
269
- if ($product->getEan() !== null) {
270
- $productXml->addChild('EAN', $product->getEan());
271
  }
272
 
273
- // ISBN *O
274
- if ($product->getIsbn() !== null) {
275
- $productXml->addChild('ISBN', $product->getIsbn());
276
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
- // Brand *R
279
- if ($product->getBrand() !== null && $product->getAttributeText('brand')) {
280
- $productXml->addChild('Brand', htmlspecialchars($product->getAttributeText('brand')));
 
281
  }
282
 
283
- // Manufacturer *O
284
- if ($product->getManufacturer() !== null && $product->getAttributeText('manufacturer')) {
285
- $productXml->addChild('Manufacturer', htmlspecialchars($product->getAttributeText('manufacturer')));
 
 
 
 
 
 
 
 
 
 
286
  }
287
 
288
  // Category *R
@@ -293,8 +344,17 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
293
  ->addAttributeToFilter('is_active', '1')
294
  ->getFirstItem();
295
 
296
- if ($categoryEntity->getName() !== null) {
297
- $productXml->addChild('Category', htmlspecialchars($categoryEntity->getName()));
 
 
 
 
 
 
 
 
 
298
  }
299
 
300
  // Imageurl1 *M
@@ -315,8 +375,10 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
315
 
316
  if ($stockItem === null || !$stockItem->getIsInStock()) {
317
  $productXml->addChild('StockStatus', 'OUTOFSTOCK');
 
318
  } else {
319
  $stocklevel = (int)$stockItem->getQty();
 
320
  if ($stocklevel <= 0) {
321
  $productXml->addChild('StockStatus', 'OUTOFSTOCK');
322
  $productXml->addChild('StockQuantity', 0);
@@ -354,70 +416,62 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
354
 
355
  // Make sure product has description based on store config
356
  $descriptionType = Mage::getStoreConfig('integration_options/products_options/descrption_type', $store);
357
- $descriptionsArray = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), array(
358
- 'description',
359
- 'short_description',
360
- ), $store->getId());
361
-
362
- // This is a workaround for a bug in getAttributeRawValue which returns the wrong values for product attributes that have been
363
- // modified in another site so to save db hits we try and get them and if they differ from the main product we are processing we
364
- // then load them individually
365
- if (isset($descriptionsArray['description']) && ($product->getDescription() != $descriptionsArray['description'])) {
366
- $descriptionsArray['description'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'description', $store->getId());
367
- }
368
-
369
- if (isset($descriptionsArray['short_description']) && ($product->getShortDescription() != $descriptionsArray['short_description'])) {
370
- $descriptionsArray['short_description'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'short_description', $store->getId());
371
- }
372
 
373
  // check product description not null for each store
374
- if ($descriptionType == 'long' && $descriptionsArray['description'] === null) {
375
- continue;
376
- } elseif ($descriptionType == 'short' && $descriptionsArray['short_description'] === null) {
377
- continue;
378
- }
379
 
380
- // Add description when no store selected for language OR the store is selected for the langauge in storelangsMapping array
381
- if ($storelangsMapping == null || $storelangsMapping[$localeCode] == "" || $storelangsMapping[$localeCode] == $store->getCode()) {
382
- // Language *R
383
- $descriptionXml = $productXml->addChild('Description');
384
- $descriptionXml->addChild('Language', $language);
385
 
386
- $attributes = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), array(
387
- 'shoe_size',
388
- 'size',
389
- 'color',
390
- 'fit',
391
- 'length',
392
- 'width',
393
- ), $store->getId());
394
-
395
- // This is a workaround for a bug in getAttributeRawValue which returns the wrong values for product attributes that have been
396
- // modified in another site so to save db hits we try and get them and if they differ from the main product we are processing we
397
- // then load them individually
398
- if (isset($attributes['fit']) && ($product->getFit() != $attributes['fit'])) {
399
- $attributes['fit'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'fit', $store->getId());
400
  }
401
 
402
- if (isset($attributes['color']) && ($product->getColor() != $attributes['color'])) {
403
- $attributes['color'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'color', $store->getId());
 
 
404
  }
405
 
406
- if (isset($attributes['size']) && ($product->getSize() != $attributes['size'])) {
407
- $attributes['size'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'size', $store->getId());
 
408
  }
 
 
 
 
409
 
410
- if (isset($attributes['shoe_size']) && ($product->getShoe_size() != $attributes['shoe_size'])) {
411
- $attributes['shoe_size'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'shoe_size', $store->getId());
 
412
  }
413
 
414
- if (isset($attributes['length']) && ($product->getLength() != $attributes['length'])) {
415
- $attributes['length'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'length', $store->getId());
 
 
416
  }
417
 
418
- if (isset($attributes['width']) && ($product->getWidth() != $attributes['width'])) {
419
- $attributes['width'] = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'width', $store->getId());
 
420
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
  // title
423
  if (isset($parentProduct)) {
@@ -440,28 +494,7 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
440
  $this->_addCData($nestedDescriptionXml, $descriptionsArray['description'] . PHP_EOL . $descriptionsArray['short_description']);
441
  }
442
 
443
- // AttributeColor *R
444
- if (!empty($attributes['color'])) {
445
- $descriptionXml->addChild('AttributeColor', $this->_getAttributesText($language, 'color', $attributes['color'], $store->getId()));
446
- }
447
-
448
- // AttributeSize *R
449
- if (!empty($attributes['shoe_size'])) {
450
- $descriptionXml->addChild('AttributeSize', $this->_getAttributesText($language, 'shoe_size', $attributes['shoe_size'], $store->getId()));
451
- } elseif (!empty($attributes['size'])) {
452
- $descriptionXml->addChild('AttributeSize', $this->_getAttributesText($language, 'size', $attributes['size'], $store->getId()));
453
- }
454
-
455
- // optional attributes: Arrtibute1 - Attribute10 *O
456
- if (!empty($attributes['fit'])) {
457
- $descriptionXml->addChild('Attribute1', $this->_getAttributesText($language, 'fit', $attributes['fit'], $store->getId()));
458
- }
459
- if (!empty($attributes['length'])) {
460
- $descriptionXml->addChild('Attribute2', $this->_getAttributesText($language, 'length', $attributes['length'], $store->getId()));
461
- }
462
- if (!empty($attributes['width'])) {
463
- $descriptionXml->addChild('Attribute3', $this->_getAttributesText($language, 'width', $attributes['width'], $store->getId()));
464
- }
465
 
466
  array_push($addedLanguages, $language);
467
  $descriptionNodeCount++;
@@ -572,6 +605,70 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
572
  return $productStr;
573
  }
574
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  protected function _convertCurrency($price, $baseCurrencyCode, $currencyCode)
576
  {
577
  if ($this->shouldConvertCurrency && $baseCurrencyCode != $currencyCode) {
@@ -587,7 +684,7 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
587
 
588
  // This caches the options label translation for selectable product attributes.
589
  // They are cached by attribute name, language and then value.
590
- protected function _getAttributesText($language, $attributeName, $optionId, $storeId)
591
  {
592
  // These are the asset keys that are cached, you should only cache attributes
593
  // that have selectable items
@@ -600,21 +697,29 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
600
  );
601
  }
602
 
 
603
  if (!isset($this->attributeMap[$attributeName])) {
604
- return $optionId;
605
  }
606
 
 
607
  if (!isset($this->attributeMap[$attributeName][$language])) {
608
  $this->attributeMap[$attributeName][$language] = array();
609
  }
610
 
611
  if (!isset($this->attributeMap[$attributeName][$language][$optionId])) {
 
612
  $this->attributeMap[$attributeName][$language][$optionId] = null;
613
  } else {
 
614
  return $this->attributeMap[$attributeName][$language][$optionId];
615
  }
616
 
617
- $attributeId = Mage::getResourceModel('eav/entity_attribute')->getIdByCode('catalog_product', $attributeName);
 
 
 
 
618
  $collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
619
  ->setPositionOrder('asc')
620
  ->setAttributeFilter($attributeId)
@@ -622,6 +727,7 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
622
  ->load()
623
  ->toOptionArray();
624
 
 
625
  $found = false;
626
  foreach ($collection as $option) {
627
  if ($option['value'] == $optionId) {
@@ -631,14 +737,46 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
631
  $this->attributeMap[$attributeName][$language][$option['value']] = $option['label'];
632
  }
633
 
 
634
  if (!$found) {
635
  return $optionId;
636
  }
637
 
 
638
  return $this->attributeMap[$attributeName][$language][$optionId];
639
  }
640
 
641
- protected $tempProductObj = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  protected function _getProductImages($product, $parentProduct)
643
  {
644
  if (!isset($this->tempProductObj) || $this->tempProductObj === false) {
@@ -709,15 +847,68 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
709
 
710
  protected function _shouldInclude($product)
711
  {
712
- if ($product->getSku() === null || $product->getName() === null || $product->getPrice() === null) {
713
  return false;
714
  }
715
 
716
- if ($product->getDescription() === null && $product->getShortDescription() === null) {
717
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
718
  }
719
 
720
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  }
722
 
723
  protected function _getParentProduct($product)
@@ -739,7 +930,7 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
739
  {
740
  $node = dom_import_simplexml($xml);
741
  $no = $node->ownerDocument;
742
- $node->appendChild($no->createCDATASection(htmlspecialchars($cdata_text)));
743
  return $xml;
744
  }
745
 
@@ -750,7 +941,46 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
750
 
751
  protected function _writeReport($report)
752
  {
753
- file_put_contents($this->reportPath, json_encode($report));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
  }
755
 
756
  protected function _writeLog($message, $level = Fruugo_Integration_Helper_Logger::DEBUG)
@@ -769,9 +999,10 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
769
  }
770
 
771
  $this->PAGE_SIZE = Mage::getStoreConfig('integration_options/products_options/export_page_size');
772
- $this->MAX_RESOURCES = Mage::getStoreConfig('integration_options/products_options/max_resources_load');
773
- $this->SLEEP_TIME_SEC = Mage::getStoreConfig('integration_options/products_options/sleep_time_sec');
774
- $this->MAX_ERRORS = Mage::getStoreConfig('integration_options/products_options/max_errors');
 
775
 
776
  $this->taxHelper = Mage::helper('tax');
777
  $this->currencyConverter = Mage::helper('directory');
@@ -795,14 +1026,75 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
795
  'time_paused_sec' => 0,
796
  'xml_file_size_mb' => 0,
797
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
798
  }
799
 
800
  // This monitors system resources and pauses execution if the utilisation is
801
  // above the configured threshold.
802
  // Recommended for systems with large numbers of products
803
  // Note: this feature is not available on windows servers.
804
- protected $loadCheckCount = 0;
805
- protected $loadCheckTotal = 0.0;
806
  protected function checkServerLoad()
807
  {
808
  if (stristr(PHP_OS, 'win')) {
@@ -823,7 +1115,7 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
823
  }
824
 
825
  $systemLoad = sys_getloadavg();
826
- if ($this->devMode && $systemLoad[0] > $this->MAX_RESOURCES) {
827
  $this->_writeLog(
828
  'High server load detected. Usage of ' . $systemLoad[0] .
829
  ' is greater than configured maximum of ' . $this->MAX_RESOURCES .
@@ -838,6 +1130,4 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
838
  $this->_writeLog('Fruugo export resumed after waiting ' . $this->SLEEP_TIME_SEC . ' seconds.', self::$DEBUG);
839
  }
840
  }
841
-
842
-
843
  }
24
 
25
  class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
26
  {
27
+ // Should the script check resource usage while running
28
+ protected $MONITOR_RESOURCES = true;
29
+
30
+ // Maximum load average allowed in the last minute
31
+ protected $MAX_RESOURCES = 0.5;
32
+
33
+ // Time to sleep for if over load limit
34
+ protected $SLEEP_TIME_SEC = 20;
35
+
36
+ // The number of errors after which the script will abort, set to -1 to disable
37
+ protected $MAX_ERRORS = 30;
38
+
39
+ // The number of products to process per batch
40
+ protected $PAGE_SIZE = 100;
41
+
42
+ // Whether or not to track the last id processed to avoid a double process
43
+ // if items are deleted in between selecting batches, incurs a small
44
+ // performance cost.
45
  protected $TRACK_LAST_ID = true;
46
+ protected $NUMBER_OF_REPORTS_TO_KEEP = 10;
47
 
48
+ protected $loadCheckCount = 0;
49
+ protected $loadCheckTotal = 0.0;
50
  protected $stores = null;
51
  protected $taxHelper = null;
52
  protected $taxCalculation = null;
59
  protected $currencyConverter = null;
60
  protected $storeBaseCurrencies = null;
61
  protected $shouldConvertCurrency = true;
62
+ protected $tempProductObj = false;
63
 
64
  protected static $ALWAYS = Fruugo_Integration_Helper_Logger::ALWAYS;
65
  protected static $ERROR = Fruugo_Integration_Helper_Logger::ERROR;
67
  protected static $INFO = Fruugo_Integration_Helper_Logger::INFO;
68
  protected static $DEBUG = Fruugo_Integration_Helper_Logger::DEBUG;
69
 
 
70
  public function generateProdcutsFeed($cached = false)
71
  {
72
  $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
87
  $f = fopen($lockFile, 'w');
88
  if ($f === false) {
89
  $this->_writeLog('Did not start Fruugo products export because the script is already running.', self::$WARNING);
90
+ die('FuugoMagentoProductsFeed: Cannot create lock file');
91
  }
92
 
93
  if (!flock($f, LOCK_EX | LOCK_NB)) {
94
  $this->_writeLog('Did not start Fruugo products export because the script is already running.', self::$WARNING);
95
+ die('FuugoMagentoProductsFeed: Cannot create lock file');
96
  } else {
97
  $this->_writeLog('Beginning export of products feed...', self::$ALWAYS);
98
  }
174
 
175
  if ($productXml) {
176
  $xmlBuffer .= $productXml;
177
+ $numOfProds++;
178
  }
 
 
179
  } catch (Exception $ex) {
180
  $errorsCount += 1;
181
  Mage::logException($ex);
275
  }
276
 
277
  // SkuId *M
278
+ if ($product->getSku()) {
279
+ $productXml->addChild('SkuId', htmlspecialchars($product->getSku()));
280
+ } else {
281
+ $skuId = isset($parentProduct)
282
+ ? "dbid{$parentProduct->getId()}_attr{$product->getId()}"
283
+ : "dbid{$product->getId()}";
284
 
285
+ $productXml->addChild('SkuId', $skuId);
 
 
286
  }
287
 
288
+ $mappedAttributes = $this->_getMappedProductAttributes(array(
289
+ 'EAN' => $this->eanAttributes,
290
+ 'ISBN' => $this->isbnAttributes,
291
+ 'Brand' => $this->brandAttributes,
292
+ 'Manufacturer' => $this->manufacturerAttributes
293
+ ), $product);
294
+
295
+ // Get parent ones if variant's Brand & Manufacturer are empty
296
+ if (isset($parentProduct)) {
297
+ if (!array_key_exists('Brand', $mappedAttributes)
298
+ || empty($mappedAttributes['Brand'])
299
+ || !array_key_exists('brand', $mappedAttributes['Brand'])
300
+ || $mappedAttributes['Brand']['brand'] == false) {
301
+ $parentMappedBrandAttributes = $this->_getMappedProductAttributes(array(
302
+ 'Brand' => $this->brandAttributes
303
+ ), $parentProduct);
304
+
305
+ if (array_key_exists('Brand', $parentMappedBrandAttributes)) {
306
+ $mappedAttributes['Brand'] = $parentMappedBrandAttributes['Brand'];
307
+ }
308
+ }
309
+
310
+ if (!array_key_exists('Manufacturer', $mappedAttributes)
311
+ || empty($mappedAttributes['Manufacturer'])
312
+ || !array_key_exists('manufacturer', $mappedAttributes['Manufacturer'])
313
+ || $mappedAttributes['Manufacturer']['manufacturer'] == false) {
314
+ $parentMappedManufacturerAttributes = $this->_getMappedProductAttributes(array(
315
+ 'Manufacturer' => $this->manufacturerAttributes
316
+ ), $parentProduct);
317
 
318
+ if (array_key_exists('Manufacturer', $parentMappedManufacturerAttributes)) {
319
+ $mappedAttributes['Manufacturer'] = $parentMappedManufacturerAttributes['Manufacturer'];
320
+ }
321
+ }
322
  }
323
 
324
+ foreach ($mappedAttributes as $attributeName => $attributes) {
325
+ $attributeText = $this->_getAttributesText(
326
+ 0, // Language
327
+ array_keys($attributes)[0],
328
+ array_values($attributes)[0]
329
+ );
330
+
331
+ if (!empty($attributeText)) {
332
+ $productXml->addChild(
333
+ $attributeName,
334
+ htmlspecialchars($attributeText)
335
+ );
336
+ }
337
  }
338
 
339
  // Category *R
344
  ->addAttributeToFilter('is_active', '1')
345
  ->getFirstItem();
346
 
347
+ $categories = array();
348
+
349
+ // Get parent category for each category until the root catalog
350
+ while (isset($categoryEntity) && $categoryEntity->getName() !== null && $categoryEntity->getName() !== 'Root Catalog') {
351
+ array_push($categories, $categoryEntity->getName());
352
+ $categoryEntity = $categoryEntity->getParentCategory();
353
+ }
354
+
355
+ if (!empty($categories)) {
356
+ // Top level category comes first
357
+ $productXml->addChild('Category', htmlspecialchars(implode('>', array_reverse($categories))));
358
  }
359
 
360
  // Imageurl1 *M
375
 
376
  if ($stockItem === null || !$stockItem->getIsInStock()) {
377
  $productXml->addChild('StockStatus', 'OUTOFSTOCK');
378
+ $productXml->addChild('StockQuantity', 0);
379
  } else {
380
  $stocklevel = (int)$stockItem->getQty();
381
+
382
  if ($stocklevel <= 0) {
383
  $productXml->addChild('StockStatus', 'OUTOFSTOCK');
384
  $productXml->addChild('StockQuantity', 0);
416
 
417
  // Make sure product has description based on store config
418
  $descriptionType = Mage::getStoreConfig('integration_options/products_options/descrption_type', $store);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
  // check product description not null for each store
421
+ $descriptionsArray = array();
 
 
 
 
422
 
423
+ foreach ($this->descriptionAttributes['AttributeDescription'] as $attributeName) {
424
+ $descriptionValue = null;
 
 
 
425
 
426
+ if ($attributeName && $product->getData($attributeName)) {
427
+ $descriptionValue = Mage::getResourceModel('catalog/product')
428
+ ->getAttributeRawValue($product->getId(), $attributeName, $store->getId());
 
 
 
 
 
 
 
 
 
 
 
429
  }
430
 
431
+ // use parent description if empty
432
+ if ($descriptionValue == null && isset($parentProduct)) {
433
+ $descriptionValue = Mage::getResourceModel('catalog/product')
434
+ ->getAttributeRawValue($parentProduct->getId(), $attributeName, $store->getId());
435
  }
436
 
437
+ if (!empty($descriptionValue)) {
438
+ $descriptionsArray['description'] = $descriptionValue;
439
+ break;
440
  }
441
+ }
442
+
443
+ foreach ($this->descriptionAttributes['AttributeShortDescription'] as $attributeName) {
444
+ $shortDescriptionValue = null;
445
 
446
+ if ($attributeName && $product->getData($attributeName)) {
447
+ $shortDescriptionValue = Mage::getResourceModel('catalog/product')
448
+ ->getAttributeRawValue($product->getId(), $attributeName, $store->getId());
449
  }
450
 
451
+ // use parent short description if empty
452
+ if ($shortDescriptionValue == null && isset($parentProduct)) {
453
+ $shortDescriptionValue = Mage::getResourceModel('catalog/product')
454
+ ->getAttributeRawValue($parentProduct->getId(), $attributeName, $store->getId());
455
  }
456
 
457
+ if (!empty($shortDescriptionValue)) {
458
+ $descriptionsArray['short_description'] = $shortDescriptionValue;
459
+ break;
460
  }
461
+ }
462
+
463
+ // Check product description not null for each store
464
+ if ($descriptionType == 'long' && empty($descriptionsArray['description'])) {
465
+ continue;
466
+ } elseif ($descriptionType == 'short' && empty($descriptionsArray['short_description'])) {
467
+ continue;
468
+ }
469
+
470
+ // Add description when no store selected for language OR the store is selected for the langauge in storelangsMapping array
471
+ if ($storelangsMapping == null || $storelangsMapping[$localeCode] == "" || $storelangsMapping[$localeCode] == $store->getCode()) {
472
+ // Language *R
473
+ $descriptionXml = $productXml->addChild('Description');
474
+ $descriptionXml->addChild('Language', $language);
475
 
476
  // title
477
  if (isset($parentProduct)) {
494
  $this->_addCData($nestedDescriptionXml, $descriptionsArray['description'] . PHP_EOL . $descriptionsArray['short_description']);
495
  }
496
 
497
+ $this->_addAttributesXml($descriptionXml, $product, $store, $language);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
 
499
  array_push($addedLanguages, $language);
500
  $descriptionNodeCount++;
605
  return $productStr;
606
  }
607
 
608
+ protected function _getMappedProductAttributes($attributes, $product, $store = false)
609
+ {
610
+ // Check that at least one attribute is mapped
611
+ if (!$attributes = array_filter($attributes)) {
612
+ return array();
613
+ }
614
+
615
+ // Create a flat array of attribute names, and remove duplicates, and
616
+ // find the product's attribute values for each
617
+ $iterableAttributes = array_unique(call_user_func_array('array_merge', $attributes));
618
+
619
+ $foundAttributes = $this->_getAttributes(
620
+ $product,
621
+ $iterableAttributes,
622
+ $store ? $store->getId() : 0
623
+ );
624
+
625
+ // Map the attributes found for the product with the configured
626
+ // configured Fruugo attributes
627
+ $mappedAttributes = array();
628
+
629
+ foreach ($attributes as $key => $attributeNames) {
630
+ if (!$attributeNames) {
631
+ continue;
632
+ }
633
+
634
+ foreach ($attributeNames as $attributeName) {
635
+ // Skip if the attribute isn't defined for the product
636
+ if (!isset($foundAttributes[$attributeName])) {
637
+ continue;
638
+ }
639
+
640
+ $mappedAttributes[$key][$attributeName] = $foundAttributes[$attributeName];
641
+ }
642
+ }
643
+
644
+ return $mappedAttributes;
645
+ }
646
+
647
+ protected function _addAttributesXml($descriptionXml, $product, $store, $language)
648
+ {
649
+ $mappedAttributes = $this->_getMappedProductAttributes(
650
+ $this->attributes,
651
+ $product,
652
+ $store
653
+ );
654
+
655
+ foreach ($mappedAttributes as $attributeName => $attributes) {
656
+ $attributeText = $this->_getAttributesText(
657
+ $language,
658
+ array_keys($attributes)[0],
659
+ array_values($attributes)[0],
660
+ $store->getId()
661
+ );
662
+
663
+ if (!empty($attributeText)) {
664
+ $descriptionXml->addChild(
665
+ $attributeName,
666
+ htmlspecialchars($attributeText)
667
+ );
668
+ }
669
+ }
670
+ }
671
+
672
  protected function _convertCurrency($price, $baseCurrencyCode, $currencyCode)
673
  {
674
  if ($this->shouldConvertCurrency && $baseCurrencyCode != $currencyCode) {
684
 
685
  // This caches the options label translation for selectable product attributes.
686
  // They are cached by attribute name, language and then value.
687
+ protected function _getAttributesText($language, $attributeName, $optionId, $storeId = 0)
688
  {
689
  // These are the asset keys that are cached, you should only cache attributes
690
  // that have selectable items
697
  );
698
  }
699
 
700
+ // Creates attribute array if not present
701
  if (!isset($this->attributeMap[$attributeName])) {
702
+ $this->attributeMap[$attributeName] = array();
703
  }
704
 
705
+ // Creates language array if not present
706
  if (!isset($this->attributeMap[$attributeName][$language])) {
707
  $this->attributeMap[$attributeName][$language] = array();
708
  }
709
 
710
  if (!isset($this->attributeMap[$attributeName][$language][$optionId])) {
711
+ // Set the option value to null if it's not yet cached
712
  $this->attributeMap[$attributeName][$language][$optionId] = null;
713
  } else {
714
+ // Return the option value if it's already cached
715
  return $this->attributeMap[$attributeName][$language][$optionId];
716
  }
717
 
718
+ // Find the attribute id for given attribute name
719
+ $attributeId = Mage::getResourceModel('eav/entity_attribute')
720
+ ->getIdByCode('catalog_product', $attributeName);
721
+
722
+ // Find all availble attribute options for the given attribute and store id
723
  $collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
724
  ->setPositionOrder('asc')
725
  ->setAttributeFilter($attributeId)
727
  ->load()
728
  ->toOptionArray();
729
 
730
+ // Cache attribute values
731
  $found = false;
732
  foreach ($collection as $option) {
733
  if ($option['value'] == $optionId) {
737
  $this->attributeMap[$attributeName][$language][$option['value']] = $option['label'];
738
  }
739
 
740
+ // Return option id if option value isn't found
741
  if (!$found) {
742
  return $optionId;
743
  }
744
 
745
+ // Return the option value
746
  return $this->attributeMap[$attributeName][$language][$optionId];
747
  }
748
 
749
+ protected function _getAttributes($product, $attributes, $storeId = 0)
750
+ {
751
+ $attributeValues = [];
752
+
753
+ // Looping through $attributes arary rather than passing the it directly to getAttributeRawValue
754
+ // Reason is: getAttributeRawValue returns array if $attributes has more than 1 attribute, but it returns string
755
+ // if there is only on attribute in $attributes.
756
+ // This make sure we always got an array
757
+ foreach ($attributes as $attribute) {
758
+ $attributeValue = Mage::getResourceModel('catalog/product')
759
+ ->getAttributeRawValue($product->getId(), $attribute, $storeId);
760
+
761
+ $attributeValues[$attribute] = $attributeValue;
762
+ }
763
+
764
+ // This is a workaround for a bug in getAttributeRawValue which returns
765
+ // the wrong values for product attributes that have been modified in
766
+ // another site so to save db hits we try and get them and if they
767
+ // differ from the main product we are processing we then load them
768
+ // individually
769
+ foreach ($attributes as $name) {
770
+ if (isset($attributeValues[$name])
771
+ && ($product->getData($name) != $attributeValues[$name])) {
772
+ $attributeValues[$name] = Mage::getResourceModel('catalog/product')
773
+ ->getAttributeRawValue($product->getId(), $name, $storeId);
774
+ }
775
+ }
776
+
777
+ return $attributeValues;
778
+ }
779
+
780
  protected function _getProductImages($product, $parentProduct)
781
  {
782
  if (!isset($this->tempProductObj) || $this->tempProductObj === false) {
847
 
848
  protected function _shouldInclude($product)
849
  {
850
+ if ($product->getName() === null || $product->getPrice() === null) {
851
  return false;
852
  }
853
 
854
+ // Check if any mapped product desscription attribute is set
855
+ $hasDescription = null;
856
+ foreach (($this->descriptionAttributes['AttributeDescription'] ?: []) as $attributeName) {
857
+ if (!$product->getData($attributeName)) {
858
+ $hasDescription = false;
859
+ } else {
860
+ $hasDescription = true;
861
+ }
862
+ }
863
+
864
+ $parentProduct = null;
865
+ // If no description, check parent
866
+ if (!$hasDescription) {
867
+ $parentProduct = $this->_getParentProduct($product);
868
+
869
+ if (isset($parentProduct)) {
870
+ foreach (($this->descriptionAttributes['AttributeDescription'] ?: []) as $attributeName) {
871
+ $description = Mage::getResourceModel('catalog/product')
872
+ ->getAttributeRawValue($parentProduct->getId(), $attributeName, 0);
873
+
874
+ if (!empty($description)) {
875
+ $hasDescription = true;
876
+ } else {
877
+ $hasDescription = false;
878
+ }
879
+ }
880
+ }
881
  }
882
 
883
+ // Check if any mapped product short desscription attribute is set
884
+ $hasShortDescription = null;
885
+ foreach (($this->descriptionAttributes['AttributeShortDescription'] ?: []) as $attributeName) {
886
+ if (!$product->getData($attributeName)) {
887
+ $hasShortDescription = false;
888
+ } else {
889
+ $hasShortDescription = true;
890
+ }
891
+ }
892
+
893
+ // If no short description, check parent
894
+ if (!$hasShortDescription) {
895
+ $parentProduct = $parentProduct == null ? $this->_getParentProduct($product) : $parentProduct;
896
+
897
+ if (isset($parentProduct)) {
898
+ foreach (($this->descriptionAttributes['AttributeShortDescription'] ?: []) as $attributeName) {
899
+ $shortDescription = Mage::getResourceModel('catalog/product')
900
+ ->getAttributeRawValue($parentProduct->getId(), $attributeName, 0);
901
+
902
+ if (!empty($shortDescription)) {
903
+ $hasShortDescription = true;
904
+ } else {
905
+ $hasShortDescription = false;
906
+ }
907
+ }
908
+ }
909
+ }
910
+
911
+ return ($hasDescription || $hasShortDescription);
912
  }
913
 
914
  protected function _getParentProduct($product)
930
  {
931
  $node = dom_import_simplexml($xml);
932
  $no = $node->ownerDocument;
933
+ $node->appendChild($no->createCDATASection(htmlspecialchars($cdata_text, ENT_COMPAT | ENT_HTML401 | ENT_DISALLOWED)));
934
  return $xml;
935
  }
936
 
941
 
942
  protected function _writeReport($report)
943
  {
944
+ $reportsToWrite = array();
945
+
946
+ if (file_exists($this->reportPath)) {
947
+ $existingReportsArray = array();
948
+ $existingReport = json_decode(file_get_contents($this->reportPath));
949
+
950
+ if (!is_array($existingReport)) {
951
+ // If a report object has already been generated before, add it to a new array
952
+ array_push($existingReportsArray, $existingReport);
953
+ } else {
954
+ // Otherwise let the new array = existing reports array
955
+ $existingReportsArray = $existingReport;
956
+ }
957
+
958
+ $numberOfExistingReports = count($existingReportsArray);
959
+
960
+ if ($numberOfExistingReports == $this->NUMBER_OF_REPORTS_TO_KEEP) {
961
+ // Remove first report in array
962
+ array_shift($existingReportsArray);
963
+ } else if ($numberOfExistingReports > $this->NUMBER_OF_REPORTS_TO_KEEP) {
964
+ // If eixsting reports are more than number to keep (most likely NUMBER_OF_REPORTS_TO_KEEP is
965
+ // changed to a smaller value), remove the difference and add 1 in order to add one more report
966
+ $numOfReportsToRemoveBackwards = -$this->NUMBER_OF_REPORTS_TO_KEEP + 1;
967
+ // array_splice: If length is given and is negative then the sequence will stop that many elements from the end of the array
968
+ if ($numOfReportsToRemoveBackwards == 0) {
969
+ // Remove all
970
+ array_splice($existingReportsArray, 0);
971
+ } else {
972
+ array_splice($existingReportsArray, 0, $numOfReportsToRemoveBackwards);
973
+ }
974
+ }
975
+
976
+ // Add new report to array
977
+ array_push($existingReportsArray, $report);
978
+ $reportsToWrite = $existingReportsArray;
979
+ } else {
980
+ array_push($reportsToWrite, $report);
981
+ }
982
+
983
+ file_put_contents($this->reportPath, json_encode($reportsToWrite));
984
  }
985
 
986
  protected function _writeLog($message, $level = Fruugo_Integration_Helper_Logger::DEBUG)
999
  }
1000
 
1001
  $this->PAGE_SIZE = Mage::getStoreConfig('integration_options/products_options/export_page_size');
1002
+ $this->MAX_RESOURCES = Mage::getStoreConfig('integration_options/products_feed_advanced_options/max_resources_load');
1003
+ $this->SLEEP_TIME_SEC = Mage::getStoreConfig('integration_options/products_feed_advanced_options/sleep_time_sec');
1004
+ $this->MAX_ERRORS = Mage::getStoreConfig('integration_options/products_feed_advanced_options/max_errors');
1005
+ $this->NUMBER_OF_REPORTS_TO_KEEP = Mage::getStoreConfig('integration_options/products_feed_advanced_options/performance_reports_to_keep');
1006
 
1007
  $this->taxHelper = Mage::helper('tax');
1008
  $this->currencyConverter = Mage::helper('directory');
1026
  'time_paused_sec' => 0,
1027
  'xml_file_size_mb' => 0,
1028
  );
1029
+
1030
+ $this->_setupAttributeMappings();
1031
+ }
1032
+
1033
+ protected function _setupAttributeMappings()
1034
+ {
1035
+ // Check that attributes are configured
1036
+ if (Mage::getStoreConfig('integration_options/products_options/attribute_description') === null
1037
+ && Mage::getStoreConfig('integration_options/products_options/attribute_short_description') === null) {
1038
+ $this->_writeLog(
1039
+ 'Did not start Fruugo products export because attribute mappings have not been configured.',
1040
+ self::$WARNING
1041
+ );
1042
+
1043
+ die('FuugoMagentoProductsFeed: Attribute mappings not configured');
1044
+ }
1045
+
1046
+ // Create an array from all configuration values
1047
+ $this->attributes = array(
1048
+ 'AttributeColor' => $this->_getAttributeConfig('attribute_color'),
1049
+ 'AttributeSize' => $this->_getAttributeConfig('attribute_size'),
1050
+ 'Attribute1' => $this->_getAttributeConfig('attribute_1'),
1051
+ 'Attribute2' => $this->_getAttributeConfig('attribute_2'),
1052
+ 'Attribute3' => $this->_getAttributeConfig('attribute_3'),
1053
+ 'Attribute4' => $this->_getAttributeConfig('attribute_4'),
1054
+ 'Attribute5' => $this->_getAttributeConfig('attribute_5'),
1055
+ 'Attribute6' => $this->_getAttributeConfig('attribute_6'),
1056
+ 'Attribute7' => $this->_getAttributeConfig('attribute_7'),
1057
+ 'Attribute8' => $this->_getAttributeConfig('attribute_8'),
1058
+ 'Attribute9' => $this->_getAttributeConfig('attribute_9'),
1059
+ 'Attribute10' => $this->_getAttributeConfig('attribute_10')
1060
+ );
1061
+
1062
+ $this->descriptionAttributes = array(
1063
+ 'AttributeDescription' => $this->_getAttributeConfig('attribute_description'),
1064
+ 'AttributeShortDescription' => $this->_getAttributeConfig('attribute_short_description'),
1065
+ );
1066
+ $this->brandAttributes = $this->_getAttributeConfig('brand');
1067
+ $this->manufacturerAttributes = $this->_getAttributeConfig('manufacturer');
1068
+ $this->eanAttributes = $this->_getAttributeConfig('ean');
1069
+ $this->isbnAttributes = $this->_getAttributeConfig('isbn');
1070
+ }
1071
+
1072
+ protected function _getAttributeConfig($handle)
1073
+ {
1074
+ // Configuration path prefix
1075
+ $p = 'integration_options/products_options';
1076
+
1077
+ // Find the configuration value and deserealize into an array
1078
+ $attributes = unserialize(Mage::getStoreConfig("{$p}/{$handle}"));
1079
+
1080
+ // Sort the values by priority
1081
+ if (!$attributes) {
1082
+ return;
1083
+ }
1084
+
1085
+ usort($attributes, function ($a, $b) {
1086
+ return (int)$a['priority'] - (int)$b['priority'];
1087
+ });
1088
+
1089
+ return array_filter(array_map(function ($x) {
1090
+ return $x['attribute'];
1091
+ }, $attributes));
1092
  }
1093
 
1094
  // This monitors system resources and pauses execution if the utilisation is
1095
  // above the configured threshold.
1096
  // Recommended for systems with large numbers of products
1097
  // Note: this feature is not available on windows servers.
 
 
1098
  protected function checkServerLoad()
1099
  {
1100
  if (stristr(PHP_OS, 'win')) {
1115
  }
1116
 
1117
  $systemLoad = sys_getloadavg();
1118
+ if ($systemLoad[0] > $this->MAX_RESOURCES) {
1119
  $this->_writeLog(
1120
  'High server load detected. Usage of ' . $systemLoad[0] .
1121
  ' is greater than configured maximum of ' . $this->MAX_RESOURCES .
1130
  $this->_writeLog('Fruugo export resumed after waiting ' . $this->SLEEP_TIME_SEC . ' seconds.', self::$DEBUG);
1131
  }
1132
  }
 
 
1133
  }
app/code/community/Fruugo/Integration/Helper/ProductsFeedGeneratorProfiler.php CHANGED
@@ -61,7 +61,7 @@ class Fruugo_Integration_ProductsFeedGeneratorProfiler extends Fruugo_Integratio
61
  return $val;
62
  }
63
 
64
- protected function _getAttributesText($language, $attributeName, $optionId, $storeId) {
65
  $this->_startTimer('_getAttributesText');
66
  $val = parent::_getAttributesText($language, $attributeName, $optionId, $storeId);
67
  $this->_stopTimer('_getAttributesText');
@@ -337,4 +337,4 @@ class Fruugo_Integration_ProductsFeedGeneratorProfiler extends Fruugo_Integratio
337
 
338
  $this->_generateTree();
339
  }
340
- }
61
  return $val;
62
  }
63
 
64
+ protected function _getAttributesText($language, $attributeName, $optionId, $storeId = 0) {
65
  $this->_startTimer('_getAttributesText');
66
  $val = parent::_getAttributesText($language, $attributeName, $optionId, $storeId);
67
  $this->_stopTimer('_getAttributesText');
337
 
338
  $this->_generateTree();
339
  }
340
+ }
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/Attributes.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ class Fruugo_Integration_Model_Adminhtml_System_Config_Source_Attributes
24
+ {
25
+ protected $_options = array();
26
+ protected $excludeAttributeCodes = array();
27
+
28
+ public function toOptionArray($isMultiselect = false)
29
+ {
30
+ if (!$this->_options) {
31
+ $this->_options = $this->_getOptions();
32
+ }
33
+
34
+ $options = $this->_options;
35
+
36
+ if (!$isMultiselect) {
37
+ array_unshift($options, array(
38
+ 'value' => '',
39
+ 'label' => Mage::helper('adminhtml')->__('--Please Select--')
40
+ ));
41
+ }
42
+
43
+ return $options;
44
+ }
45
+
46
+ protected function _getOptions()
47
+ {
48
+ $options = array();
49
+
50
+ foreach (Mage::getResourceModel('catalog/product_attribute_collection')->loadData() as $attribute) {
51
+ if (!$attribute->getIsVisible()
52
+ || in_array($attribute->getAttributeCode(), $this->excludeAttributeCodes)) {
53
+ continue;
54
+ }
55
+
56
+ array_push($options, array(
57
+ 'value' => $attribute->getAttributeCode(),
58
+ 'label' => $attribute->getAttributeCode()
59
+ ));
60
+ }
61
+
62
+ return $options;
63
+ }
64
+ }
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/AttributesFiltered.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ class Fruugo_Integration_Model_Adminhtml_System_Config_Source_AttributesFiltered
24
+ extends Fruugo_Integration_Model_Adminhtml_System_Config_Source_Attributes
25
+ {
26
+ /**
27
+ * Constructor
28
+ *
29
+ * @return void
30
+ */
31
+ public function __construct()
32
+ {
33
+ $this->excludeAttributeCodes = array('size', 'shoe_size', 'color');
34
+ }
35
+ }
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/NumberOfPerformanceReports.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * NOTICE OF LICENSE
4
+ *
5
+ * Magento extension which extracts a product feed from Magento, imports the feed into Fruugo and uses the Fruugo Order API to export all Fruugo orders into Magento.
6
+ *
7
+ * Copyright (C) 2015 Fruugo.com Ltd
8
+ *
9
+ * This program is free software: you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation, either version 3 of the License, or
12
+ * (at your option) any later version.
13
+ *
14
+ * This program is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ * See the GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License along with this program.
20
+ * If not, see <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+ /**
24
+ * Used in creating options for Hour config value selection
25
+ *
26
+ */
27
+ class Fruugo_Integration_Model_Adminhtml_System_Config_Source_NumberOfPerformanceReports
28
+ {
29
+ /**
30
+ * Options getter
31
+ *
32
+ * @return array
33
+ */
34
+ public function toOptionArray()
35
+ {
36
+ $numOfPerformanceReports = array();
37
+
38
+ array_push($numOfPerformanceReports, array('value' => 1, 'label' => 1));
39
+ array_push($numOfPerformanceReports, array('value' => 5, 'label' => 5));
40
+ array_push($numOfPerformanceReports, array('value' => 10, 'label' => 10));
41
+ array_push($numOfPerformanceReports, array('value' => 15, 'label' => 15));
42
+ array_push($numOfPerformanceReports, array('value' => 20, 'label' => 20));
43
+
44
+ return $numOfPerformanceReports;
45
+ }
46
+ }
app/code/community/Fruugo/Integration/Model/AttributeMapping.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Fruugo_Integration_Model_AttributeMapping
4
+ extends Mage_Adminhtml_Model_System_Config_Backend_Serialized_Array
5
+ {
6
+ public function save()
7
+ {
8
+ $values = $this->getValue();
9
+ $session = Mage::getSingleton('core/session');
10
+
11
+ if (isset($values['__empty'])) {
12
+ unset($values['__empty']);
13
+ }
14
+
15
+ if (!sizeof($values)) {
16
+ return parent::save();
17
+ }
18
+
19
+ $usedPriorities = array();
20
+ $validMappings = array();
21
+
22
+ foreach ($values as $key => $value) {
23
+ if (in_array($value['priority'], $usedPriorities)) {
24
+ $session->addError('Attribute mapping priorities must be unique.');
25
+
26
+ continue;
27
+ }
28
+
29
+ $usedPriorities[] = $value['priority'];
30
+
31
+ if (empty($value['attribute'])) {
32
+ $session->addError('Attribute mapping attributes must not be blank.');
33
+
34
+ continue;
35
+ }
36
+
37
+ if (empty($value['priority'])) {
38
+ $session->addError('Attribute mapping priorities must not be blank.');
39
+
40
+ continue;
41
+ }
42
+
43
+ if (!is_numeric($value['priority']) || $value['priority'] < 1) {
44
+ $session->addError('Attribute mapping priorities be positive whole numbers.');
45
+
46
+ continue;
47
+ }
48
+
49
+ $validMappings[$key] = $value;
50
+ }
51
+
52
+ $this->setValue($validMappings);
53
+
54
+ return parent::save();
55
+ }
56
+ }
app/code/community/Fruugo/Integration/Model/Observer.php CHANGED
@@ -29,6 +29,12 @@ use \DOMXpath as DOMXpath;
29
 
30
  class Fruugo_Integration_Model_Observer
31
  {
 
 
 
 
 
 
32
  public function beforeSaveOrder(Varien_Event_Observer $observer)
33
  {
34
  try {
@@ -36,34 +42,44 @@ class Fruugo_Integration_Model_Observer
36
  $order = $event->getOrder();
37
  $fruugoId = $order->getFruugoOrderId();
38
 
39
- if (!empty($fruugoId) && $fruugoId !== null) {
40
- $existingStatus = null;
41
- $orderId = $order->getId();
42
- if (!empty($orderId) && $orderId !== 0) {
43
- $origData = $order->getOrigData();
44
- }
 
 
 
 
 
 
 
45
  }
46
  } catch (Exception $ex) {
47
  Mage::logException($ex);
 
 
48
  }
49
  }
50
 
51
- public function orderInvoiced(Varien_Event_Observer $observer)
52
  {
53
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
 
54
  try {
55
  $event = $observer->getEvent();
56
  $invoice = $observer->getEvent()->getInvoice();
57
  $order = $invoice->getOrder();
58
  $fruugoId = $order->getFruugoOrderId();
59
 
60
- if (!empty($fruugoId) && $fruugoId !== null) {
61
  $data = array();
62
  $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
63
 
64
  $postFields = 'orderId='.$fruugoId;
65
 
66
- Fruugo_Integration_Helper_Logger::log("Invoice created for Fruugo order {$fruugoId}");
67
 
68
  $itemsInvoiced = $invoice->getAllItems();
69
  foreach ($itemsInvoiced as $invoiceItem) {
@@ -89,38 +105,41 @@ class Fruugo_Integration_Model_Observer
89
  list($httpcode, $response) = $this->_sendToApi($apiUrl, $data);
90
 
91
  if ($httpcode == 200) {
92
- $this->_saveHistoryComment($order, "Sent confirmation to Fruugo of order {$fruugoId}. Details: {$postFields}.");
93
- Fruugo_Integration_Helper_Logger::log("Sent confirmation to Fruugo of order {$fruugoId}. Details: {$postFields}.");
 
 
94
  } else {
95
- $this->_saveHistoryComment($order, "Failed to send notification to Fruugo of confirmation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
96
- Fruugo_Integration_Helper_Logger::log("Failed to send notification to Fruugo of confirmation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
 
97
  }
98
  }
99
-
100
  } catch (Exception $ex) {
101
  Mage::logException($ex);
 
 
 
102
  }
103
  }
104
 
105
- public function orderCancelled(Varien_Event_Observer $observer)
106
  {
107
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
108
  try {
109
- $event = $observer->getEvent();
110
- $orderItem = $event->getItem();
111
- $order = $orderItem->getOrder();
112
- $fruugoId = $order->getFruugoOrderId();
113
 
114
- if (!empty($fruugoId) && $fruugoId !== null) {
115
- $data = array();
116
- $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
117
 
 
118
  if ($devMode == '1') {
119
- $apiUrl = Mage::getStoreConfig('integration_options/orders_options/order_api_url');
120
-
121
  if (strpos($apiUrl, '127.0.0.1')) {
122
- $data['mock_api_operation'] = 'cancel';
123
  $data['orderId'] = $fruugoId;
 
 
124
  }
125
  }
126
 
@@ -141,19 +160,21 @@ class Fruugo_Integration_Model_Observer
141
 
142
  if ($httpcode == 200) {
143
  $this->_saveHistoryComment($order, "Sent notification to Fruugo of cancellation of order {$fruugoId}. Details: {$postFields}");
144
- Fruugo_Integration_Helper_Logger::log("Sent notification to Fruugo of cancellation of order {$fruugoId}. Details: {$postFields}");
145
  } else {
146
- $this->_saveHistoryComment($order, "Failed to send notification to Fruugo of cancellation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
147
- Fruugo_Integration_Helper_Logger::log("Failed to send notification to Fruugo of cancellation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
 
148
  }
149
  }
150
-
151
  } catch (Exception $ex) {
152
  Mage::logException($ex);
 
 
153
  }
154
  }
155
 
156
- public function orderShipment(Varien_Event_Observer $observer)
157
  {
158
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
159
 
@@ -258,10 +279,12 @@ class Fruugo_Integration_Model_Observer
258
 
259
  if ($orders->length == 0) {
260
  $this->_saveHistoryComment($order, "Fruugo returned invalid data for shipped order $fruugoId. Response: $shippedOrder");
261
- Fruugo_Integration_Helper_Logger::log("Fruugo returned invalid data for shipped order $fruugoId. Response: $shippedOrder");
 
 
262
  } else {
263
  $this->_saveHistoryComment($order, "Sent notification to Fruugo of shipment of order $fruugoId");
264
- Fruugo_Integration_Helper_Logger::log("Sent notification to Fruugo of shipment of order $fruugoId");
265
  foreach ($orders as $orderXml) {
266
  $orderArray = $ordersFeedProcessor->convertXmlToArray($orderXml);
267
  foreach ($orderArray['shipments'] as $shipment) {
@@ -271,50 +294,56 @@ class Fruugo_Integration_Model_Observer
271
  'created_at' => date_format(new DateTime('NOW'), 'Y-m-d H:i:s'));
272
 
273
  $model = Mage::getModel('integration/shipment')->setData($data);
274
- try {
275
- $insertId = $model->save()->getId();
276
- Fruugo_Integration_Helper_Logger::log("Saved shipment info of Fruugo order {$fruugoId}. ShipmentId: {$shipmentId}");
277
- } catch (Exception $ex) {
278
- Mage::logException($ex);
279
- }
280
  }
281
  }
282
  }
283
- Fruugo_Integration_Helper_Logger::log("Sent notification to Fruugo of shipment of order {$fruugoId}. Details: {$postFields}");
 
284
  } else {
285
- Fruugo_Integration_Helper_Logger::log("Failed to send notification to Fruugo of shipment of order {$fruugoId}. Server response code: {$httpcode}. Response message: {$response}");
 
 
286
  }
287
  }
288
  } catch (Exception $ex) {
289
  Mage::logException($ex);
 
 
 
290
  }
291
  }
292
 
293
- public function orderRefunded(Varien_Event_Observer $observer)
294
  {
295
- $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
296
  try {
297
- $event = $observer->getEvent();
298
- $creditmemo = $event->getCreditmemo();
299
- $order = $creditmemo->getOrder();
300
- $fruugoId = $order->getFruugoOrderId();
301
 
302
- if (!empty($fruugoId) && $fruugoId !== null) {
303
- $data = array();
304
- $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
305
 
306
- if ($devMode == '1') {
307
- $apiUrl = Mage::getStoreConfig('integration_options/orders_options/order_api_url');
 
308
 
309
- if (strpos($apiUrl, '127.0.0.1')) {
310
- $data['mock_api_operation'] = 'return';
311
- $data['orderId'] = $fruugoId;
312
- }
 
 
313
  }
 
314
 
315
- $apiUrl .= '/return';
316
- $postFields = 'orderId='.$fruugoId;
 
 
317
  $itemsRefunded = $creditmemo->getAllItems();
 
318
  foreach ($itemsRefunded as $refundedItem) {
319
  $orderItem = $refundedItem->getOrderItem();
320
  $itemInfo = $orderItem->getFruugoProductId().','
@@ -323,20 +352,24 @@ class Fruugo_Integration_Model_Observer
323
 
324
  $postFields .= '&item=' . $itemInfo;
325
  }
 
326
 
327
- $data['postFields'] = $postFields;
328
- list($httpcode, $response) = $this->_sendToApi($apiUrl, $data);
329
-
330
- if ($httpcode == 200) {
331
- $this->_saveHistoryComment($order, "Sent return notification to Fruugo of order {$fruugoId}. Details: {$postFields}");
332
- Fruugo_Integration_Helper_Logger::log("Sent return notification to Fruugo of order {$fruugoId}. Details: {$postFields}");
333
- } else {
334
- $this->_saveHistoryComment($order, "Failed to send notification to Fruugo of return of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
335
- Fruugo_Integration_Helper_Logger::log("Failed to send notification to Fruugo of return of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}");
336
- }
 
337
  }
338
  } catch (Exception $ex) {
339
  Mage::logException($ex);
 
 
340
  }
341
  }
342
 
@@ -383,4 +416,9 @@ class Fruugo_Integration_Model_Observer
383
 
384
  return array($httpcode, $response);
385
  }
 
 
 
 
 
386
  }
29
 
30
  class Fruugo_Integration_Model_Observer
31
  {
32
+ protected static $ALWAYS = Fruugo_Integration_Helper_Logger::ALWAYS;
33
+ protected static $ERROR = Fruugo_Integration_Helper_Logger::ERROR;
34
+ protected static $WARNING = Fruugo_Integration_Helper_Logger::WARNING;
35
+ protected static $INFO = Fruugo_Integration_Helper_Logger::INFO;
36
+ protected static $DEBUG = Fruugo_Integration_Helper_Logger::DEBUG;
37
+
38
  public function beforeSaveOrder(Varien_Event_Observer $observer)
39
  {
40
  try {
42
  $order = $event->getOrder();
43
  $fruugoId = $order->getFruugoOrderId();
44
 
45
+ if (empty($fruugoId) && $fruugoId === null) {
46
+ $message = "Cannot process order, no fruugoId found for order: " . $order->getId();
47
+ $this->_writeLog($message, self::$ERROR);
48
+ throw new Exception($message);
49
+ }
50
+
51
+ if ($order->getStatus() == 'canceled') {
52
+ $this->beforeCancelOrder($order, $fruugoId);
53
+ }
54
+
55
+ // When creating credit memo, order status changes to "processing"
56
+ if ($order->getStatus() == 'processing') {
57
+ $this->beforeSaveRefund($order, $fruugoId);
58
  }
59
  } catch (Exception $ex) {
60
  Mage::logException($ex);
61
+ Mage::getSingleton('core/session')->addError($ex->getMessage());
62
+ throw $ex;
63
  }
64
  }
65
 
66
+ public function beforeSaveInvoice(Varien_Event_Observer $observer)
67
  {
68
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
69
+
70
  try {
71
  $event = $observer->getEvent();
72
  $invoice = $observer->getEvent()->getInvoice();
73
  $order = $invoice->getOrder();
74
  $fruugoId = $order->getFruugoOrderId();
75
 
76
+ if (!empty($fruugoId) && $fruugoId !== null && $order->getStatus() == 'pending') {
77
  $data = array();
78
  $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
79
 
80
  $postFields = 'orderId='.$fruugoId;
81
 
82
+ $this->_writeLog("Creating invoice Fruugo order {$fruugoId}", self::$ALWAYS);
83
 
84
  $itemsInvoiced = $invoice->getAllItems();
85
  foreach ($itemsInvoiced as $invoiceItem) {
105
  list($httpcode, $response) = $this->_sendToApi($apiUrl, $data);
106
 
107
  if ($httpcode == 200) {
108
+ $message = "Sent confirmation to Fruugo of order {$fruugoId}. Details: {$postFields}.";
109
+ $this->_saveHistoryComment($order, $message);
110
+ $this->_writeLog("Invoice created for Fruugo order {$fruugoId}", self::$ALWAYS);
111
+ $this->_writeLog($message, self::$INFO);
112
  } else {
113
+ $message = "Failed to send notification to Fruugo of confirmation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}";
114
+ $this->_writeLog($message, self::$ERROR);
115
+ throw new Exception($message);
116
  }
117
  }
 
118
  } catch (Exception $ex) {
119
  Mage::logException($ex);
120
+ // Throw exception to stop invoice/order save
121
+ Mage::getSingleton('core/session')->addError($message);
122
+ throw $ex;
123
  }
124
  }
125
 
126
+ public function beforeCancelOrder($order, $fruugoId)
127
  {
128
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
129
  try {
130
+ $data = array();
131
+ $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
 
 
132
 
133
+ if ($devMode == '1') {
134
+ $apiUrl = Mage::getStoreConfig('integration_options/orders_options/order_api_url');
135
+ }
136
 
137
+ foreach ($order->getAllItems() as $orderItem) {
138
  if ($devMode == '1') {
 
 
139
  if (strpos($apiUrl, '127.0.0.1')) {
 
140
  $data['orderId'] = $fruugoId;
141
+ $data['mock_api_operation'] = 'cancel_item';
142
+ $data['fruugoProductId'] = $orderItem->getFruugoProductId();
143
  }
144
  }
145
 
160
 
161
  if ($httpcode == 200) {
162
  $this->_saveHistoryComment($order, "Sent notification to Fruugo of cancellation of order {$fruugoId}. Details: {$postFields}");
163
+ $this->_writeLog("Sent notification to Fruugo of cancellation of order {$fruugoId}. Details: {$postFields}", self::$ALWAYS);
164
  } else {
165
+ $message = "Failed to send notification to Fruugo of cancellation of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}";
166
+ $this->_writeLog($message, self::$ERROR);
167
+ throw new Exception($message);
168
  }
169
  }
 
170
  } catch (Exception $ex) {
171
  Mage::logException($ex);
172
+ // Throw exception to stop invoice/order save
173
+ throw $ex;
174
  }
175
  }
176
 
177
+ public function beforeSaveShipment(Varien_Event_Observer $observer)
178
  {
179
  $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
180
 
279
 
280
  if ($orders->length == 0) {
281
  $this->_saveHistoryComment($order, "Fruugo returned invalid data for shipped order $fruugoId. Response: $shippedOrder");
282
+ $message = "Fruugo returned invalid data for shipped order $fruugoId. Response: $shippedOrder";
283
+ $this->_writeLog($message, self::$WARNING);
284
+ throw new Exception($message);
285
  } else {
286
  $this->_saveHistoryComment($order, "Sent notification to Fruugo of shipment of order $fruugoId");
287
+ $this->_writeLog("Sent notification to Fruugo of shipment of order $fruugoId", self::$INFO);
288
  foreach ($orders as $orderXml) {
289
  $orderArray = $ordersFeedProcessor->convertXmlToArray($orderXml);
290
  foreach ($orderArray['shipments'] as $shipment) {
294
  'created_at' => date_format(new DateTime('NOW'), 'Y-m-d H:i:s'));
295
 
296
  $model = Mage::getModel('integration/shipment')->setData($data);
297
+ $insertId = $model->save()->getId();
298
+ $this->_writeLog("Saved shipment info of Fruugo order {$fruugoId}. ShipmentId: {$shipmentId}", self::$INFO);
 
 
 
 
299
  }
300
  }
301
  }
302
+
303
+ $this->_writeLog("Sent notification to Fruugo of shipment of order {$fruugoId}. Details: {$postFields}", self::$ALWAYS);
304
  } else {
305
+ $message = "Failed to send notification to Fruugo of shipment of order {$fruugoId}. Server response code: {$httpcode}. Response message: {$response}";
306
+ $this->_writeLog($message, self::$ERROR);
307
+ throw new Exception($message);
308
  }
309
  }
310
  } catch (Exception $ex) {
311
  Mage::logException($ex);
312
+ // Throw exception to stop shipment/order save
313
+ Mage::getSingleton('core/session')->addError($ex->getMessage());
314
+ throw $ex;
315
  }
316
  }
317
 
318
+ public function beforeSaveRefund($order, $fruugoId)
319
  {
 
320
  try {
321
+ $creditMemos = Mage::getResourceModel('sales/order_creditmemo_collection')
322
+ ->addFieldToFilter('order_id', $order->getId());
 
 
323
 
324
+ if (count($creditMemos) == 0) {
325
+ return;
326
+ }
327
 
328
+ $apiUrl = Fruugo_Integration_Helper_Defines::FRUUGO_ORDERS_ENDPOINT;
329
+ $data = array();
330
+ $devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
331
 
332
+ if ($devMode == '1') {
333
+ $apiUrl = Mage::getStoreConfig('integration_options/orders_options/order_api_url');
334
+
335
+ if (strpos($apiUrl, '127.0.0.1')) {
336
+ $data['mock_api_operation'] = 'return';
337
+ $data['orderId'] = $fruugoId;
338
  }
339
+ }
340
 
341
+ $apiUrl .= '/return';
342
+ $postFields = 'orderId='.$fruugoId;
343
+
344
+ foreach ($creditMemos as $creditmemo) {
345
  $itemsRefunded = $creditmemo->getAllItems();
346
+
347
  foreach ($itemsRefunded as $refundedItem) {
348
  $orderItem = $refundedItem->getOrderItem();
349
  $itemInfo = $orderItem->getFruugoProductId().','
352
 
353
  $postFields .= '&item=' . $itemInfo;
354
  }
355
+ }
356
 
357
+ $postFields .= '&returnReason=other';
358
+ $data['postFields'] = $postFields;
359
+ list($httpcode, $response) = $this->_sendToApi($apiUrl, $data);
360
+
361
+ if ($httpcode == 200) {
362
+ $this->_saveHistoryComment($order, "Sent return notification to Fruugo of order {$fruugoId}. Details: {$postFields}");
363
+ $this->_writeLog("Sent return notification to Fruugo of order {$fruugoId}. Details: {$postFields}", self::$ALWAYS);
364
+ } else {
365
+ $message = "Failed to send notification to Fruugo of return of order {$fruugoId}. Server response code: {$httpcode}, response message: {$response}";
366
+ $this->_writeLog($message, self::$ERROR);
367
+ throw new Exception($message);
368
  }
369
  } catch (Exception $ex) {
370
  Mage::logException($ex);
371
+ // Throw exception to stop invoice/order save
372
+ throw $ex;
373
  }
374
  }
375
 
416
 
417
  return array($httpcode, $response);
418
  }
419
+
420
+ protected function _writeLog($message, $level = Fruugo_Integration_Helper_Logger::DEBUG)
421
+ {
422
+ Fruugo_Integration_Helper_Logger::log($message, $level);
423
+ }
424
  }
app/code/community/Fruugo/Integration/controllers/ProductsController.php CHANGED
@@ -125,4 +125,18 @@ class Fruugo_Integration_ProductsController extends Mage_Core_Controller_Front_A
125
  Fruugo_Integration_Helper_FruugoCountriesSeeder::getFruugoCountries();
126
  $this->_redirectReferer();
127
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
125
  Fruugo_Integration_Helper_FruugoCountriesSeeder::getFruugoCountries();
126
  $this->_redirectReferer();
127
  }
128
+
129
+ public function performanceReportAction()
130
+ {
131
+ $reportPath = Mage::getModuleDir('', 'Fruugo_Integration') . '/controllers/report.json';
132
+
133
+ if (!file_exists($reportPath)) {
134
+ die('No performance report has been generated.');
135
+ }
136
+
137
+ $json = file_get_contents($reportPath);
138
+ header('Content-disposition: attachment; filename=performance_report.json');
139
+ header('Content-type: application/json');
140
+ echo $json;
141
+ }
142
  }
app/code/community/Fruugo/Integration/data/fruugo_attributes_setup/data-install-0.1.1.php CHANGED
@@ -26,6 +26,24 @@ require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/FruugoCount
26
 
27
  try {
28
  Fruugo_Integration_Helper_Logger::log('Running Fruugo plugin data script.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  // create a new store for Fruugo orders
30
  Mage::registry('isSecureArea');
31
  $website_id = Mage::app()->getWebsite()->getId();
26
 
27
  try {
28
  Fruugo_Integration_Helper_Logger::log('Running Fruugo plugin data script.');
29
+ // clear left files from last installation
30
+ $filesToRemove = array(
31
+ '/controllers/products.lock',
32
+ '/controllers/report.json',
33
+ '/controllers/tmp_products.xml',
34
+ '/controllers/products.xml',
35
+ '/controllers/.DS_Store',
36
+ '/'. Fruugo_Integration_Helper_Defines::FRUUGO_COUNTRIES_FILE_NAME,
37
+ );
38
+
39
+ foreach ($filesToRemove as $filename) {
40
+ $file = Mage::getModuleDir('', 'Fruugo_Integration') . $filename;
41
+
42
+ if (file_exists($file)) {
43
+ unlink($file);
44
+ }
45
+ }
46
+
47
  // create a new store for Fruugo orders
48
  Mage::registry('isSecureArea');
49
  $website_id = Mage::app()->getWebsite()->getId();
app/code/community/Fruugo/Integration/etc/config.xml CHANGED
@@ -24,7 +24,7 @@
24
  <config>
25
  <modules>
26
  <Fruugo_Integration>
27
- <version>1.0.9</version> <!-- Version number of your module -->
28
  </Fruugo_Integration>
29
  </modules>
30
  <global>
@@ -146,14 +146,17 @@
146
  <export_frequency>12</export_frequency>
147
  <descrption_type>long</descrption_type>
148
  <export_page_size>100</export_page_size>
149
- <max_resources_load>0.5</max_resources_load>
150
- <sleep_time_sec>20</sleep_time_sec>
151
- <max_errors>30</max_errors>
152
  <track_last_id>1</track_last_id>
153
  </products_options>
154
  <orders_options>
155
  <fetch_frequency>4</fetch_frequency>
156
  </orders_options>
 
 
 
 
 
 
157
  </integration_options>
158
  </default>
159
  <crontab>
24
  <config>
25
  <modules>
26
  <Fruugo_Integration>
27
+ <version>1.1.2</version> <!-- Version number of your module -->
28
  </Fruugo_Integration>
29
  </modules>
30
  <global>
146
  <export_frequency>12</export_frequency>
147
  <descrption_type>long</descrption_type>
148
  <export_page_size>100</export_page_size>
 
 
 
149
  <track_last_id>1</track_last_id>
150
  </products_options>
151
  <orders_options>
152
  <fetch_frequency>4</fetch_frequency>
153
  </orders_options>
154
+ <products_feed_advanced_options>
155
+ <max_resources_load>0.5</max_resources_load>
156
+ <sleep_time_sec>20</sleep_time_sec>
157
+ <max_errors>30</max_errors>
158
+ <performance_reports_to_keep>10</performance_reports_to_keep>
159
+ </products_feed_advanced_options>
160
  </integration_options>
161
  </default>
162
  <crontab>
app/code/community/Fruugo/Integration/etc/system.xml CHANGED
@@ -113,7 +113,7 @@
113
  <label>Langage Store Mapping</label>
114
  <frontend_model>Fruugo_Integration_Block_Languagestoremapping</frontend_model>
115
  <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
116
- <sort_order>50</sort_order>
117
  <show_in_default>1</show_in_default>
118
  <show_in_website>1</show_in_website>
119
  <show_in_store>1</show_in_store>
@@ -123,42 +123,192 @@
123
  <label>Export Page Size</label>
124
  <frontend_type>select</frontend_type>
125
  <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_ExportPageSize</source_model>
126
- <sort_order>60</sort_order>
127
  <show_in_default>1</show_in_default>
128
  <show_in_website>0</show_in_website>
129
  <show_in_store>0</show_in_store>
130
  <comment>The number of products to process and write to xml per batch.</comment>
131
  </export_page_size>
132
- <max_resources_load translate="label">
133
- <label>Max Resources</label>
134
- <frontend_type>select</frontend_type>
135
- <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_MaxResourcesLoad</source_model>
 
 
 
 
 
 
 
 
 
 
136
  <sort_order>70</sort_order>
137
  <show_in_default>1</show_in_default>
138
- <show_in_website>0</show_in_website>
139
- <show_in_store>0</show_in_store>
140
- <comment>Maximum average system load (the number of processes in the system run queue, based on /proc/loadavg) allowed over the last minute, not available on Windows Servers because php sys_getloadavg() function is not implemented on Windows platforms.</comment>
141
- </max_resources_load>
142
- <sleep_time_sec translate="label">
143
- <label>Sleep time seconds</label>
144
- <frontend_type>select</frontend_type>
145
- <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_SleepTimeSec</source_model>
146
  <sort_order>80</sort_order>
147
  <show_in_default>1</show_in_default>
148
- <show_in_website>0</show_in_website>
149
- <show_in_store>0</show_in_store>
150
- <comment>Time to sleep for if over load limit during products export</comment>
151
- </sleep_time_sec>
152
- <max_errors translate="label">
153
- <label>Max Errors Allowed</label>
154
- <frontend_type>select</frontend_type>
155
- <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_MaxErrors</source_model>
156
  <sort_order>90</sort_order>
157
  <show_in_default>1</show_in_default>
158
- <show_in_website>0</show_in_website>
159
- <show_in_store>0</show_in_store>
160
- <comment>The number of errors after which the exporting products process will abort, set to -1 to disable</comment>
161
- </max_errors>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  </fields>
163
  </products_options>
164
  <orders_options translate="label" module="integration">
@@ -221,14 +371,65 @@
221
  <comment>Fruugo Orders Api for testing</comment>
222
  </order_api_url>
223
  <orders_endpoint_last_checked translate="label">
224
- <frontend_type>hidden</frontend_type>
 
225
  <sort_order>70</sort_order>
226
  <show_in_default>1</show_in_default>
227
  <show_in_website>1</show_in_website>
228
  <show_in_store>1</show_in_store>
 
229
  </orders_endpoint_last_checked>
230
  </fields>
231
  </orders_options>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  </groups>
233
  </integration_options>
234
  </sections>
113
  <label>Langage Store Mapping</label>
114
  <frontend_model>Fruugo_Integration_Block_Languagestoremapping</frontend_model>
115
  <backend_model>adminhtml/system_config_backend_serialized_array</backend_model>
116
+ <sort_order>40</sort_order>
117
  <show_in_default>1</show_in_default>
118
  <show_in_website>1</show_in_website>
119
  <show_in_store>1</show_in_store>
123
  <label>Export Page Size</label>
124
  <frontend_type>select</frontend_type>
125
  <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_ExportPageSize</source_model>
126
+ <sort_order>50</sort_order>
127
  <show_in_default>1</show_in_default>
128
  <show_in_website>0</show_in_website>
129
  <show_in_store>0</show_in_store>
130
  <comment>The number of products to process and write to xml per batch.</comment>
131
  </export_page_size>
132
+ <attribute_description translate="label">
133
+ <label>Description Mapping</label>
134
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
135
+ <backend_model>integration/AttributeMapping</backend_model>
136
+ <sort_order>60</sort_order>
137
+ <show_in_default>1</show_in_default>
138
+ <show_in_website>1</show_in_website>
139
+ <show_in_store>1</show_in_store>
140
+ <comment>Select attributes which describes the description of an item</comment>
141
+ </attribute_description>
142
+ <attribute_short_description translate="label">
143
+ <label>Short Description Mapping</label>
144
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
145
+ <backend_model>integration/AttributeMapping</backend_model>
146
  <sort_order>70</sort_order>
147
  <show_in_default>1</show_in_default>
148
+ <show_in_website>1</show_in_website>
149
+ <show_in_store>1</show_in_store>
150
+ <comment>Select attributes which describes the short description of an item</comment>
151
+ </attribute_short_description>
152
+ <attribute_size translate="label">
153
+ <label>Size Attribute Mapping</label>
154
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
155
+ <backend_model>integration/AttributeMapping</backend_model>
156
  <sort_order>80</sort_order>
157
  <show_in_default>1</show_in_default>
158
+ <show_in_website>1</show_in_website>
159
+ <show_in_store>1</show_in_store>
160
+ <comment>Select attributes which describes the size of an item</comment>
161
+ </attribute_size>
162
+ <attribute_color translate="label">
163
+ <label>Color Attribute Mapping</label>
164
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
165
+ <backend_model>integration/AttributeMapping</backend_model>
166
  <sort_order>90</sort_order>
167
  <show_in_default>1</show_in_default>
168
+ <show_in_website>1</show_in_website>
169
+ <show_in_store>1</show_in_store>
170
+ <comment>Select attributes which describes the color of an item</comment>
171
+ </attribute_color>
172
+ <brand translate="label">
173
+ <label>Brand Attribute Mapping</label>
174
+ <frontend_model>Fruugo_Integration_Block_AttributeMappingFiltered</frontend_model>
175
+ <backend_model>integration/AttributeMapping</backend_model>
176
+ <sort_order>100</sort_order>
177
+ <show_in_default>1</show_in_default>
178
+ <show_in_website>1</show_in_website>
179
+ <show_in_store>1</show_in_store>
180
+ <comment>Select attributes which describes the brand of an item</comment>
181
+ </brand>
182
+ <manufacturer translate="label">
183
+ <label>Manufacturer Attribute Mapping</label>
184
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
185
+ <backend_model>integration/AttributeMapping</backend_model>
186
+ <sort_order>110</sort_order>
187
+ <show_in_default>1</show_in_default>
188
+ <show_in_website>1</show_in_website>
189
+ <show_in_store>1</show_in_store>
190
+ <comment>Select attributes which describes the manufacturer of an item</comment>
191
+ </manufacturer>
192
+ <ean translate="label">
193
+ <label>EAN</label>
194
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
195
+ <backend_model>integration/AttributeMapping</backend_model>
196
+ <sort_order>120</sort_order>
197
+ <show_in_default>1</show_in_default>
198
+ <show_in_website>1</show_in_website>
199
+ <show_in_store>1</show_in_store>
200
+ <comment>Select attributes which describes the manufacturer of an item</comment>
201
+ </ean>
202
+ <isbn translate="label">
203
+ <label>ISBN</label>
204
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
205
+ <backend_model>integration/AttributeMapping</backend_model>
206
+ <sort_order>140</sort_order>
207
+ <show_in_default>1</show_in_default>
208
+ <show_in_website>1</show_in_website>
209
+ <show_in_store>1</show_in_store>
210
+ <comment>Select attributes which describes the manufacturer of an item</comment>
211
+ </isbn>
212
+ <attribute_1 translate="label">
213
+ <label>Custom Attribute 1 Mapping</label>
214
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
215
+ <backend_model>integration/AttributeMapping</backend_model>
216
+ <sort_order>150</sort_order>
217
+ <show_in_default>1</show_in_default>
218
+ <show_in_website>1</show_in_website>
219
+ <show_in_store>1</show_in_store>
220
+ <comment>Select attributes which describes the custom attribute 1 of an item</comment>
221
+ </attribute_1>
222
+ <attribute_2 translate="label">
223
+ <label>Custom Attribute 2 Mapping</label>
224
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
225
+ <backend_model>integration/AttributeMapping</backend_model>
226
+ <sort_order>160</sort_order>
227
+ <show_in_default>1</show_in_default>
228
+ <show_in_website>1</show_in_website>
229
+ <show_in_store>1</show_in_store>
230
+ <comment>Select attributes which describes the custom attribute 2 of an item</comment>
231
+ </attribute_2>
232
+ <attribute_3 translate="label">
233
+ <label>Custom Attribute 3 Mapping</label>
234
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
235
+ <backend_model>integration/AttributeMapping</backend_model>
236
+ <sort_order>170</sort_order>
237
+ <show_in_default>1</show_in_default>
238
+ <show_in_website>1</show_in_website>
239
+ <show_in_store>1</show_in_store>
240
+ <comment>Select attributes which describes the custom attribute 3 of an item</comment>
241
+ </attribute_3>
242
+ <attribute_4 translate="label">
243
+ <label>Custom Attribute 4 Mapping</label>
244
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
245
+ <backend_model>integration/AttributeMapping</backend_model>
246
+ <sort_order>180</sort_order>
247
+ <show_in_default>1</show_in_default>
248
+ <show_in_website>1</show_in_website>
249
+ <show_in_store>1</show_in_store>
250
+ <comment>Select attributes which describes the custom attribute 4 of an item</comment>
251
+ </attribute_4>
252
+ <attribute_5 translate="label">
253
+ <label>Custom Attribute 5 Mapping</label>
254
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
255
+ <backend_model>integration/AttributeMapping</backend_model>
256
+ <sort_order>190</sort_order>
257
+ <show_in_default>1</show_in_default>
258
+ <show_in_website>1</show_in_website>
259
+ <show_in_store>1</show_in_store>
260
+ <comment>Select attributes which describes the custom attribute 5 of an item</comment>
261
+ </attribute_5>
262
+ <attribute_6 translate="label">
263
+ <label>Custom Attribute 6 Mapping</label>
264
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
265
+ <backend_model>integration/AttributeMapping</backend_model>
266
+ <sort_order>200</sort_order>
267
+ <show_in_default>1</show_in_default>
268
+ <show_in_website>1</show_in_website>
269
+ <show_in_store>1</show_in_store>
270
+ <comment>Select attributes which describes the custom attribute 6 of an item</comment>
271
+ </attribute_6>
272
+ <attribute_7 translate="label">
273
+ <label>Custom Attribute 7 Mapping</label>
274
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
275
+ <backend_model>integration/AttributeMapping</backend_model>
276
+ <sort_order>210</sort_order>
277
+ <show_in_default>1</show_in_default>
278
+ <show_in_website>1</show_in_website>
279
+ <show_in_store>1</show_in_store>
280
+ <comment>Select attributes which describes the custom attribute 7 of an item</comment>
281
+ </attribute_7>
282
+ <attribute_8 translate="label">
283
+ <label>Custom Attribute 8 Mapping</label>
284
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
285
+ <backend_model>integration/AttributeMapping</backend_model>
286
+ <sort_order>220</sort_order>
287
+ <show_in_default>1</show_in_default>
288
+ <show_in_website>1</show_in_website>
289
+ <show_in_store>1</show_in_store>
290
+ <comment>Select attributes which describes the custom attribute 8 of an item</comment>
291
+ </attribute_8>
292
+ <attribute_9 translate="label">
293
+ <label>Custom Attribute 9 Mapping</label>
294
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
295
+ <backend_model>integration/AttributeMapping</backend_model>
296
+ <sort_order>240</sort_order>
297
+ <show_in_default>1</show_in_default>
298
+ <show_in_website>1</show_in_website>
299
+ <show_in_store>1</show_in_store>
300
+ <comment>Select attributes which describes the custom attribute 9 of an item</comment>
301
+ </attribute_9>
302
+ <attribute_10 translate="label">
303
+ <label>Custom Attribute 10 Mapping</label>
304
+ <frontend_model>Fruugo_Integration_Block_AttributeMapping</frontend_model>
305
+ <backend_model>integration/AttributeMapping</backend_model>
306
+ <sort_order>250</sort_order>
307
+ <show_in_default>1</show_in_default>
308
+ <show_in_website>1</show_in_website>
309
+ <show_in_store>1</show_in_store>
310
+ <comment>Select attributes which describes the custom attribute 10 of an item</comment>
311
+ </attribute_10>
312
  </fields>
313
  </products_options>
314
  <orders_options translate="label" module="integration">
371
  <comment>Fruugo Orders Api for testing</comment>
372
  </order_api_url>
373
  <orders_endpoint_last_checked translate="label">
374
+ <label>Fruugo orders endpoint last checked time</label>
375
+ <frontend_type>text</frontend_type>
376
  <sort_order>70</sort_order>
377
  <show_in_default>1</show_in_default>
378
  <show_in_website>1</show_in_website>
379
  <show_in_store>1</show_in_store>
380
+ <comment>Set the value of the last time when orders endpoint was checked. Format: yyyy-MM-ddThh:mm:ss+0000, where 0000 is timezone info</comment>
381
  </orders_endpoint_last_checked>
382
  </fields>
383
  </orders_options>
384
+ <products_feed_advanced_options translate="label" module="integration">
385
+ <label>Products Feed Advanced Options</label>
386
+ <sort_order>4000</sort_order>
387
+ <show_in_default>1</show_in_default>
388
+ <show_in_website>1</show_in_website>
389
+ <show_in_store>1</show_in_store>
390
+ <fields>
391
+ <max_resources_load translate="label">
392
+ <label>Max Resources</label>
393
+ <frontend_type>select</frontend_type>
394
+ <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_MaxResourcesLoad</source_model>
395
+ <sort_order>10</sort_order>
396
+ <show_in_default>1</show_in_default>
397
+ <show_in_website>0</show_in_website>
398
+ <show_in_store>0</show_in_store>
399
+ <comment>Maximum average system load (the number of processes in the system run queue, based on /proc/loadavg) allowed over the last minute, not available on Windows Servers because php sys_getloadavg() function is not implemented on Windows platforms.</comment>
400
+ </max_resources_load>
401
+ <sleep_time_sec translate="label">
402
+ <label>Sleep time seconds</label>
403
+ <frontend_type>select</frontend_type>
404
+ <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_SleepTimeSec</source_model>
405
+ <sort_order>20</sort_order>
406
+ <show_in_default>1</show_in_default>
407
+ <show_in_website>0</show_in_website>
408
+ <show_in_store>0</show_in_store>
409
+ <comment>Time to sleep for if over load limit during products export</comment>
410
+ </sleep_time_sec>
411
+ <max_errors translate="label">
412
+ <label>Max Errors Allowed</label>
413
+ <frontend_type>select</frontend_type>
414
+ <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_MaxErrors</source_model>
415
+ <sort_order>30</sort_order>
416
+ <show_in_default>1</show_in_default>
417
+ <show_in_website>0</show_in_website>
418
+ <show_in_store>0</show_in_store>
419
+ <comment>The number of errors after which the exporting products process will abort, set to -1 to disable</comment>
420
+ </max_errors>
421
+ <performance_reports_to_keep translate="label">
422
+ <label>Number of Performance Reports to Keep</label>
423
+ <frontend_type>select</frontend_type>
424
+ <source_model>Fruugo_Integration_Model_Adminhtml_System_Config_Source_NumberOfPerformanceReports</source_model>
425
+ <sort_order>40</sort_order>
426
+ <show_in_default>1</show_in_default>
427
+ <show_in_website>0</show_in_website>
428
+ <show_in_store>0</show_in_store>
429
+ <comment>The number of products xml feed performance reports to keep. Reports can be downloaded at RootUrl/index.php/fruugo-integration/products/performancereport.</comment>
430
+ </performance_reports_to_keep>
431
+ </fields>
432
+ </products_feed_advanced_options>
433
  </groups>
434
  </integration_options>
435
  </sections>
app/etc/modules/Fruugo_Integration.xml CHANGED
@@ -48,42 +48,24 @@
48
  </Fruugo_Integration_Model_Observer>
49
  </observers>
50
  </sales_order_save_before>
51
- <sales_order_invoice_pay>
52
  <observers>
53
  <Fruugo_Integration_Model_Observer>
54
  <type>singleton</type>
55
  <class>Fruugo_Integration_Model_Observer</class>
56
- <method>orderInvoiced</method>
57
  </Fruugo_Integration_Model_Observer>
58
  </observers>
59
- </sales_order_invoice_pay>
60
- <sales_order_item_cancel>
61
  <observers>
62
- <Fruugo_Integration_Model_Observer>
63
- <type>singleton</type>
64
- <class>Fruugo_Integration_Model_Observer</class>
65
- <method>orderCancelled</method>
66
- </Fruugo_Integration_Model_Observer>
67
- </observers>
68
- </sales_order_item_cancel>
69
- <sales_order_shipment_save_after>
70
- <observers>
71
- <Fruugo_Integration_Model_Observer>
72
  <type>singleton</type>
73
  <class>Fruugo_Integration_Model_Observer</class>
74
- <method>orderShipment</method>
75
- </Fruugo_Integration_Model_Observer>
76
- </observers>
77
- </sales_order_shipment_save_after>
78
- <sales_order_payment_refund>
79
- <observers>
80
- <Fruugo_Integration_Model_Observer>
81
- <type>singleton</type>
82
- <class>Fruugo_Integration_Model_Observer</class>
83
- <method>orderRefunded</method>
84
- </Fruugo_Integration_Model_Observer>
85
  </observers>
86
- </sales_order_payment_refund>
87
  </events>
88
  </global>
89
  </config>
48
  </Fruugo_Integration_Model_Observer>
49
  </observers>
50
  </sales_order_save_before>
51
+ <sales_order_invoice_save_before>
52
  <observers>
53
  <Fruugo_Integration_Model_Observer>
54
  <type>singleton</type>
55
  <class>Fruugo_Integration_Model_Observer</class>
56
+ <method>beforeSaveInvoice</method>
57
  </Fruugo_Integration_Model_Observer>
58
  </observers>
59
+ </sales_order_invoice_save_before>
60
+ <sales_order_shipment_save_before>
61
  <observers>
62
+ <Fruugo_Integration>
 
 
 
 
 
 
 
 
 
63
  <type>singleton</type>
64
  <class>Fruugo_Integration_Model_Observer</class>
65
+ <method>beforeSaveShipment</method>
66
+ </Fruugo_Integration>
 
 
 
 
 
 
 
 
 
67
  </observers>
68
+ </sales_order_shipment_save_before>
69
  </events>
70
  </global>
71
  </config>
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Fruugo_Integration</name>
4
- <version>1.0.9</version>
5
  <stability>stable</stability>
6
  <license uri="http://www.gnu.org/licenses/">GNU</license>
7
  <channel>community</channel>
@@ -14,13 +14,11 @@ This plugin mainly performs two tasks:&#xD;
14
  - Generate products xml feed from Magento periodically on a specified frequency.&#xD;
15
  &#xD;
16
  - Read from Fruugo Rrders API periodically on a specified frequency, and notifies Magento order events to Fruugo Orders API.</description>
17
- <notes>This update includes:&#xD;
18
- &#xD;
19
- - fixed bug: U.S orders are set wrong region. Used Fruugo order province value to set the region_id of orders</notes>
20
  <authors><author><name>inoutput.io</name><user>inoutput</user><email>support@inoutput.io</email></author><author><name>fruugo.com</name><user>Fruugo</user><email>support@fruugo.com</email></author></authors>
21
- <date>2016-03-02</date>
22
- <time>10:36:52</time>
23
- <contents><target name="magecommunity"><dir name="Fruugo"><dir name="Integration"><dir name="Block"><dir name="Catalog"><dir name="Product"><file name="Tab.php" hash="412d5a38c07f78fd56e3509809038008"/></dir></dir><file name="Languagestoremapping.php" hash="7852846c2428b4fc15434a1e78a37de7"/><file name="Refreshcountriesbutton.php" hash="5a7e3af708dd470725981ec187b2c07d"/><dir name="Sales"><dir name="Order"><dir name="View"><file name="Tabs.php" hash="d98faeeede3c06d5f28f8f9731438314"/></dir></dir></dir></dir><dir name="Helper"><file name="ConfigLoader.php" hash="f930d687b44fe0a3a9e70383e0500a7c"/><file name="Data.php" hash="866fe8e1ea50749218d6efcab8454f28"/><file name="Defines.php" hash="a97616fe105332db06292c68eabecdf1"/><file name="FruugoCountriesSeeder.php" hash="68949469a1b1ce6a605fccd132e9e3c0"/><file name="Logger.php" hash="a83fdd017e59261e059a8d18d223dd59"/><file name="OrdersFeedProcessor.php" hash="cdf23b9aa5f330c20745ab15695b5050"/><file name="ProductsFeedGenerator.php" hash="2e2c74a2c7128e58d524b9c3fd2dd8d9"/><file name="ProductsFeedGeneratorProfiler.php" hash="33cbf6db6cecda919011d23432d9bdb9"/></dir><dir name="Model"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Backend"><file name="OrderCron.php" hash="8187697b999171b8940be7e5306a3bc7"/><file name="ProductCron.php" hash="a8bdd62ad14cdb85d9ef0ae2b85cecad"/></dir><dir name="Source"><file name="ExportPageSize.php" hash="a322cb1b507d33444078ba6494b461fd"/><file name="Hour.php" hash="fab1337425532af32cf8642b8b244930"/><file name="MaxErrors.php" hash="03213889323c47349b5aaa7d7b6752f5"/><file name="MaxResourcesLoad.php" hash="8b941802b5a45f2744abe83b117dceba"/><file name="ProductDescriptionType.php" hash="4a2810baeabe1a74b1b06b92cc2e4481"/><file name="SleepTimeSec.php" hash="9c9790275cf3ec3347746bee53cb50ae"/></dir></dir></dir></dir><file name="Countries.php" hash="a23378525c616fd87c5589933aec48c6"/><file name="CronJobObserver.php" hash="303c9619a9abb2489365b08c94507b31"/><file name="Observer.php" hash="1e16ebea05d59b472cc9f279965cd17e"/><file name="Payment.php" hash="4e7d2d72e7662ea71c178d85c9b9bd6c"/><dir name="Resource"><dir name="Countries"><file name="Collection.php" hash="618a95f1535d4c63724a2f7eaebf6c2b"/></dir><file name="Countries.php" hash="dc3da7aec3c3472a4ccd73e2b2f255f0"/><dir name="Shipment"><file name="Collection.php" hash="66d038f890e36403f9ffb2e283e88daf"/></dir><file name="Shipment.php" hash="d8c13444ba5089cf606241f2c7c9a87d"/></dir><file name="Shipment.php" hash="23b1c0b4bc4e29cb137c9b3819723a63"/></dir><dir name="controllers"><file name="OrdersController.php" hash="a2d9d2f4c916c376f146cfccecb4110d"/><file name="PackinglistController.php" hash="d76ac40e81f51635a073fb20492883f8"/><file name="ProductsController.php" hash="111475a85b64e4c14753f333568842ec"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/><file name=".gitignore" hash="a1d994a3c45d2cb16e44c5a311159532"/></dir><dir name="data"><dir name="fruugo_attributes_setup"><file name="data-install-0.1.1.php" hash="73a6210fdb6c5a2edfa0222a6a239e9a"/></dir><file name=".DS_Store" hash="d3071693cbf676ab90bb393a4d874cfc"/></dir><dir name="design"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="integration.xml" hash="3f173b27f9f483c00b33b713c0bad737"/></dir><dir name="template"><dir name="integration"><dir name="catalog"><dir name="product"><file name="fruugo-allowed-countries.phtml" hash="066098b4583e6bd3882c01645bd9ed1d"/></dir></dir><dir name="sales"><dir name="order"><dir name="view"><dir name="tab"><file name="packinglist.phtml" hash="9ec64369d5c23043976b08573f3435dd"/></dir></dir></dir></dir></dir></dir></dir></dir></dir></dir><dir name="etc"><file name="config.xml" hash="b98b2b21d0836702326e5d82289386cd"/><file name="system.xml" hash="fbc772bf4764682c912c97be3959ce12"/></dir><dir name="sql"><dir name="fruugo_attributes_setup"><file name="install-0.1.1.php" hash="326629d7305e3c3b20aaf8d97de1f17b"/></dir></dir><file name=".DS_Store" hash="2c8e8f48f57f17b51fddb66b074380aa"/><file name=".gitignore" hash="f0cb20b35e2469e9e9617f658ed452a5"/></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="integration.xml" hash="3f173b27f9f483c00b33b713c0bad737"/></dir><dir name="template"><dir name="integration"><dir name="catalog"><dir name="product"><file name="fruugo-allowed-countries.phtml" hash="066098b4583e6bd3882c01645bd9ed1d"/></dir></dir><dir name="sales"><dir name="order"><dir name="view"><dir name="tab"><file name="packinglist.phtml" hash="9ec64369d5c23043976b08573f3435dd"/></dir></dir></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Fruugo_Integration.xml" hash="b01dcb5088d487517bd87af216d6d7c8"/></dir></target></contents>
24
  <compatible/>
25
  <dependencies><required><php><min>5.4.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.7</min><max/></package></required></dependencies>
26
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Fruugo_Integration</name>
4
+ <version>1.1.2</version>
5
  <stability>stable</stability>
6
  <license uri="http://www.gnu.org/licenses/">GNU</license>
7
  <channel>community</channel>
14
  - Generate products xml feed from Magento periodically on a specified frequency.&#xD;
15
  &#xD;
16
  - Read from Fruugo Rrders API periodically on a specified frequency, and notifies Magento order events to Fruugo Orders API.</description>
17
+ <notes>- Fixed: use parent product's attributes when skus' are empty, including: Brand, Manufacturer, Description and Short Description</notes>
 
 
18
  <authors><author><name>inoutput.io</name><user>inoutput</user><email>support@inoutput.io</email></author><author><name>fruugo.com</name><user>Fruugo</user><email>support@fruugo.com</email></author></authors>
19
+ <date>2016-05-13</date>
20
+ <time>01:13:06</time>
21
+ <contents><target name="magecommunity"><dir name="Fruugo"><dir name="Integration"><dir name="Block"><file name="AttributeMapping.php" hash="404e26cc04af15812e4658b890cf8a3b"/><file name="AttributeMappingFiltered.php" hash="f1ab925472234af316bb905c3956037b"/><file name="AttributesDropdown.php" hash="4ba397594b404eda631b44088e38c914"/><file name="AttributesDropdownFiltered.php" hash="efd6945b9917cdeab93b97329875f4a9"/><dir name="Catalog"><dir name="Product"><file name="Tab.php" hash="412d5a38c07f78fd56e3509809038008"/></dir></dir><file name="Languagestoremapping.php" hash="7852846c2428b4fc15434a1e78a37de7"/><file name="Refreshcountriesbutton.php" hash="5a7e3af708dd470725981ec187b2c07d"/><dir name="Sales"><dir name="Order"><dir name="View"><file name="Tabs.php" hash="d98faeeede3c06d5f28f8f9731438314"/></dir></dir></dir></dir><dir name="Helper"><file name="ConfigLoader.php" hash="f930d687b44fe0a3a9e70383e0500a7c"/><file name="Data.php" hash="866fe8e1ea50749218d6efcab8454f28"/><file name="Defines.php" hash="a97616fe105332db06292c68eabecdf1"/><file name="FruugoCountriesSeeder.php" hash="68949469a1b1ce6a605fccd132e9e3c0"/><file name="Logger.php" hash="a83fdd017e59261e059a8d18d223dd59"/><file name="OrdersFeedProcessor.php" hash="13ad2489306efa271b2a3c9cdf8df56c"/><file name="ProductsFeedGenerator.php" hash="c107ad9c705842f71b41ab25afa36c1b"/><file name="ProductsFeedGeneratorProfiler.php" hash="78519bf2785bc19f6debfbe433297f84"/></dir><dir name="Model"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Backend"><file name="OrderCron.php" hash="8187697b999171b8940be7e5306a3bc7"/><file name="ProductCron.php" hash="a8bdd62ad14cdb85d9ef0ae2b85cecad"/></dir><dir name="Source"><file name="Attributes.php" hash="efefa53fce3319717eeb5afd00444189"/><file name="AttributesFiltered.php" hash="3ab28b20474b54ae82b7b4cd54c8dbb8"/><file name="ExportPageSize.php" hash="a322cb1b507d33444078ba6494b461fd"/><file name="Hour.php" hash="fab1337425532af32cf8642b8b244930"/><file name="MaxErrors.php" hash="03213889323c47349b5aaa7d7b6752f5"/><file name="MaxResourcesLoad.php" hash="8b941802b5a45f2744abe83b117dceba"/><file name="NumberOfPerformanceReports.php" hash="17973e45d1bcb123359e29b1396a882d"/><file name="ProductDescriptionType.php" hash="4a2810baeabe1a74b1b06b92cc2e4481"/><file name="SleepTimeSec.php" hash="9c9790275cf3ec3347746bee53cb50ae"/></dir></dir></dir></dir><file name="AttributeMapping.php" hash="202770a2c3a8e37c34a029f29c0cf4e1"/><file name="Countries.php" hash="a23378525c616fd87c5589933aec48c6"/><file name="CronJobObserver.php" hash="303c9619a9abb2489365b08c94507b31"/><file name="Observer.php" hash="cfe67be1c9720875300293267a984766"/><file name="Payment.php" hash="4e7d2d72e7662ea71c178d85c9b9bd6c"/><dir name="Resource"><dir name="Countries"><file name="Collection.php" hash="618a95f1535d4c63724a2f7eaebf6c2b"/></dir><file name="Countries.php" hash="dc3da7aec3c3472a4ccd73e2b2f255f0"/><dir name="Shipment"><file name="Collection.php" hash="66d038f890e36403f9ffb2e283e88daf"/></dir><file name="Shipment.php" hash="d8c13444ba5089cf606241f2c7c9a87d"/></dir><file name="Shipment.php" hash="23b1c0b4bc4e29cb137c9b3819723a63"/></dir><dir name="controllers"><file name="OrdersController.php" hash="a2d9d2f4c916c376f146cfccecb4110d"/><file name="PackinglistController.php" hash="d76ac40e81f51635a073fb20492883f8"/><file name="ProductsController.php" hash="9dc5bf284d9813cb0c59bd8c11b7af7d"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/><file name=".gitignore" hash="a1d994a3c45d2cb16e44c5a311159532"/></dir><dir name="data"><dir name="fruugo_attributes_setup"><file name="data-install-0.1.1.php" hash="bd62b8624704473d9621dc3cf279e09a"/></dir><file name=".DS_Store" hash="d3071693cbf676ab90bb393a4d874cfc"/></dir><dir name="design"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="integration.xml" hash="3f173b27f9f483c00b33b713c0bad737"/></dir><dir name="template"><dir name="integration"><dir name="catalog"><dir name="product"><file name="fruugo-allowed-countries.phtml" hash="066098b4583e6bd3882c01645bd9ed1d"/></dir></dir><dir name="sales"><dir name="order"><dir name="view"><dir name="tab"><file name="packinglist.phtml" hash="9ec64369d5c23043976b08573f3435dd"/></dir></dir></dir></dir></dir></dir></dir></dir></dir></dir><dir name="etc"><file name="config.xml" hash="a3488e14d6b0099a753997fa1a5b95a5"/><file name="system.xml" hash="9a636de3f2feaf4e57e9fc4c754059d3"/></dir><dir name="sql"><dir name="fruugo_attributes_setup"><file name="install-0.1.1.php" hash="326629d7305e3c3b20aaf8d97de1f17b"/></dir></dir><file name=".DS_Store" hash="f1c8a7553e431a94f2a6a04691414584"/><file name=".gitignore" hash="f0cb20b35e2469e9e9617f658ed452a5"/></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="integration.xml" hash="3f173b27f9f483c00b33b713c0bad737"/></dir><dir name="template"><dir name="integration"><dir name="catalog"><dir name="product"><file name="fruugo-allowed-countries.phtml" hash="066098b4583e6bd3882c01645bd9ed1d"/></dir></dir><dir name="sales"><dir name="order"><dir name="view"><dir name="tab"><file name="packinglist.phtml" hash="9ec64369d5c23043976b08573f3435dd"/></dir></dir></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Fruugo_Integration.xml" hash="d04b1a6c988bc77c1ef229967e3095c4"/></dir></target></contents>
22
  <compatible/>
23
  <dependencies><required><php><min>5.4.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.7</min><max/></package></required></dependencies>
24
  </package>