Commit 269ca555 by Anthony Mangano

Publisher entitlement-aware edit courserun views

LEARNER-3770
parent 229f37ef
...@@ -173,35 +173,37 @@ ...@@ -173,35 +173,37 @@
</div> </div>
</div> </div>
<div class="field-title">{% trans "CERTIFICATE TYPE AND PRICE" %} <span class="required float-right">* Required for review</span></div> {% if seat_form %}
<div class="row"> <div class="field-title">{% trans "CERTIFICATE TYPE AND PRICE" %} <span class="required float-right">* Required for review</span></div>
<div class="col col-6 help-text"> <div class="row">
{% trans "If the course offers a verified or professional education certificate, select the certificate type and enter the price for the certificate." %} <div class="col col-6 help-text">
</div> {% trans "If the course offers a verified or professional education certificate, select the certificate type and enter the price for the certificate." %}
<div class="col col-6"> </div>
<div class="row"> <div class="col col-6">
<div class="col col-6"> <div class="row">
<label class="field-label ">{{ seat_form.type.label_tag }} <div class="col col-6">
</label> {{ seat_form.type }} <label class="field-label ">{{ seat_form.type.label_tag }}
</div> </label> {{ seat_form.type }}
<div id="seatPriceBlock" class="col col-6 {% if seat_form.type.value == 'audit' or not seat_form.price.value %}hidden{% endif %}"> </div>
<label class="field-label ">{{ seat_form.price.label_tag }}</label> <div id="seatPriceBlock" class="col col-6 {% if seat_form.type.value == 'audit' or not seat_form.price.value %}hidden{% endif %}">
{{ seat_form.price }} <label class="field-label ">{{ seat_form.price.label_tag }}</label>
<div class="{% if seat_form.type.value != 'credit' or not seat_form.credit_price.value %}hidden{% endif %}" id="creditPrice"> {{ seat_form.price }}
<label class="field-label">{{ seat_form.credit_price.label }}</label> <div class="{% if seat_form.type.value != 'credit' or not seat_form.credit_price.value %}hidden{% endif %}" id="creditPrice">
{{ seat_form.credit_price }} <label class="field-label">{{ seat_form.credit_price.label }}</label>
{{ seat_form.credit_price }}
</div>
</div> </div>
</div> </div>
{% if seat_form.price.errors %}
<div class="field-message has-error">
<span class="field-message-content">
{{ seat_form.price.errors|escape }}
</span>
</div>
{% endif %}
</div> </div>
{% if seat_form.price.errors %}
<div class="field-message has-error">
<span class="field-message-content">
{{ seat_form.price.errors|escape }}
</span>
</div>
{% endif %}
</div> </div>
</div> {% endif %}
<div class="field-title">{% trans "COURSE STAFF" %} <span class="required float-right">* Required for review</span></div> <div class="field-title">{% trans "COURSE STAFF" %} <span class="required float-right">* Required for review</span></div>
<div class="row"> <div class="row">
...@@ -313,7 +315,6 @@ ...@@ -313,7 +315,6 @@
<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/instructors.js' %}"></script> <script src="{% static 'js/publisher/instructors.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/program-types.js' %}"></script> <script src="{% static 'js/publisher/program-types.js' %}"></script>
<script src="{% static 'js/publisher/modal-screen.js' %}"></script> <script src="{% static 'js/publisher/modal-screen.js' %}"></script>
...@@ -321,4 +322,7 @@ ...@@ -321,4 +322,7 @@
{% block js_without_compress %} {% block js_without_compress %}
{{ run_form.media }} {{ run_form.media }}
{% if seat_form %}
<script src="{% static 'js/publisher/seat-type-change.js' %}"></script>
{% endif %}
{% endblock %} {% endblock %}
...@@ -3087,6 +3087,24 @@ class CourseRunEditViewTests(SiteMixin, TestCase): ...@@ -3087,6 +3087,24 @@ class CourseRunEditViewTests(SiteMixin, TestCase):
toggle_switch('enable_publisher_email_notifications', True) toggle_switch('enable_publisher_email_notifications', True)
def test_courserun_edit_form_for_course_with_entitlements(self):
""" Verify that the edit form does not include Seat fields for courses that use entitlements. """
self.course_run.course.version = Course.ENTITLEMENT_VERSION
self.course_run.course.save()
url = reverse('publisher:publisher_course_runs_edit', kwargs={'pk': self.course_run.id})
response = self.client.get(url)
self.assertNotContains(response, 'CERTIFICATE TYPE AND PRICE', status_code=200)
def test_courserun_edit_form_for_course_without_entitlements(self):
""" Verify that the edit form includes Seat fields for courses that do not use entitlements. """
self.course_run.course.version = Course.SEAT_VERSION
self.course_run.course.save()
url = reverse('publisher:publisher_course_runs_edit', kwargs={'pk': self.course_run.id})
response = self.client.get(url)
self.assertContains(response, 'CERTIFICATE TYPE AND PRICE', status_code=200)
def test_edit_page_with_two_seats(self): def test_edit_page_with_two_seats(self):
""" """
Verify that if a course run has both audit and verified seats, Verified seat is displayed Verify that if a course run has both audit and verified seats, Verified seat is displayed
...@@ -3231,6 +3249,45 @@ class CourseRunEditViewTests(SiteMixin, TestCase): ...@@ -3231,6 +3249,45 @@ class CourseRunEditViewTests(SiteMixin, TestCase):
self.assertEqual(course_run.seats.first().history.all().count(), 1) self.assertEqual(course_run.seats.first().history.all().count(), 1)
def test_update_course_run_for_course_that_uses_entitlements(self):
""" Verify that a user cannot change Seat data when editing courseruns for courses that use entitlements. """
self.user.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME))
self.new_course.version = Course.ENTITLEMENT_VERSION
self.new_course.save()
post_data = self._post_data({}, self.new_course, self.new_course_run)
post_data.update({'image': '', 'max_effort': 123, 'type': Seat.PROFESSIONAL, 'price': 10.00})
# Create a Seat so we can verify that it does not get modified.
before_seat = self.new_course_run.seats.create(type=Seat.VERIFIED, price=5)
self.assertNotEqual(before_seat.type, post_data['type'])
self.assertNotEqual(before_seat.price, post_data['price'])
self.assertNotEqual(self.new_course_run.max_effort, post_data['max_effort'])
# This request should fail since it includes Seat data
response = self.client.post(self.edit_page_url, post_data)
self.assertContains(response, 'The page could not be updated.', status_code=400)
del post_data['type']
del post_data['price']
# Now that the Seat data has been removed, the request should succeed.
response = self.client.post(self.edit_page_url, post_data)
self.assertRedirects(
response,
expected_url=reverse('publisher:publisher_course_run_detail', kwargs={'pk': self.new_course_run.id}),
status_code=302,
target_status_code=200
)
# Make sure that max_effort was updated, but the Seat data was not.
course_run = CourseRun.objects.get(id=self.new_course_run.id)
self.assertEqual(post_data['max_effort'], course_run.max_effort)
after_seat = course_run.seats.latest('created')
self.assertEqual(before_seat.type, after_seat.type)
self.assertEqual(before_seat.price, after_seat.price)
def test_logging(self): def test_logging(self):
""" Verify view logs the errors in case of errors. """ """ Verify view logs the errors in case of errors. """
with mock.patch('django.forms.models.BaseModelForm.is_valid') as mocked_is_valid: with mock.patch('django.forms.models.BaseModelForm.is_valid') as mocked_is_valid:
......
...@@ -802,11 +802,13 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix ...@@ -802,11 +802,13 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
context['run_form'] = self.run_form( context['run_form'] = self.run_form(
instance=course_run, is_project_coordinator=context.get('is_project_coordinator') instance=course_run, is_project_coordinator=context.get('is_project_coordinator')
) )
course_run_paid_seat = course_run.seats.filter(type__in=PAID_SEATS).first()
if course_run_paid_seat: if not course.uses_entitlements:
context['seat_form'] = self.seat_form(instance=course_run_paid_seat) course_run_paid_seat = course_run.seats.filter(type__in=PAID_SEATS).first()
else: if course_run_paid_seat:
context['seat_form'] = self.seat_form(instance=course_run.seats.first()) context['seat_form'] = self.seat_form(instance=course_run_paid_seat)
else:
context['seat_form'] = self.seat_form(instance=course_run.seats.first())
start_date = course_run.start.strftime("%B %d, %Y") if course_run.start else None start_date = course_run.start.strftime("%B %d, %Y") if course_run.start else None
context['breadcrumbs'] = make_bread_crumbs( context['breadcrumbs'] = make_bread_crumbs(
...@@ -831,8 +833,19 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix ...@@ -831,8 +833,19 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
run_form = self.run_form( run_form = self.run_form(
request.POST, instance=course_run, is_project_coordinator=context.get('is_project_coordinator') request.POST, instance=course_run, is_project_coordinator=context.get('is_project_coordinator')
) )
seat_form = self.seat_form(request.POST, instance=course_run.seats.first()) context['run_form'] = run_form
if run_form.is_valid() and seat_form.is_valid(): form_data_is_valid = run_form.is_valid()
if course_run.course.uses_entitlements:
seat_data_in_form = any([key for key in self.seat_form.declared_fields.keys() if key in request.POST])
form_data_is_valid = form_data_is_valid and not seat_data_in_form
seat_form = None
else:
seat_form = self.seat_form(request.POST, instance=course_run.seats.first())
form_data_is_valid = form_data_is_valid and seat_form.is_valid()
context['seat_form'] = seat_form
if form_data_is_valid:
try: try:
with transaction.atomic(): with transaction.atomic():
...@@ -841,7 +854,7 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix ...@@ -841,7 +854,7 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
run_form.save_m2m() run_form.save_m2m()
# If price-type comes with request then save the seat object. # If price-type comes with request then save the seat object.
if request.POST.get('type'): if seat_form and request.POST.get('type'):
seat_form.save(changed_by=user, course_run=course_run) seat_form.save(changed_by=user, course_run=course_run)
# in case of any updating move the course-run state to draft except draft and published state. # in case of any updating move the course-run state to draft except draft and published state.
...@@ -879,12 +892,6 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix ...@@ -879,12 +892,6 @@ class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMix
messages.error( messages.error(
request, _('The page could not be updated. Make sure that all values are correct, then try again.') request, _('The page could not be updated. Make sure that all values are correct, then try again.')
) )
context.update(
{
'run_form': run_form,
'seat_form': seat_form
}
)
return render(request, self.template_name, context, status=400) return render(request, self.template_name, context, status=400)
......
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