Version Notes
This update includes:
- Only add one price tag for each product
- Enabled currency conversion
Download this release
Release Info
Developer | inoutput.io |
Extension | Fruugo_Integration |
Version | 1.0.6 |
Comparing to | |
See all releases |
Code changes from version 1.0.5 to 1.0.6
- app/code/community/Fruugo/Integration/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Block/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Block/Catalog/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Block/Catalog/Product/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Block/Sales/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Block/Sales/Order/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Block/Sales/Order/View/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Helper/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Helper/Logger.php +40 -2
- app/code/community/Fruugo/Integration/Helper/ProductsFeedGenerator.php +634 -137
- app/code/community/Fruugo/Integration/Helper/ProductsFeedGeneratorProfiler.php +340 -0
- app/code/community/Fruugo/Integration/Model/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Backend/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/ExportPageSize.php +52 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/MaxErrors.php +52 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/MaxResourcesLoad.php +46 -0
- app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/SleepTimeSec.php +52 -0
- app/code/community/Fruugo/Integration/Model/CronJobObserver.php +1 -5
- app/code/community/Fruugo/Integration/controllers/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/controllers/.gitignore +3 -1
- app/code/community/Fruugo/Integration/controllers/ProductsController.php +26 -32
- app/code/community/Fruugo/Integration/data/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/catalog/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/catalog/product/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/sales/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/sales/order/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/sales/order/view/.DS_Store +0 -0
- app/code/community/Fruugo/Integration/etc/config.xml +6 -1
- app/code/community/Fruugo/Integration/etc/system.xml +40 -0
- app/design/adminhtml/default/default/template/integration/.DS_Store +0 -0
- app/design/adminhtml/default/default/template/integration/catalog/.DS_Store +0 -0
- app/design/adminhtml/default/default/template/integration/catalog/product/.DS_Store +0 -0
- app/design/adminhtml/default/default/template/integration/sales/.DS_Store +0 -0
- app/design/adminhtml/default/default/template/integration/sales/order/.DS_Store +0 -0
- app/design/adminhtml/default/default/template/integration/sales/order/view/.DS_Store +0 -0
- package.xml +6 -12
app/code/community/Fruugo/Integration/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Block/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Block/Catalog/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Block/Catalog/Product/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Block/Sales/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Block/Sales/Order/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Block/Sales/Order/View/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Helper/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Helper/Logger.php
CHANGED
@@ -22,8 +22,46 @@
|
|
22 |
|
23 |
class Fruugo_Integration_Helper_Logger extends Mage_Core_Helper_Abstract
|
24 |
{
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
{
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
29 |
}
|
22 |
|
23 |
class Fruugo_Integration_Helper_Logger extends Mage_Core_Helper_Abstract
|
24 |
{
|
25 |
+
const ALWAYS = 0;
|
26 |
+
const ERROR = 1;
|
27 |
+
const WARNING = 2;
|
28 |
+
const INFO = 3;
|
29 |
+
const DEBUG = 4;
|
30 |
+
|
31 |
+
public static $LOG_LEVEL = self::WARNING;
|
32 |
+
public static $CODE = array(
|
33 |
+
self::ALWAYS => 'NOTICE',
|
34 |
+
self::ERROR => 'ERROR',
|
35 |
+
self::WARNING=> 'WARNING',
|
36 |
+
self::INFO => 'INFO',
|
37 |
+
self::DEBUG => 'DEBUG',
|
38 |
+
);
|
39 |
+
|
40 |
+
public static function log($message, $level = self::INFO)
|
41 |
+
{
|
42 |
+
if ($level <= self::$LOG_LEVEL) {
|
43 |
+
Mage::log("Fruugo - " . self::$CODE[$level] . ': ' . $message);
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
public static function getFormattedReport($report)
|
48 |
{
|
49 |
+
$repStr = "\n\n---------------------------[ Fruugo Product Export Results ]---------------------------\n";
|
50 |
+
$repStr .= "Status: " . $report['status'] . "\n";
|
51 |
+
$repStr .= "Total products exported: " . $report['total_exported'] . "\n";
|
52 |
+
$repStr .= "Total products processed: " . $report['total_processed'] . "\n";
|
53 |
+
$repStr .= "Start time: " . $report['start_time_utc'] . "\n";
|
54 |
+
$repStr .= "End time: " . $report['end_time_utc'] . "\n";
|
55 |
+
$repStr .= "Total processing time: " . $report['processing_time_sec'] . " seconds\n";
|
56 |
+
$repStr .= "Total paused time: " . $report['time_paused_sec'] . " seconds\n";
|
57 |
+
$repStr .= "Max memory used: " . $report['max_ram_usage_mb'] . " MB\n";
|
58 |
+
$repStr .= "Avg memory used: " . round($report['avg_ram_usage_mb'], 2) . " MB\n";
|
59 |
+
$repStr .= "Avg resource load: " . round($report['avg_load'], 2) . "\n";
|
60 |
+
$repStr .= "Error count: " . $report['error_count'] . "\n";
|
61 |
+
$repStr .= "Resource threshold triggers: " . $report['load_above_threshold_count'] . "\n";
|
62 |
+
$repStr .= "XML File size: " . round($report['xml_file_size_mb'], 2) . " MB\n";
|
63 |
+
$repStr .= "----------------------------------------------------------------------------------------\n\n";
|
64 |
+
|
65 |
+
return $repStr;
|
66 |
}
|
67 |
}
|
app/code/community/Fruugo/Integration/Helper/ProductsFeedGenerator.php
CHANGED
@@ -24,64 +24,236 @@ require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/Logger.php'
|
|
24 |
|
25 |
class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
|
26 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
public function generateProdcutsFeed($cached = false)
|
28 |
{
|
29 |
-
$
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
} else {
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
$productsXml;
|
35 |
|
36 |
-
$devMode
|
37 |
-
|
38 |
-
$productsXml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>'
|
39 |
-
.'<Products xmlns="http://schemas.fruugo.com/fruugoflat"></Products>');
|
40 |
} else {
|
41 |
-
$productsXml =
|
42 |
-
|
|
|
|
|
|
|
43 |
}
|
44 |
|
45 |
-
$
|
46 |
-
->getCollection()
|
47 |
-
->addAttributeToSelect('*') // select all attributes
|
48 |
-
->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED) // only select enabled products
|
49 |
-
->addAttributeToFilter('type_id', Mage_Catalog_Model_Product_Type::TYPE_SIMPLE); // only select simple products
|
50 |
|
51 |
// Get store langauge map or use null if none
|
52 |
$storelangsSettings = Mage::getStoreConfig('integration_options/products_options/language_store');
|
53 |
$storelangsMapping = empty($storelangsSettings) ? null : unserialize($storelangsSettings);
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
-
$
|
63 |
-
$
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
$
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
}
|
|
|
|
|
|
|
|
|
75 |
|
76 |
-
|
77 |
-
|
|
|
|
|
78 |
}
|
|
|
|
|
79 |
}
|
80 |
|
81 |
-
|
82 |
{
|
83 |
-
// M: Mandatory R: Recommended O: Optional
|
84 |
$parentProduct = $this->_getParentProduct($product);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
// ProductId *M
|
87 |
if (isset($parentProduct)) {
|
@@ -115,33 +287,27 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
|
|
115 |
|
116 |
// Category *R
|
117 |
$categoryEntity = $product->getCategoryCollection()
|
|
|
118 |
->addAttributeToSort('level', 'DESC')
|
119 |
->addAttributeToSort('position', 'DESC')
|
120 |
->addAttributeToFilter('is_active', '1')
|
121 |
->getFirstItem();
|
122 |
|
123 |
-
$
|
124 |
-
->
|
125 |
-
|
126 |
-
if ($category->getName() !== null) {
|
127 |
-
$productXml->addChild('Category', htmlspecialchars($category->getName()));
|
128 |
}
|
129 |
|
130 |
// Imageurl1 *M
|
131 |
// Imageurl2, Imageurl3, Imageurl4, Imageurl5 *R
|
132 |
-
$images = $this->_getProductImages($product, $parentProduct);
|
133 |
$imageIndex = 0;
|
134 |
|
135 |
-
foreach ($images as $
|
136 |
if ($imageIndex >= 5) {
|
137 |
break;
|
138 |
}
|
139 |
|
140 |
-
$
|
141 |
-
|
142 |
-
$productXml->addChild('Imageurl'.($imageIndex + 1), $imageUrl);
|
143 |
-
$imageIndex++;
|
144 |
-
}
|
145 |
}
|
146 |
|
147 |
// StockStatus & StockQuantity *M
|
@@ -150,9 +316,14 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
|
|
150 |
if ($stockItem === null || !$stockItem->getIsInStock()) {
|
151 |
$productXml->addChild('StockStatus', 'OUTOFSTOCK');
|
152 |
} else {
|
153 |
-
$productXml->addChild('StockStatus', 'INSTOCK');
|
154 |
$stocklevel = (int)$stockItem->getQty();
|
155 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
}
|
157 |
|
158 |
// RestockDate *O
|
@@ -165,178 +336,504 @@ class Fruugo_Integration_ProductsFeedGenerator extends Mage_Core_Helper_Abstract
|
|
165 |
}
|
166 |
|
167 |
// Description Node
|
168 |
-
$stores = Mage::app()->getStores(false);
|
169 |
-
|
170 |
$addedLanguages = array();
|
171 |
-
|
|
|
172 |
// store 'fruugo' is for Fruugo orders
|
173 |
-
if ($store->getCode()
|
174 |
-
|
175 |
-
|
176 |
-
$localeCode = Mage::getStoreConfig('general/locale/code', $store->getId());
|
177 |
-
$language = substr($localeCode, 0, strpos($localeCode, '_'));
|
178 |
-
|
179 |
-
// Make sure no language is added more than once
|
180 |
-
if (!in_array($language, $addedLanguages)) {
|
181 |
-
// Add description when no store selected for language OR the store is selected for the langauge in storelangsMapping array
|
182 |
-
if ($storelangsMapping == null || $storelangsMapping[$localeCode] == "" || $storelangsMapping[$localeCode] == $store->getCode()) {
|
183 |
-
// Language *R
|
184 |
-
$descriptionXml = $productXml->addChild('Description');
|
185 |
-
$descriptionXml->addChild('Language', $language);
|
186 |
-
|
187 |
-
$descriptionXml->addChild('Title', htmlspecialchars($product->getName()));
|
188 |
-
|
189 |
-
// description
|
190 |
-
$nestedDescriptionXml = $descriptionXml->addChild('Description');
|
191 |
-
$descriptionType = Mage::getStoreConfig('integration_options/products_options/descrption_type');
|
192 |
-
|
193 |
-
if ($descriptionType == 'long') {
|
194 |
-
$this->_addCData($nestedDescriptionXml, $product->getDescription());
|
195 |
-
} elseif ($descriptionType == 'short') {
|
196 |
-
$this->_addCData($nestedDescriptionXml, $product->getShortDescription());
|
197 |
-
} elseif ($descriptionType == 'merge_short_first') {
|
198 |
-
$this->_addCData($nestedDescriptionXml, $product->getShortDescription() . PHP_EOL . $product->getDescription());
|
199 |
-
} else {
|
200 |
-
$this->_addCData($nestedDescriptionXml, $product->getDescription() . PHP_EOL . $product->getShortDescription());
|
201 |
-
}
|
202 |
|
203 |
-
|
204 |
-
|
205 |
-
$descriptionXml->addChild('AttributeColor', $product->getAttributeText('color'));
|
206 |
-
}
|
207 |
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
$descriptionXml->addChild('AttributeSize', $product->getAttributeText('size'));
|
213 |
-
}
|
214 |
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
|
|
|
|
|
|
225 |
|
226 |
-
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
}
|
230 |
}
|
231 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
232 |
$addedCurrencies = array();
|
233 |
-
foreach ($stores as $store) {
|
234 |
-
if ($store->getCode()
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
$currencyCode = $store->getCurrentCurrencyCode();
|
|
|
|
|
|
|
236 |
if (in_array($currencyCode, $addedCurrencies)) {
|
237 |
continue;
|
238 |
} else {
|
239 |
array_push($addedCurrencies, $currencyCode);
|
240 |
}
|
241 |
-
|
242 |
// Price Node
|
243 |
$priceXml = $productXml->addChild('Price');
|
244 |
// Currency *R
|
245 |
$priceXml->addChild('Currency', $currencyCode);
|
246 |
// Country
|
247 |
-
|
248 |
-
if (isset($existingProductCountries) && $existingProductCountries->getFruugoCountries() != null) {
|
249 |
-
$existingList = $existingProductCountries->getFruugoCountries();
|
250 |
$priceXml->addChild('Country', $existingList);
|
251 |
}
|
252 |
|
253 |
// Normal price.
|
254 |
-
$
|
255 |
-
$
|
256 |
-
$priceXml->addChild('NormalPriceWithoutVAT', number_format($
|
257 |
-
|
258 |
-
// VATRate.
|
259 |
-
$taxCalculation = Mage::getModel('tax/calculation');
|
260 |
-
$request = $taxCalculation->getRateRequest(null, null, null, $store);
|
261 |
-
$taxClassId = $product->getTaxClassId();
|
262 |
-
$percent = $taxCalculation->getRate($request->setProductClassId($taxClassId));
|
263 |
-
$priceXml->addChild('VATRate', number_format($percent, 2, '.', ''));
|
264 |
|
265 |
// Discount price
|
266 |
-
$finalPriceExclTax = $taxHelper->getPrice($product, $product->getFinalPrice(), false);
|
267 |
-
$rulePriceExclTax = Mage::getModel('catalogrule/rule')->calcProductPriceRule($product, $normalPriceExclTax);
|
268 |
$discountedPriceExclTax = null;
|
|
|
|
|
|
|
269 |
|
270 |
if ($rulePriceExclTax == null || $finalPriceExclTax < $rulePriceExclTax) {
|
271 |
$discountedPriceExclTax = $finalPriceExclTax;
|
272 |
} else {
|
273 |
$discountedPriceExclTax = $rulePriceExclTax;
|
274 |
}
|
275 |
-
|
276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
// DiscountPriceStartDate *O
|
278 |
if ($product->getSpecialFromDate()) {
|
279 |
$fromTime = strtotime($product->getSpecialFromDate());
|
280 |
-
$formatedFromTimeStr = date('Y-m-d'
|
281 |
$priceXml->addChild('DiscountPriceStartDate', $formatedFromTimeStr);
|
282 |
}
|
283 |
|
284 |
// DiscountPriceEndDate *O
|
285 |
if ($product->getSpecialToDate()) {
|
286 |
$toTime = strtotime($product->getSpecialToDate());
|
287 |
-
$fromatedToTimeStr = date('Y-m-d'
|
288 |
$priceXml->addChild('DiscountPriceEndDate', $fromatedToTimeStr);
|
289 |
}
|
|
|
|
|
|
|
|
|
290 |
}
|
|
|
|
|
|
|
291 |
}
|
292 |
}
|
293 |
|
294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
}
|
296 |
|
297 |
-
|
|
|
298 |
{
|
299 |
-
$
|
|
|
|
|
300 |
$images = array();
|
301 |
-
$skuImages = array_values($productObj->getMediaGalleryImages()->getItems());
|
302 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
if (!empty($skuImages)) {
|
304 |
foreach ($skuImages as $skuImage) {
|
305 |
-
|
|
|
|
|
|
|
306 |
}
|
307 |
}
|
308 |
|
309 |
if (isset($parentProduct)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
$parentImages = array_values($parentProduct->getMediaGalleryImages()->getItems());
|
311 |
if (!empty($parentImages)) {
|
312 |
foreach ($parentImages as $parentImage) {
|
313 |
-
|
|
|
|
|
|
|
314 |
}
|
315 |
}
|
316 |
}
|
317 |
|
318 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
}
|
320 |
|
321 |
-
|
322 |
{
|
323 |
$parentProduct = null;
|
324 |
|
325 |
$parentIds = Mage::getResourceSingleton('catalog/product_type_configurable')
|
326 |
-
|
327 |
|
328 |
if (isset($parentIds[0])) {
|
329 |
-
$parentProduct = Mage::
|
|
|
330 |
}
|
331 |
|
332 |
-
return
|
333 |
}
|
334 |
|
335 |
-
|
336 |
{
|
337 |
$node = dom_import_simplexml($xml);
|
338 |
$no = $node->ownerDocument;
|
339 |
$node->appendChild($no->createCDATASection(htmlspecialchars($cdata_text)));
|
340 |
return $xml;
|
341 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
}
|
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;
|
40 |
+
protected $devMode = null;
|
41 |
+
protected $currentTimer = array();
|
42 |
+
protected $categoryRule = null;
|
43 |
+
protected $tmpProductsXmlPath = null;
|
44 |
+
protected $productsXmlPath = null;
|
45 |
+
protected $report = null;
|
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;
|
52 |
+
protected static $WARNING = Fruugo_Integration_Helper_Logger::WARNING;
|
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');
|
60 |
+
$this->devMode = ($devMode == '1') ? true : false;
|
61 |
+
|
62 |
+
$this->productsXmlPath = Mage::getModuleDir('', 'Fruugo_Integration') . '/controllers/products.xml';
|
63 |
+
if ($cached === true && file_exists($this->productsXmlPath)) {
|
64 |
+
return $this->productsXmlPath;
|
65 |
+
}
|
66 |
+
|
67 |
+
// BEGIN LOCK CHECK
|
68 |
+
// This makes sure that another copy of this script is not executing already
|
69 |
+
$lockFile = Mage::getModuleDir('', 'Fruugo_Integration') . '/controllers/products.lock';
|
70 |
+
if (!file_exists($lockFile)) {
|
71 |
+
touch($lockFile);
|
72 |
+
}
|
73 |
+
|
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 |
+
}
|
86 |
+
// END LOCK CHECK
|
87 |
+
|
88 |
+
if ($this->devMode) {
|
89 |
+
Fruugo_Integration_Helper_Logger::$LOG_LEVEL = self::$DEBUG;
|
90 |
+
$this->MONITOR_RESOURCES = false;
|
91 |
+
}
|
92 |
+
|
93 |
+
try {
|
94 |
+
$time = time();
|
95 |
+
$this->_setupGlobalData();
|
96 |
+
$this->report['start_time_utc'] = date("Y-m-d H:i:s", time());
|
97 |
$productsXml;
|
98 |
|
99 |
+
if ($this->devMode) {
|
100 |
+
$productsXml = '<?xml version="1.0" encoding="UTF-8"?><Products xmlns="http://schemas.fruugo.com/fruugoflat">';
|
|
|
|
|
101 |
} else {
|
102 |
+
$productsXml = '<?xml version="1.0" encoding="UTF-8"?><Products>';
|
103 |
+
}
|
104 |
+
|
105 |
+
if (file_exists($this->tmpProductsXmlPath)) {
|
106 |
+
unlink($this->tmpProductsXmlPath);
|
107 |
}
|
108 |
|
109 |
+
$this->_writeProductsXml($productsXml);
|
|
|
|
|
|
|
|
|
110 |
|
111 |
// Get store langauge map or use null if none
|
112 |
$storelangsSettings = Mage::getStoreConfig('integration_options/products_options/language_store');
|
113 |
$storelangsMapping = empty($storelangsSettings) ? null : unserialize($storelangsSettings);
|
114 |
|
115 |
+
$numOfProds = 0;
|
116 |
+
$currentPage = 0;
|
117 |
+
$numResults = $this->PAGE_SIZE;
|
118 |
+
$numIterations = 0;
|
119 |
+
$totalMemory = 0;
|
120 |
+
$xmlBuffer = '';
|
121 |
+
$errorsCount = 0;
|
122 |
+
$totalProcessed = 0;
|
123 |
+
$finalException = null; // This is a hack because old versions of PHP(<5.5) don't have finally
|
124 |
+
$lastProcessedId = 0;
|
125 |
+
|
126 |
+
do {
|
127 |
+
$products = Mage::getModel('catalog/product')->getCollection();
|
128 |
+
$products->addAttributeToSelect('*'); // select all attributes
|
129 |
+
if ($this->TRACK_LAST_ID) {
|
130 |
+
$products->addAttributeToFilter('entity_id', array('gt' => $lastProcessedId)); // make sure we don't double process items due to deletions during the process
|
131 |
}
|
132 |
+
$products->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED); // only select enabled products
|
133 |
+
$products->addAttributeToFilter('type_id', Mage_Catalog_Model_Product_Type::TYPE_SIMPLE);
|
134 |
+
$products->setOrder('entity_id', 'ASC');
|
135 |
+
if ($this->TRACK_LAST_ID) {
|
136 |
+
$products->getSelect()->limit($this->PAGE_SIZE);
|
137 |
+
} else {
|
138 |
+
$products->getSelect()->limit($this->PAGE_SIZE, $currentPage);
|
139 |
+
}
|
140 |
+
|
141 |
+
$this->_writeLog("Processing page " . $currentPage. ', max memory used ' . ((memory_get_peak_usage(true) /1024)/1024) . 'MB.', self::$DEBUG);
|
142 |
|
143 |
+
$numResults = 0;
|
144 |
+
foreach ($products as $product) {
|
145 |
+
$numResults++;
|
146 |
+
$totalProcessed++;
|
147 |
+
$lastProcessedId = $product->getId();
|
148 |
+
|
149 |
+
$disabledOnFruugo = false;
|
150 |
+
$productCountries = Mage::getModel('integration/countries')->load($product->getId(), 'product_id');
|
151 |
+
|
152 |
+
if (isset($productCountries) && $productCountries->getFruugoCountries() == 'Disabled') {
|
153 |
+
$disabledOnFruugo = true;
|
154 |
+
continue;
|
155 |
+
}
|
156 |
+
|
157 |
+
unset($productCountries);
|
158 |
+
if ($this->_shouldInclude($product)) {
|
159 |
+
try {
|
160 |
+
$productXml = $this->_fillProductXml($product, $storelangsMapping);
|
161 |
+
|
162 |
+
if ($productXml) {
|
163 |
+
$xmlBuffer .= $productXml;
|
164 |
+
}
|
165 |
+
|
166 |
+
$numOfProds++;
|
167 |
+
} catch (Exception $ex) {
|
168 |
+
$errorsCount += 1;
|
169 |
+
Mage::logException($ex);
|
170 |
+
|
171 |
+
if (count($this->report['errors']) < 10) {
|
172 |
+
$errorLog = array(
|
173 |
+
'product_id' => $product->getId(),
|
174 |
+
'stack_trace' => $ex->getTraceAsString(),
|
175 |
+
'message' => $ex->getMessage(),
|
176 |
+
);
|
177 |
+
array_push($this->report['errors'], $errorLog);
|
178 |
+
}
|
179 |
+
|
180 |
+
if ($this->MAX_ERRORS != -1 && $errorsCount > $this->MAX_ERRORS) {
|
181 |
+
$this->_writeLog('Product export aborting due to hitting maximum error threshold of ' . $this->MAX_ERRORS, self::$ERROR);
|
182 |
+
throw new Exception('Fruugo product export aborting due to hitting maximum error threshold of ' . $this->MAX_ERRORS, 500, $ex);
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
}
|
187 |
}
|
188 |
+
|
189 |
+
$this->_writeProductsXml($xmlBuffer);
|
190 |
+
$numIterations += 1;
|
191 |
+
$xmlBuffer = '';
|
192 |
+
$totalMemory += ((memory_get_usage(false) /1024)/1024);
|
193 |
+
|
194 |
+
$currentPage = $currentPage + $this->PAGE_SIZE;
|
195 |
+
|
196 |
+
//clear collection and free memory
|
197 |
+
$products->resetData();
|
198 |
+
$products->clear();
|
199 |
+
unset($products);
|
200 |
+
|
201 |
+
$this->checkServerLoad();
|
202 |
+
|
203 |
+
} while ($numResults >= $this->PAGE_SIZE);
|
204 |
+
|
205 |
+
// write file end and rename
|
206 |
+
$this->_writeProductsXml('</Products>');
|
207 |
+
rename($this->tmpProductsXmlPath, $this->productsXmlPath);
|
208 |
+
|
209 |
+
$this->report['xml_file_size_mb'] = ((filesize($this->productsXmlPath) /1024)/1024);
|
210 |
+
} catch (Exception $ex) {
|
211 |
+
$this->report['status'] = 'failed';
|
212 |
+
$numOfProds = 0;
|
213 |
+
$finalException = $ex;
|
214 |
+
}
|
215 |
+
|
216 |
+
// This should probably go in a finally block but older
|
217 |
+
// version of PHP don't support it,
|
218 |
+
$this->report['total_exported'] = $numOfProds;
|
219 |
+
$this->report['processing_time_sec'] = (time() - $time);
|
220 |
+
$this->report['end_time_utc'] = date("Y-m-d H:i:s", time());
|
221 |
+
$this->report['avg_ram_usage_mb'] = $totalMemory / $numIterations;
|
222 |
+
$this->report['max_ram_usage_mb'] = ((memory_get_peak_usage(true) /1024)/1024);
|
223 |
+
$this->report['total_processed'] = $totalProcessed;
|
224 |
+
$this->report['error_count'] = $errorsCount;
|
225 |
+
|
226 |
+
|
227 |
+
if (!$this->report['status']) {
|
228 |
+
if ($errorsCount == 0) {
|
229 |
+
$this->report['status'] = 'success';
|
230 |
+
} else {
|
231 |
+
$this->report['status'] = 'success_with_errors';
|
232 |
}
|
233 |
+
}
|
234 |
+
|
235 |
+
$this->_writeReport($this->report);
|
236 |
+
$this->_writeLog(Fruugo_Integration_Helper_Logger::getFormattedReport($this->report), self::$INFO);
|
237 |
|
238 |
+
if ($finalException !== null) {
|
239 |
+
$this->_writeLog('The Fruugo product export has stopped due to an error: ' . $ex->getMessage(), self::$ERROR);
|
240 |
+
Mage::logException($ex);
|
241 |
+
throw $ex;
|
242 |
}
|
243 |
+
|
244 |
+
return $this->productsXmlPath;
|
245 |
}
|
246 |
|
247 |
+
protected function _fillProductXml($product, $storelangsMapping)
|
248 |
{
|
|
|
249 |
$parentProduct = $this->_getParentProduct($product);
|
250 |
+
$images = $this->_getProductImages($product, $parentProduct);
|
251 |
+
|
252 |
+
if (count($images) == 0) {
|
253 |
+
return false;
|
254 |
+
}
|
255 |
+
// M: Mandatory R: Recommended O: Optional
|
256 |
+
$productXml = new SimpleXMLElement('<Product></Product>');
|
257 |
|
258 |
// ProductId *M
|
259 |
if (isset($parentProduct)) {
|
287 |
|
288 |
// Category *R
|
289 |
$categoryEntity = $product->getCategoryCollection()
|
290 |
+
->addAttributeToSelect('name')
|
291 |
->addAttributeToSort('level', 'DESC')
|
292 |
->addAttributeToSort('position', 'DESC')
|
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
|
301 |
// Imageurl2, Imageurl3, Imageurl4, Imageurl5 *R
|
|
|
302 |
$imageIndex = 0;
|
303 |
|
304 |
+
foreach ($images as $imageUrl) {
|
305 |
if ($imageIndex >= 5) {
|
306 |
break;
|
307 |
}
|
308 |
|
309 |
+
$productXml->addChild('Imageurl'.($imageIndex + 1), $imageUrl);
|
310 |
+
$imageIndex++;
|
|
|
|
|
|
|
311 |
}
|
312 |
|
313 |
// StockStatus & StockQuantity *M
|
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);
|
323 |
+
} else {
|
324 |
+
$productXml->addChild('StockStatus', 'INSTOCK');
|
325 |
+
$productXml->addChild('StockQuantity', $stocklevel);
|
326 |
+
}
|
327 |
}
|
328 |
|
329 |
// RestockDate *O
|
336 |
}
|
337 |
|
338 |
// Description Node
|
|
|
|
|
339 |
$addedLanguages = array();
|
340 |
+
$descriptionNodeCount = 0;
|
341 |
+
foreach ($this->stores as $store) {
|
342 |
// store 'fruugo' is for Fruugo orders
|
343 |
+
if ($store->getCode() == 'fruugo') {
|
344 |
+
continue;
|
345 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
346 |
|
347 |
+
$localeCode = Mage::getStoreConfig('general/locale/code', $store->getId());
|
348 |
+
$language = substr($localeCode, 0, strpos($localeCode, '_'));
|
|
|
|
|
349 |
|
350 |
+
// Make sure no language is added more than once
|
351 |
+
if (in_array($language, $addedLanguages)) {
|
352 |
+
continue;
|
353 |
+
}
|
|
|
|
|
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 |
+
$name = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'name', $store->getId());
|
424 |
+
$descriptionXml->addChild('Title', htmlspecialchars($name));
|
425 |
+
|
426 |
+
// description
|
427 |
+
$nestedDescriptionXml = $descriptionXml->addChild('Description');
|
428 |
+
|
429 |
+
if ($descriptionType == 'long') {
|
430 |
+
$this->_addCData($nestedDescriptionXml, $descriptionsArray['description']);
|
431 |
+
} elseif ($descriptionType == 'short') {
|
432 |
+
$this->_addCData($nestedDescriptionXml, $descriptionsArray['short_description']);
|
433 |
+
} elseif ($descriptionType == 'merge_short_first') {
|
434 |
+
$this->_addCData($nestedDescriptionXml, $descriptionsArray['short_description'] . PHP_EOL . $descriptionsArray['description']);
|
435 |
+
} else {
|
436 |
+
$this->_addCData($nestedDescriptionXml, $descriptionsArray['description'] . PHP_EOL . $descriptionsArray['short_description']);
|
437 |
+
}
|
438 |
+
|
439 |
+
// AttributeColor *R
|
440 |
+
if (!empty($attributes['color'])) {
|
441 |
+
$descriptionXml->addChild('AttributeColor', $this->_getAttributesText($language, 'color', $attributes['color'], $store->getId()));
|
442 |
+
}
|
443 |
+
|
444 |
+
// AttributeSize *R
|
445 |
+
if (!empty($attributes['shoe_size'])) {
|
446 |
+
$descriptionXml->addChild('AttributeSize', $this->_getAttributesText($language, 'shoe_size', $attributes['shoe_size'], $store->getId()));
|
447 |
+
} elseif (!empty($attributes['size'])) {
|
448 |
+
$descriptionXml->addChild('AttributeSize', $this->_getAttributesText($language, 'size', $attributes['size'], $store->getId()));
|
449 |
+
}
|
450 |
+
|
451 |
+
// optional attributes: Arrtibute1 - Attribute10 *O
|
452 |
+
if (!empty($attributes['fit'])) {
|
453 |
+
$descriptionXml->addChild('Attribute1', $this->_getAttributesText($language, 'fit', $attributes['fit'], $store->getId()));
|
454 |
+
}
|
455 |
+
if (!empty($attributes['length'])) {
|
456 |
+
$descriptionXml->addChild('Attribute2', $this->_getAttributesText($language, 'length', $attributes['length'], $store->getId()));
|
457 |
+
}
|
458 |
+
if (!empty($attributes['width'])) {
|
459 |
+
$descriptionXml->addChild('Attribute3', $this->_getAttributesText($language, 'width', $attributes['width'], $store->getId()));
|
460 |
+
}
|
461 |
+
|
462 |
+
array_push($addedLanguages, $language);
|
463 |
+
$descriptionNodeCount++;
|
464 |
}
|
465 |
}
|
466 |
|
467 |
+
// Ignore if no descriptions added
|
468 |
+
if ($descriptionNodeCount == 0) {
|
469 |
+
return false;
|
470 |
+
}
|
471 |
+
|
472 |
+
$existingProductCountries = Mage::getModel('integration/countries')->load($product->getId(), 'product_id');
|
473 |
+
|
474 |
+
if (isset($existingProductCountries)) {
|
475 |
+
$existingList = $existingProductCountries->getFruugoCountries();
|
476 |
+
}
|
477 |
+
|
478 |
$addedCurrencies = array();
|
479 |
+
foreach ($this->stores as $store) {
|
480 |
+
if ($store->getCode() == 'fruugo') {
|
481 |
+
continue;
|
482 |
+
}
|
483 |
+
|
484 |
+
// Add price for store in language mapping.
|
485 |
+
$localeCode = Mage::getStoreConfig('general/locale/code', $store->getId());
|
486 |
+
if ($storelangsMapping == null || $storelangsMapping[$localeCode] == $store->getCode()) {
|
487 |
$currencyCode = $store->getCurrentCurrencyCode();
|
488 |
+
$baseCurrencyCode = $this->storeBaseCurrencies[$store->getId()];
|
489 |
+
|
490 |
+
// Skip if currency has been added.
|
491 |
if (in_array($currencyCode, $addedCurrencies)) {
|
492 |
continue;
|
493 |
} else {
|
494 |
array_push($addedCurrencies, $currencyCode);
|
495 |
}
|
496 |
+
|
497 |
// Price Node
|
498 |
$priceXml = $productXml->addChild('Price');
|
499 |
// Currency *R
|
500 |
$priceXml->addChild('Currency', $currencyCode);
|
501 |
// Country
|
502 |
+
if (isset($existingList)) {
|
|
|
|
|
503 |
$priceXml->addChild('Country', $existingList);
|
504 |
}
|
505 |
|
506 |
// Normal price.
|
507 |
+
$normalPriceExclTax = $this->taxHelper->getPrice($product, $product->getPrice(), false, null, null, null, $store);
|
508 |
+
$normalPriceExclTaxConv = $this->_convertCurrency($normalPriceExclTax, $baseCurrencyCode, $currencyCode);
|
509 |
+
$priceXml->addChild('NormalPriceWithoutVAT', number_format($normalPriceExclTaxConv, 2, '.', ''));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
|
511 |
// Discount price
|
|
|
|
|
512 |
$discountedPriceExclTax = null;
|
513 |
+
$finalPrice = $product->getFinalPrice();
|
514 |
+
$finalPriceExclTax = $this->taxHelper->getPrice($product, $finalPrice, false, null, null, null, $store);
|
515 |
+
$rulePriceExclTax = $this->categoryRule->clearInstance()->calcProductPriceRule($product, $normalPriceExclTax);
|
516 |
|
517 |
if ($rulePriceExclTax == null || $finalPriceExclTax < $rulePriceExclTax) {
|
518 |
$discountedPriceExclTax = $finalPriceExclTax;
|
519 |
} else {
|
520 |
$discountedPriceExclTax = $rulePriceExclTax;
|
521 |
}
|
522 |
+
|
523 |
+
// VATRate.
|
524 |
+
$request = $this->taxCalculation->clearInstance()->getRateRequest(null, null, null, $store);
|
525 |
+
$taxClassId = $product->getTaxClassId();
|
526 |
+
$percent = $this->taxCalculation->clearInstance()->getRate($request->setProductClassId($taxClassId));
|
527 |
+
|
528 |
+
if ($discountedPriceExclTax && $normalPriceExclTax > $discountedPriceExclTax) {
|
529 |
+
$discountedPriceExclTaxConv = $this->_convertCurrency($discountedPriceExclTax, $baseCurrencyCode, $currencyCode);
|
530 |
+
$priceXml->addChild('DiscountPriceWithoutVAT', number_format($discountedPriceExclTaxConv, 2, '.', ''));
|
531 |
+
|
532 |
+
$priceXml->addChild('VATRate', number_format($percent, 2, '.', ''));
|
533 |
// DiscountPriceStartDate *O
|
534 |
if ($product->getSpecialFromDate()) {
|
535 |
$fromTime = strtotime($product->getSpecialFromDate());
|
536 |
+
$formatedFromTimeStr = date('Y-m-d', $fromTime);
|
537 |
$priceXml->addChild('DiscountPriceStartDate', $formatedFromTimeStr);
|
538 |
}
|
539 |
|
540 |
// DiscountPriceEndDate *O
|
541 |
if ($product->getSpecialToDate()) {
|
542 |
$toTime = strtotime($product->getSpecialToDate());
|
543 |
+
$fromatedToTimeStr = date('Y-m-d', $toTime);
|
544 |
$priceXml->addChild('DiscountPriceEndDate', $fromatedToTimeStr);
|
545 |
}
|
546 |
+
} else {
|
547 |
+
// This looks strange because the VATRate is also added above but the sequence
|
548 |
+
// defined in the XSD requires it appears after DiscountPriceWithoutVAT if it is present
|
549 |
+
$priceXml->addChild('VATRate', number_format($percent, 2, '.', ''));
|
550 |
}
|
551 |
+
|
552 |
+
// Only need one price tag for each product now.
|
553 |
+
break;
|
554 |
}
|
555 |
}
|
556 |
|
557 |
+
$productStr = $productXml->asXML();
|
558 |
+
|
559 |
+
if ($productStr) {
|
560 |
+
$productStr = str_replace('<?xml version="1.0"?>', '', trim($productStr));
|
561 |
+
}
|
562 |
+
|
563 |
+
foreach ($productXml->children as $child) {
|
564 |
+
unset($child);
|
565 |
+
}
|
566 |
+
unset($productXml);
|
567 |
+
|
568 |
+
return $productStr;
|
569 |
+
}
|
570 |
+
|
571 |
+
protected function _convertCurrency($price, $baseCurrencyCode, $currencyCode)
|
572 |
+
{
|
573 |
+
if ($this->shouldConvertCurrency && $baseCurrencyCode != $currencyCode) {
|
574 |
+
return $this->currencyConverter->currencyConvert(
|
575 |
+
$price,
|
576 |
+
$baseCurrencyCode,
|
577 |
+
$currencyCode
|
578 |
+
);
|
579 |
+
}
|
580 |
+
|
581 |
+
return $price;
|
582 |
+
}
|
583 |
+
|
584 |
+
// This caches the options label translation for selectable product attributes.
|
585 |
+
// They are cached by attribute name, language and then value.
|
586 |
+
protected function _getAttributesText($language, $attributeName, $optionId, $storeId)
|
587 |
+
{
|
588 |
+
// These are the asset keys that are cached, you should only cache attributes
|
589 |
+
// that have selectable items
|
590 |
+
if (!isset($this->attributeMap)) {
|
591 |
+
$this->attributeMap = array(
|
592 |
+
'color' => array(),
|
593 |
+
'shoe_size' => array(),
|
594 |
+
'size' => array(),
|
595 |
+
'fit' => array(),
|
596 |
+
);
|
597 |
+
}
|
598 |
+
|
599 |
+
if (!isset($this->attributeMap[$attributeName])) {
|
600 |
+
return $optionId;
|
601 |
+
}
|
602 |
+
|
603 |
+
if (!isset($this->attributeMap[$attributeName][$language])) {
|
604 |
+
$this->attributeMap[$attributeName][$language] = array();
|
605 |
+
}
|
606 |
+
|
607 |
+
if (!isset($this->attributeMap[$attributeName][$language][$optionId])) {
|
608 |
+
$this->attributeMap[$attributeName][$language][$optionId] = null;
|
609 |
+
} else {
|
610 |
+
return $this->attributeMap[$attributeName][$language][$optionId];
|
611 |
+
}
|
612 |
+
|
613 |
+
$attributeId = Mage::getResourceModel('eav/entity_attribute')->getIdByCode('catalog_product', $attributeName);
|
614 |
+
$collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
|
615 |
+
->setPositionOrder('asc')
|
616 |
+
->setAttributeFilter($attributeId)
|
617 |
+
->setStoreFilter($storeId)
|
618 |
+
->load()
|
619 |
+
->toOptionArray();
|
620 |
+
|
621 |
+
$found = false;
|
622 |
+
foreach ($collection as $option) {
|
623 |
+
if ($option['value'] == $optionId) {
|
624 |
+
$found = true;
|
625 |
+
}
|
626 |
+
|
627 |
+
$this->attributeMap[$attributeName][$language][$option['value']] = $option['label'];
|
628 |
+
}
|
629 |
+
|
630 |
+
if (!$found) {
|
631 |
+
return $optionId;
|
632 |
+
}
|
633 |
+
|
634 |
+
return $this->attributeMap[$attributeName][$language][$optionId];
|
635 |
}
|
636 |
|
637 |
+
protected $tempProductObj = false;
|
638 |
+
protected function _getProductImages($product, $parentProduct)
|
639 |
{
|
640 |
+
if (!isset($this->tempProductObj) || $this->tempProductObj === false) {
|
641 |
+
$this->tempProductObj = Mage::getModel('catalog/product');
|
642 |
+
}
|
643 |
$images = array();
|
|
|
644 |
|
645 |
+
// This is a workaround to avoid the memory leaks that seem to occur when fully
|
646 |
+
// loading an image gallery on a product model.
|
647 |
+
$productObj = $this->tempProductObj->clearInstance()->setId($product->getId());
|
648 |
+
$attributes = $productObj->getTypeInstance(true)->getSetAttributes($productObj);
|
649 |
+
$media_gallery = $attributes['media_gallery'];
|
650 |
+
$backend = $media_gallery->getBackend();
|
651 |
+
$backend->afterLoad($productObj);
|
652 |
+
|
653 |
+
// Add base image
|
654 |
+
$baseImage = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'image', 0);
|
655 |
+
$productMediaConfig = Mage::getSingleton('catalog/product_media_config');
|
656 |
+
if (!empty($baseImage) && $baseImage !== 'no_selection') {
|
657 |
+
$baseImageUrl = $productMediaConfig->getMediaUrl($baseImage);
|
658 |
+
array_push($images, $baseImageUrl);
|
659 |
+
}
|
660 |
+
|
661 |
+
// Add gallery
|
662 |
+
$galleryImages = $productObj->getMediaGalleryImages();
|
663 |
+
$skuImages = array_values($galleryImages->getItems());
|
664 |
if (!empty($skuImages)) {
|
665 |
foreach ($skuImages as $skuImage) {
|
666 |
+
$imageUrl = $skuImage->getUrl();
|
667 |
+
if (strpos($imageUrl, 'placeholder/image') !== true) {
|
668 |
+
array_push($images, $imageUrl);
|
669 |
+
}
|
670 |
}
|
671 |
}
|
672 |
|
673 |
if (isset($parentProduct)) {
|
674 |
+
// Add parent base image
|
675 |
+
$attributes = $parentProduct->getTypeInstance(true)->getSetAttributes($parentProduct);
|
676 |
+
$media_gallery = $attributes['media_gallery'];
|
677 |
+
$backend = $media_gallery->getBackend();
|
678 |
+
$backend->afterLoad($parentProduct);
|
679 |
+
|
680 |
+
$parentBaseImage = Mage::getResourceModel('catalog/product')->getAttributeRawValue($parentProduct->getId(), 'image', 0);
|
681 |
+
if (!empty($parentBaseImage) && $parentBaseImage !== 'no_selection') {
|
682 |
+
$baseImageUrl = $productMediaConfig->getMediaUrl($parentBaseImage);
|
683 |
+
array_push($images, $baseImageUrl);
|
684 |
+
}
|
685 |
+
|
686 |
+
// Add parent gallery
|
687 |
$parentImages = array_values($parentProduct->getMediaGalleryImages()->getItems());
|
688 |
if (!empty($parentImages)) {
|
689 |
foreach ($parentImages as $parentImage) {
|
690 |
+
$imageUrl = $parentImage->getUrl();
|
691 |
+
if (strpos($imageUrl, 'placeholder/image') !== true) {
|
692 |
+
array_push($images, $imageUrl);
|
693 |
+
}
|
694 |
}
|
695 |
}
|
696 |
}
|
697 |
|
698 |
+
$productObj->clearInstance();
|
699 |
+
unset($productObj);
|
700 |
+
|
701 |
+
// If base image is not 'disabled' there will be duplicates
|
702 |
+
$unique = array_keys(array_flip($images)); // array_unique is supposed to be slower
|
703 |
+
return $unique;
|
704 |
+
}
|
705 |
+
|
706 |
+
protected function _shouldInclude($product)
|
707 |
+
{
|
708 |
+
if ($product->getSku() === null || $product->getName() === null || $product->getPrice() === null) {
|
709 |
+
return false;
|
710 |
+
}
|
711 |
+
|
712 |
+
if ($product->getDescription() === null && $product->getShortDescription() === null) {
|
713 |
+
return false;
|
714 |
+
}
|
715 |
+
|
716 |
+
return true;
|
717 |
}
|
718 |
|
719 |
+
protected function _getParentProduct($product)
|
720 |
{
|
721 |
$parentProduct = null;
|
722 |
|
723 |
$parentIds = Mage::getResourceSingleton('catalog/product_type_configurable')
|
724 |
+
->getParentIdsByChild($product->getId());
|
725 |
|
726 |
if (isset($parentIds[0])) {
|
727 |
+
$parentProduct = Mage::getSingleton('catalog/product')->clearInstance()->setId($parentIds[0]);
|
728 |
+
return $parentProduct;
|
729 |
}
|
730 |
|
731 |
+
return null;
|
732 |
}
|
733 |
|
734 |
+
protected function _addCData($xml, $cdata_text)
|
735 |
{
|
736 |
$node = dom_import_simplexml($xml);
|
737 |
$no = $node->ownerDocument;
|
738 |
$node->appendChild($no->createCDATASection(htmlspecialchars($cdata_text)));
|
739 |
return $xml;
|
740 |
}
|
741 |
+
|
742 |
+
protected function _writeProductsXml($xmlStr)
|
743 |
+
{
|
744 |
+
file_put_contents($this->tmpProductsXmlPath, $xmlStr, FILE_APPEND | LOCK_EX);
|
745 |
+
}
|
746 |
+
|
747 |
+
protected function _writeReport($report)
|
748 |
+
{
|
749 |
+
file_put_contents($this->reportPath, json_encode($report));
|
750 |
+
}
|
751 |
+
|
752 |
+
protected function _writeLog($message, $level = Fruugo_Integration_Helper_Logger::DEBUG)
|
753 |
+
{
|
754 |
+
Fruugo_Integration_Helper_Logger::log($message, $level);
|
755 |
+
}
|
756 |
+
|
757 |
+
protected function _setupGlobalData()
|
758 |
+
{
|
759 |
+
// Cache the stores list to avoid frequent lookups
|
760 |
+
$this->stores = array();
|
761 |
+
$this->storeBaseCurrencies = array();
|
762 |
+
foreach (Mage::app()->getStores(true) as $store) {
|
763 |
+
array_push($this->stores, $store);
|
764 |
+
$this->storeBaseCurrencies[$store->getId()] = $store->getBaseCurrencyCode();
|
765 |
+
}
|
766 |
+
|
767 |
+
$this->PAGE_SIZE = Mage::getStoreConfig('integration_options/products_options/export_page_size');
|
768 |
+
$this->MAX_RESOURCES = Mage::getStoreConfig('integration_options/products_options/max_resources_load');
|
769 |
+
$this->SLEEP_TIME_SEC = Mage::getStoreConfig('integration_options/products_options/sleep_time_sec');
|
770 |
+
$this->MAX_ERRORS = Mage::getStoreConfig('integration_options/products_options/max_errors');
|
771 |
+
|
772 |
+
$this->taxHelper = Mage::helper('tax');
|
773 |
+
$this->currencyConverter = Mage::helper('directory');
|
774 |
+
$this->taxCalculation = Mage::getSingleton('tax/calculation');
|
775 |
+
$this->categoryRule = Mage::getSingleton('catalogrule/rule');
|
776 |
+
$this->tmpProductsXmlPath = Mage::getModuleDir('', 'Fruugo_Integration') . '/controllers/tmp_products.xml';
|
777 |
+
$this->reportPath = Mage::getModuleDir('', 'Fruugo_Integration') . '/controllers/report.json';
|
778 |
+
$this->report = array(
|
779 |
+
'processing_time_sec' => 0,
|
780 |
+
'start_time_utc' => 0,
|
781 |
+
'end_time_utc' => 0,
|
782 |
+
'total_exported' => 0,
|
783 |
+
'total_processed' => 0,
|
784 |
+
'max_ram_usage_mb' => 0,
|
785 |
+
'avg_ram_usage_mb' => 0,
|
786 |
+
'error_count' => 0,
|
787 |
+
'errors' => array(),
|
788 |
+
'status' => '',
|
789 |
+
'avg_load' => 0,
|
790 |
+
'load_above_threshold_count' => 0,
|
791 |
+
'time_paused_sec' => 0,
|
792 |
+
'xml_file_size_mb' => 0,
|
793 |
+
);
|
794 |
+
}
|
795 |
+
|
796 |
+
// This monitors system resources and pauses execution if the utilisation is
|
797 |
+
// above the configured threshold.
|
798 |
+
// Recommended for systems with large numbers of products
|
799 |
+
// Note: this feature is not available on windows servers.
|
800 |
+
protected $loadCheckCount = 0;
|
801 |
+
protected $loadCheckTotal = 0.0;
|
802 |
+
protected function checkServerLoad()
|
803 |
+
{
|
804 |
+
if (stristr(PHP_OS, 'win')) {
|
805 |
+
return;
|
806 |
+
}
|
807 |
+
$systemLoad = sys_getloadavg();
|
808 |
+
$this->loadCheckTotal += $systemLoad[0];
|
809 |
+
$this->loadCheckCount++;
|
810 |
+
|
811 |
+
$this->report['avg_load'] = $this->loadCheckTotal / $this->loadCheckCount;
|
812 |
+
|
813 |
+
if ($this->devMode) {
|
814 |
+
$this->_writeLog('Server load is ' . $systemLoad[0], self::$DEBUG);
|
815 |
+
}
|
816 |
+
|
817 |
+
if (!$this->MONITOR_RESOURCES) {
|
818 |
+
return;
|
819 |
+
}
|
820 |
+
|
821 |
+
$systemLoad = sys_getloadavg();
|
822 |
+
if ($this->devMode && $systemLoad[0] > $this->MAX_RESOURCES) {
|
823 |
+
$this->_writeLog(
|
824 |
+
'High server load detected. Usage of ' . $systemLoad[0] .
|
825 |
+
' is greater than configured maximum of ' . $this->MAX_RESOURCES .
|
826 |
+
'. The product export job will now pause for ' . $this->SLEEP_TIME_SEC . ' seconds.',
|
827 |
+
self::$DEBUG
|
828 |
+
);
|
829 |
+
$this->report['load_above_threshold_count'] += 1;
|
830 |
+
|
831 |
+
sleep($this->SLEEP_TIME_SEC);
|
832 |
+
|
833 |
+
$this->report['time_paused_sec'] += $this->SLEEP_TIME_SEC;
|
834 |
+
$this->_writeLog('Fruugo export resumed after waiting ' . $this->SLEEP_TIME_SEC . ' seconds.', self::$DEBUG);
|
835 |
+
}
|
836 |
+
}
|
837 |
+
|
838 |
+
|
839 |
}
|
app/code/community/Fruugo/Integration/Helper/ProductsFeedGeneratorProfiler.php
ADDED
@@ -0,0 +1,340 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.bitbu
|
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 |
+
require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/Logger.php';
|
24 |
+
|
25 |
+
/*
|
26 |
+
* This class is for profiling and testing the performance of the feed generation. Make sure you run
|
27 |
+
* this after you make changes to check for memory leaks and performance issues.
|
28 |
+
* It can be run by hitting this URL: http://127.0.0.1:8080/index.php/fruugo-integration/products/profiler
|
29 |
+
* NOTE: you must be in dev mode for this to work.
|
30 |
+
*/
|
31 |
+
class Fruugo_Integration_ProductsFeedGeneratorProfiler extends Fruugo_Integration_ProductsFeedGenerator
|
32 |
+
{
|
33 |
+
protected $currentTimer = array();
|
34 |
+
|
35 |
+
protected $openTimers = array();
|
36 |
+
|
37 |
+
protected $executionTree = array();
|
38 |
+
|
39 |
+
public function generateProdcutsFeed($cached = false) {
|
40 |
+
$devMode = Mage::getStoreConfig('integration_options/orders_options/dev_mode');
|
41 |
+
if($devMode != '1') {
|
42 |
+
throw new Exception('You must be in dev mode to use this feature');
|
43 |
+
}
|
44 |
+
Fruugo_Integration_Helper_Logger::$LOG_LEVEL = self::$DEBUG;
|
45 |
+
|
46 |
+
$this->_writeLog('--------------------------------------------', self::$DEBUG);
|
47 |
+
//$this->_testReport();
|
48 |
+
//return false;
|
49 |
+
$this->_writeLog('Profiling the product feed generator...', self::$DEBUG);
|
50 |
+
$this->_startTimer('generateProdcutsFeed');
|
51 |
+
$val = parent::generateProdcutsFeed($cached);
|
52 |
+
$this->_stopTimer('generateProdcutsFeed');
|
53 |
+
$this->_writeTimerLog();
|
54 |
+
return $val;
|
55 |
+
}
|
56 |
+
|
57 |
+
protected function _fillProductXml($product, $storelangsMapping) {
|
58 |
+
$this->_startTimer('_fillProductXml');
|
59 |
+
$val = parent::_fillProductXml($product, $storelangsMapping);
|
60 |
+
$this->_stopTimer('_fillProductXml');
|
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');
|
68 |
+
return $val;
|
69 |
+
}
|
70 |
+
|
71 |
+
protected function _convertCurrency($price, $baseCurrencyCode, $currencyCode) {
|
72 |
+
$this->_startTimer('_convertCurrency');
|
73 |
+
$val = parent::_convertCurrency($price, $baseCurrencyCode, $currencyCode);
|
74 |
+
$this->_stopTimer('_convertCurrency');
|
75 |
+
return $val;
|
76 |
+
}
|
77 |
+
|
78 |
+
protected function _getProductImages($product, $parentProduct) {
|
79 |
+
$this->_startTimer('_getProductImages');
|
80 |
+
$val = parent::_getProductImages($product, $parentProduct);
|
81 |
+
$this->_stopTimer('_getProductImages');
|
82 |
+
return $val;
|
83 |
+
}
|
84 |
+
|
85 |
+
protected function _getParentProduct($product)
|
86 |
+
{
|
87 |
+
$this->_startTimer('_getParentProduct');
|
88 |
+
$val = parent::_getParentProduct($product);
|
89 |
+
$this->_stopTimer('_getParentProduct');
|
90 |
+
return $val;
|
91 |
+
}
|
92 |
+
|
93 |
+
protected function _startTimer($id, $message=null)
|
94 |
+
{
|
95 |
+
if (!array_key_exists($id, $this->currentTimer)) {
|
96 |
+
$this->currentTimer[$id] = array(
|
97 |
+
'start_time' => 0,
|
98 |
+
'stop_time' => 0,
|
99 |
+
'total' => 0,
|
100 |
+
'length' => 0,
|
101 |
+
'count' => 0,
|
102 |
+
'callers' => array(),
|
103 |
+
'start_memory' => 0,
|
104 |
+
'total_memory_leaked' => 0,
|
105 |
+
);
|
106 |
+
}
|
107 |
+
|
108 |
+
$this->currentTimer[$id]['start_memory'] = memory_get_usage(false);
|
109 |
+
|
110 |
+
if(count($this->openTimers) > 0) {
|
111 |
+
$caller = $this->openTimers[0];
|
112 |
+
if(!array_key_exists($caller, $this->currentTimer[$id]['callers'])) {
|
113 |
+
$this->currentTimer[$id]['callers'][$caller] = 1;
|
114 |
+
}
|
115 |
+
$this->currentTimer[$id]['callers'][$caller]++;
|
116 |
+
|
117 |
+
$this->_addToTree($id, $caller);
|
118 |
+
}
|
119 |
+
else {
|
120 |
+
$this->_addToTree($id, null);
|
121 |
+
}
|
122 |
+
|
123 |
+
array_unshift($this->openTimers, $id);
|
124 |
+
|
125 |
+
$this->currentTimer[$id]['start_time'] = microtime(true);
|
126 |
+
if($message) {
|
127 |
+
$this->_writeLog($message, self::$DEBUG);
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
protected function _stopTimer($id, $message=null, $threshhold = 1000, $threshholdMessage = '')
|
132 |
+
{
|
133 |
+
if (!array_key_exists($id, $this->currentTimer)) {
|
134 |
+
return;
|
135 |
+
}
|
136 |
+
|
137 |
+
$start_memory = $this->currentTimer[$id]['start_memory'];
|
138 |
+
$this->currentTimer[$id]['total_memory_leaked'] += memory_get_usage(false) - $start_memory;
|
139 |
+
|
140 |
+
$this->currentTimer[$id]['stop_time'] = microtime(true);
|
141 |
+
$stopTimer = ($this->currentTimer[$id]['stop_time'] - $this->currentTimer[$id]['start_time']);
|
142 |
+
$this->currentTimer[$id]['length'] = $stopTimer;
|
143 |
+
$this->currentTimer[$id]['total'] += $this->currentTimer[$id]['length'];
|
144 |
+
$this->currentTimer[$id]['count'] += 1;
|
145 |
+
|
146 |
+
if (round($stopTimer) >= $threshhold) {
|
147 |
+
$this->_writeLog('=================================================', self::$DEBUG);
|
148 |
+
$this->_writeLog($id . ' exceeded threshold of ' . $threshhold . ' with the time of ' . $stopTimer . ' threshholdMessage ' . $threshholdMessage, self::$DEBUG);
|
149 |
+
}
|
150 |
+
|
151 |
+
if($message) {
|
152 |
+
$this->_writeLog($message . ' in ' . $stopTimer . ' seconds.' . ' Running total: ' . $this->currentTimer[$id]['total'], self::$DEBUG);
|
153 |
+
}
|
154 |
+
|
155 |
+
array_shift($this->openTimers);
|
156 |
+
}
|
157 |
+
|
158 |
+
protected function _writeTimerLog()
|
159 |
+
{
|
160 |
+
$this->_writeLog('Profiling Statistics', self::$DEBUG);
|
161 |
+
foreach ($this->currentTimer as $key => $value) {
|
162 |
+
$this->_writeLog("\n", self::$DEBUG);
|
163 |
+
$this->_writeLog('--------------------------------------------------', self::$DEBUG);
|
164 |
+
$this->_writeLog('Stats for ' . $key);
|
165 |
+
$this->_writeLog('Total executing time:' . round($value['total'], 7), self::$DEBUG);
|
166 |
+
$this->_writeLog('Average execution time: ' . round($value['total']/$value['count'], 7), self::$DEBUG);
|
167 |
+
$this->_writeLog('Times executed: ' . $value['count'], self::$DEBUG);
|
168 |
+
$this->_writeLog('Memory leaked: ' . (($value['total_memory_leaked'] / 1024) / 1024) . ' MB', self::$DEBUG);
|
169 |
+
$this->_writeLog('Called by: ', self::$DEBUG);
|
170 |
+
foreach ($value['callers'] as $caller => $callerCount) {
|
171 |
+
$this->_writeLog("\t" . $caller . ' x ' . $callerCount, self::$DEBUG);
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
$this->_writeLog("\n", self::$DEBUG);
|
176 |
+
$this->_writeLog('--------------------------------------------', self::$DEBUG);
|
177 |
+
$this->_generateTree();
|
178 |
+
}
|
179 |
+
|
180 |
+
protected function _generateTree() {
|
181 |
+
foreach($this->executionTree[0] as $parentkey => $val) {
|
182 |
+
$this->_writeLog($parentkey, self::$DEBUG);
|
183 |
+
$this->_writeTree(1, $parentkey, $parentkey, 0);
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
// Note: the tree view is not a finished feature, and there may be display issues
|
188 |
+
protected function _writeTree($x, $parent, $caller, $indentLen) {
|
189 |
+
|
190 |
+
if($x >= count($this->executionTree)) {
|
191 |
+
return;
|
192 |
+
}
|
193 |
+
if(!array_key_exists($caller, $this->executionTree[$x])) {
|
194 |
+
return;
|
195 |
+
}
|
196 |
+
foreach($this->executionTree[$x][$caller]['children'] as $key => $val) {
|
197 |
+
$len = strlen($caller) + $indentLen;
|
198 |
+
$indent = str_pad("", $len, " ");
|
199 |
+
$line = str_pad("", strlen($caller)/3, "-");
|
200 |
+
$arrow = '|' . $line . '(x ' . $val['call_count'] . ')' . $line . '> ';
|
201 |
+
$this->_writeLog( $indent . $arrow . $key, self::$DEBUG);
|
202 |
+
$this->_writeTree($x + 1, $caller, $key, strlen($caller . $arrow));
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
protected function _addToTree($id, $caller=null) {
|
207 |
+
$depth = count($this->openTimers);
|
208 |
+
if(!isset($this->executionTree[$depth])) {
|
209 |
+
$this->executionTree[$depth] = array();
|
210 |
+
}
|
211 |
+
$data = array(
|
212 |
+
'name' => $id,
|
213 |
+
'depth' => $depth,
|
214 |
+
'children' => array(),
|
215 |
+
'parents' => array()
|
216 |
+
);
|
217 |
+
|
218 |
+
$childData = array(
|
219 |
+
'name' => $id,
|
220 |
+
'depth' => $depth,
|
221 |
+
'children' => array(),
|
222 |
+
'parents' => array(),
|
223 |
+
'call_count' => 0,
|
224 |
+
);
|
225 |
+
|
226 |
+
if($caller === null) {
|
227 |
+
$this->executionTree[$depth] = array();
|
228 |
+
$this->executionTree[$depth][$id] = $data;
|
229 |
+
return;
|
230 |
+
}
|
231 |
+
else{
|
232 |
+
if(!array_key_exists($caller, $this->executionTree[$depth])) {
|
233 |
+
$this->executionTree[$depth][$caller] = $data;
|
234 |
+
}
|
235 |
+
if(!array_key_exists($id, $this->executionTree[$depth][$caller]['children'])) {
|
236 |
+
$this->executionTree[$depth][$caller]['children'][$id] = $childData;
|
237 |
+
}
|
238 |
+
$this->executionTree[$depth][$caller]['children'][$id]['call_count'] += 1;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
// Some fixtures data for testing the report generation
|
243 |
+
protected function _testReport() {
|
244 |
+
|
245 |
+
$this->executionTree = Array (
|
246 |
+
0 => Array
|
247 |
+
(
|
248 |
+
'generateProdcutsFeed' => Array
|
249 |
+
(
|
250 |
+
'name' => 'generateProdcutsFeed',
|
251 |
+
'depth' => 0,
|
252 |
+
'children' => Array(),
|
253 |
+
'parents' => Array(),
|
254 |
+
)
|
255 |
+
),
|
256 |
+
|
257 |
+
1 => Array
|
258 |
+
(
|
259 |
+
'generateProdcutsFeed' => Array
|
260 |
+
(
|
261 |
+
'name' => '_fillProductXml',
|
262 |
+
'depth' => 1,
|
263 |
+
'children' => Array
|
264 |
+
(
|
265 |
+
'_fillProductXml' => Array
|
266 |
+
(
|
267 |
+
'name' => '_fillProductXml',
|
268 |
+
'depth' => 1,
|
269 |
+
'children' => Array(),
|
270 |
+
'parents' => Array(),
|
271 |
+
|
272 |
+
'call_count' => 477,
|
273 |
+
)
|
274 |
+
|
275 |
+
),
|
276 |
+
|
277 |
+
'parents' => Array(),
|
278 |
+
)
|
279 |
+
|
280 |
+
),
|
281 |
+
|
282 |
+
2 => Array
|
283 |
+
(
|
284 |
+
'_fillProductXml' => Array
|
285 |
+
(
|
286 |
+
'name' => '_getParentProduct',
|
287 |
+
'depth' => 2,
|
288 |
+
'children' => Array
|
289 |
+
(
|
290 |
+
'_getParentProduct' => Array
|
291 |
+
(
|
292 |
+
'name' => '_getParentProduct',
|
293 |
+
'depth' => 2,
|
294 |
+
'children' => Array(),
|
295 |
+
'parents' => Array(),
|
296 |
+
'call_count' => 477,
|
297 |
+
),
|
298 |
+
|
299 |
+
'_getProductImages' => Array
|
300 |
+
(
|
301 |
+
'name' => '_getProductImages',
|
302 |
+
'depth' => 2,
|
303 |
+
'children' => Array(),
|
304 |
+
'parents' => Array(),
|
305 |
+
'call_count' => 477,
|
306 |
+
),
|
307 |
+
|
308 |
+
'_getAttributesText' => Array
|
309 |
+
(
|
310 |
+
'name' => '_getAttributesText',
|
311 |
+
'depth' => 2,
|
312 |
+
'children' => Array(),
|
313 |
+
'parents' => Array(),
|
314 |
+
'call_count' => 3762,
|
315 |
+
),
|
316 |
+
|
317 |
+
'_convertCurrency' => Array
|
318 |
+
(
|
319 |
+
'name' => '_convertCurrency',
|
320 |
+
'depth' => 2,
|
321 |
+
'children' => Array(),
|
322 |
+
'parents' => Array(),
|
323 |
+
'call_count' => 1455,
|
324 |
+
),
|
325 |
+
|
326 |
+
),
|
327 |
+
|
328 |
+
'parents' => Array
|
329 |
+
(
|
330 |
+
),
|
331 |
+
|
332 |
+
)
|
333 |
+
|
334 |
+
)
|
335 |
+
|
336 |
+
);
|
337 |
+
|
338 |
+
$this->_generateTree();
|
339 |
+
}
|
340 |
+
}
|
app/code/community/Fruugo/Integration/Model/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Model/Adminhtml/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Backend/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/ExportPageSize.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_ExportPageSize
|
28 |
+
{
|
29 |
+
/**
|
30 |
+
* Options getter
|
31 |
+
*
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
public function toOptionArray()
|
35 |
+
{
|
36 |
+
$exportPageSize = array();
|
37 |
+
|
38 |
+
array_push($exportPageSize, array('value' => 20, 'label' => 20));
|
39 |
+
array_push($exportPageSize, array('value' => 30, 'label' => 30));
|
40 |
+
array_push($exportPageSize, array('value' => 50, 'label' => 50));
|
41 |
+
array_push($exportPageSize, array('value' => 70, 'label' => 70));
|
42 |
+
array_push($exportPageSize, array('value' => 100, 'label' => 100));
|
43 |
+
array_push($exportPageSize, array('value' => 150, 'label' => 150));
|
44 |
+
array_push($exportPageSize, array('value' => 200, 'label' => 200));
|
45 |
+
array_push($exportPageSize, array('value' => 300, 'label' => 300));
|
46 |
+
array_push($exportPageSize, array('value' => 500, 'label' => 500));
|
47 |
+
array_push($exportPageSize, array('value' => 1000, 'label' => 1000));
|
48 |
+
array_push($exportPageSize, array('value' => 3000, 'label' => 3000));
|
49 |
+
|
50 |
+
return $exportPageSize;
|
51 |
+
}
|
52 |
+
}
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/MaxErrors.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_MaxErrors
|
28 |
+
{
|
29 |
+
/**
|
30 |
+
* Options getter
|
31 |
+
*
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
public function toOptionArray()
|
35 |
+
{
|
36 |
+
$maxErrors = array();
|
37 |
+
|
38 |
+
array_push($maxErrors, array('value' => -1, 'label' => -1));
|
39 |
+
array_push($maxErrors, array('value' => 5, 'label' => 5));
|
40 |
+
array_push($maxErrors, array('value' => 10, 'label' => 10));
|
41 |
+
array_push($maxErrors, array('value' => 15, 'label' => 15));
|
42 |
+
array_push($maxErrors, array('value' => 20, 'label' => 20));
|
43 |
+
array_push($maxErrors, array('value' => 25, 'label' => 25));
|
44 |
+
array_push($maxErrors, array('value' => 30, 'label' => 30));
|
45 |
+
array_push($maxErrors, array('value' => 40, 'label' => 40));
|
46 |
+
array_push($maxErrors, array('value' => 50, 'label' => 50));
|
47 |
+
array_push($maxErrors, array('value' => 70, 'label' => 70));
|
48 |
+
array_push($maxErrors, array('value' => 100, 'label' => 100));
|
49 |
+
|
50 |
+
return $maxErrors;
|
51 |
+
}
|
52 |
+
}
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/MaxResourcesLoad.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_MaxResourcesLoad
|
28 |
+
{
|
29 |
+
/**
|
30 |
+
* Options getter
|
31 |
+
*
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
public function toOptionArray()
|
35 |
+
{
|
36 |
+
$maxResources = array();
|
37 |
+
|
38 |
+
array_push($maxResources, array('value' => 0.25, 'label' => '0.25'));
|
39 |
+
array_push($maxResources, array('value' => 0.5, 'label' => '0.5'));
|
40 |
+
array_push($maxResources, array('value' => 1.0, 'label' => '1.0'));
|
41 |
+
array_push($maxResources, array('value' => 1.5, 'label' => '1.5'));
|
42 |
+
array_push($maxResources, array('value' => 2.0, 'label' => '2.0'));
|
43 |
+
|
44 |
+
return $maxResources;
|
45 |
+
}
|
46 |
+
}
|
app/code/community/Fruugo/Integration/Model/Adminhtml/System/Config/Source/SleepTimeSec.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_SleepTimeSec
|
28 |
+
{
|
29 |
+
/**
|
30 |
+
* Options getter
|
31 |
+
*
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
public function toOptionArray()
|
35 |
+
{
|
36 |
+
$sleepTimeSecs = array();
|
37 |
+
|
38 |
+
array_push($sleepTimeSecs, array('value' => 5, 'label' => 5));
|
39 |
+
array_push($sleepTimeSecs, array('value' => 10, 'label' => 10));
|
40 |
+
array_push($sleepTimeSecs, array('value' => 15, 'label' => 15));
|
41 |
+
array_push($sleepTimeSecs, array('value' => 20, 'label' => 20));
|
42 |
+
array_push($sleepTimeSecs, array('value' => 25, 'label' => 25));
|
43 |
+
array_push($sleepTimeSecs, array('value' => 30, 'label' => 30));
|
44 |
+
array_push($sleepTimeSecs, array('value' => 40, 'label' => 40));
|
45 |
+
array_push($sleepTimeSecs, array('value' => 50, 'label' => 50));
|
46 |
+
array_push($sleepTimeSecs, array('value' => 60, 'label' => 60));
|
47 |
+
array_push($sleepTimeSecs, array('value' => 90, 'label' => 90));
|
48 |
+
array_push($sleepTimeSecs, array('value' => 120, 'label' => 120));
|
49 |
+
|
50 |
+
return $sleepTimeSecs;
|
51 |
+
}
|
52 |
+
}
|
app/code/community/Fruugo/Integration/Model/CronJobObserver.php
CHANGED
@@ -30,14 +30,10 @@ class Fruugo_Integration_Model_CronJobObserver
|
|
30 |
{
|
31 |
// Export products xml
|
32 |
$productsFeedGenerator = new Fruugo_Integration_ProductsFeedGenerator();
|
33 |
-
$xml = $productsFeedGenerator->generateProdcutsFeed(false)->asXML();
|
34 |
|
35 |
try {
|
36 |
Fruugo_Integration_Helper_Logger::log("Writing exported products to file.");
|
37 |
-
$
|
38 |
-
$myfile = fopen($outputDir, "w");
|
39 |
-
fwrite($myfile, $xml);
|
40 |
-
fclose($myfile);
|
41 |
Fruugo_Integration_Helper_Logger::log("Writing products data feed finished.");
|
42 |
} catch (Exception $e) {
|
43 |
Mage::logException($e);
|
30 |
{
|
31 |
// Export products xml
|
32 |
$productsFeedGenerator = new Fruugo_Integration_ProductsFeedGenerator();
|
|
|
33 |
|
34 |
try {
|
35 |
Fruugo_Integration_Helper_Logger::log("Writing exported products to file.");
|
36 |
+
$productsFeedGenerator->generateProdcutsFeed(false);
|
|
|
|
|
|
|
37 |
Fruugo_Integration_Helper_Logger::log("Writing products data feed finished.");
|
38 |
} catch (Exception $e) {
|
39 |
Mage::logException($e);
|
app/code/community/Fruugo/Integration/controllers/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/controllers/.gitignore
CHANGED
@@ -1 +1,3 @@
|
|
1 |
-
*.xml
|
|
|
|
1 |
+
*.xml
|
2 |
+
report.json
|
3 |
+
products.lock
|
app/code/community/Fruugo/Integration/controllers/ProductsController.php
CHANGED
@@ -21,6 +21,7 @@
|
|
21 |
*/
|
22 |
|
23 |
require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/ProductsFeedGenerator.php';
|
|
|
24 |
require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/FruugoCountriesSeeder.php';
|
25 |
|
26 |
class Fruugo_Integration_ProductsController extends Mage_Core_Controller_Front_Action
|
@@ -28,50 +29,43 @@ class Fruugo_Integration_ProductsController extends Mage_Core_Controller_Front_A
|
|
28 |
public function indexAction()
|
29 |
{
|
30 |
$productsFeedGenerator = new Fruugo_Integration_ProductsFeedGenerator();
|
31 |
-
$
|
32 |
-
$this->
|
33 |
-
$this->streamXmlFile($productsXml->asXML());
|
34 |
}
|
35 |
|
36 |
-
public function
|
37 |
{
|
38 |
-
$productsFeedGenerator = new
|
39 |
-
$cachedFile = $productsFeedGenerator->generateProdcutsFeed(
|
40 |
-
|
41 |
-
$productsXmlStr = file_get_contents($cachedFile);
|
42 |
-
$this->streamXmlFile($productsXmlStr);
|
43 |
-
} else {
|
44 |
-
$this->indexAction();
|
45 |
-
}
|
46 |
}
|
47 |
|
48 |
-
|
49 |
{
|
50 |
-
$
|
51 |
-
$
|
52 |
-
|
53 |
-
fclose($productsFeedFile);
|
54 |
}
|
55 |
|
56 |
-
private function streamXmlFile($
|
57 |
{
|
58 |
-
if (
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
}
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
|
72 |
-
$
|
73 |
-
|
74 |
-
|
75 |
exit;
|
76 |
}
|
77 |
|
21 |
*/
|
22 |
|
23 |
require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/ProductsFeedGenerator.php';
|
24 |
+
require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/ProductsFeedGeneratorProfiler.php';
|
25 |
require_once Mage::getModuleDir('', 'Fruugo_Integration') . '/Helper/FruugoCountriesSeeder.php';
|
26 |
|
27 |
class Fruugo_Integration_ProductsController extends Mage_Core_Controller_Front_Action
|
29 |
public function indexAction()
|
30 |
{
|
31 |
$productsFeedGenerator = new Fruugo_Integration_ProductsFeedGenerator();
|
32 |
+
$cachedFile = $productsFeedGenerator->generateProdcutsFeed(false);
|
33 |
+
$this->streamXmlFile($cachedFile);
|
|
|
34 |
}
|
35 |
|
36 |
+
public function profilerAction()
|
37 |
{
|
38 |
+
$productsFeedGenerator = new Fruugo_Integration_ProductsFeedGeneratorProfiler();
|
39 |
+
$cachedFile = $productsFeedGenerator->generateProdcutsFeed(false);
|
40 |
+
$this->streamXmlFile($cachedFile);
|
|
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
|
43 |
+
public function dataFeedAction()
|
44 |
{
|
45 |
+
$productsFeedGenerator = new Fruugo_Integration_ProductsFeedGenerator();
|
46 |
+
$cachedFile = $productsFeedGenerator->generateProdcutsFeed(true);
|
47 |
+
$this->streamXmlFile($cachedFile);
|
|
|
48 |
}
|
49 |
|
50 |
+
private function streamXmlFile($cachedFile)
|
51 |
{
|
52 |
+
if (!is_file($cachedFile) || !is_readable($cachedFile)) {
|
53 |
+
// return 404
|
54 |
+
$this->norouteAction();
|
55 |
+
return;
|
56 |
}
|
57 |
|
58 |
+
$this->getResponse()
|
59 |
+
->setHttpResponseCode(200)
|
60 |
+
->setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0', true)
|
61 |
+
->setHeader('Pragma', 'public', true)
|
62 |
+
->setHeader('Content-type', 'application/force-download')
|
63 |
+
->setHeader('Content-Length', filesize($cachedFile))
|
64 |
+
->setHeader('Content-Disposition', 'attachment' . '; filename=' . basename($cachedFile));
|
65 |
|
66 |
+
$this->getResponse()->clearBody();
|
67 |
+
$this->getResponse()->sendHeaders();
|
68 |
+
readfile($cachedFile);
|
69 |
exit;
|
70 |
}
|
71 |
|
app/code/community/Fruugo/Integration/data/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/catalog/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/catalog/product/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/sales/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/sales/order/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/design/adminhtml/default/default/template/integration/sales/order/view/.DS_Store
DELETED
Binary file
|
app/code/community/Fruugo/Integration/etc/config.xml
CHANGED
@@ -24,7 +24,7 @@
|
|
24 |
<config>
|
25 |
<modules>
|
26 |
<Fruugo_Integration>
|
27 |
-
<version>1.0.
|
28 |
</Fruugo_Integration>
|
29 |
</modules>
|
30 |
<global>
|
@@ -145,6 +145,11 @@
|
|
145 |
<products_options>
|
146 |
<export_frequency>12</export_frequency>
|
147 |
<descrption_type>long</descrption_type>
|
|
|
|
|
|
|
|
|
|
|
148 |
</products_options>
|
149 |
<orders_options>
|
150 |
<fetch_frequency>4</fetch_frequency>
|
24 |
<config>
|
25 |
<modules>
|
26 |
<Fruugo_Integration>
|
27 |
+
<version>1.0.6</version> <!-- Version number of your module -->
|
28 |
</Fruugo_Integration>
|
29 |
</modules>
|
30 |
<global>
|
145 |
<products_options>
|
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>
|
app/code/community/Fruugo/Integration/etc/system.xml
CHANGED
@@ -119,6 +119,46 @@
|
|
119 |
<show_in_store>1</show_in_store>
|
120 |
<comment>Choose a default store for a language to export product description.</comment>
|
121 |
</language_store>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
</fields>
|
123 |
</products_options>
|
124 |
<orders_options translate="label" module="integration">
|
119 |
<show_in_store>1</show_in_store>
|
120 |
<comment>Choose a default store for a language to export product description.</comment>
|
121 |
</language_store>
|
122 |
+
<export_page_size translate="label">
|
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">
|
app/design/adminhtml/default/default/template/integration/.DS_Store
DELETED
Binary file
|
app/design/adminhtml/default/default/template/integration/catalog/.DS_Store
DELETED
Binary file
|
app/design/adminhtml/default/default/template/integration/catalog/product/.DS_Store
DELETED
Binary file
|
app/design/adminhtml/default/default/template/integration/sales/.DS_Store
DELETED
Binary file
|
app/design/adminhtml/default/default/template/integration/sales/order/.DS_Store
DELETED
Binary file
|
app/design/adminhtml/default/default/template/integration/sales/order/view/.DS_Store
DELETED
Binary file
|
package.xml
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Fruugo_Integration</name>
|
4 |
-
<version>1.0.
|
5 |
<stability>stable</stability>
|
6 |
<license uri="http://www.gnu.org/licenses/">GNU</license>
|
7 |
<channel>community</channel>
|
@@ -16,19 +16,13 @@ This plugin mainly performs two tasks:
|
|
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:
|
18 |

|
19 |
-
-
|
20 |

|
21 |
-
-
|
22 |
-

|
23 |
-
- Added options to choose product description and short description merge order
|
24 |
-

|
25 |
-
- Add product discount price when it is set
|
26 |
-

|
27 |
-
- Custom Fruugo shipping method for Fruugo orders</notes>
|
28 |
<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>
|
29 |
-
<date>2015-
|
30 |
-
<time>10:
|
31 |
-
<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"
|
32 |
<compatible/>
|
33 |
<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.8</min><max/></package></required></dependencies>
|
34 |
</package>
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Fruugo_Integration</name>
|
4 |
+
<version>1.0.6</version>
|
5 |
<stability>stable</stability>
|
6 |
<license uri="http://www.gnu.org/licenses/">GNU</license>
|
7 |
<channel>community</channel>
|
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:
|
18 |

|
19 |
+
- Only add one price tag for each product
|
20 |

|
21 |
+
- Enabled currency conversion</notes>
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
<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>
|
23 |
+
<date>2015-12-21</date>
|
24 |
+
<time>10:13:13</time>
|
25 |
+
<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="c0969f7217b6e4eb1c875e00afc21227"/><file name="ProductsFeedGenerator.php" hash="a1238387664193d9afb988a4c2114242"/><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=".gitignore" hash="a1d994a3c45d2cb16e44c5a311159532"/></dir><dir name="data"><dir name="fruugo_attributes_setup"><file name="data-install-0.1.1.php" hash="73a6210fdb6c5a2edfa0222a6a239e9a"/></dir></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="57014c6e82e14791ec5ddf82b15e8a83"/><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=".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>
|
26 |
<compatible/>
|
27 |
<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.8</min><max/></package></required></dependencies>
|
28 |
</package>
|