Merge lp:~arthru/prestashoperpconnect/import-product-combinations into lp:prestashoperpconnect
- import-product-combinations
- Merge into 7.0
Proposed by
arthru
Status: | Superseded |
---|---|
Proposed branch: | lp:~arthru/prestashoperpconnect/import-product-combinations |
Merge into: | lp:prestashoperpconnect |
Diff against target: |
664 lines (+521/-17) 8 files modified
prestashoperpconnect/__init__.py (+1/-0) prestashoperpconnect/__openerp__.py (+2/-0) prestashoperpconnect/product.py (+47/-12) prestashoperpconnect/product_combination.py (+397/-0) prestashoperpconnect/security/ir.model.access.csv (+3/-1) prestashoperpconnect/unit/binder.py (+3/-0) prestashoperpconnect/unit/import_synchronizer.py (+54/-0) prestashoperpconnect/unit/mapper.py (+14/-4) |
To merge this branch: | bzr merge lp:~arthru/prestashoperpconnect/import-product-combinations |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
arthru | Needs Resubmitting | ||
Guewen Baconnier @ Camptocamp | code review | Needs Fixing | |
Review via email: mp+192704@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-10-29.
Commit message
Description of the change
This permits to import a product by combination in prestashop
To post a comment you must log in.
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
review:
Needs Fixing
(code review)
- 278. By arthru
-
Add a comment on product_combination
- 279. By arthru
-
Clean calls to the pool using the session API
- 280. By arthru
-
Add unidecode to the external dependencies
- 281. By arthru
-
Explicitly set the unwrap keywrod when used in binder.to_openerp
- 282. By arthru
-
Use isinstance instead of type comparisons
Revision history for this message
arthru (arthru) wrote : | # |
I made most of the changes. Thanks a lot for these advice, I'll try to apply them in the code that already exist !
review:
Needs Resubmitting
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'prestashoperpconnect/__init__.py' | |||
2 | --- prestashoperpconnect/__init__.py 2013-08-12 17:37:07 +0000 | |||
3 | +++ prestashoperpconnect/__init__.py 2013-10-29 09:24:05 +0000 | |||
4 | @@ -27,6 +27,7 @@ | |||
5 | 27 | import prestashop_model | 27 | import prestashop_model |
6 | 28 | import partner | 28 | import partner |
7 | 29 | import product | 29 | import product |
8 | 30 | import product_combination | ||
9 | 30 | import sale | 31 | import sale |
10 | 31 | import setting | 32 | import setting |
11 | 32 | import delivery | 33 | import delivery |
12 | 33 | 34 | ||
13 | === modified file 'prestashoperpconnect/__openerp__.py' | |||
14 | --- prestashoperpconnect/__openerp__.py 2013-10-08 14:32:58 +0000 | |||
15 | +++ prestashoperpconnect/__openerp__.py 2013-10-29 09:24:05 +0000 | |||
16 | @@ -32,7 +32,9 @@ | |||
17 | 32 | "product_m2mcategories", | 32 | "product_m2mcategories", |
18 | 33 | "connector_ecommerce", | 33 | "connector_ecommerce", |
19 | 34 | "product_images", | 34 | "product_images", |
20 | 35 | "product_custom_attributes", | ||
21 | 35 | ], | 36 | ], |
22 | 37 | "external_dependencies": ["unidecode"], | ||
23 | 36 | "author": "PrestashopERPconnect Core Editors", | 38 | "author": "PrestashopERPconnect Core Editors", |
24 | 37 | "description": """This module connects OpenERP and Prestashop. | 39 | "description": """This module connects OpenERP and Prestashop. |
25 | 38 | 40 | ||
26 | 39 | 41 | ||
27 | === modified file 'prestashoperpconnect/product.py' | |||
28 | --- prestashoperpconnect/product.py 2013-10-25 12:39:24 +0000 | |||
29 | +++ prestashoperpconnect/product.py 2013-10-29 09:24:05 +0000 | |||
30 | @@ -203,6 +203,24 @@ | |||
31 | 203 | return {'date_upd': datetime.datetime.now()} | 203 | return {'date_upd': datetime.datetime.now()} |
32 | 204 | return {'date_upd': record['date_upd']} | 204 | return {'date_upd': record['date_upd']} |
33 | 205 | 205 | ||
34 | 206 | def has_combinations(self, record): | ||
35 | 207 | combinations = record.get('associations', {}).get( | ||
36 | 208 | 'combinations', {}).get('combinations', []) | ||
37 | 209 | return len(combinations) != 0 | ||
38 | 210 | |||
39 | 211 | @mapping | ||
40 | 212 | def attribute_set_id(self, record): | ||
41 | 213 | if self.has_combinations(record) and 'attribute_set_id' in record: | ||
42 | 214 | return {'attribute_set_id': record['attribute_set_id']} | ||
43 | 215 | return {} | ||
44 | 216 | |||
45 | 217 | def _product_code_exists(self, code): | ||
46 | 218 | model = self.session.pool.get('product.product') | ||
47 | 219 | product_ids = model.search(self.session.cr, self.session.uid, [ | ||
48 | 220 | ('default_code', '=', code) | ||
49 | 221 | ]) | ||
50 | 222 | return len(product_ids) > 0 | ||
51 | 223 | |||
52 | 206 | @mapping | 224 | @mapping |
53 | 207 | def image(self, record): | 225 | def image(self, record): |
54 | 208 | if record['id_default_image']['value'] == '': | 226 | if record['id_default_image']['value'] == '': |
55 | @@ -216,9 +234,17 @@ | |||
56 | 216 | 234 | ||
57 | 217 | @mapping | 235 | @mapping |
58 | 218 | def default_code(self, record): | 236 | def default_code(self, record): |
62 | 219 | if record.get('reference'): | 237 | if not record.get('reference'): |
63 | 220 | return {'default_code': record.get('reference')} | 238 | return {} |
64 | 221 | return {} | 239 | code = record.get('reference') |
65 | 240 | if not self._product_code_exists(code): | ||
66 | 241 | return {'default_code': code} | ||
67 | 242 | i = 1 | ||
68 | 243 | current_code = '%s_%d' % (code, i) | ||
69 | 244 | while self._product_code_exists(current_code): | ||
70 | 245 | i += 1 | ||
71 | 246 | current_code = '%s_%d' % (code, i) | ||
72 | 247 | return {'default_code': current_code} | ||
73 | 222 | 248 | ||
74 | 223 | @mapping | 249 | @mapping |
75 | 224 | def active(self, record): | 250 | def active(self, record): |
76 | @@ -226,7 +252,11 @@ | |||
77 | 226 | 252 | ||
78 | 227 | @mapping | 253 | @mapping |
79 | 228 | def sale_ok(self, record): | 254 | def sale_ok(self, record): |
81 | 229 | return {'sale_ok': record['available_for_order'] == '1'} | 255 | # if this product has combinations, we do not want to sell this product, |
82 | 256 | # but its combinations (so sale_ok = False in that case). | ||
83 | 257 | sale_ok = (record['available_for_order'] == '1' | ||
84 | 258 | and not self.has_combinations(record)) | ||
85 | 259 | return {'sale_ok': sale_ok} | ||
86 | 230 | 260 | ||
87 | 231 | @mapping | 261 | @mapping |
88 | 232 | def categ_id(self, record): | 262 | def categ_id(self, record): |
89 | @@ -286,10 +316,14 @@ | |||
90 | 286 | 316 | ||
91 | 287 | @mapping | 317 | @mapping |
92 | 288 | def type(self, record): | 318 | def type(self, record): |
97 | 289 | product_type = {"type": 'product'} | 319 | # If the product has combinations, this main product is not a real |
98 | 290 | if record['type']['value'] and record['type']['value'] == 'virtual': | 320 | # product. So it is set to a 'service' kind of product. Should better be |
99 | 291 | product_type = {"type": 'consu'} | 321 | # a 'virtual' product... but it does not exist... |
100 | 292 | return product_type | 322 | # The same if the product is a virtual one in prestashop. |
101 | 323 | if ((record['type']['value'] and record['type']['value'] == 'virtual') | ||
102 | 324 | or self.has_combinations(record)): | ||
103 | 325 | return {"type": 'service'} | ||
104 | 326 | return {"type": 'product'} | ||
105 | 293 | 327 | ||
106 | 294 | 328 | ||
107 | 295 | class product_product(orm.Model): | 329 | class product_product(orm.Model): |
108 | @@ -328,10 +362,6 @@ | |||
109 | 328 | 'always_available': fields.boolean( | 362 | 'always_available': fields.boolean( |
110 | 329 | 'Active', | 363 | 'Active', |
111 | 330 | help='if check, this object is always available'), | 364 | help='if check, this object is always available'), |
112 | 331 | 'sale_ok': fields.boolean( | ||
113 | 332 | 'For sale', | ||
114 | 333 | help='see parent field' | ||
115 | 334 | ), | ||
116 | 335 | 'quantity': fields.float( | 365 | 'quantity': fields.float( |
117 | 336 | 'Computed Quantity', | 366 | 'Computed Quantity', |
118 | 337 | help="Last computed quantity to send on Prestashop." | 367 | help="Last computed quantity to send on Prestashop." |
119 | @@ -363,6 +393,11 @@ | |||
120 | 363 | translate=True, | 393 | translate=True, |
121 | 364 | required=True, | 394 | required=True, |
122 | 365 | ), | 395 | ), |
123 | 396 | 'combinations_ids': fields.one2many( | ||
124 | 397 | 'prestashop.product.combination', | ||
125 | 398 | 'main_product_id', | ||
126 | 399 | string='Combinations' | ||
127 | 400 | ), | ||
128 | 366 | } | 401 | } |
129 | 367 | 402 | ||
130 | 368 | _sql_constraints = [ | 403 | _sql_constraints = [ |
131 | 369 | 404 | ||
132 | === added file 'prestashoperpconnect/product_combination.py' | |||
133 | --- prestashoperpconnect/product_combination.py 1970-01-01 00:00:00 +0000 | |||
134 | +++ prestashoperpconnect/product_combination.py 2013-10-29 09:24:05 +0000 | |||
135 | @@ -0,0 +1,397 @@ | |||
136 | 1 | ''' | ||
137 | 2 | A product combination is a product with different attributes in prestashop. | ||
138 | 3 | In prestashop, we can sell a product or a combination of a product with some | ||
139 | 4 | attributes. | ||
140 | 5 | |||
141 | 6 | For example, for the iPod product we can found in demo data, it has some | ||
142 | 7 | combinations with different colors and different storage size. | ||
143 | 8 | |||
144 | 9 | We map that in OpenERP to a product.product with an attribute.set defined for | ||
145 | 10 | the main product. | ||
146 | 11 | ''' | ||
147 | 12 | |||
148 | 13 | from unidecode import unidecode | ||
149 | 14 | |||
150 | 15 | from openerp.osv import fields, orm | ||
151 | 16 | from backend import prestashop | ||
152 | 17 | from .unit.backend_adapter import GenericAdapter | ||
153 | 18 | from .unit.import_synchronizer import PrestashopImportSynchronizer | ||
154 | 19 | from .unit.import_synchronizer import TranslatableRecordImport | ||
155 | 20 | from .unit.mapper import PrestashopImportMapper | ||
156 | 21 | from openerp.addons.connector.unit.backend_adapter import BackendAdapter | ||
157 | 22 | from openerp.addons.connector.unit.mapper import mapping | ||
158 | 23 | |||
159 | 24 | |||
160 | 25 | class product_product(orm.Model): | ||
161 | 26 | _inherit = 'product.product' | ||
162 | 27 | |||
163 | 28 | _columns = { | ||
164 | 29 | 'prestashop_combinations_bind_ids': fields.one2many( | ||
165 | 30 | 'prestashop.product.combination', | ||
166 | 31 | 'openerp_id', | ||
167 | 32 | string='PrestaShop Bindings (combinations)' | ||
168 | 33 | ), | ||
169 | 34 | } | ||
170 | 35 | |||
171 | 36 | |||
172 | 37 | class prestashop_product_combination(orm.Model): | ||
173 | 38 | _name = 'prestashop.product.combination' | ||
174 | 39 | _inherit = 'prestashop.binding' | ||
175 | 40 | _inherits = {'product.product': 'openerp_id'} | ||
176 | 41 | |||
177 | 42 | _columns = { | ||
178 | 43 | 'openerp_id': fields.many2one( | ||
179 | 44 | 'product.product', | ||
180 | 45 | string='Product', | ||
181 | 46 | required=True, | ||
182 | 47 | ondelete='cascade' | ||
183 | 48 | ), | ||
184 | 49 | 'main_product_id': fields.many2one( | ||
185 | 50 | 'prestashop.product.product', | ||
186 | 51 | string='Main product', | ||
187 | 52 | required=True, | ||
188 | 53 | ondelete='cascade' | ||
189 | 54 | ), | ||
190 | 55 | } | ||
191 | 56 | |||
192 | 57 | |||
193 | 58 | @prestashop | ||
194 | 59 | class ProductCombinationAdapter(GenericAdapter): | ||
195 | 60 | _model_name = 'prestashop.product.combination' | ||
196 | 61 | _prestashop_model = 'combinations' | ||
197 | 62 | |||
198 | 63 | |||
199 | 64 | @prestashop | ||
200 | 65 | class ProductCombinationRecordImport(PrestashopImportSynchronizer): | ||
201 | 66 | _model_name = 'prestashop.product.combination' | ||
202 | 67 | |||
203 | 68 | def _import_dependencies(self): | ||
204 | 69 | record = self.prestashop_record | ||
205 | 70 | option_values = record.get('associations', {}).get( | ||
206 | 71 | 'product_option_values', {}).get('product_option_value', []) | ||
207 | 72 | if not isinstance(option_values, list): | ||
208 | 73 | option_values = [option_values] | ||
209 | 74 | for option_value in option_values: | ||
210 | 75 | backend_adapter = self.get_connector_unit_for_model( | ||
211 | 76 | BackendAdapter, | ||
212 | 77 | 'prestashop.product.combination.option.value' | ||
213 | 78 | ) | ||
214 | 79 | option_value = backend_adapter.read(option_value['id']) | ||
215 | 80 | self._check_dependency( | ||
216 | 81 | option_value['id_attribute_group'], | ||
217 | 82 | 'prestashop.product.combination.option', | ||
218 | 83 | ) | ||
219 | 84 | |||
220 | 85 | self.check_location(option_value) | ||
221 | 86 | |||
222 | 87 | def check_location(self, option_value): | ||
223 | 88 | option_binder = self.get_binder_for_model( | ||
224 | 89 | 'prestashop.product.combination.option') | ||
225 | 90 | attribute_id = option_binder.to_openerp( | ||
226 | 91 | option_value['id_attribute_group'], unwrap=True) | ||
227 | 92 | product = self.mapper.main_product(self.prestashop_record) | ||
228 | 93 | attribute_group_id = product.attribute_set_id.attribute_group_ids[0].id | ||
229 | 94 | |||
230 | 95 | attribute_location_ids = self.session.search( | ||
231 | 96 | 'attribute.location', | ||
232 | 97 | [ | ||
233 | 98 | ('attribute_id', '=', attribute_id), | ||
234 | 99 | ('attribute_group_id', '=', attribute_group_id) | ||
235 | 100 | ] | ||
236 | 101 | ) | ||
237 | 102 | if not attribute_location_ids: | ||
238 | 103 | self.session.create( | ||
239 | 104 | 'attribute.location', | ||
240 | 105 | { | ||
241 | 106 | 'attribute_id': attribute_id, | ||
242 | 107 | 'attribute_group_id': attribute_group_id, | ||
243 | 108 | } | ||
244 | 109 | ) | ||
245 | 110 | |||
246 | 111 | |||
247 | 112 | @prestashop | ||
248 | 113 | class ProductCombinationMapper(PrestashopImportMapper): | ||
249 | 114 | _model_name = 'prestashop.product.combination' | ||
250 | 115 | |||
251 | 116 | direct = [ | ||
252 | 117 | ('weight', 'weight'), | ||
253 | 118 | ('wholesale_price', 'standard_price'), | ||
254 | 119 | ('price', 'lst_price'), | ||
255 | 120 | ] | ||
256 | 121 | |||
257 | 122 | from_main = [ | ||
258 | 123 | 'name', | ||
259 | 124 | 'categ_id', | ||
260 | 125 | 'categ_ids', | ||
261 | 126 | 'taxes_ids', | ||
262 | 127 | 'type', | ||
263 | 128 | 'company_id', | ||
264 | 129 | ] | ||
265 | 130 | |||
266 | 131 | @mapping | ||
267 | 132 | def from_main_product(self, record): | ||
268 | 133 | main_product = self.main_product(record) | ||
269 | 134 | result = {} | ||
270 | 135 | for attribute in self.from_main: | ||
271 | 136 | if attribute not in main_product: | ||
272 | 137 | continue | ||
273 | 138 | if hasattr(main_product[attribute], 'id'): | ||
274 | 139 | result[attribute] = main_product[attribute].id | ||
275 | 140 | elif isinstance(main_product[attribute]), list): | ||
276 | 141 | ids = [] | ||
277 | 142 | for element in main_product[attribute]: | ||
278 | 143 | ids.append(element.id) | ||
279 | 144 | result[attribute] = [(6, 0, ids)] | ||
280 | 145 | else: | ||
281 | 146 | result[attribute] = main_product[attribute] | ||
282 | 147 | return result | ||
283 | 148 | |||
284 | 149 | def main_product(self, record): | ||
285 | 150 | if hasattr(self, '_main_product'): | ||
286 | 151 | return self._main_product | ||
287 | 152 | product_id = self.get_main_product_id(record) | ||
288 | 153 | self._main_product = self.session.browse( | ||
289 | 154 | 'prestashop.product.product', | ||
290 | 155 | product_id | ||
291 | 156 | ) | ||
292 | 157 | return self._main_product | ||
293 | 158 | |||
294 | 159 | def get_main_product_id(self, record): | ||
295 | 160 | product_binder = self.get_binder_for_model( | ||
296 | 161 | 'prestashop.product.product') | ||
297 | 162 | return product_binder.to_openerp(record['id_product']) | ||
298 | 163 | |||
299 | 164 | @mapping | ||
300 | 165 | def attribute_set_id(self, record): | ||
301 | 166 | product = self.main_product(record) | ||
302 | 167 | if 'attribute_set_id' in product: | ||
303 | 168 | return {'attribute_set_id': product.attribute_set_id.id} | ||
304 | 169 | return {} | ||
305 | 170 | |||
306 | 171 | @mapping | ||
307 | 172 | def attributes_values(self, record): | ||
308 | 173 | option_values = record['associations']['product_option_values'][ | ||
309 | 174 | 'product_option_value'] | ||
310 | 175 | if isinstance(option_values, dict): | ||
311 | 176 | option_values = [option_values] | ||
312 | 177 | |||
313 | 178 | results = {} | ||
314 | 179 | for option_value in option_values: | ||
315 | 180 | |||
316 | 181 | option_value_binder = self.get_binder_for_model( | ||
317 | 182 | 'prestashop.product.combination.option.value') | ||
318 | 183 | option_value_openerp_id = option_value_binder.to_openerp( | ||
319 | 184 | option_value['id']) | ||
320 | 185 | |||
321 | 186 | option_value_object = self.session.browse( | ||
322 | 187 | 'prestashop.product.combination.option.value', | ||
323 | 188 | option_value_openerp_id | ||
324 | 189 | ) | ||
325 | 190 | field_name = option_value_object.attribute_id.name | ||
326 | 191 | results[field_name] = option_value_object.id | ||
327 | 192 | return results | ||
328 | 193 | |||
329 | 194 | @mapping | ||
330 | 195 | def main_product_id(self, record): | ||
331 | 196 | return {'main_product_id': self.get_main_product_id(record)} | ||
332 | 197 | |||
333 | 198 | def _product_code_exists(self, code): | ||
334 | 199 | product_ids = self.session.search('product.product', | ||
335 | 200 | [('default_code', '=', code)]) | ||
336 | 201 | return len(product_ids) > 0 | ||
337 | 202 | |||
338 | 203 | @mapping | ||
339 | 204 | def default_code(self, record): | ||
340 | 205 | if not record.get('reference'): | ||
341 | 206 | return {} | ||
342 | 207 | code = record.get('reference') | ||
343 | 208 | if not self._product_code_exists(code): | ||
344 | 209 | return {'default_code': code} | ||
345 | 210 | i = 1 | ||
346 | 211 | current_code = '%s_%d' % (code, i) | ||
347 | 212 | while self._product_code_exists(current_code): | ||
348 | 213 | i += 1 | ||
349 | 214 | current_code = '%s_%d' % (code, i) | ||
350 | 215 | return {'default_code': current_code} | ||
351 | 216 | |||
352 | 217 | ##@mapping | ||
353 | 218 | ##def active(self, record): | ||
354 | 219 | ## return {'always_available': bool(int(record['active']))} | ||
355 | 220 | |||
356 | 221 | ##@mapping | ||
357 | 222 | ##def sale_ok(self, record): | ||
358 | 223 | ## return {'sale_ok': record['available_for_order'] == '1'} | ||
359 | 224 | |||
360 | 225 | @mapping | ||
361 | 226 | def backend_id(self, record): | ||
362 | 227 | return {'backend_id': self.backend_record.id} | ||
363 | 228 | |||
364 | 229 | @mapping | ||
365 | 230 | def ean13(self, record): | ||
366 | 231 | if record['ean13'] == '0': | ||
367 | 232 | return {} | ||
368 | 233 | return {'ean13': record['ean13']} | ||
369 | 234 | |||
370 | 235 | |||
371 | 236 | class attribute_attribute(orm.Model): | ||
372 | 237 | _inherit = 'attribute.attribute' | ||
373 | 238 | |||
374 | 239 | _columns = { | ||
375 | 240 | 'prestashop_bind_ids': fields.one2many( | ||
376 | 241 | 'prestashop.product.combination.option', | ||
377 | 242 | 'openerp_id', | ||
378 | 243 | string='PrestaShop Bindings (combinations)' | ||
379 | 244 | ), | ||
380 | 245 | } | ||
381 | 246 | |||
382 | 247 | |||
383 | 248 | class prestashop_product_combination_option(orm.Model): | ||
384 | 249 | _name = 'prestashop.product.combination.option' | ||
385 | 250 | _inherit = 'prestashop.binding' | ||
386 | 251 | _inherits = {'attribute.attribute': 'openerp_id'} | ||
387 | 252 | |||
388 | 253 | _columns = { | ||
389 | 254 | 'openerp_id': fields.many2one( | ||
390 | 255 | 'attribute.attribute', | ||
391 | 256 | string='Attribute', | ||
392 | 257 | required=True, | ||
393 | 258 | ondelete='cascade' | ||
394 | 259 | ), | ||
395 | 260 | } | ||
396 | 261 | |||
397 | 262 | |||
398 | 263 | @prestashop | ||
399 | 264 | class ProductCombinationOptionAdapter(GenericAdapter): | ||
400 | 265 | _model_name = 'prestashop.product.combination.option' | ||
401 | 266 | _prestashop_model = 'product_options' | ||
402 | 267 | |||
403 | 268 | |||
404 | 269 | @prestashop | ||
405 | 270 | class ProductCombinationOptionRecordImport(PrestashopImportSynchronizer): | ||
406 | 271 | _model_name = 'prestashop.product.combination.option' | ||
407 | 272 | |||
408 | 273 | def _import_values(self): | ||
409 | 274 | record = self.prestashop_record | ||
410 | 275 | option_values = record.get('associations', {}).get( | ||
411 | 276 | 'product_option_values', {}).get('product_option_value', []) | ||
412 | 277 | if not isinstance(option_values, list): | ||
413 | 278 | option_values = [option_values] | ||
414 | 279 | for option_value in option_values: | ||
415 | 280 | self._check_dependency( | ||
416 | 281 | option_value['id'], | ||
417 | 282 | 'prestashop.product.combination.option.value' | ||
418 | 283 | ) | ||
419 | 284 | |||
420 | 285 | def run(self, ext_id): | ||
421 | 286 | super(ProductCombinationOptionRecordImport, self).run(ext_id) | ||
422 | 287 | |||
423 | 288 | self._import_values() | ||
424 | 289 | |||
425 | 290 | |||
426 | 291 | @prestashop | ||
427 | 292 | class ProductCombinationOptionMapper(PrestashopImportMapper): | ||
428 | 293 | _model_name = 'prestashop.product.combination.option' | ||
429 | 294 | |||
430 | 295 | @mapping | ||
431 | 296 | def attribute_type(self, record): | ||
432 | 297 | return {'attribute_type': 'select'} | ||
433 | 298 | |||
434 | 299 | @mapping | ||
435 | 300 | def model_id(self, record): | ||
436 | 301 | ids = self.session.search('ir.model', | ||
437 | 302 | [('model', '=', 'product.product')]) | ||
438 | 303 | assert len(ids) == 1 | ||
439 | 304 | return {'model_id': ids[0], 'model': 'product.product'} | ||
440 | 305 | |||
441 | 306 | @mapping | ||
442 | 307 | def backend_id(self, record): | ||
443 | 308 | return {'backend_id': self.backend_record.id} | ||
444 | 309 | |||
445 | 310 | @mapping | ||
446 | 311 | def name(self, record): | ||
447 | 312 | name = None | ||
448 | 313 | if 'language' in record['name']: | ||
449 | 314 | language_binder = self.get_binder_for_model('prestashop.res.lang') | ||
450 | 315 | languages = record['name']['language'] | ||
451 | 316 | if not isinstance(languages, list): | ||
452 | 317 | languages = [languages] | ||
453 | 318 | for lang in languages: | ||
454 | 319 | erp_language_id = language_binder.to_openerp( | ||
455 | 320 | lang['attrs']['id']) | ||
456 | 321 | erp_lang = self.session.read( | ||
457 | 322 | 'prestashop.res.lang', | ||
458 | 323 | erp_language_id, | ||
459 | 324 | ) | ||
460 | 325 | if erp_lang['code'] == 'en_US': | ||
461 | 326 | name = lang['value'] | ||
462 | 327 | break | ||
463 | 328 | if name is None: | ||
464 | 329 | name = languages[0]['value'] | ||
465 | 330 | else: | ||
466 | 331 | name = record['name'] | ||
467 | 332 | field_name = 'x_' + unidecode(name.replace(' ', '')) | ||
468 | 333 | return {'name': field_name, 'field_description': name} | ||
469 | 334 | |||
470 | 335 | |||
471 | 336 | class attribute_option(orm.Model): | ||
472 | 337 | _inherit = 'attribute.option' | ||
473 | 338 | |||
474 | 339 | _columns = { | ||
475 | 340 | 'prestashop_bind_ids': fields.one2many( | ||
476 | 341 | 'prestashop.product.combination.option.value', | ||
477 | 342 | 'openerp_id', | ||
478 | 343 | string='PrestaShop Bindings' | ||
479 | 344 | ), | ||
480 | 345 | } | ||
481 | 346 | |||
482 | 347 | |||
483 | 348 | class prestashop_product_combination_option_value(orm.Model): | ||
484 | 349 | _name = 'prestashop.product.combination.option.value' | ||
485 | 350 | _inherit = 'prestashop.binding' | ||
486 | 351 | _inherits = {'attribute.option': 'openerp_id'} | ||
487 | 352 | |||
488 | 353 | _columns = { | ||
489 | 354 | 'openerp_id': fields.many2one( | ||
490 | 355 | 'attribute.option', | ||
491 | 356 | string='Attribute', | ||
492 | 357 | required=True, | ||
493 | 358 | ondelete='cascade' | ||
494 | 359 | ), | ||
495 | 360 | } | ||
496 | 361 | |||
497 | 362 | |||
498 | 363 | @prestashop | ||
499 | 364 | class ProductCombinationOptionValueAdapter(GenericAdapter): | ||
500 | 365 | _model_name = 'prestashop.product.combination.option.value' | ||
501 | 366 | _prestashop_model = 'product_option_values' | ||
502 | 367 | |||
503 | 368 | |||
504 | 369 | @prestashop | ||
505 | 370 | class ProductCombinationOptionValueRecordImport(TranslatableRecordImport): | ||
506 | 371 | _model_name = 'prestashop.product.combination.option.value' | ||
507 | 372 | |||
508 | 373 | _translatable_fields = { | ||
509 | 374 | 'prestashop.product.combination.option.value': ['name'], | ||
510 | 375 | } | ||
511 | 376 | |||
512 | 377 | |||
513 | 378 | @prestashop | ||
514 | 379 | class ProductCombinationOptionValueMapper(PrestashopImportMapper): | ||
515 | 380 | _model_name = 'prestashop.product.combination.option.value' | ||
516 | 381 | |||
517 | 382 | direct = [ | ||
518 | 383 | ('name', 'name'), | ||
519 | 384 | ('position', 'sequence'), | ||
520 | 385 | ] | ||
521 | 386 | |||
522 | 387 | @mapping | ||
523 | 388 | def attribute_id(self, record): | ||
524 | 389 | binder = self.get_binder_for_model( | ||
525 | 390 | 'prestashop.product.combination.option') | ||
526 | 391 | attribute_id = binder.to_openerp(record['id_attribute_group'], | ||
527 | 392 | unwrap=True) | ||
528 | 393 | return {'attribute_id': attribute_id} | ||
529 | 394 | |||
530 | 395 | @mapping | ||
531 | 396 | def backend_id(self, record): | ||
532 | 397 | return {'backend_id': self.backend_record.id} | ||
533 | 0 | 398 | ||
534 | === modified file 'prestashoperpconnect/security/ir.model.access.csv' | |||
535 | --- prestashoperpconnect/security/ir.model.access.csv 2013-08-13 08:22:25 +0000 | |||
536 | +++ prestashoperpconnect/security/ir.model.access.csv 2013-10-29 09:24:05 +0000 | |||
537 | @@ -20,4 +20,6 @@ | |||
538 | 20 | access_prestashop_product_category_full,Full access on prestashop.product.category,model_prestashop_product_category,connector.group_connector_manager,1,1,1,1 | 20 | access_prestashop_product_category_full,Full access on prestashop.product.category,model_prestashop_product_category,connector.group_connector_manager,1,1,1,1 |
539 | 21 | access_prestashop_product_image_full,Full access on prestashop.product.image,model_prestashop_product_image,connector.group_connector_manager,1,1,1,1 | 21 | access_prestashop_product_image_full,Full access on prestashop.product.image,model_prestashop_product_image,connector.group_connector_manager,1,1,1,1 |
540 | 22 | access_prestashop_product_product_full,Full access on prestashop.product.product,model_prestashop_product_product,connector.group_connector_manager,1,1,1,1 | 22 | access_prestashop_product_product_full,Full access on prestashop.product.product,model_prestashop_product_product,connector.group_connector_manager,1,1,1,1 |
542 | 23 | 23 | access_prestashop_product_combination_full,Full access on prestashop.product.combination,model_prestashop_product_combination,connector.group_connector_manager,1,1,1,1 | |
543 | 24 | access_prestashop_product_combination_option_full,Full access on prestashop.product.combination.option,model_prestashop_product_combination_option,connector.group_connector_manager,1,1,1,1 | ||
544 | 25 | access_prestashop_product_combination_option_value_full,Full access on prestashop.product.combination.option.value,model_prestashop_product_combination_option_value,connector.group_connector_manager,1,1,1,1 | ||
545 | 24 | 26 | ||
546 | === modified file 'prestashoperpconnect/unit/binder.py' | |||
547 | --- prestashoperpconnect/unit/binder.py 2013-07-12 13:35:45 +0000 | |||
548 | +++ prestashoperpconnect/unit/binder.py 2013-10-29 09:24:05 +0000 | |||
549 | @@ -51,6 +51,9 @@ | |||
550 | 51 | 'prestashop.product.category', | 51 | 'prestashop.product.category', |
551 | 52 | 'prestashop.product.image', | 52 | 'prestashop.product.image', |
552 | 53 | 'prestashop.product.product', | 53 | 'prestashop.product.product', |
553 | 54 | 'prestashop.product.combination', | ||
554 | 55 | 'prestashop.product.combination.option', | ||
555 | 56 | 'prestashop.product.combination.option.value', | ||
556 | 54 | 'prestashop.sale.order', | 57 | 'prestashop.sale.order', |
557 | 55 | 'prestashop.sale.order.state', | 58 | 'prestashop.sale.order.state', |
558 | 56 | 'prestashop.delivery.carrier', | 59 | 'prestashop.delivery.carrier', |
559 | 57 | 60 | ||
560 | === modified file 'prestashoperpconnect/unit/import_synchronizer.py' | |||
561 | --- prestashoperpconnect/unit/import_synchronizer.py 2013-10-25 12:40:37 +0000 | |||
562 | +++ prestashoperpconnect/unit/import_synchronizer.py 2013-10-29 09:24:05 +0000 | |||
563 | @@ -531,6 +531,20 @@ | |||
564 | 531 | 531 | ||
565 | 532 | prestashop_record = self._get_prestashop_data() | 532 | prestashop_record = self._get_prestashop_data() |
566 | 533 | associations = prestashop_record.get('associations', {}) | 533 | associations = prestashop_record.get('associations', {}) |
567 | 534 | |||
568 | 535 | combinations = associations.get('combinations', {}).get( | ||
569 | 536 | 'combinations', {}) | ||
570 | 537 | if not isinstance(combinations, list): | ||
571 | 538 | combinations = [combinations] | ||
572 | 539 | for combination in combinations: | ||
573 | 540 | if 'id' in combination: | ||
574 | 541 | import_record( | ||
575 | 542 | self.session, | ||
576 | 543 | 'prestashop.product.combination', | ||
577 | 544 | self.backend_record.id, | ||
578 | 545 | combination['id'] | ||
579 | 546 | ) | ||
580 | 547 | |||
581 | 534 | images = associations.get('images', {}).get('image', {}) | 548 | images = associations.get('images', {}).get('image', {}) |
582 | 535 | if not isinstance(images, list): | 549 | if not isinstance(images, list): |
583 | 536 | images = [images] | 550 | images = [images] |
584 | @@ -546,11 +560,51 @@ | |||
585 | 546 | ) | 560 | ) |
586 | 547 | 561 | ||
587 | 548 | def _import_dependencies(self): | 562 | def _import_dependencies(self): |
588 | 563 | self._import_default_category() | ||
589 | 564 | self._import_categories() | ||
590 | 565 | self._import_attribute_set() | ||
591 | 566 | |||
592 | 567 | def _import_attribute_set(self): | ||
593 | 568 | record = self.prestashop_record | ||
594 | 569 | |||
595 | 570 | combinations = record.get('associations', {}).get( | ||
596 | 571 | 'combinations', {}).get('combinations', []) | ||
597 | 572 | if len(combinations) == 0: | ||
598 | 573 | return | ||
599 | 574 | |||
600 | 575 | splitted_record = self._split_per_language(self.prestashop_record) | ||
601 | 576 | if self._default_language in splitted_record: | ||
602 | 577 | name = splitted_record[self._default_language]['name'] | ||
603 | 578 | else: | ||
604 | 579 | name = splitted_record.values()[0]['name'] | ||
605 | 580 | |||
606 | 581 | product_model_id = self.get_product_model_id() | ||
607 | 582 | attribute_group = { | ||
608 | 583 | 'model_id': product_model_id, | ||
609 | 584 | 'name': 'Combinations options', | ||
610 | 585 | 'sequence': 0, | ||
611 | 586 | } | ||
612 | 587 | attribute_set = { | ||
613 | 588 | 'model_id': product_model_id, | ||
614 | 589 | 'name': name + ' Options', | ||
615 | 590 | 'attribute_group_ids': [(0, 0, attribute_group)], | ||
616 | 591 | } | ||
617 | 592 | attribute_set_id = self.session.create('attribute.set', attribute_set) | ||
618 | 593 | self.prestashop_record['attribute_set_id'] = attribute_set_id | ||
619 | 594 | |||
620 | 595 | def get_product_model_id(self): | ||
621 | 596 | ids = model.search('ir.model', [('model', '=', 'product.product')]) | ||
622 | 597 | assert len(ids) == 1 | ||
623 | 598 | return ids[0] | ||
624 | 599 | |||
625 | 600 | def _import_default_category(self): | ||
626 | 549 | record = self.prestashop_record | 601 | record = self.prestashop_record |
627 | 550 | if int(record['id_category_default']): | 602 | if int(record['id_category_default']): |
628 | 551 | self._check_dependency(record['id_category_default'], | 603 | self._check_dependency(record['id_category_default'], |
629 | 552 | 'prestashop.product.category') | 604 | 'prestashop.product.category') |
630 | 553 | 605 | ||
631 | 606 | def _import_categories(self): | ||
632 | 607 | record = self.prestashop_record | ||
633 | 554 | associations = record.get('associations', {}) | 608 | associations = record.get('associations', {}) |
634 | 555 | categories = associations.get('categories', {}).get('category', []) | 609 | categories = associations.get('categories', {}).get('category', []) |
635 | 556 | if not isinstance(categories, list): | 610 | if not isinstance(categories, list): |
636 | 557 | 611 | ||
637 | === modified file 'prestashoperpconnect/unit/mapper.py' | |||
638 | --- prestashoperpconnect/unit/mapper.py 2013-10-25 12:41:52 +0000 | |||
639 | +++ prestashoperpconnect/unit/mapper.py 2013-10-29 09:24:05 +0000 | |||
640 | @@ -419,10 +419,20 @@ | |||
641 | 419 | 419 | ||
642 | 420 | @mapping | 420 | @mapping |
643 | 421 | def product_id(self, record): | 421 | def product_id(self, record): |
648 | 422 | return {'product_id': self.get_openerp_id( | 422 | if ('product_attribute_id' in record and |
649 | 423 | 'prestashop.product.product', | 423 | record['product_attribute_id'] != '0'): |
650 | 424 | record['product_id'] | 424 | combination_binder = self.get_binder_for_model( |
651 | 425 | )} | 425 | 'prestashop.product.combination') |
652 | 426 | product_id = combination_binder.to_openerp( | ||
653 | 427 | record['product_attribute_id'], | ||
654 | 428 | unwrap=True | ||
655 | 429 | ) | ||
656 | 430 | else: | ||
657 | 431 | product_id = self.get_openerp_id( | ||
658 | 432 | 'prestashop.product.product', | ||
659 | 433 | record['product_id'] | ||
660 | 434 | ) | ||
661 | 435 | return {'product_id': product_id} | ||
662 | 426 | 436 | ||
663 | 427 | @mapping | 437 | @mapping |
664 | 428 | def backend_id(self, record): | 438 | def backend_id(self, record): |
Good piece of work Arthur,
Some comments:
--
What is a "product combination". At least a few words (in the docstring of the Python module product_ combination. py) to explain this concept could be useful.
--
l.44-47, l.217ff. l.226ff (and other places, possibly on methods search, browse, read, create, unlink)
model = self.session. pool.get( 'product. product' ) self.session. cr, self.session.uid, [
('default_ code', '=', code)
product_ids = model.search(
])
A better idiom is:
product_ids = self.session. search( 'product. product' , [('default_code', '=', code)])
(side note on the alignment: "Arguments on first line forbidden when not using vertical alignment", source http:// www.python. org/dev/ peps/pep- 0008/#indentati on)
--
l.134 dependencies" in __openerp__.py
"unidecode" is to add in the "external_
--
l.212 (and each time you use the unwrap keyword argument of to_openerp())
Please use the keyword when calling a keyword argument, so instead of
attribute_id = option_ binder. to_openerp(
option_ value[' id_attribute_ group'] , True)
Use
attribute_id = option_ binder. to_openerp(
option_ value[' id_attribute_ group'] , unwrap=True)
--
l.264, l.302, l.453
Instead of:
type( main_product[ attribute] ) is list
Use:
isinstance( main_product[ attribute] , list)
--
l.306-321:
suggestion (as you want): externalize the part which get the option value in another ConnectorUnit, allowing to customize the way the options values are get.
--
l.599
if len(combinations) == 0:
=>
if not combinations:
--
Thanks!