Commit 9aa095dd by Diana Huang Committed by Julia Hansbrough

Adapt flow for new 'professional' mode.

ECOM-41
parent 805c3256
...@@ -113,6 +113,21 @@ class CourseMode(models.Model): ...@@ -113,6 +113,21 @@ class CourseMode(models.Model):
return None return None
@classmethod @classmethod
def verified_mode_for_course(cls, course_id):
"""
Since we have two separate modes that can go through the verify flow,
we want to be able to select the 'correct' verified mode for a given course.
Currently, we prefer to return the professional mode over the verified one
if both exist for the given course.
"""
modes_dict = cls.modes_for_course_dict(course_id)
verified_mode = modes_dict.get('verified', None)
professional_mode = modes_dict.get('professional', None)
# we prefer professional over verify
return professional_mode if professional_mode else verified_mode
@classmethod
def min_course_price_for_verified_for_currency(cls, course_id, currency): def min_course_price_for_verified_for_currency(cls, course_id, currency):
""" """
Returns the minimum price of the course int he appropriate currency over all the Returns the minimum price of the course int he appropriate currency over all the
......
...@@ -113,3 +113,17 @@ class CourseModeModelTest(TestCase): ...@@ -113,3 +113,17 @@ class CourseModeModelTest(TestCase):
modes = CourseMode.modes_for_course(SlashSeparatedCourseKey('TestOrg', 'TestCourse', 'TestRun')) modes = CourseMode.modes_for_course(SlashSeparatedCourseKey('TestOrg', 'TestCourse', 'TestRun'))
self.assertEqual([CourseMode.DEFAULT_MODE], modes) self.assertEqual([CourseMode.DEFAULT_MODE], modes)
def test_verified_mode_for_course(self):
self.create_mode('verified', 'Verified Certificate')
mode = CourseMode.verified_mode_for_course(self.course_key)
self.assertEqual(mode.slug, 'verified')
# verify that the professional mode is preferred
self.create_mode('professional', 'Professional Education Verified Certificate')
mode = CourseMode.verified_mode_for_course(self.course_key)
self.assertEqual(mode.slug, 'professional')
...@@ -59,8 +59,6 @@ class ChooseModeView(View): ...@@ -59,8 +59,6 @@ class ChooseModeView(View):
) )
) )
donation_for_course = request.session.get("donation_for_course", {}) donation_for_course = request.session.get("donation_for_course", {})
chosen_price = donation_for_course.get(course_key, None) chosen_price = donation_for_course.get(course_key, None)
...@@ -135,11 +133,6 @@ class ChooseModeView(View): ...@@ -135,11 +133,6 @@ class ChooseModeView(View):
donation_for_course = request.session.get("donation_for_course", {}) donation_for_course = request.session.get("donation_for_course", {})
donation_for_course[course_key] = amount_value donation_for_course[course_key] = amount_value
request.session["donation_for_course"] = donation_for_course request.session["donation_for_course"] = donation_for_course
if SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
return redirect(
reverse('verify_student_verified',
kwargs={'course_id': course_key.to_deprecated_string()}) + "?upgrade={}".format(upgrade)
)
return redirect( return redirect(
reverse('verify_student_show_requirements', reverse('verify_student_show_requirements',
......
...@@ -701,8 +701,13 @@ class CertificateItem(OrderItem): ...@@ -701,8 +701,13 @@ class CertificateItem(OrderItem):
item.qty = 1 item.qty = 1
item.unit_cost = cost item.unit_cost = cost
course_name = modulestore().get_course(course_id).display_name course_name = modulestore().get_course(course_id).display_name
item.line_desc = _("Certificate of Achievement, {mode_name} for course {course}").format(mode_name=mode_info.name, # Translators: In this particular case, mode_name refers to a
course=course_name) # particular mode (i.e. Honor Code Certificate, Verified Certificate, etc)
# by which a user could enroll in the given course.
item.line_desc = _("{mode_name} for course {course}").format(
mode_name=mode_info.name,
course=course_name
)
item.currency = currency item.currency = currency
order.currency = currency order.currency = currency
order.save() order.save()
...@@ -725,7 +730,7 @@ class CertificateItem(OrderItem): ...@@ -725,7 +730,7 @@ class CertificateItem(OrderItem):
@property @property
def single_item_receipt_template(self): def single_item_receipt_template(self):
if self.mode == 'verified': if self.mode in ('verified', 'professional'):
return 'shoppingcart/verified_cert_receipt.html' return 'shoppingcart/verified_cert_receipt.html'
else: else:
return super(CertificateItem, self).single_item_receipt_template return super(CertificateItem, self).single_item_receipt_template
......
...@@ -222,7 +222,7 @@ class ItemizedPurchaseReportTest(ModuleStoreTestCase): ...@@ -222,7 +222,7 @@ class ItemizedPurchaseReportTest(ModuleStoreTestCase):
self.CORRECT_CSV = dedent(""" self.CORRECT_CSV = dedent("""
Purchase Time,Order ID,Status,Quantity,Unit Cost,Total Cost,Currency,Description,Comments Purchase Time,Order ID,Status,Quantity,Unit Cost,Total Cost,Currency,Description,Comments
{time_str},1,purchased,1,40,40,usd,Registration for Course: Robot Super Course,Ba\xc3\xbc\xe5\x8c\x85 {time_str},1,purchased,1,40,40,usd,Registration for Course: Robot Super Course,Ba\xc3\xbc\xe5\x8c\x85
{time_str},1,purchased,1,40,40,usd,"Certificate of Achievement, verified cert for course Robot Super Course", {time_str},1,purchased,1,40,40,usd,verified cert for course Robot Super Course,
""".format(time_str=str(self.now))) """.format(time_str=str(self.now)))
def test_purchased_items_btw_dates(self): def test_purchased_items_btw_dates(self):
......
...@@ -74,18 +74,27 @@ class VerifyView(View): ...@@ -74,18 +74,27 @@ class VerifyView(View):
# bookkeeping-wise just to start over. # bookkeeping-wise just to start over.
progress_state = "start" progress_state = "start"
modes_dict = CourseMode.modes_for_course_dict(course_id) # we prefer professional over verify
verify_mode = modes_dict.get('verified', None) current_mode = CourseMode.verified_mode_for_course(course_id)
# if the course doesn't have a verified mode, we want to kick them # if the course doesn't have a verified mode, we want to kick them
# from the flow # from the flow
if not verify_mode: if not current_mode:
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
if course_id.to_deprecated_string() in request.session.get("donation_for_course", {}): if course_id.to_deprecated_string() in request.session.get("donation_for_course", {}):
chosen_price = request.session["donation_for_course"][course_id.to_deprecated_string()] chosen_price = request.session["donation_for_course"][course_id.to_deprecated_string()]
else: else:
chosen_price = verify_mode.min_price chosen_price = current_mode.min_price
course = modulestore().get_course(course_id) course = modulestore().get_course(course_id)
if current_mode.suggested_prices != '':
suggested_prices = [
decimal.Decimal(price)
for price in current_mode.suggested_prices.split(",")
]
else:
suggested_prices = []
context = { context = {
"progress_state": progress_state, "progress_state": progress_state,
"user_full_name": request.user.profile.name, "user_full_name": request.user.profile.name,
...@@ -95,15 +104,13 @@ class VerifyView(View): ...@@ -95,15 +104,13 @@ class VerifyView(View):
"course_org": course.display_org_with_default, "course_org": course.display_org_with_default,
"course_num": course.display_number_with_default, "course_num": course.display_number_with_default,
"purchase_endpoint": get_purchase_endpoint(), "purchase_endpoint": get_purchase_endpoint(),
"suggested_prices": [ "suggested_prices": suggested_prices,
decimal.Decimal(price) "currency": current_mode.currency.upper(),
for price in verify_mode.suggested_prices.split(",")
],
"currency": verify_mode.currency.upper(),
"chosen_price": chosen_price, "chosen_price": chosen_price,
"min_price": verify_mode.min_price, "min_price": current_mode.min_price,
"upgrade": upgrade == u'True', "upgrade": upgrade == u'True',
"can_audit": "audit" in modes_dict, "can_audit": CourseMode.mode_for_course(course_id, 'audit') is not None,
"modes_dict": CourseMode.modes_for_course_dict(course_id),
} }
return render_to_response('verify_student/photo_verification.html', context) return render_to_response('verify_student/photo_verification.html', context)
...@@ -124,19 +131,20 @@ class VerifiedView(View): ...@@ -124,19 +131,20 @@ class VerifiedView(View):
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True): if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
modes_dict = CourseMode.modes_for_course_dict(course_id) modes_dict = CourseMode.modes_for_course_dict(course_id)
verify_mode = modes_dict.get('verified', None)
if verify_mode is None: # we prefer professional over verify
return redirect(reverse('dashboard')) current_mode = CourseMode.verified_mode_for_course(course_id)
chosen_price = request.session.get( # if the course doesn't have a verified mode, we want to kick them
"donation_for_course", # from the flow
{} if not current_mode:
).get( return redirect(reverse('dashboard'))
course_id.to_deprecated_string(), if course_id.to_deprecated_string() in request.session.get("donation_for_course", {}):
verify_mode.min_price chosen_price = request.session["donation_for_course"][course_id.to_deprecated_string()]
) else:
chosen_price = current_mode.min_price
course = modulestore().get_course(course_id) course = modulestore().get_course(course_id)
context = { context = {
...@@ -146,11 +154,12 @@ class VerifiedView(View): ...@@ -146,11 +154,12 @@ class VerifiedView(View):
"course_org": course.display_org_with_default, "course_org": course.display_org_with_default,
"course_num": course.display_number_with_default, "course_num": course.display_number_with_default,
"purchase_endpoint": get_purchase_endpoint(), "purchase_endpoint": get_purchase_endpoint(),
"currency": verify_mode.currency.upper(), "currency": current_mode.currency.upper(),
"chosen_price": chosen_price, "chosen_price": chosen_price,
"create_order_url": reverse("verify_student_create_order"), "create_order_url": reverse("verify_student_create_order"),
"upgrade": upgrade == u'True', "upgrade": upgrade == u'True',
"can_audit": "audit" in modes_dict, "can_audit": "audit" in modes_dict,
"modes_dict": modes_dict,
} }
return render_to_response('verify_student/verified.html', context) return render_to_response('verify_student/verified.html', context)
...@@ -185,19 +194,24 @@ def create_order(request): ...@@ -185,19 +194,24 @@ def create_order(request):
donation_for_course[course_id] = amount donation_for_course[course_id] = amount
request.session['donation_for_course'] = donation_for_course request.session['donation_for_course'] = donation_for_course
verified_mode = CourseMode.modes_for_course_dict(course_id).get('verified', None) # prefer professional mode over verified_mode
current_mode = CourseMode.verified_mode_for_course(course_id)
if current_mode.slug == 'professional':
amount = current_mode.min_price
# make sure this course has a verified mode # make sure this course has a verified mode
if not verified_mode: if not current_mode:
return HttpResponseBadRequest(_("This course doesn't support verified certificates")) return HttpResponseBadRequest(_("This course doesn't support verified certificates"))
if amount < verified_mode.min_price: if amount < current_mode.min_price:
return HttpResponseBadRequest(_("No selected price or selected price is below minimum.")) return HttpResponseBadRequest(_("No selected price or selected price is below minimum."))
# I know, we should check this is valid. All kinds of stuff missing here # I know, we should check this is valid. All kinds of stuff missing here
cart = Order.get_cart_for_user(request.user) cart = Order.get_cart_for_user(request.user)
cart.clear() cart.clear()
CertificateItem.add_to_order(cart, course_id, amount, 'verified') enrollment_mode = current_mode.slug
CertificateItem.add_to_order(cart, course_id, amount, enrollment_mode)
params = get_signed_purchase_params(cart) params = get_signed_purchase_params(cart)
...@@ -288,12 +302,20 @@ def show_requirements(request, course_id): ...@@ -288,12 +302,20 @@ def show_requirements(request, course_id):
""" """
Show the requirements necessary for the verification flow. Show the requirements necessary for the verification flow.
""" """
# TODO: seems borked for professional; we're told we need to take photos even if there's a pending verification
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
upgrade = request.GET.get('upgrade', False)
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True): if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
if SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
return redirect(
reverse('verify_student_verified',
kwargs={'course_id': course_id.to_deprecated_string()}) + "?upgrade={}".format(upgrade)
)
upgrade = request.GET.get('upgrade', False) upgrade = request.GET.get('upgrade', False)
course = modulestore().get_course(course_id) course = modulestore().get_course(course_id)
modes_dict = CourseMode.modes_for_course_dict(course_id)
context = { context = {
"course_id": course_id.to_deprecated_string(), "course_id": course_id.to_deprecated_string(),
"course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_id.to_deprecated_string()}), "course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_id.to_deprecated_string()}),
...@@ -303,6 +325,7 @@ def show_requirements(request, course_id): ...@@ -303,6 +325,7 @@ def show_requirements(request, course_id):
"course_num": course.display_number_with_default, "course_num": course.display_number_with_default,
"is_not_active": not request.user.is_active, "is_not_active": not request.user.is_active,
"upgrade": upgrade == u'True', "upgrade": upgrade == u'True',
"modes_dict": modes_dict,
} }
return render_to_response("verify_student/show_requirements.html", context) return render_to_response("verify_student/show_requirements.html", context)
......
...@@ -177,12 +177,13 @@ ...@@ -177,12 +177,13 @@
<dt class="faq-question">${_("What do you do with this picture?")}</dt> <dt class="faq-question">${_("What do you do with this picture?")}</dt>
<dd class="faq-answer">${_("We only use it to verify your identity. It is not displayed anywhere.")}</dd> <dd class="faq-answer">${_("We only use it to verify your identity. It is not displayed anywhere.")}</dd>
<dt class="faq-question">${_("What if my camera isn't working?")}</dt> %if "professional" not in modes_dict:
<dt class="faq-question">${_("What if my camera isn't working?")}</dt>
%if upgrade: %if upgrade:
<dd class="faq-answer">${_("You can always continue to audit the course without verifying.")}</dd> <dd class="faq-answer">${_("You can always continue to audit the course without verifying.")}</dd>
%else: %else:
<dd class="faq-answer">${_("You can always {a_start} audit the course for free {a_end} without verifying.").format(a_start='<a rel="external" href="{}">'.format(course_modes_choose_url), a_end="</a>")}</dd> <dd class="faq-answer">${_("You can always {a_start} audit the course for free {a_end} without verifying.").format(a_start='<a rel="external" href="{}">'.format(course_modes_choose_url), a_end="</a>")}</dd>
%endif
%endif %endif
</dl> </dl>
</div> </div>
...@@ -366,6 +367,7 @@ ...@@ -366,6 +367,7 @@
</ul> </ul>
</li> </li>
%if len(suggested_prices) > 0:
<li class="review-task review-task-contribution"> <li class="review-task review-task-contribution">
<h4 class="title">${_("Check Your Contribution Level")}</h4> <h4 class="title">${_("Check Your Contribution Level")}</h4>
...@@ -376,12 +378,28 @@ ...@@ -376,12 +378,28 @@
<%include file="/course_modes/_contribution.html" args="suggested_prices=suggested_prices, currency=currency, chosen_price=chosen_price, min_price=min_price"/> <%include file="/course_modes/_contribution.html" args="suggested_prices=suggested_prices, currency=currency, chosen_price=chosen_price, min_price=min_price"/>
</li> </li>
%else:
<li class="review-task review-task-contribution">
<h4 class="title">${_("Your Course Total")}</h4>
<div class="copy">
<p>${_("To complete your registration, you will need to pay:")}</p>
</div>
<ul class="list-fields contribution-options">
<li class="field contribution-option">
<span class="deco-denomination">$</span>
<span class="label-value">${chosen_price}</span>
<span class="denomination-name">${currency}</span>
</label>
</li>
</ul>
</li>
%endif
</ol> </ol>
</div> </div>
<nav class="nav-wizard"> <nav class="nav-wizard">
<div class="prompt-verify"> <div class="prompt-verify">
<h3 class="title">Before proceeding, please confirm that your details match</h3> <h3 class="title">${_("Before proceeding, please confirm that your details match")}</h3>
<p class="copy"> ${_("Once you verify your details match the requirements, you can move on to step 4, payment on our secure server.")}</p> <p class="copy"> ${_("Once you verify your details match the requirements, you can move on to step 4, payment on our secure server.")}</p>
......
...@@ -85,8 +85,11 @@ $(document).ready(function() { ...@@ -85,8 +85,11 @@ $(document).ready(function() {
</div> </div>
<nav class="nav-wizard is-ready"> <nav class="nav-wizard is-ready">
%if "professional" in modes_dict:
<span class="help help-inline price-value">${_("Your Course Total is $ ")} <strong>${chosen_price}</strong></span>
%else:
<span class="help help-inline price-value">${_("You have decided to pay $ ")} <strong>${chosen_price}</strong></span> <span class="help help-inline price-value">${_("You have decided to pay $ ")} <strong>${chosen_price}</strong></span>
%endif
<ol class="wizard-steps"> <ol class="wizard-steps">
<li class="wizard-step step-proceed"> <li class="wizard-step step-proceed">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment