From 86bb0cfa2c8fd162e288dda0c2139ae5ae08d29b Mon Sep 17 00:00:00 2001
From: Matthew Piatetsky <mpiatetsky@edx.org>
Date: Tue, 22 Oct 2019 16:40:04 -0400
Subject: [PATCH] add discount percentage configuration

---
 common/djangoapps/course_modes/views.py       |  2 +-
 openedx/features/discounts/admin.py           | 25 +++++++++-
 openedx/features/discounts/applicability.py   |  8 ++--
 .../migrations/0002_auto_20191022_1720.py     | 46 +++++++++++++++++++
 openedx/features/discounts/models.py          | 15 ++++++
 openedx/features/discounts/utils.py           |  2 +-
 openedx/features/discounts/views.py           |  4 +-
 7 files changed, 94 insertions(+), 8 deletions(-)
 create mode 100644 openedx/features/discounts/migrations/0002_auto_20191022_1720.py

diff --git a/common/djangoapps/course_modes/views.py b/common/djangoapps/course_modes/views.py
index 77d12334be4..586fe8315c8 100644
--- a/common/djangoapps/course_modes/views.py
+++ b/common/djangoapps/course_modes/views.py
@@ -216,7 +216,7 @@ class ChooseModeView(View):
             )
             if offer_banner_fragment:
                 context['offer_banner_fragment'] = offer_banner_fragment
-                discounted_price = "{:0.2f}".format(price_before_discount * ((100.0 - discount_percentage()) / 100))
+                discounted_price = "{:0.2f}".format(price_before_discount * ((100.0 - discount_percentage(course)) / 100))
                 context["min_price"] = discounted_price
                 context["price_before_discount"] = price_before_discount
 
diff --git a/openedx/features/discounts/admin.py b/openedx/features/discounts/admin.py
index 7efc67cc848..e3a65692635 100644
--- a/openedx/features/discounts/admin.py
+++ b/openedx/features/discounts/admin.py
@@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
 
 from openedx.core.djangoapps.config_model_utils.admin import StackedConfigModelAdmin
 
-from .models import DiscountRestrictionConfig
+from .models import DiscountPercentageConfig, DiscountRestrictionConfig
 
 
 class DiscountRestrictionConfigAdmin(StackedConfigModelAdmin):
@@ -43,3 +43,26 @@ class DiscountRestrictionConfigAdmin(StackedConfigModelAdmin):
     raw_id_fields = ('course',)
 
 admin.site.register(DiscountRestrictionConfig, DiscountRestrictionConfigAdmin)
+
+
+class DiscountPercentageConfigAdmin(StackedConfigModelAdmin):
+    """
+    Admin to configure discount percentage
+    """
+    fieldsets = (
+        ('Context', {
+            'fields': DiscountRestrictionConfig.KEY_FIELDS,
+            'description': _(
+                'These define the context to configure the percentage for the first purchase discount.'
+                'If multiple contexts apply to a course (for example, if configuration '
+                'is specified for the course specifically, and for the org that the course '
+                'is in, then the more specific context overrides the more general context.'
+            ),
+        }),
+        ('Configuration', {
+            'fields': ('percentage',),
+        })
+    )
+    raw_id_fields = ('course',)
+
+admin.site.register(DiscountPercentageConfig, DiscountPercentageConfigAdmin)
diff --git a/openedx/features/discounts/applicability.py b/openedx/features/discounts/applicability.py
index 4a5eac5cd9e..f967feba08d 100644
--- a/openedx/features/discounts/applicability.py
+++ b/openedx/features/discounts/applicability.py
@@ -20,7 +20,7 @@ from course_modes.models import CourseMode
 from entitlements.models import CourseEntitlement
 from lms.djangoapps.experiments.stable_bucketing import stable_bucketing_hash_group
 from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace
-from openedx.features.discounts.models import DiscountRestrictionConfig
+from openedx.features.discounts.models import DiscountPercentageConfig, DiscountRestrictionConfig
 from student.models import CourseEnrollment
 from track import segment
 
@@ -181,9 +181,11 @@ def _is_in_holdback(user):
     return bucket == 0
 
 
-def discount_percentage():
+def discount_percentage(course):
     """
     Get the configured discount amount.
     """
-    # TODO: Add configuration information here
+    configured_percentage = DiscountPercentageConfig.current(course_key=course.id).percentage
+    if configured_percentage:
+        return configured_percentage
     return 15
