Skip to content

Commit

Permalink
Merge pull request #33 from q-verse/develop
Browse files Browse the repository at this point in the history
release-2020-06-03
  • Loading branch information
mumarkhan999 authored Jun 3, 2020
2 parents 25dad78 + 0893648 commit bc15a5e
Show file tree
Hide file tree
Showing 24 changed files with 193 additions and 60 deletions.
5 changes: 5 additions & 0 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1575,3 +1575,8 @@
# setting for the FileWrapper class used to iterate over the export file data.
# See: https://docs.python.org/2/library/wsgiref.html#wsgiref.util.FileWrapper
COURSE_EXPORT_DOWNLOAD_CHUNK_SIZE = 8192

############## Qverse Features #########################

# edX does not set the following variable in cms. But we have to set these, otherwise it gives an error.
PAID_COURSE_REGISTRATION_CURRENCY = ['usd', '$']
6 changes: 6 additions & 0 deletions cms/envs/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,9 @@
########################## Derive Any Derived Settings #######################

derive_settings(__name__)

############## Qverse Features #########################

# edX does not set the following variable in cms. But we have to set these, otherwise it gives an error.
PAID_COURSE_REGISTRATION_CURRENCY = ENV_TOKENS.get('PAID_COURSE_REGISTRATION_CURRENCY',
PAID_COURSE_REGISTRATION_CURRENCY)
15 changes: 8 additions & 7 deletions common/djangoapps/course_modes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
'bulk_sku',
])

CURRENCY = settings.PAID_COURSE_REGISTRATION_CURRENCY[0]

