@@ -219,31 +219,43 @@ def search_variant(self, value_ids, custom_values=None):
219219
220220 :returns: product.product recordset of products matching domain
221221 """
222+ self .ensure_one ()
223+
222224 if custom_values is None :
223225 custom_values = {}
224226 attr_obj = self .env ['product.attribute' ]
225- for product_tmpl in self :
226- domain = [('product_tmpl_id' , '=' , product_tmpl .id )]
227227
228- for value_id in value_ids :
229- domain .append (('attribute_value_ids' , '=' , value_id ))
228+ domain = [('product_tmpl_id' , '=' , self .id )]
230229
231- attr_search = attr_obj .search ([
232- ('search_ok' , '=' , True ),
233- ('custom_type' , 'not in' , attr_obj ._get_nosearch_fields ())
234- ])
230+ for value_id in value_ids :
231+ domain .append (('attribute_value_ids' , '=' , value_id ))
235232
236- for attr_id , value in custom_values .iteritems ():
237- if attr_id not in attr_search .ids :
238- domain .append (
239- ('value_custom_ids.attribute_id' , '!=' , int (attr_id )))
240- else :
241- domain .append (
242- ('value_custom_ids.attribute_id' , '=' , int (attr_id )))
243- domain .append (('value_custom_ids.value' , '=' , value ))
233+ attr_search = attr_obj .search ([
234+ ('search_ok' , '=' , True ),
235+ ('custom_type' , 'not in' , attr_obj ._get_nosearch_fields ())
236+ ])
244237
245- products = self .env ['product.product' ].search (domain )
246- return products
238+ for attr_id , value in custom_values .iteritems ():
239+ if attr_id not in attr_search .ids :
240+ domain .append (
241+ ('value_custom_ids.attribute_id' , '!=' , int (attr_id )))
242+ else :
243+ domain .append (
244+ ('value_custom_ids.attribute_id' , '=' , int (attr_id )))
245+ domain .append (('value_custom_ids.value' , '=' , value ))
246+
247+ products = self .env ['product.product' ].search (domain )
248+
249+ # At this point, we might have found products with all of the passed
250+ # in values, but it might have more attributes! These are NOT
251+ # matches
252+ more_attrs = products .filtered (
253+ lambda p :
254+ len (p .attribute_value_ids ) != len (value_ids ) or
255+ len (p .value_custom_ids ) != len (custom_values )
256+ )
257+ products -= more_attrs
258+ return products
247259
248260 def get_config_image_obj (self , value_ids , size = None ):
249261 """
@@ -349,13 +361,22 @@ def create_get_variant(self, value_ids, custom_values=None):
349361 if not valid :
350362 raise ValidationError (_ ('Invalid Configuration' ))
351363
352- duplicates = self .search_variant (value_ids )
364+ duplicates = self .search_variant (value_ids ,
365+ custom_values = custom_values )
353366
354- # Only return duplicates without custom values for now:
355- if duplicates .filtered (lambda p : not p .value_custom_ids ):
367+ # At the moment, I don't have enough confidence with my understanding
368+ # of binary attributes, so will leave these as not matching...
369+ # In theory, they should just work, if they are set to "non search"
370+ # in custom field def!
371+ # TODO: Check the logic with binary attributes
372+ if custom_values :
373+ value_custom_ids = self .encode_custom_values (custom_values )
374+ if any ('attachment_ids' in cv [2 ] for cv in value_custom_ids ):
375+ duplicates = False
376+
377+ if duplicates :
356378 return duplicates [0 ]
357379
358- # TODO: Handle duplicates with custom values
359380 vals = self .get_variant_vals (value_ids , custom_values )
360381 variant = self .env ['product.product' ].create (vals )
361382
@@ -521,20 +542,29 @@ def _check_duplicate_product(self):
521542 if not self .config_ok :
522543 return None
523544
524- # All duplicates with and without custom values
525- duplicates = self .product_tmpl_id .search_variant (
526- self .attribute_value_ids .ids ).filtered (lambda p : p .id != self .id )
527-
528- # Prevent duplicates without custom values (only attribute values)
529- if duplicates .filtered (lambda p : not p .value_custom_ids ):
530- raise ValidationError (
531- _ ("Configurable Products cannot have duplicates "
532- "(identical attribute values)" )
533- )
534-
535- # TODO: For the future prevent duplicates with identical custom values
536- # or implement custom values on the order line level since they are
537- # specific to each order.
545+ # At the moment, I don't have enough confidence with my understanding
546+ # of binary attributes, so will leave these as not matching...
547+ # In theory, they should just work, if they are set to "non search"
548+ # in custom field def!
549+ # TODO: Check the logic with binary attributes
550+ if self .value_custom_ids .filtered (lambda cv : cv .attachment_ids ):
551+ pass
552+ else :
553+ custom_values = {
554+ cv .attribute_id .id : cv .value
555+ for cv in self .value_custom_ids
556+ }
557+
558+ duplicates = self .product_tmpl_id .search_variant (
559+ self .attribute_value_ids .ids ,
560+ custom_values = custom_values
561+ ).filtered (lambda p : p .id != self .id )
562+
563+ if duplicates :
564+ raise ValidationError (
565+ _ ("Configurable Products cannot have duplicates "
566+ "(identical attribute values)" )
567+ )
538568
539569 @api .multi
540570 def _compute_product_price_extra (self ):
0 commit comments