diff --git a/openedx/features/discounts/migrations/0002_auto_20191022_1720.py b/openedx/features/discounts/migrations/0002_auto_20191022_1720.py
new file mode 100644
index 00000000000..8de17c57175
--- /dev/null
+++ b/openedx/features/discounts/migrations/0002_auto_20191022_1720.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.25 on 2019-10-22 17:20
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import openedx.core.djangoapps.config_model_utils.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('course_overviews', '0017_auto_20191002_0823'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('sites', '0002_alter_domain_unique'),
+        ('discounts', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='DiscountPercentageConfig',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
+                ('enabled', models.NullBooleanField(default=None, verbose_name='Enabled')),
+                ('org', models.CharField(blank=True, db_index=True, help_text='Configure values for all course runs associated with this Organization. This is the organization string (i.e. edX, MITx).', max_length=255, null=True)),
+                ('org_course', models.CharField(blank=True, db_index=True, help_text="Configure values for all course runs associated with this course. This is should be formatted as 'org+course' (i.e. MITx+6.002x, HarvardX+CS50).", max_length=255, null=True, validators=[openedx.core.djangoapps.config_model_utils.models.validate_course_in_org], verbose_name='Course in Org')),
+                ('percentage', models.PositiveIntegerField()),
+                ('changed_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Changed by')),
+                ('course', models.ForeignKey(blank=True, help_text='Configure values for this course run. This should be formatted as the CourseKey (i.e. course-v1://MITx+6.002x+2019_Q1)', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='course_overviews.CourseOverview', verbose_name='Course Run')),
+                ('site', models.ForeignKey(blank=True, help_text='Configure values for all course runs associated with this site.', null=True, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.AddIndex(
+            model_name='discountpercentageconfig',
+            index=models.Index(fields=['site', 'org', 'course'], name='discounts_d_site_id_f87020_idx'),
+        ),
+        migrations.AddIndex(
+            model_name='discountpercentageconfig',
+            index=models.Index(fields=['site', 'org', 'org_course', 'course'], name='discounts_d_site_id_9fe8d6_idx'),
+        ),
+    ]
diff --git a/openedx/features/discounts/models.py b/openedx/features/discounts/models.py
index 0023320224c..bd2ff87c896 100644
--- a/openedx/features/discounts/models.py
+++ b/openedx/features/discounts/models.py
@@ -39,3 +39,18 @@ class DiscountRestrictionConfig(StackedConfigurationModel):
         return "DiscountRestrictionConfig(disabled={!r})".format(
             self.disabled
         )
+
+
+@python_2_unicode_compatible
+class DiscountPercentageConfig(StackedConfigurationModel):
+    """
+    A ConfigurationModel to configure the discount percentage for the first purchase discount
+    """
+    STACKABLE_FIELDS = ('percentage',)
+    percentage = models.PositiveIntegerField()
+
+    def __str__(self):
+        return "DiscountPercentageConfig(enabled={!r},percentage={!r})".format(
+            self.enabled,
+            self.percentage
+        )
diff --git a/openedx/features/discounts/utils.py b/openedx/features/discounts/utils.py
index dcc70b66191..1e19621f0f8 100644
--- a/openedx/features/discounts/utils.py
+++ b/openedx/features/discounts/utils.py
@@ -52,7 +52,7 @@ def format_strikeout_price(user, course, base_price=None, check_for_discount=Tru
     original_price = format_course_price(base_price)
 
     if not check_for_discount or can_receive_discount(user, course):
-        discount_price = base_price * ((100.0 - discount_percentage()) / 100)
+        discount_price = base_price * ((100.0 - discount_percentage(course)) / 100)
         if discount_price == int(discount_price):
             discount_price = format_course_price("{:0.0f}".format(discount_price))
         else:
diff --git a/openedx/features/discounts/views.py b/openedx/features/discounts/views.py
index 548b283838f..5229b199ba6 100644
--- a/openedx/features/discounts/views.py
+++ b/openedx/features/discounts/views.py
@@ -73,7 +73,7 @@ class CourseUserDiscount(DeveloperErrorViewMixin, APIView):
         course_key = CourseKey.from_string(course_key_string)
         course = CourseOverview.get_from_id(course_key)
         discount_applicable = can_receive_discount(user=request.user, course=course)
-        discount_percent = discount_percentage()
+        discount_percent = discount_percentage(course)
         payload = {'discount_applicable': discount_applicable, 'discount_percent': discount_percent}
         return Response({
             'discount_applicable': discount_applicable,
@@ -136,7 +136,7 @@ class CourseUserDiscountWithUserParam(DeveloperErrorViewMixin, APIView):
         course = CourseOverview.get_from_id(course_key)
         user = User.objects.get(id=user_id)
         discount_applicable = can_receive_discount(user=user, course=course)
-        discount_percent = discount_percentage()
+        discount_percent = discount_percentage(course)
         payload = {'discount_applicable': discount_applicable, 'discount_percent': discount_percent}
         return Response({
             'discount_applicable': discount_applicable,
-- 
GitLab