diff --git a/common/djangoapps/course_modes/models.py b/common/djangoapps/course_modes/models.py index f95f94feb2..4046877c15 100644 --- a/common/djangoapps/course_modes/models.py +++ b/common/djangoapps/course_modes/models.py @@ -202,8 +202,18 @@ class CourseMode(models.Model): raise ValidationError( _(u"Professional education modes are not allowed to have expiration_datetime set.") ) - if self.is_verified_slug(self.mode_slug) and self.min_price <= 0: - raise ValidationError(_(u"Verified modes cannot be free.")) + + mode_config = settings.COURSE_ENROLLMENT_MODES.get(self.mode_slug, {}) + min_price_for_mode = mode_config.get('min_price', 0) + if self.min_price < min_price_for_mode: + mode_display_name = mode_config.get('display_name', self.mode_slug) + raise ValidationError( + _( + u"The {course_mode} course mode has a minimum price of {min_price}. You must set a price greater than or equal to {min_price}.".format( + course_mode=mode_display_name, min_price=min_price_for_mode + ) + ) + ) def save(self, force_insert=False, force_update=False, using=None): # Ensure currency is always lowercase. diff --git a/common/djangoapps/course_modes/tests/test_models.py b/common/djangoapps/course_modes/tests/test_models.py index 46062cca5d..596c2fbd42 100644 --- a/common/djangoapps/course_modes/tests/test_models.py +++ b/common/djangoapps/course_modes/tests/test_models.py @@ -474,6 +474,7 @@ class CourseModeModelTest(TestCase): (CourseMode.CREDIT_MODE, False), (CourseMode.PROFESSIONAL, True), (CourseMode.NO_ID_PROFESSIONAL_MODE, False), + (CourseMode.MASTERS, False), ) @ddt.unpack def test_verified_min_price(self, mode_slug, is_error_expected): diff --git a/lms/envs/common.py b/lms/envs/common.py index b015946259..7de94110cf 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3314,48 +3314,52 @@ ENTERPRISE_CUSTOMER_COOKIE_NAME = 'enterprise_customer_uuid' BASE_COOKIE_DOMAIN = 'localhost' ############## Settings for Course Enrollment Modes ###################### +# The min_price key refers to the minimum price allowed for an instance +# of a particular type of course enrollment mode. This is not to be confused +# with the min_price field of the CourseMode model, which refers to the actual +# price of the CourseMode. COURSE_ENROLLMENT_MODES = { "audit": { "id": 1, "slug": "audit", "display_name": _("Audit"), - "min_price": 0 + "min_price": 0, }, "verified": { "id": 2, "slug": "verified", "display_name": _("Verified"), - "min_price": 0 + "min_price": 1, }, "professional": { "id": 3, "slug": "professional", "display_name": _("Professional"), - "min_price": 0 + "min_price": 1, }, "no-id-professional": { "id": 4, "slug": "no-id-professional", "display_name": _("No-Id-Professional"), - "min_price": 0 + "min_price": 0, }, "credit": { "id": 5, "slug": "credit", "display_name": _("Credit"), - "min_price": 0 + "min_price": 0, }, "honor": { "id": 6, "slug": "honor", "display_name": _("Honor"), - "min_price": 0 + "min_price": 0, }, "masters": { "id": 7, "slug": "masters", "display_name": _("Master's"), - "min_price": 0 + "min_price": 0, }, }