wp_simple_products_generator - Version 1.1.1

Version Notes

+ info about attr. Set Name, notes

Download this release

Release Info

Developer Magento Core Team
Extension wp_simple_products_generator
Version 1.1.1
Comparing to
See all releases


Version 1.1.1

app/code/community/WP/GenerateSimpleProducts/Block/About.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WP_GenerateSimpleProducts_Block_About
4
+ extends Mage_Adminhtml_Block_Abstract
5
+ implements Varien_Data_Form_Element_Renderer_Interface
6
+ {
7
+
8
+ /**
9
+ * Render fieldset html
10
+ *
11
+ * @param Varien_Data_Form_Element_Abstract $element
12
+ * @return string
13
+ */
14
+ public function render(Varien_Data_Form_Element_Abstract $element)
15
+ {
16
+ $default = <<<HTML
17
+ <div style="background-color:#EAF0EE;border:1px solid #CCCCCC;margin-bottom:10px;padding:20px;">
18
+ <p>
19
+ <b style="font-size:12px;">WebAndPeople</b>, a family of niche sites, provides small businesses with everything they need to start selling online.
20
+ </p>
21
+ <p>
22
+ <strong>PREMIUM and FREE MAGENTO TEMPALTES and EXTENSIONS</strong><br />
23
+ <a href="http://web-experiment.info" target="_blank">Web-Experiment.info</a> offers a wide choice of nice-looking and easily editable free and premium Magento Themes. At Web-Experiment, you can find free downloads or buy premium tempaltes for the extremely popular Magento eCommerce platform.<br />
24
+ <strong>MAGENTO HOSTING</strong></strong><br />
25
+ <a href="http://magenting.com" target="_blank">Magenting.com</a>, a new and improved hosting solution, is allowing you to easily create, promote, and manage your online store with Magento. Magenting users will receive a valuable set of tools and features, including automatic Magento eCommerce installation, automatic Magento template installation and a free or paid professional Magento hosting account.<br />
26
+ <strong>WEB DEVELOPMENT</strong><br />
27
+ <a href="http://webandpeople.com" target="_blank">WebAndPeople.com</a> is a team of professional Web developers and designers who are some of the best in the industry. WebAndPeople provides Web application development, custom Magento theme designs, and Website design services.<br />
28
+ <br />
29
+ </p>
30
+ <p>
31
+ Our themes and extensions on <a href="http://www.magentocommerce.com/magento-connect/developer/WebAndPeople" target="_blank">MagentoConnect</a><br />
32
+ Should you have any questions <a href="http://webandpeople.com/contact.html" target="_blank">Contact Us</a> or email at <a href="mailto:support@web-experiment.info">support@web-experiment.info</a>
33
+ <br />
34
+ </p>
35
+ </div>
36
+ HTML;
37
+ $default = json_encode($default);
38
+ $html = '<div id="wp_aboutus_content"></div>
39
+
40
+ <script type="text/javascript"> //<![CDATA[
41
+
42
+ /* JSON-P implementation for Prototype.js somewhat by Dan Dean (http://www.dandean.com)
43
+ *
44
+ * *HEAVILY* based on Tobie Langel\'s version: http://gist.github.com/145466.
45
+ * Might as well just call this an iteration.
46
+ *
47
+ * This version introduces:
48
+ * - Support for predefined callbacks (Necessary for OAuth signed requests, by @rboyce)
49
+ * - Partial integration with Ajax.Responders (Thanks to @sr3d for the kick in this direction)
50
+ * - Compatibility with Prototype 1.7 (Thanks to @soung3 for the bug report)
51
+ * - Will not break if page lacks a <head> element
52
+ *
53
+ * See examples in README for usage
54
+ *
55
+ * VERSION 1.1.2
56
+ *
57
+ * new Ajax.JSONRequest(url, options);
58
+ * - url (String): JSON-P endpoint url.
59
+ * - options (Object): Configuration options for the request.
60
+ */
61
+ Ajax.JSONRequest = Class.create(Ajax.Base, (function() {
62
+ var id = 0, head = document.getElementsByTagName(\'head\')[0] || document.body;
63
+ return {
64
+ initialize: function($super, url, options) {
65
+ $super(options);
66
+ this.options.url = url;
67
+ this.options.callbackParamName = this.options.callbackParamName || \'callback\';
68
+ this.options.timeout = this.options.timeout || 10; // Default timeout: 10 seconds
69
+ this.options.invokeImmediately = (!Object.isUndefined(this.options.invokeImmediately)) ? this.options.invokeImmediately : true ;
70
+
71
+ if (!Object.isUndefined(this.options.parameters) && Object.isString(this.options.parameters)) {
72
+ this.options.parameters = this.options.parameters.toQueryParams();
73
+ }
74
+
75
+ if (this.options.invokeImmediately) {
76
+ this.request();
77
+ }
78
+ },
79
+
80
+ /**
81
+ * Ajax.JSONRequest#_cleanup() -> undefined
82
+ * Cleans up after the request
83
+ **/
84
+ _cleanup: function() {
85
+ if (this.timeout) {
86
+ clearTimeout(this.timeout);
87
+ this.timeout = null;
88
+ }
89
+ if (this.transport && Object.isElement(this.transport)) {
90
+ this.transport.remove();
91
+ this.transport = null;
92
+ }
93
+ },
94
+
95
+ /**
96
+ * Ajax.JSONRequest#request() -> undefined
97
+ * Invokes the JSON-P request lifecycle
98
+ **/
99
+ request: function() {
100
+
101
+ // Define local vars
102
+ var response = new Ajax.JSONResponse(this);
103
+ var key = this.options.callbackParamName,
104
+ name = \'_prototypeJSONPCallback_\' + (id++),
105
+ complete = function() {
106
+ if (Object.isFunction(this.options.onComplete)) {
107
+ this.options.onComplete.call(this, response);
108
+ }
109
+ Ajax.Responders.dispatch(\'onComplete\', this, response);
110
+ }.bind(this);
111
+
112
+ // If the callback parameter is already defined, use that
113
+ if (this.options.parameters[key] !== undefined) {
114
+ name = this.options.parameters[key];
115
+ }
116
+ // Otherwise, add callback as a parameter
117
+ else {
118
+ this.options.parameters[key] = name;
119
+ }
120
+
121
+ // Build request URL
122
+ this.options.parameters[key] = name;
123
+ var url = this.options.url + ((this.options.url.include(\'?\') ? \'&\' : \'?\') + Object.toQueryString(this.options.parameters));
124
+
125
+ // Define callback function
126
+ window[name] = function(json) {
127
+ this._cleanup(); // Garbage collection
128
+ window[name] = undefined;
129
+
130
+ response.status = 200;
131
+ response.statusText = "OK";
132
+ response.setResponseContent(json);
133
+
134
+ if (Object.isFunction(this.options.onSuccess)) {
135
+ this.options.onSuccess.call(this, response);
136
+ }
137
+ Ajax.Responders.dispatch(\'onSuccess\', this, response);
138
+
139
+ complete();
140
+
141
+ }.bind(this);
142
+
143
+ this.transport = new Element(\'script\', { type: \'text/javascript\', src: url });
144
+
145
+ if (Object.isFunction(this.options.onCreate)) {
146
+ this.options.onCreate.call(this, response);
147
+ }
148
+ Ajax.Responders.dispatch(\'onCreate\', this);
149
+
150
+ head.appendChild(this.transport);
151
+
152
+ this.timeout = setTimeout(function() {
153
+ this._cleanup();
154
+ window[name] = Prototype.emptyFunction;
155
+ if (Object.isFunction(this.options.onFailure)) {
156
+ response.status = 504;
157
+ response.statusText = "Gateway Timeout";
158
+ this.options.onFailure.call(this, response);
159
+ }
160
+ complete();
161
+ }.bind(this), this.options.timeout * 1000);
162
+ },
163
+ toString: function() { return "[object Ajax.JSONRequest]"; }
164
+ };
165
+ })());
166
+
167
+ Ajax.JSONResponse = Class.create({
168
+ initialize: function(request) {
169
+ this.request = request;
170
+ },
171
+ request: undefined,
172
+ status: 0,
173
+ statusText: \'\',
174
+ responseJSON: undefined,
175
+ responseText: undefined,
176
+ setResponseContent: function(json) {
177
+ this.responseJSON = json;
178
+ this.responseText = Object.toJSON(json);
179
+ },
180
+ getTransport: function() {
181
+ if (this.request) return this.request.transport;
182
+ },
183
+ toString: function() { return "[object Ajax.JSONResponse]"; }
184
+ });
185
+
186
+ window.onload = function(){
187
+ var html = ' . $default . ';
188
+
189
+ new Ajax.JSONRequest(\'http://web-experiment.info/about-us.php\', {
190
+ callbackParamName: "jsoncallback",
191
+ onComplete: function(response) {
192
+ if (response.responseJSON && response.responseJSON.html) {
193
+ html = response.responseJSON.html;
194
+ }
195
+ Element.replace(\'wp_aboutus_content\', html);
196
+ }
197
+ });
198
+ };
199
+ //]]></script>';
200
+ return $html;
201
+ }
202
+ }
app/code/community/WP/GenerateSimpleProducts/Block/Config.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WP_GenerateSimpleProducts_Block_Config extends Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Super_Config
4
+ {
5
+ public function __construct()
6
+ {
7
+ parent::__construct();
8
+ $this->setTemplate('generatesimpleproducts/config.phtml');
9
+ }
10
+
11
+ protected function _prepareLayout()
12
+ {
13
+ if ($this->_getProduct()->getId()) {
14
+ $this->setChild('auto_generate_from_configurable',
15
+ $this->getLayout()->createBlock('adminhtml/widget_button')
16
+ ->setData(array(
17
+ 'label' => Mage::helper('catalog')->__('Generate / Assign All Combinations of Simple Products'),
18
+ 'class' => 'add',
19
+ 'onclick' => 'wpGenerateSimpleProducts();'
20
+ ))
21
+ );
22
+ }
23
+ return parent::_prepareLayout();
24
+ }
25
+
26
+ public function getProductId()
27
+ {
28
+ return $this->_getProduct()->getId();
29
+ }
30
+
31
+ public function getReloadUrl()
32
+ {
33
+ return $this->getUrl(
34
+ '*/*/edit',
35
+ array(
36
+ 'id' => $this->getProductId(),
37
+ 'back' => 'edit',
38
+ 'tab' => 'product_info_tabs_configurable',
39
+ )
40
+ );
41
+ }
42
+ }
app/code/community/WP/GenerateSimpleProducts/Helper/Data.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WP_GenerateSimpleProducts_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+
6
+ }
app/code/community/WP/GenerateSimpleProducts/controllers/IndexController.php ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WP_GenerateSimpleProducts_IndexController extends Mage_Adminhtml_Controller_action
4
+ {
5
+ const COMBINATIONS_MAXIMUM = 200002;
6
+ const PORTION_COUNT = 10;
7
+
8
+ private $_configurableAttributes = null;
9
+ private $_existingCombinations = null;
10
+ private $_configurableProduct = null;
11
+ private $_baseData = null;
12
+ private $_configurableAttributesIterator = null;
13
+
14
+ public function indexAction()
15
+ {
16
+ $json = array();
17
+ $blocked = (int)Mage::getSingleton('admin/session')->getGenerateSPBlocked();
18
+ $startProcess = $this->getRequest()->getParam('startProcess') ? 1 : 0;
19
+ if ($startProcess) $blocked = 0;
20
+ if (!$blocked)
21
+ {
22
+ Mage::getSingleton('admin/session')->setGenerateSPBlocked(1);
23
+ $data = $this->getRequest()->getParams();
24
+ list($done, $count, $fndCount, $crtCount, $attrSetName) = $this->_generateMissingSimpleProducts($data);
25
+ // --- save statistic ---
26
+ Mage::getSingleton('admin/session')->setGenerateSPDone($done);
27
+ Mage::getSingleton('admin/session')->setGenerateSPCount($count);
28
+ Mage::getSingleton('admin/session')->setGenerateSPFndCount($fndCount);
29
+ Mage::getSingleton('admin/session')->setGenerateSPCrtCount($crtCount);
30
+ Mage::getSingleton('admin/session')->setGenerateSPAttrSetName($attrSetName);
31
+ // --- / save statistic ---
32
+ // --- save data ---
33
+ Mage::getSingleton('admin/session')->setGenerateMainData(array(
34
+ $this->_configurableAttributes,
35
+ $this->_existingCombinations,
36
+ $this->_baseData,
37
+ $this->_configurableAttributesIterator
38
+ ));
39
+ // --- / save data ---
40
+ if ($done >= $count || $done >= self::COMBINATIONS_MAXIMUM) $json['finish'] = true;
41
+ $json['text'] = sprintf(
42
+ 'Attribute Set: <b>%s</b><br /><br />Process: %d of %d<br />Found Simple Products: %d<br />Created Simple Products: %d',
43
+ $attrSetName, $done, $count, $fndCount, $crtCount);
44
+ Mage::getSingleton('admin/session')->setGenerateSPBlocked(0);
45
+ }
46
+ $this->getResponse()->setBody(Zend_Json::encode($json));
47
+ }
48
+
49
+ private function _generateMissingSimpleProducts($data)
50
+ {
51
+ list($done, $count, $fndCount, $crtCount, $productIds, $attrSetName) = $this->_initData($data);
52
+ $i = 0; $combination = $this->_readCurrentCombination();
53
+ while ($combination)
54
+ {
55
+ $productId = $this->_getProductIdByCombination($combination);
56
+ if ($productId)
57
+ {
58
+ $productIds[] = $productId;
59
+ $fndCount++;
60
+ }
61
+ else
62
+ {
63
+ // --- create Simple Product ---
64
+ $productIds[] = $this->_createSimpleProduct($combination);
65
+ $crtCount++;
66
+ }
67
+ $combination = $this->_getNextCombination();
68
+ $done++; $i++;
69
+ if ($i >= self::PORTION_COUNT || $done >= self::COMBINATIONS_MAXIMUM) break;
70
+ }
71
+
72
+ /**
73
+ * Save product relations
74
+ */
75
+ if (count($productIds))
76
+ {
77
+ Mage::getResourceModel('catalog/product_type_configurable')
78
+ ->saveProducts($this->_configurableProduct, $productIds);
79
+
80
+ Mage::getSingleton('admin/session')->setGenerateSPProductIds($productIds);
81
+ }
82
+
83
+ return array($done, $count, $fndCount, $crtCount, $attrSetName);
84
+ }
85
+
86
+ private function _initData($data)
87
+ {
88
+ $startProcess = isset($data['startProcess']) && $data['startProcess'] ? 1 : 0;
89
+ $productId = $data['productId'];
90
+ $this->_configurableProduct = Mage::getModel('catalog/product')->load($productId);
91
+ if ($startProcess)
92
+ {
93
+ $confProduct = $this->_configurableProduct;
94
+ $attributeSetId = $confProduct->getAttributeSetId();
95
+ // --- get attributes of conf. product ---
96
+ $baseAttributes = $confProduct->getAttributes();
97
+ $baseData = $confProduct->getData();
98
+ foreach ($baseAttributes as $attr)
99
+ {
100
+ $attrCode = $attr->getAttributeCode();
101
+ if (isset($baseData[$attrCode]))
102
+ {
103
+ $attributes[$attrCode] = $baseData[$attrCode];
104
+ }
105
+ }
106
+ // --- exclude attributes ---
107
+ unset($attributes['entity_id']);
108
+ unset($attributes['type_id']);
109
+ unset($attributes['entity_type_id']);
110
+ unset($attributes['url_key']);
111
+ unset($attributes['url_path']);
112
+ unset($attributes['required_options']);
113
+ unset($attributes['has_options']);
114
+ unset($attributes['created_at']);
115
+ unset($attributes['updated_at']);
116
+ unset($attributes['thumbnail']);
117
+ unset($attributes['small_image']);
118
+ unset($attributes['image']);
119
+ unset($attributes['media_gallery']);
120
+ $this->_baseData = $attributes;
121
+ // --- / get attributes by set ---
122
+ $collection = Mage::getModel('catalog/product')->getCollection()
123
+ ->addAttributeToFilter('attribute_set_id', $attributeSetId);
124
+ $attrCodes = $iterator = array();
125
+ $maximum = 1;
126
+ foreach ($confProduct->getTypeInstance()->getConfigurableAttributes() as $attribute)
127
+ {
128
+ $attrCode = $attribute->getProductAttribute()->getAttributeCode();
129
+ $collection->addAttributeToFilter($attrCode, array('neq' => ''));
130
+ $values = self::_getAttributeValues($attribute->getProductAttribute()->getSource()->getAllOptions());
131
+ $attrCodes[$attrCode] = array(
132
+ 'id' => $attribute->getId(),
133
+ 'label' => $attribute->getLabel(),
134
+ 'position' => $attribute->getPosition(),
135
+ 'attribute_id' => $attribute->getProductAttribute()->getId(),
136
+ 'attribute_code' => $attribute->getProductAttribute()->getAttributeCode(),
137
+ 'frontend_label' => $attribute->getProductAttribute()->getFrontend()->getLabel(),
138
+ 'store_label' => $attribute->getProductAttribute()->getStoreLabel(),
139
+ 'values' => $values,
140
+ );
141
+ $maximum *= count($values);
142
+ $iterator[$attrCode] = array(
143
+ 'code' => $attrCode,
144
+ 'index' => 0,
145
+ 'count' => count($values),
146
+ 'values' => array_keys($values)
147
+ );
148
+ }
149
+ $this->_configurableAttributesIterator = $iterator;
150
+ $this->_configurableAttributes = $attrCodes;
151
+ $existingCombinations = array();
152
+ foreach ($collection as $item)
153
+ {
154
+ $itemData = $item->getData();
155
+ $key = $this->_getCombinationKey($itemData);
156
+ #Mage::log(array($itemData, $key));
157
+ if (!$key) continue;
158
+ $existingCombinations[$key] = $itemData['entity_id'];
159
+ }
160
+ $this->_existingCombinations = $existingCombinations;
161
+ $done = $fndCount = $crtCount = 0; $count = $maximum;
162
+ $productIds = array();
163
+ // --- attr Set Name ---
164
+ $attributeSetModel = Mage::getModel('eav/entity_attribute_set');
165
+ $attributeSetModel->load($attributeSetId);
166
+ $attrSetName = $attributeSetModel->getAttributeSetName();
167
+ }
168
+ else // --- read from session ---
169
+ {
170
+ // --- data ---
171
+ list(
172
+ $this->_configurableAttributes,
173
+ $this->_existingCombinations,
174
+ $this->_baseData,
175
+ $this->_configurableAttributesIterator
176
+ ) = Mage::getSingleton('admin/session')->getGenerateMainData();
177
+ // --- statistic ---
178
+ $done = Mage::getSingleton('admin/session')->getGenerateSPDone();
179
+ $count = Mage::getSingleton('admin/session')->getGenerateSPCount();
180
+ $fndCount = Mage::getSingleton('admin/session')->getGenerateSPFndCount();
181
+ $crtCount = Mage::getSingleton('admin/session')->getGenerateSPCrtCount();
182
+ $productIds = Mage::getSingleton('admin/session')->getGenerateSPProductIds();
183
+ $attrSetName = Mage::getSingleton('admin/session')->getGenerateSPAttrSetName();
184
+ }
185
+ return array($done, $count, $fndCount, $crtCount, $productIds, $attrSetName);
186
+ }
187
+
188
+ private static function _getAttributeValues($data)
189
+ {
190
+ $values = array();
191
+ foreach ($data as $info)
192
+ {
193
+ if (!$info['value']) continue;
194
+ $values[$info['value']] = $info['label'];
195
+ }
196
+ return $values;
197
+ }
198
+
199
+ private function _getCombinationKey($data)
200
+ {
201
+ $keyParts = array();
202
+ foreach ($this->_configurableAttributes as $attrCode => $info)
203
+ {
204
+ if (isset($data[$attrCode]))
205
+ {
206
+ $values = array_keys($info['values']);
207
+ if (in_array($data[$attrCode], $values))
208
+ {
209
+ $keyParts[] = $data[$attrCode];
210
+ }
211
+ }
212
+ else
213
+ {
214
+ return;
215
+ }
216
+ }
217
+ $key = implode('-', $keyParts);
218
+ return $key;
219
+ }
220
+
221
+ private function _getProductIdByCombination($combination)
222
+ {
223
+ $key = $this->_getCombinationKey($combination);
224
+ if (isset($this->_existingCombinations[$key])) return $this->_existingCombinations[$key];
225
+ return false;
226
+ }
227
+
228
+ private function _readCurrentCombination()
229
+ {
230
+ $combination = array(); $iterator = $this->_configurableAttributesIterator;
231
+ foreach ($this->_configurableAttributesIterator as $attrCode => $info)
232
+ {
233
+ if (isset($info['values'][$info['index']]))
234
+ {
235
+ $combination[$attrCode] = $info['values'][$info['index']];
236
+ }
237
+ else
238
+ {
239
+ return;
240
+ }
241
+ }
242
+ return $combination;
243
+ }
244
+
245
+ private function _getNextCombination()
246
+ {
247
+ $iterator = $this->_configurableAttributesIterator;
248
+ $lastItem = end($iterator);
249
+ $item = reset($iterator); $i = 0; $max = self::COMBINATIONS_MAXIMUM * 2;
250
+ while ($item && $i < $max)
251
+ {
252
+ $attrCode = $item['code'];
253
+ $iterator[$attrCode]['index']++;
254
+ if ($iterator[$attrCode]['index'] > $iterator[$attrCode]['count']-1 && $lastItem['code'] != $attrCode)
255
+ {
256
+ $iterator[$attrCode]['index'] = 0;
257
+ $item = next($iterator);
258
+ }
259
+ else
260
+ {
261
+ $item = false;
262
+ }
263
+ $i++;
264
+ }
265
+ $this->_configurableAttributesIterator = $iterator;
266
+ return $this->_readCurrentCombination();
267
+ }
268
+
269
+ private function _createSimpleProduct($combination)
270
+ {
271
+ $product = Mage::getModel('catalog/product');
272
+ // --- set data ---
273
+ $data = $this->_getSimpleProductDataByCombination($combination);
274
+ $product->setData($data);
275
+ $product->setWebsiteIds(self::_getWebsiteIds());
276
+ // --- add stock info ---
277
+ $stockData = $product->getStockData();
278
+ $stockData['qty'] = 1000;
279
+ $stockData['is_in_stock'] = 1;
280
+ $product->setStockData($stockData);
281
+ // --- / add stock info ---
282
+ $product->save();
283
+ return $product->getId();
284
+ }
285
+
286
+ private function _getSimpleProductDataByCombination($combination)
287
+ {
288
+ list($skuPrefix, $namePrefix) = $this->_getSimpleProductPrefixes($combination);
289
+ $data = $this->_baseData;
290
+ $data['sku'] = $data['sku'] . $skuPrefix;
291
+ $data['name'] = $data['name'] . $namePrefix;
292
+ $data['type_id'] = Mage_Catalog_Model_Product_Type::DEFAULT_TYPE; // Simple
293
+ $data['weight'] = 1.0;
294
+ $data['visibility'] = 1; // Not Visible Individually
295
+ $data['status'] = 1; // enabled
296
+ foreach ($combination as $attrCode => $value)
297
+ {
298
+ $data[$attrCode] = $value;
299
+ }
300
+ #Mage::log($data);
301
+ return $data;
302
+ }
303
+
304
+ private function _getSimpleProductPrefixes($combination)
305
+ {
306
+ $skuPrefixParts = $namePrefixParts = array();
307
+ $configurableAttributes = $this->_configurableAttributes;
308
+ foreach ($combination as $attrCode => $value)
309
+ {
310
+ // --- generate Name of the Simple Product ---
311
+ $prefix = $configurableAttributes[$attrCode]['label'] . ' ' . $configurableAttributes[$attrCode]['values'][$value];
312
+ $namePrefixParts[] = $prefix;
313
+ $skuPrefixParts[] = str_replace(' ', '-', $prefix);
314
+ }
315
+ $skuPrefix = '-' . implode('-', $skuPrefixParts);
316
+ $namePrefix = ' - ' . implode(' - ', $namePrefixParts);
317
+ return array($skuPrefix, $namePrefix);
318
+ }
319
+
320
+ private static function _getWebsiteIds()
321
+ {
322
+ $websiteIds = Mage::getSingleton('admin/session')->getProductWebsiteIds();
323
+ if (is_null($websiteIds))
324
+ {
325
+ $websites = Mage::app()->getWebsites();
326
+ $websiteIds = array();
327
+ foreach ($websites as $website)
328
+ $websiteIds[] = $website->getId();
329
+ Mage::getSingleton('admin/session')->setProductWebsiteIds($websiteIds);
330
+ }
331
+ return $websiteIds;
332
+ }
333
+ }
app/code/community/WP/GenerateSimpleProducts/etc/config.xml ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <WP_GenerateSimpleProducts>
5
+ <version>1.1.1</version>
6
+ </WP_GenerateSimpleProducts>
7
+ </modules>
8
+ <stores>
9
+ <admin>
10
+ <design>
11
+ <package>
12
+ <name>default</name>
13
+ </package>
14
+ <theme>
15
+ <default>WP</default>
16
+ </theme>
17
+ </design>
18
+ </admin>
19
+ </stores>
20
+ <admin>
21
+ <routers>
22
+ <generatesimpleproducts>
23
+ <use>admin</use>
24
+ <args>
25
+ <module>WP_GenerateSimpleProducts</module>
26
+ <frontName>generatesimpleproducts</frontName>
27
+ </args>
28
+ </generatesimpleproducts>
29
+ </routers>
30
+ </admin>
31
+ <adminhtml>
32
+ <layout>
33
+ <updates>
34
+ <amazonimportproducts>
35
+ <file>generatesimpleproducts.xml</file>
36
+ </amazonimportproducts>
37
+ </updates>
38
+ </layout>
39
+ </adminhtml>
40
+ <global>
41
+ <blocks>
42
+ <adminhtml>
43
+ <rewrite>
44
+ <catalog_product_edit_tab_super_config>WP_GenerateSimpleProducts_Block_Config</catalog_product_edit_tab_super_config>
45
+ </rewrite>
46
+ </adminhtml>
47
+ </blocks>
48
+ <helpers>
49
+ <generatesimpleproducts>
50
+ <class>WP_GenerateSimpleProducts_Helper</class>
51
+ </generatesimpleproducts>
52
+ </helpers>
53
+ </global>
54
+ <adminhtml>
55
+ <acl>
56
+ <resources>
57
+ <all>
58
+ <title>Allow Everything</title>
59
+ </all>
60
+ <admin>
61
+ <children>
62
+ <system>
63
+ <children>
64
+ <config>
65
+ <children>
66
+ <generate_simple_products translate="title" module="generatesimpleproducts">
67
+ <title><![CDATA[Web & People: Generate Simple Products]]></title>
68
+ <sort_order>2000</sort_order>
69
+ </generate_simple_products>
70
+ </children>
71
+ </config>
72
+ </children>
73
+ </system>
74
+ </children>
75
+ </admin>
76
+ </resources>
77
+ </acl>
78
+ </adminhtml>
79
+ <default>
80
+ <generate_simple_products>
81
+ <general>
82
+ <version>1.1.1</version>
83
+ </general>
84
+ </generate_simple_products>
85
+ </default>
86
+ <adminhtml>
87
+ <acl>
88
+ <resources>
89
+ <admin>
90
+ <children>
91
+ <system>
92
+ <children>
93
+ <config>
94
+ <children>
95
+ <about_webandpeople translate="title" module="generatesimpleproducts">
96
+ <title><![CDATA[Web & People: About Us]]></title>
97
+ <sort_order>1000</sort_order>
98
+ </about_webandpeople>
99
+ </children>
100
+ </config>
101
+ </children>
102
+ </system>
103
+ </children>
104
+ </admin>
105
+ </resources>
106
+ </acl>
107
+ </adminhtml>
108
+ <global>
109
+ <blocks>
110
+ <generatesimpleproducts>
111
+ <class>WP_GenerateSimpleProducts_Block</class>
112
+ </generatesimpleproducts>
113
+ </blocks>
114
+ </global>
115
+ </config>
app/code/community/WP/GenerateSimpleProducts/etc/system.xml ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <web_and_people translate="label" module="generatesimpleproducts">
5
+ <label>Web-And-People</label>
6
+ <sort_order>150</sort_order>
7
+ </web_and_people>
8
+ </tabs>
9
+ <sections>
10
+ <generate_simple_products translate="label" module="generatesimpleproducts">
11
+ <label>Simple Products Generator</label>
12
+ <tab>web_and_people</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>200</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <general translate="label">
20
+ <label>General</label>
21
+ <comment><![CDATA[<b>How to use it?</b><br />1. Create a configurable product based on a set of attributes, e.g. "T-Shirt Attribs".<br />2. Go to the edit page of the product, the section "Associated Products".<br />3. Click the button "Generate / Assign All Combinations of Simple Products". ]]></comment>
22
+ <frontend_type>text</frontend_type>
23
+ <sort_order>10</sort_order>
24
+ <show_in_default>1</show_in_default>
25
+ <show_in_website>1</show_in_website>
26
+ <show_in_store>1</show_in_store>
27
+ <fields>
28
+ <version translate="label">
29
+ <label>Extension Release</label>
30
+ <frontend_type>label</frontend_type>
31
+ <sort_order>100</sort_order>
32
+ <show_in_default>1</show_in_default>
33
+ <show_in_website>1</show_in_website>
34
+ <show_in_store>1</show_in_store>
35
+ </version>
36
+ </fields>
37
+ </general>
38
+ </groups>
39
+ </generate_simple_products>
40
+ <about_webandpeople translate="label" module="generatesimpleproducts">
41
+ <label>About Us</label>
42
+ <tab>web_and_people</tab>
43
+ <frontend_type>text</frontend_type>
44
+ <sort_order>99</sort_order>
45
+ <show_in_default>1</show_in_default>
46
+ <show_in_website>1</show_in_website>
47
+ <show_in_store>1</show_in_store>
48
+ <groups>
49
+ <info>
50
+ <frontend_model>generatesimpleproducts/about</frontend_model>
51
+ <sort_order>10</sort_order>
52
+ <show_in_default>1</show_in_default>
53
+ <show_in_website>1</show_in_website>
54
+ <show_in_store>1</show_in_store>
55
+ </info>
56
+ </groups>
57
+ </about_webandpeople>
58
+ </sections>
59
+ </config>
app/design/adminhtml/default/WP/layout/generatesimpleproducts.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout>
3
+ <adminhtml_catalog_product_edit>
4
+ <reference name="head">
5
+ <action method="addCss"><stylesheet>generatesimpleproducts/style.css</stylesheet></action>
6
+ </reference>
7
+ </adminhtml_catalog_product_edit>
8
+ </layout>
app/design/adminhtml/default/WP/template/generatesimpleproducts/config.phtml ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if (!$this->isReadonly() && $this->_getProduct()->getId()):?>
2
+ <?php echo $this->getLayout()->createBlock('adminhtml/template')->setTemplate('generatesimpleproducts/popup.phtml')->toHtml(); ?>
3
+ <div class="entry-edit">
4
+ <div class="entry-edit-head">
5
+ <h4 class="icon-head head-edit-form fieldset-legend"><?php echo Mage::helper('catalog')->__('Auto Generate Simple Products') ?></h4>
6
+ </div>
7
+ <fieldset>
8
+ <ul class="messages">
9
+ <li class="notice-msg">
10
+ <ul>
11
+ <li>
12
+ What we need is a one-click method to auto create all of the simple products that can be associated with a configurable. For example:<br />
13
+ I create two attributes, Size and Color:<br />
14
+ - Size: Red, Green, Blue<br />
15
+ - Color: Small, Medium, Large<br />
16
+ ...and an attribute set "T-Shirt Attribs".<br />
17
+ Then I create a configurable product, and press "Generate / Assign All Combinations of Simple Products".<br />
18
+ It automatically creates 9 simple products for me, representing the possible combinations of size and color. This way, instead of 27 clicks (It’s at least 27, count it out) for one configurable, its just one.
19
+ </li>
20
+ </ul>
21
+ </li>
22
+ </ul>
23
+ <div style="text-align:right;"><?php echo $this->getChildHtml('auto_generate_from_configurable') ?></div>
24
+ </fieldset>
25
+ </div>
26
+ <script type="text/javascript">//<![CDATA[
27
+ function wpGenerateSimpleProducts()
28
+ {
29
+ wpGeneratePopupParams.url = '<?php echo $this->getUrl('generatesimpleproducts/index') ?>';
30
+ wpGeneratePopupParams.title = '<?php echo $this->__('Auto Generate Simple Products'); ?>';
31
+ wpGeneratePopupParams.startText = '<?php echo $this->__('Process...') ?>';
32
+ wpGeneratePopupParams.reloadUrl = '<?php echo $this->getReloadUrl(); ?>';
33
+ wpGeneratePopupParams.data = {
34
+ productId: '<?php echo $this->getProductId() ?>',
35
+ startProcess: '1'
36
+ }
37
+ wpOpenGeneratePopup();
38
+ }
39
+ //]]></script>
40
+ <?php endif ?>
41
+
42
+ <?php echo $this->getLayout()->createBlock('adminhtml/catalog_product_edit_tab_super_config')->setTemplate('catalog/product/edit/super/config.phtml')->toHtml(); ?>
app/design/adminhtml/default/WP/template/generatesimpleproducts/popup.phtml ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="spagen-popup-window-mask" style="display:none;"></div>
2
+ <div id="spagen-popup-window" class="message-popup">
3
+ <div class="message-popup-head">
4
+ <h2 id="spagen-popup-process-title"></h2>
5
+ </div>
6
+ <div class="message-popup-content">
7
+ <div class="message spagen-message-box">
8
+ <div class="spagen-load-box" id="spagen-img-loader">
9
+ <img src="<?php echo $this->getSkinUrl('images/ajax-loader-tr.gif') ?>" alt="<?php echo $this->__('Process...') ?>" />
10
+ </div>
11
+ <div class="spagen-popup-process-info">
12
+ <p id="spagen-popup-process-message"></p>
13
+ <p id="spagen-popup-process-error"></p>
14
+ </div>
15
+ </div>
16
+ <div class="spagen-load-stop">
17
+ <button id="spagen-popup-process-stop-button" onclick="wpStopGenerate(); return false;" class="scalable delete" type="button"><span id="spagen-load-stop-message"><?php echo $this->__('Stop') ?></span></button>
18
+ <button id="spagen-popup-process-finish-button" onclick="wpReloadPage(); return false;" class="scalable nodisp" type="button"><span><?php echo $this->__('Close') ?></span></button>
19
+ </div>
20
+ </div>
21
+ </div>
22
+
23
+ <script type="text/javascript">//<![CDATA[
24
+ var wpTimeoutNext = 1000;
25
+ var wpGeneratePopupParams = {};
26
+
27
+ function wpOpenGeneratePopup()
28
+ {
29
+ $('spagen-popup-process-message').innerHTML = wpGeneratePopupParams.startText;
30
+ $('spagen-popup-process-title').innerHTML = wpGeneratePopupParams.title;
31
+ // ---
32
+ var height = $('html-body').getHeight();
33
+ $('spagen-popup-window-mask').setStyle({'height':height+'px'});
34
+ toggleSelectsUnderBlock($('spagen-popup-window-mask'), false);
35
+ Element.show('spagen-popup-window-mask');
36
+ $('spagen-popup-window-mask').scrollTo();
37
+ $('spagen-popup-window').addClassName('show');
38
+ // ---
39
+ wpGeneratePopupParams.stop = false;
40
+ wpGeneratePopupParams.next = true;
41
+ wpGeneratePopupParams.iteration = setInterval(function() {
42
+ if (!wpGeneratePopupParams.next) return;
43
+ wpProcessGenerate();
44
+ }, wpTimeoutNext);
45
+ }
46
+
47
+ function wpProcessGenerate()
48
+ {
49
+ if (wpGeneratePopupParams.stop)
50
+ {
51
+ wpFinishProcess();
52
+ return;
53
+ }
54
+ wpGeneratePopupParams.next = false;
55
+ new Ajax.Request(wpGeneratePopupParams.url, {
56
+ method: 'post',
57
+ loaderArea: false,
58
+ parameters: wpGeneratePopupParams.data,
59
+ onComplete: function(transport) {
60
+ wpGeneratePopupParams.data.startProcess = 0;
61
+ wpGeneratePopupParams.next = true;
62
+ var responseJSON = transport.responseText.evalJSON();
63
+ if (responseJSON.text) $('spagen-popup-process-message').innerHTML = responseJSON.text;
64
+ if (responseJSON.finish) wpFinishProcess();
65
+ }
66
+ });
67
+ }
68
+
69
+ function wpStopGenerate()
70
+ {
71
+ wpGeneratePopupParams.stop = true;
72
+ $('spagen-load-stop-message').innerHTML = '<?php echo $this->__('The process is being stopped...') ?>';
73
+ }
74
+
75
+ function wpReloadPage()
76
+ {
77
+ setLocation(wpGeneratePopupParams.reloadUrl);
78
+ }
79
+
80
+ function wpFinishProcess()
81
+ {
82
+ clearInterval(wpGeneratePopupParams.iteration);
83
+ Element.hide('spagen-popup-process-stop-button');
84
+ $('spagen-img-loader').style.display = 'none';
85
+ $('spagen-popup-process-finish-button').removeClassName('nodisp');
86
+ }
87
+ //]]></script>
app/etc/modules/WP_GenerateSimpleProducts.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <WP_GenerateSimpleProducts>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </WP_GenerateSimpleProducts>
8
+ </modules>
9
+ </config>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>wp_simple_products_generator</name>
4
+ <version>1.1.1</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Generate Simple Products for Configurable Product (WebAndPeople.com)</summary>
10
+ <description>Generate Simple Products for Configurable Product (WebAndPeople.com)</description>
11
+ <notes>+ info about attr. Set Name, notes</notes>
12
+ <authors><author><name>WebAndPeople</name><user>auto-converted</user><email>design@webandpeople.com</email></author><author><name>Yura Gerassimenko</name><user>auto-converted</user><email>y.gerassimenko@webandpeople.com</email></author></authors>
13
+ <date>2012-03-14</date>
14
+ <time>02:47:21</time>
15
+ <contents><target name="mageweb"><dir name="app"><dir name="code"><dir name="community"><dir name="WP"><dir name="GenerateSimpleProducts"><dir name="Block"><file name="About.php" hash="7462dd289bc2624a76c3d06340ac22a3"/><file name="Config.php" hash="df14d12b4ff1138b3352e923da4853b7"/></dir><dir name="Helper"><file name="Data.php" hash="28720f88dd4e75f789cb69024acd7fbf"/></dir><dir name="controllers"><file name="IndexController.php" hash="1dc0974ed5cc926d2d95a5bdb5f7c05a"/></dir><dir name="etc"><file name="config.xml" hash="d9d2f3683d4e99786ee9a088ec09b965"/><file name="system.xml" hash="7ee9deaea0f62ddbcc987fe31e327ddc"/></dir></dir></dir></dir></dir><dir name="design"><dir name="adminhtml"><dir name="default"><dir name="WP"><dir name="layout"><file name="generatesimpleproducts.xml" hash="7f8b8b1956ba050cba34f38af2cbc05c"/></dir><dir name="template"><dir name="generatesimpleproducts"><file name="config.phtml" hash="138666749d257136907444e78af6fefc"/><file name="popup.phtml" hash="4dda845468d4e1b3974b53b7b5eb1520"/></dir></dir></dir></dir></dir></dir><dir name="etc"><dir name="modules"><file name="WP_GenerateSimpleProducts.xml" hash="800a66c8fd03bb430b5e8964f89e7c0b"/></dir></dir></dir><dir name="skin"><dir name="adminhtml"><dir name="default"><dir name="WP"><dir name="generatesimpleproducts"><file name="style.css" hash="6e4963f2abc0f4f9a2877460c5af1c2b"/></dir></dir></dir></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies/>
18
+ </package>
skin/adminhtml/default/WP/generatesimpleproducts/style.css ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #spagen-popup-window{
2
+ z-index: 111111;
3
+ }
4
+
5
+ #spagen-popup-window-mask, #spagen-catselector-window-mask {
6
+ background-color: #EFEFEF;
7
+ bottom: 0;
8
+ height: 100%;
9
+ left: 0;
10
+ opacity: 0.5;
11
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
12
+ filter: alpha(opacity=50);
13
+ position: absolute;
14
+ right: 0;
15
+ top: 0;
16
+ width: 100%;
17
+ z-index: 9999;
18
+ }
19
+
20
+ .spagen-popup-process-info {
21
+ float: left;
22
+ }
23
+
24
+ .spagen-popup-process-info p {
25
+ margin: 0;
26
+ }
27
+
28
+ .spagen-load-box {
29
+ margin: 0 10px 0;
30
+ width: 35px;
31
+ float :left;
32
+ }
33
+
34
+ .spagen-load-stop {
35
+ text-align: right;
36
+ }
37
+
38
+ p.spagen-attention {
39
+ background: url("images/error_msg_icon.gif") no-repeat scroll 0 0;
40
+ line-height: 16px;
41
+ padding: 0 0 0 20px;
42
+ }
43
+
44
+ p.spagen-notification {
45
+ background: url("images/note_msg_icon.gif") no-repeat scroll 0 0;
46
+ line-height: 16px;
47
+ padding: 0 0 0 20px;
48
+ }
49
+
50
+ div.message.spagen-message-box {
51
+ margin-bottom: 10px;
52
+ }
53
+
54
+ .nodisp {
55
+ display:none;
56
+ }