Repository: django-oscar/django-oscar

Issue:
Non abstract Benefit model with unique proxy_class is hard to modify or extend
offer/Benefit is a non abstract model so it can't be overridden/extended by overriding the application and making some new migrations for locally changed Benefit model. Second problem is unique proxy_class which prevents adding more than one Voucher with custom proxy_class (and  I have to set them as... Benefit is not abstract).

I've make manual migration to remove the unique from proxy_class and now I can use multiple vouchers with a proxy class ("most expensive for free") for different product sets with no problems. So the question is why Benefit model is done as it is? Why it is non-abstract and why proxy_class is unique?



Discussion:
`ConditionalOffer` not having an ABC seems to cause [similar problems](https://groups.google.com/forum/#!topic/django-oscar/Ok1UEd9nxe8)

> Second problem is unique proxy_class which prevents adding more than one Voucher with custom proxy_class (and I have to set them as... Benefit is not abstract).

Can you expand on this?  A benefit using a custom proxy class can be reused by multiple vouchers (although it may not be supported in the dashboard to build vouchers this way).  

I've added a new option for benefit type in Voucher dashboard form, and on signal I set the proxy_class if the benefit type is my custom one. Second Voucher would violate the unique constraint on proxy_class in the Benefit model.

(I don't have the code right now so I'm writing from memory ;))

You might have a point here.  I'll review the unique constraint on `proxy_class` for the 0.6 release.  

We are hitting the same problem.

By having a unique=True we can't have multiple benfits or conditons with the same proxy class. This is required if we want to have several offers with the same benefits/conditions but different ranges or values

Pull requests for removing the `unique` constraint on `proxy_class` and/or making `ConditionalOffer` inherit from an abstract base class are welcome.


Resolving patch:
diff --git a/src/oscar/apps/offer/abstract_models.py b/src/oscar/apps/offer/abstract_models.py
--- a/src/oscar/apps/offer/abstract_models.py
+++ b/src/oscar/apps/offer/abstract_models.py
@@ -418,7 +418,7 @@ class AbstractBenefit(models.Model):
     # A custom benefit class can be used instead.  This means the
     # type/value/max_affected_items fields should all be None.
     proxy_class = fields.NullCharField(
-        _("Custom class"), max_length=255, unique=True, default=None)
+        _("Custom class"), max_length=255, default=None)
 
     class Meta:
         abstract = True
diff --git a/src/oscar/apps/offer/migrations/0002_auto_20151210_1053.py b/src/oscar/apps/offer/migrations/0002_auto_20151210_1053.py
new file mode 100644
--- /dev/null
+++ b/src/oscar/apps/offer/migrations/0002_auto_20151210_1053.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import oscar.models.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('offer', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='benefit',
+            name='proxy_class',
+            field=oscar.models.fields.NullCharField(default=None, max_length=255, verbose_name='Custom class'),
+            preserve_default=True,
+        ),
+    ]


Test patch: