openedx.features.discounts package#
Submodules#
openedx.features.discounts.applicability module#
Contains code related to computing discount percentage and discount applicability.
WARNING: Keep in mind that the code in this file only applies to discounts controlled in the lms like the first purchase offer, not other discounts like coupons or enterprise/program offers configured in ecommerce.
- openedx.features.discounts.applicability.can_receive_discount(user, course, discount_expiration_date=None)#
Check all the business logic about whether this combination of user and course can receive a discount.
- openedx.features.discounts.applicability.can_show_streak_discount_coupon(user, course)#
Check whether this combination of user and course can receive the streak discount.
- openedx.features.discounts.applicability.discount_percentage(course)#
Get the configured discount amount.
- openedx.features.discounts.applicability.get_discount_expiration_date(user, course)#
Returns the date when the discount expires for the user. Returns none if the user is not enrolled.
openedx.features.discounts.apps module#
Discounts application configuration
openedx.features.discounts.models module#
DiscountRestrictionConfig Models
- class openedx.features.discounts.models.DiscountPercentageConfig(*args, **kwargs)#
Bases:
StackedConfigurationModelA ConfigurationModel to configure the discount percentage for the first purchase discount
- exception DoesNotExist#
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned#
Bases:
MultipleObjectsReturned
- STACKABLE_FIELDS = ('percentage',)#
- change_date#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- changed_by#
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- changed_by_id#
- course#
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- course_id#
- enabled#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- get_next_by_change_date(*, field=<django.db.models.fields.DateTimeField: change_date>, is_next=True, **kwargs)#
- get_previous_by_change_date(*, field=<django.db.models.fields.DateTimeField: change_date>, is_next=False, **kwargs)#
- id#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- org#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- org_course#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- percentage#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- site#
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- site_id#
- class openedx.features.discounts.models.DiscountRestrictionConfig(*args, **kwargs)#
Bases:
StackedConfigurationModelA ConfigurationModel used to manage restrictons for lms-controlled discounts
- exception DoesNotExist#
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned#
Bases:
MultipleObjectsReturned
- STACKABLE_FIELDS = ('disabled',)#
- change_date#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- changed_by#
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- changed_by_id#
- course#
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- course_id#
- disabled#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- classmethod disabled_for_course_stacked_config(course)#
Return whether lms-controlled discounts are disabled for this course. Checks if discounts are disabled for attributes of this course like Site, Partner, Course or Course Run.
- Parameters:
course – The CourseOverview of the course being queried.
- enabled#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- get_next_by_change_date(*, field=<django.db.models.fields.DateTimeField: change_date>, is_next=True, **kwargs)#
- get_previous_by_change_date(*, field=<django.db.models.fields.DateTimeField: change_date>, is_next=False, **kwargs)#
- id#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- org#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- org_course#
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- site#
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- site_id#
openedx.features.discounts.urls module#
Discount API URLs
openedx.features.discounts.utils module#
Utility functions for working with discounts and discounted pricing.
- openedx.features.discounts.utils.format_strikeout_price(user, course)#
- Return a formatted price, including a struck-out original price if a discount applies, and also
whether a discount was applied, as the tuple (formatted_price, has_discount).
- openedx.features.discounts.utils.generate_offer_data(user, course)#
Create a dictionary of information about the current discount offer.
Used by serializers to pass onto frontends and by the LMS locally to generate HTML for template rendering.
Returns a dictionary of data, or None if no offer is applicable.
- openedx.features.discounts.utils.offer_banner_wrapper(user, block, view, frag, context)#
A wrapper that prepends the First Purchase Discount banner if the user hasn’t upgraded yet.
openedx.features.discounts.views module#
The Discount API Views should return information about discounts that apply to the user and course.
- class openedx.features.discounts.views.CourseUserDiscount(**kwargs)#
Bases:
DeveloperErrorViewMixin,APIViewUse Cases
Request discount information for a user and course
Example Requests
GET /api/discounts/course/{course_key_string}
Response Values
- Body consists of the following fields:
- discount_applicable:
whether the user can receive a discount for this course
- jwt:
the jwt with user information and discount information
Parameters:
- course_key_string:
The course key for which the discount should be applied
Returns
200 on success with above fields.
401 if there is no user signed in.
Example response: {
“discount_applicable”: false, “jwt”: xxxxxxxx.xxxxxxxx.xxxxxxx
}
- authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'openedx.core.lib.api.authentication.BearerAuthenticationAllowInactiveUser'>, <class 'edx_rest_framework_extensions.auth.session.authentication.SessionAuthenticationAllowInactiveUser'>)#
- get(request, course_key_string)#
Return the discount percent, if the user has appropriate permissions.
- permission_classes = (<class 'openedx.core.lib.api.permissions.ApiKeyHeaderPermissionIsAuthenticated'>,)#
- class openedx.features.discounts.views.CourseUserDiscountWithUserParam(**kwargs)#
Bases:
DeveloperErrorViewMixin,APIViewDO NOT USE
This should not be used for anything other than getting the course/user discount information from ecommerce after payment in order to build an order. We plan to build orders before payment in this ticket: REV-692, at which point, this endpoint will no longer be necessary and should be removed.
Use Cases
Request discount information for a user and course
Example Requests
GET /api/discounts/user/{user_id}/course/{course_key_string}
Response Values
- Body consists of the following fields:
- discount_applicable:
whether the user can receive a discount for this course
- jwt:
the jwt with user information and discount information
Parameters:
- course_key_string:
The course key for which the discount should be applied
- user_id
The user id for which the discount should be applied
Returns
200 on success with above fields.
Example response: {
“discount_applicable”: false, “jwt”: xxxxxxxx.xxxxxxxx.xxxxxxx
}
- authentication_classes = (<class 'edx_rest_framework_extensions.auth.jwt.authentication.JwtAuthentication'>, <class 'openedx.core.lib.api.authentication.BearerAuthenticationAllowInactiveUser'>, <class 'edx_rest_framework_extensions.auth.session.authentication.SessionAuthenticationAllowInactiveUser'>)#
- get(request, course_key_string, user_id)#
Return the discount percent, if the user has appropriate permissions.
- permission_classes = (<class 'openedx.core.lib.api.permissions.ApiKeyHeaderPermissionIsAuthenticated'>, <class 'rest_framework.permissions.IsAdminUser'>)#
Module contents#
Discounts are determined by a combination of user and course, and have a one to one relationship with the enrollment (if already enrolled) or a join table of user and course. They are determined in LMS, because all of the data for the business rules exists here. Discount rules are meant to be permanent.