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

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
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.

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 :

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

Fix the dependency on unidecode

284. By arthru

Fix a search call

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