Commit dd7589fa by Jeff LaJoie Committed by Jeff LaJoie

Learner-3768: Adds in edit form for entitlements

parent b67c7629
...@@ -505,6 +505,10 @@ class CourseEntitlementForm(BaseForm): ...@@ -505,6 +505,10 @@ class CourseEntitlementForm(BaseForm):
mode = cleaned_data.get('mode') mode = cleaned_data.get('mode')
price = cleaned_data.get('price') price = cleaned_data.get('price')
# If there's no mode there should also be no price
if not mode:
cleaned_data['price'] = None
if mode in [CourseEntitlement.VERIFIED, CourseEntitlement.PROFESSIONAL] and price and price < 0.01: if mode in [CourseEntitlement.VERIFIED, CourseEntitlement.PROFESSIONAL] and price and price < 0.01:
self.add_error('price', _('Price must be greater than or equal to 0.01')) self.add_error('price', _('Price must be greater than or equal to 0.01'))
if mode in [CourseEntitlement.VERIFIED, CourseEntitlement.PROFESSIONAL] and not price: if mode in [CourseEntitlement.VERIFIED, CourseEntitlement.PROFESSIONAL] and not price:
......
...@@ -606,6 +606,26 @@ ...@@ -606,6 +606,26 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if entitlement_form %}
<div class="field-title">{% trans "CERTIFICATE TYPE AND PRICE" %}</span></div>
<div class="row">
<div class="col col-6 help-text">
{% trans "If the course offers a verified or professional education certificate, select the certificate type and enter the price for the certificate." %}
</div>
<div class="col col-6">
<div class="row">
<div class="col col-6">
<label class="field-label ">{{ entitlement_form.mode.label_tag }}</label>
{{ entitlement_form.mode }}
</div>
<div id="seatPriceBlock" class="col col-6 {% if not entitlement_form.mode.value %}hidden{% endif %}">
<label class="field-label ">{{ entitlement_form.price.label_tag }} <span class="required">*</span></label>
{{ entitlement_form.price }}
</div>
</div>
</div>
</div>
{% endif %}
</fieldset> </fieldset>
</div> </div>
</div> </div>
...@@ -630,6 +650,7 @@ ...@@ -630,6 +650,7 @@
<script src="{% static 'js/publisher/course-tabs.js' %}"></script> <script src="{% static 'js/publisher/course-tabs.js' %}"></script>
<script src="{% static 'js/publisher/organizations.js' %}"></script> <script src="{% static 'js/publisher/organizations.js' %}"></script>
<script src="{% static 'js/publisher/seat-type-change.js' %}"></script>
<script src="{% static 'js/publisher/image-validation.js' %}"></script> <script src="{% static 'js/publisher/image-validation.js' %}"></script>
<script src="{% static 'js/publisher/course-image.js' %}"></script> <script src="{% static 'js/publisher/course-image.js' %}"></script>
<script src="{% static 'bower_components/tinymce/tinymce.min.js' %}"></script> <script src="{% static 'bower_components/tinymce/tinymce.min.js' %}"></script>
......
...@@ -2991,6 +2991,109 @@ class CourseEditViewTests(SiteMixin, TestCase): ...@@ -2991,6 +2991,109 @@ class CourseEditViewTests(SiteMixin, TestCase):
response = self.client.get(self.edit_page_url + '?history_id={}'.format(100)) response = self.client.get(self.edit_page_url + '?history_id={}'.format(100))
self.assertIsNone(response.context['history_object']) self.assertIsNone(response.context['history_object'])
def test_seat_version_course_edit_page(self):
"""
Verify that a SEAT_VERSION Course that has course runs associated with it can be updated without changing
the version, and can change the version as long as the Course Run Seat prices and types match the Course
"""
toggle_switch(PUBLISHER_ENTITLEMENTS_WAFFLE_SWITCH, True)
self.user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME))
self.course.version = Course.SEAT_VERSION
self.course.save()
post_data = self._post_data(self.organization_extension)
post_data['team_admin'] = self.course_team_role.user.id
post_data['mode'] = ''
course_run = factories.CourseRunFactory.create(
course=self.course, lms_course_id='course-v1:edxTest+Test342+2016Q1', end=datetime.now() + timedelta(days=1)
)
factories.CourseRunStateFactory(course_run=course_run, name=CourseRunStateChoices.Published)
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.Publisher)
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.ProjectCoordinator)
seat = factories.SeatFactory.create(course_run=course_run, type=Seat.VERIFIED, price=2)
response = self.client.post(self.edit_page_url, data=post_data)
# Assert that this saves for a SEAT_VERSION
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_detail', kwargs={'pk': self.course.id}),
status_code=302,
target_status_code=200
)
# Modify the Course to try and create CourseEntitlements differing from the CourseRun and Seat type
post_data['mode'] = Seat.PROFESSIONAL
post_data['price'] = 2 # just a number different than what is used in the SeatFactory constructor
response = self.client.post(self.edit_page_url, data=post_data)
self.assertEqual(response.status_code, 400)
# Modify the Course to try and create CourseEntitlements differing from the CourseRun and Seat price
post_data['mode'] = Seat.VERIFIED
post_data['price'] = 1 # just a number different than what is used in the SeatFactory constructor
response = self.client.post(self.edit_page_url, data=post_data)
self.assertEqual(response.status_code, 400)
# Modify the Course to try and create CourseEntitlement the same as the Course Run and Seat type and price
post_data['mode'] = seat.type
post_data['price'] = seat.price # just a number different than what is used in the SeatFactory constructor
response = self.client.post(self.edit_page_url, data=post_data)
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_detail', kwargs={'pk': self.course.id}),
status_code=302,
target_status_code=200
)
def test_entitlement_version_course_edit_page(self):
"""
Verify that an ENTITLEMENT_VERSION Course cannot be reverted to a SEAT_RUN Course, but a Course can be updated
"""
toggle_switch(PUBLISHER_ENTITLEMENTS_WAFFLE_SWITCH, True)
self.user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME))
self.course.version = Course.SEAT_VERSION
self.course.save()
post_data = self._post_data(self.organization_extension)
post_data['team_admin'] = self.course_team_role.user.id
post_data['mode'] = Seat.VERIFIED
post_data['price'] = 150
response = self.client.post(self.edit_page_url, data=post_data)
# Assert that this saves for an ENTITLEMENT_VERSION
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_detail', kwargs={'pk': self.course.id}),
status_code=302,
target_status_code=200
)
# Assert that trying to switch to Audit/Credit Course (and thus allowing Course Run Seat configuration) fails
post_data['mode'] = ''
post_data['price'] = ''
response = self.client.post(self.edit_page_url, data=post_data)
self.assertEqual(response.status_code, 400)
# Assert that not setting a price for a verified course fails
post_data['mode'] = Seat.VERIFIED
post_data['price'] = ''
response = self.client.post(self.edit_page_url, data=post_data)
self.assertEqual(response.status_code, 400)
# Assert that changing the price for a course with a Verified Entitlement is allowed
new_course = factories.CourseFactory()
factories.CourseEntitlementFactory(course=new_course, mode=Seat.VERIFIED)
post_data['mode'] = Seat.VERIFIED
post_data['price'] = 1
response = self.client.post(self.edit_page_url, data=post_data)
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_detail', kwargs={'pk': self.course.id}),
status_code=302,
target_status_code=200
)
def test_course_with_published_course_run(self): def test_course_with_published_course_run(self):
""" """
Verify that editing course with published course run does not changed state Verify that editing course with published course run does not changed state
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-21 06:07+0000\n" "POT-Creation-Date: 2018-02-21 20:02+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -1398,12 +1398,14 @@ msgstr "" ...@@ -1398,12 +1398,14 @@ msgstr ""
#: apps/publisher/templates/publisher/add_course_form.html #: apps/publisher/templates/publisher/add_course_form.html
#: apps/publisher/templates/publisher/add_courserun_form.html #: apps/publisher/templates/publisher/add_courserun_form.html
#: apps/publisher/templates/publisher/course_edit_form.html
#: apps/publisher/templates/publisher/course_run/edit_run_form.html #: apps/publisher/templates/publisher/course_run/edit_run_form.html
msgid "CERTIFICATE TYPE AND PRICE" msgid "CERTIFICATE TYPE AND PRICE"
msgstr "" msgstr ""
#: apps/publisher/templates/publisher/add_course_form.html #: apps/publisher/templates/publisher/add_course_form.html
#: apps/publisher/templates/publisher/add_courserun_form.html #: apps/publisher/templates/publisher/add_courserun_form.html
#: apps/publisher/templates/publisher/course_edit_form.html
#: apps/publisher/templates/publisher/course_run/edit_run_form.html #: apps/publisher/templates/publisher/course_run/edit_run_form.html
msgid "" msgid ""
"If the course offers a verified or professional education certificate, " "If the course offers a verified or professional education certificate, "
...@@ -3313,7 +3315,29 @@ msgid "" ...@@ -3313,7 +3315,29 @@ msgid ""
msgstr "" msgstr ""
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "Course updated successfully." #, python-brace-format
msgid ""
"The entered price does not match the price for the following course run(s): "
"{course_runs}. The price that you enter must match the price of all active "
"and future course runs."
msgstr ""
#: apps/publisher/views.py
#, python-brace-format
msgid ""
"The entered seat type does not match the seat type for the following course "
"run(s): {course_runs}. The seat type that you enter must match the seat type"
" of all active and future course runs."
msgstr ""
#: apps/publisher/views.py
msgid ""
"Courses with a previously set Type and Price cannot be changed to an "
"Audit/Credit Course"
msgstr ""
#: apps/publisher/views.py
msgid "Course updated successfully."
msgstr "" msgstr ""
#: apps/publisher/views.py #: apps/publisher/views.py
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-21 06:08+0000\n" "POT-Creation-Date: 2018-02-21 20:02+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-21 06:07+0000\n" "POT-Creation-Date: 2018-02-21 20:02+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -1623,12 +1623,14 @@ msgstr "BÌÖ1.1x; BÌÖ1.2x étç. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт ...@@ -1623,12 +1623,14 @@ msgstr "BÌÖ1.1x; BÌÖ1.2x étç. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт
#: apps/publisher/templates/publisher/add_course_form.html #: apps/publisher/templates/publisher/add_course_form.html
#: apps/publisher/templates/publisher/add_courserun_form.html #: apps/publisher/templates/publisher/add_courserun_form.html
#: apps/publisher/templates/publisher/course_edit_form.html
#: apps/publisher/templates/publisher/course_run/edit_run_form.html #: apps/publisher/templates/publisher/course_run/edit_run_form.html
msgid "CERTIFICATE TYPE AND PRICE" msgid "CERTIFICATE TYPE AND PRICE"
msgstr "ÇÉRTÌFÌÇÀTÉ TÝPÉ ÀND PRÌÇÉ Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#" msgstr "ÇÉRTÌFÌÇÀTÉ TÝPÉ ÀND PRÌÇÉ Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#"
#: apps/publisher/templates/publisher/add_course_form.html #: apps/publisher/templates/publisher/add_course_form.html
#: apps/publisher/templates/publisher/add_courserun_form.html #: apps/publisher/templates/publisher/add_courserun_form.html
#: apps/publisher/templates/publisher/course_edit_form.html
#: apps/publisher/templates/publisher/course_run/edit_run_form.html #: apps/publisher/templates/publisher/course_run/edit_run_form.html
msgid "" msgid ""
"If the course offers a verified or professional education certificate, " "If the course offers a verified or professional education certificate, "
...@@ -4024,8 +4026,48 @@ msgstr "" ...@@ -4024,8 +4026,48 @@ msgstr ""
"trý ägäïn. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#" "trý ägäïn. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "Course updated successfully." #, python-brace-format
msgstr "Çöürsé üpdätéd süççéssfüllý. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#" msgid ""
"The entered price does not match the price for the following course run(s): "
"{course_runs}. The price that you enter must match the price of all active "
"and future course runs."
msgstr ""
"Thé éntéréd prïçé döés nöt mätçh thé prïçé för thé föllöwïng çöürsé rün(s): "
"{course_runs}. Thé prïçé thät ýöü éntér müst mätçh thé prïçé öf äll äçtïvé "
"änd fütüré çöürsé rüns. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg"
" єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт"
" єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт "
"αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη "
"νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт "
"σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι σƒƒι¢ια ∂#"
#: apps/publisher/views.py
#, python-brace-format
msgid ""
"The entered seat type does not match the seat type for the following course "
"run(s): {course_runs}. The seat type that you enter must match the seat type"
" of all active and future course runs."
msgstr ""
"Thé éntéréd séät týpé döés nöt mätçh thé séät týpé för thé föllöwïng çöürsé "
"rün(s): {course_runs}. Thé séät týpé thät ýöü éntér müst mätçh thé séät týpé"
" öf äll äçtïvé änd fütüré çöürsé rüns. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт "
"∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση "
"υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє "
"∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα"
" ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт #"
#: apps/publisher/views.py
msgid ""
"Courses with a previously set Type and Price cannot be changed to an "
"Audit/Credit Course"
msgstr ""
"Çöürsés wïth ä prévïöüslý sét Týpé änd Prïçé çännöt ßé çhängéd tö än "
"Àüdït/Çrédït Çöürsé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє#"
#: apps/publisher/views.py
msgid "Course updated successfully."
msgstr "Çöürsé üpdätéd süççéssfüllý. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
#: apps/publisher/views.py #: apps/publisher/views.py
msgid "course" msgid "course"
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-21 06:08+0000\n" "POT-Creation-Date: 2018-02-21 20:02+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
......
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