Merge lp:~arthru/prestashoperpconnect/import-product-combinations into lp:prestashoperpconnect

Proposed by arthru
Status: Merged
Merged at revision: 286
Proposed branch: lp:~arthru/prestashoperpconnect/import-product-combinations
Merge into: lp:prestashoperpconnect
Diff against target: 668 lines (+525/-17)
8 files modified
prestashoperpconnect/__init__.py (+1/-0)
prestashoperpconnect/__openerp__.py (+4/-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 (+56/-0)
prestashoperpconnect/unit/mapper.py (+14/-4)
To merge this branch: bzr merge lp:~arthru/prestashoperpconnect/import-product-combinations
Reviewer Review Type Date Requested Status
Guewen Baconnier @ Camptocamp code review Pending
arthru Pending
Review via email: mp+193021@code.launchpad.net

This proposal supersedes a proposal from 2013-10-25.

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 : Posted in a previous version of this proposal

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')
    product_ids = model.search(self.session.cr, self.session.uid, [
        ('default_code', '=', code)
    ])

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/#indentation)

--

l.134
"unidecode" is to add in the "external_dependencies" in __openerp__.py

--

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!

review: Needs Fixing (code review)
Revision history for this message
arthru (arthru) wrote : Posted in a previous version of this proposal

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
283. By arthru

Fix the dependency on unidecode

284. By arthru

Fix a search call

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