class CourseMode(models.Model):
"""
Expand Down Expand Up @@ -70,14 +71,14 @@ def course_id(self, value):
# The 'pretty' name that can be translated and displayed
mode_display_name = models.CharField(max_length=255, verbose_name=_("Display Name"))

# The price in USD that we would like to charge for this mode of the course
# The price in configured currency that we would like to charge for this mode of the course
# Historical note: We used to allow users to choose from several prices, but later
# switched to using a single price. Although this field is called `min_price`, it is
# really just the price of the course.
min_price = models.IntegerField(default=0, verbose_name=_("Price"))

# the currency these prices are in, using lower case ISO currency codes
currency = models.CharField(default="usd", max_length=8)
currency = models.CharField(default=CURRENCY, max_length=8)

# The datetime at which the course mode will expire.
# This is used to implement "upgrade" deadlines.
Expand Down Expand Up @@ -181,7 +182,7 @@ def course_id(self, value):
# "honor" to "audit", we still need to have the shoppingcart
# use "honor"
DEFAULT_SHOPPINGCART_MODE_SLUG = HONOR
DEFAULT_SHOPPINGCART_MODE = Mode(HONOR, _('Honor'), 0, '', 'usd', None, None, None, None)
DEFAULT_SHOPPINGCART_MODE = Mode(HONOR, _('Honor'), 0, '', CURRENCY, None, None, None, None)

CACHE_NAMESPACE = u"course_modes.CourseMode.cache."

Expand Down Expand Up @@ -757,12 +758,12 @@ def get_course_prices(course, verified_only=False):
if verified_only:
registration_price = CourseMode.min_course_price_for_verified_for_currency(
course.id,
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]
CURRENCY
)
else:
registration_price = CourseMode.min_course_price_for_currency(
course.id,
settings.PAID_COURSE_REGISTRATION_CURRENCY[0]
CURRENCY
)

if registration_price > 0:
Expand Down Expand Up @@ -813,15 +814,15 @@ class Meta(object):
# The 'pretty' name that can be translated and displayed
mode_display_name = models.CharField(max_length=255)

# minimum price in USD that we would like to charge for this mode of the course
# minimum price in configued currency that we would like to charge for this mode of the course
min_price = models.IntegerField(default=0)

# the suggested prices for this mode
suggested_prices = models.CharField(max_length=255, blank=True, default='',
validators=[validate_comma_separated_integer_list])

# the currency these prices are in, using lower case ISO currency codes
currency = models.CharField(default="usd", max_length=8)
currency = models.CharField(default=CURRENCY, max_length=8)

# turn this mode off after the given expiration date
expiration_date = models.DateField(default=None, null=True, blank=True)
Expand Down
7 changes: 5 additions & 2 deletions common/djangoapps/course_modes/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import unicode_literals
"""
Views for the course_mode module
"""
Expand All @@ -8,6 +9,7 @@

import waffle
from babel.dates import format_datetime
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import HttpResponse, HttpResponseBadRequest
Expand Down Expand Up @@ -219,6 +221,7 @@ def get(self, request, course_id, error=None):
if x.strip()
]
context["currency"] = verified_mode.currency.upper()
context["currency_symbol"] = settings.PAID_COURSE_REGISTRATION_CURRENCY[1]
context["min_price"] = verified_mode.min_price
context["verified_name"] = verified_mode.name
context["verified_description"] = verified_mode.description
Expand Down Expand Up @@ -355,7 +358,7 @@ def create_mode(request, course_id):
`sku` (str): The product SKU value.
By default, this endpoint will create an 'honor' mode for the given course with display name
'Honor Code', a minimum price of 0, no suggested prices, and using USD as the currency.
'Honor Code', a minimum price of 0, no suggested prices, and using configured default currency as the currency.
Args:
request (`Request`): The Django Request object.
Expand All @@ -369,7 +372,7 @@ def create_mode(request, course_id):
'mode_display_name': u'Honor Code Certificate',
'min_price': 0,
'suggested_prices': u'',
'currency': u'usd',
'currency': unicode(settings.PAID_COURSE_REGISTRATION_CURRENCY[0]),
'sku': None,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export class PortfolioExperimentUpsellModal extends React.Component {
</div>
);

let upgradeText = `Upgrade (${this.props.currency_symbol}100 ${this.props.currency})`;

return (
<Modal
open={this.state.isOpen}
Expand All @@ -58,8 +60,8 @@ export class PortfolioExperimentUpsellModal extends React.Component {
body={body}
buttons={[
(<Button
label={'Upgrade ($100 USD)'}
display={'Upgrade ($100 USD)'}
label={upgradeText}
display={upgradeText}
buttonType='success'
// unfortunately, Button components don't have an href attribute
onClick={() => {}}
Expand Down
7 changes: 4 additions & 3 deletions common/static/common/js/components/UpsellExperimentModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class UpsellExperimentModal extends React.Component {
</div>
);
const { buttonDestinationURL } = this.props;
let upgradeText = `Upgrade (${this.props.currency_symbol}100 ${this.props.currency})`;
return (
<Modal
open={this.state.isOpen}
Expand All @@ -74,8 +75,8 @@ export class UpsellExperimentModal extends React.Component {
body={body}
buttons={[
(<Button
label={"Upgrade ($100 USD)"}
display={"Upgrade ($100 USD)"}
label={upgradeText}
display={upgradeText}
buttonType="success"
// unfortunately, Button components don't have an href component
onClick={() => window.location = buttonDestinationURL}
Expand All @@ -88,4 +89,4 @@ export class UpsellExperimentModal extends React.Component {

UpsellExperimentModal.propTypes = {
buttonDestinationURL: PropTypes.string.isRequired,
};
};
2 changes: 1 addition & 1 deletion common/test/acceptance/pages/lms/create_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self, browser, course_id, mode_slug=None, mode_display_name=None, m
"""The mode creation page is an endpoint for HTTP GET requests.
By default, it will create an 'honor' mode for the given course with display name
'Honor Code', a minimum price of 0, no suggested prices, and using USD as the currency.
'Honor Code', a minimum price of 0, no suggested prices, and using configured currency as the currency.
Arguments:
browser (Browser): The browser instance.
Expand Down
8 changes: 7 additions & 1 deletion lms/djangoapps/courseware/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,8 @@ def course_about(request, course_id):
reviews_fragment_view = CourseReviewsModuleFragmentView().render_to_fragment(request, course=course)

context = {
'currency': settings.PAID_COURSE_REGISTRATION_CURRENCY[0],
'currency_symbol': settings.PAID_COURSE_REGISTRATION_CURRENCY[1],
'course': course,
'course_details': course_details,
'staff_access': staff_access,
Expand Down Expand Up @@ -917,7 +919,11 @@ def program_marketing(request, program_uuid):
skus = program.get('skus')
ecommerce_service = EcommerceService()

context = {'program': program}
context = {
'program': program,
'currency': settings.PAID_COURSE_REGISTRATION_CURRENCY[0],
'currency_symbol': settings.PAID_COURSE_REGISTRATION_CURRENCY[1],
}

if program.get('is_learner_eligible_for_one_click_purchase') and skus:
context['buy_button_href'] = ecommerce_service.get_checkout_page_url(*skus, program_uuid=program_uuid)
Expand Down
19 changes: 11 additions & 8 deletions lms/djangoapps/shoppingcart/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@

# we need a tuple to represent the primary key of various OrderItem subclasses
OrderItemSubclassPK = namedtuple('OrderItemSubclassPK', ['cls', 'pk'])
CURRENCY = settings.PAID_COURSE_REGISTRATION_CURRENCY[0]


class OrderTypes(object):
Expand All @@ -115,7 +116,7 @@ class Meta(object):
app_label = "shoppingcart"

user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
currency = models.CharField(default="usd", max_length=8) # lower case ISO currency codes
currency = models.CharField(default=CURRENCY, max_length=8) # lower case ISO currency codes
status = models.CharField(max_length=32, default='cart', choices=ORDER_STATUSES)
purchase_time = models.DateTimeField(null=True, blank=True)
refunded_time = models.DateTimeField(null=True, blank=True)
Expand Down Expand Up @@ -654,7 +655,7 @@ class Meta(object):
unit_cost = models.DecimalField(default=0.0, decimal_places=2, max_digits=30)
list_price = models.DecimalField(decimal_places=2, max_digits=30, null=True)
line_desc = models.CharField(default="Misc. Item", max_length=1024)
currency = models.CharField(default="usd", max_length=8) # lower case ISO currency codes
currency = models.CharField(default=CURRENCY, max_length=8) # lower case ISO currency codes
fulfilled_time = models.DateTimeField(null=True, db_index=True)
refund_requested_time = models.DateTimeField(null=True, db_index=True)
service_fee = models.DecimalField(default=0.0, decimal_places=2, max_digits=30)
Expand Down Expand Up @@ -687,7 +688,7 @@ def add_to_order(cls, order, *args, **kwargs):
# this is a validation step to verify that the currency of the item we
# are adding is the same as the currency of the order we are adding it
# to
currency = kwargs.get('currency', 'usd')
currency = kwargs.get('currency', CURRENCY)
if order.currency != currency and order.orderitem_set.exists():
raise InvalidCartItem(_("Trying to add a different currency into the cart"))

Expand Down Expand Up @@ -1009,7 +1010,7 @@ class Meta(object):
)
)
currency = models.CharField(
default="usd",
default=CURRENCY,
max_length=8,
help_text=ugettext_lazy("Lower-case ISO currency codes")
)
Expand Down Expand Up @@ -1104,7 +1105,7 @@ class Meta(object):
help_text=ugettext_lazy("The price per item sold, including discounts.")
)
currency = models.CharField(
default="usd",
default=CURRENCY,
max_length=8,
help_text=ugettext_lazy("Lower-case ISO currency codes")
)
Expand Down Expand Up @@ -1921,7 +1922,7 @@ def refund_cert_callback(sender, course_enrollment=None, skip_refund=False, **kw

@classmethod
@transaction.atomic
def add_to_order(cls, order, course_id, cost, mode, currency='usd'):
def add_to_order(cls, order, course_id, cost, mode, currency=CURRENCY):
"""
Add a CertificateItem to an order
Expand Down Expand Up @@ -2059,7 +2060,9 @@ def verified_certificates_contributing_more_than_minimum(cls, course_id):
course_id=course_id,
mode='verified',
status='purchased',
unit_cost__gt=(CourseMode.min_course_price_for_verified_for_currency(course_id, 'usd')))).count()
unit_cost__gt=(
CourseMode.min_course_price_for_verified_for_currency(course_id, CURRENCY))
)).count()

def analytics_data(self):
"""Simple function used to construct analytics data for the OrderItem.
Expand Down Expand Up @@ -2113,7 +2116,7 @@ class Meta(object):

@classmethod
@transaction.atomic
def add_to_order(cls, order, donation_amount, course_id=None, currency='usd'):
def add_to_order(cls, order, donation_amount, course_id=None, currency=CURRENCY):
"""Add a donation to an order.
Args:
Expand Down
2 changes: 1 addition & 1 deletion lms/djangoapps/shoppingcart/processors/CyberSource2.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ def _payment_accepted(order_id, auth_amount, currency, decision):
return {
'accepted': False,
'amt_charged': 0,
'currency': 'usd',
'currency': settings.PAID_COURSE_REGISTRATION_CURRENCY[0],
'order': order
}

Expand Down
4 changes: 3 additions & 1 deletion lms/djangoapps/shoppingcart/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from decimal import Decimal

import unicodecsv
from django.conf import settings
from django.utils.translation import ugettext as _
from six import text_type

Expand Down Expand Up @@ -164,6 +165,7 @@ def rows(self):
total_enrolled = counts['total']
audit_enrolled = counts['audit']
honor_enrolled = counts['honor']
currency = settings.PAID_COURSE_REGISTRATION_CURRENCY[0]

if counts['verified'] == 0:
verified_enrolled = 0
Expand All @@ -172,7 +174,7 @@ def rows(self):
else:
verified_enrolled = counts['verified']
gross_rev = CertificateItem.verified_certificates_monetary_field_sum(course_id, 'purchased', 'unit_cost')
gross_rev_over_min = gross_rev - (CourseMode.min_course_price_for_verified_for_currency(course_id, 'usd') * verified_enrolled)
gross_rev_over_min = gross_rev - (CourseMode.min_course_price_for_verified_for_currency(course_id, currency) * verified_enrolled)

num_verified_over_the_minimum = CertificateItem.verified_certificates_contributing_more_than_minimum(course_id)

Expand Down
1 change: 1 addition & 0 deletions lms/djangoapps/verify_student/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ def get(

# Render the top-level page
context = {
'currency_symbol': settings.PAID_COURSE_REGISTRATION_CURRENCY[1],
'contribution_amount': contribution_amount,
'course': course,
'course_key': unicode(course_key),
Expand Down
1 change: 1 addition & 0 deletions lms/static/js/verify_student/pay_and_verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var edx = edx || {};
function(price) { return Boolean(price); }
),
currency: $el.data('course-mode-currency'),
currencySymbol: $el.data('currency-symbol'),
processors: $el.data('processors'),
verificationDeadline: $el.data('verification-deadline'),
courseModeSlug: $el.data('course-mode-slug'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
<% if (discount_data.is_discounted) { %>
<span class='list-price'>
<%- StringUtils.interpolate(
gettext('${listPrice}'), {listPrice: discount_data.total_incl_tax_excl_discounts.toFixed(2)}
gettext('{currency_symbol}{listPrice}'), {currency_symbol: currency_symbol, listPrice: discount_data.total_incl_tax_excl_discounts.toFixed(2)}
)
%>
</span>
<% } %>
<%- StringUtils.interpolate(
gettext(' ${price} {currency} )'),
{price: full_program_price.toFixed(2), currency: discount_data.currency}
gettext(' {currency_symbol}{price} {currency} )'),
{currency_symbol:currency_symbol, price: full_program_price.toFixed(2), currency: discount_data.currency}
)
%>
</a>
Expand Down
Loading

0 comments on commit bc15a5e

Please sign in to comment.