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
......
...@@ -34,8 +34,8 @@ from course_discovery.apps.publisher.forms import ( ...@@ -34,8 +34,8 @@ from course_discovery.apps.publisher.forms import (
AdminImportCourseForm, CourseEntitlementForm, CourseForm, CourseRunForm, CourseSearchForm, SeatForm AdminImportCourseForm, CourseEntitlementForm, CourseForm, CourseRunForm, CourseSearchForm, SeatForm
) )
from course_discovery.apps.publisher.models import ( from course_discovery.apps.publisher.models import (
Course, CourseEntitlement, CourseRun, CourseRunState, CourseState, CourseUserRole, OrganizationExtension, Course, CourseEntitlement, CourseRun, CourseRunState, CourseState, CourseUserRole,
PublisherUser, Seat, UserAttributes OrganizationExtension, PublisherUser, Seat, UserAttributes
) )
from course_discovery.apps.publisher.utils import ( from course_discovery.apps.publisher.utils import (
get_internal_users, has_role_for_course, is_internal_user, is_project_coordinator_user, is_publisher_admin, get_internal_users, has_role_for_course, is_internal_user, is_project_coordinator_user, is_publisher_admin,
...@@ -362,6 +362,7 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -362,6 +362,7 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
""" Course Edit View.""" """ Course Edit View."""
model = Course model = Course
form_class = CourseForm form_class = CourseForm
entitlement_form = CourseEntitlementForm
permission = OrganizationExtension.EDIT_COURSE permission = OrganizationExtension.EDIT_COURSE
template_name = 'publisher/course_edit_form.html' template_name = 'publisher/course_edit_form.html'
success_url = 'publisher:publisher_course_detail' success_url = 'publisher:publisher_course_detail'
...@@ -369,6 +370,32 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -369,6 +370,32 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse(self.success_url, kwargs={'pk': self.object.id}) return reverse(self.success_url, kwargs={'pk': self.object.id})
def get_context_data(self, **kwargs):
context = super(CourseEditView, self).get_context_data(**kwargs)
history_id = self.request.GET.get('history_id', None)
self.object = self.get_object()
try:
history_object = self.object.history.get(history_id=history_id) if history_id else None
except Exception: # pylint: disable=broad-except
history_object = None
context.update(
{
'course': self.get_object(),
'is_internal_user': is_internal_user(self.request.user),
'history_object': history_object
}
)
if waffle.switch_is_active(PUBLISHER_ENTITLEMENTS_WAFFLE_SWITCH):
if self.object.version == Course.ENTITLEMENT_VERSION:
context['entitlement_form'] = self.entitlement_form(instance=self.object.entitlements.first())
else:
context['entitlement_form'] = self.entitlement_form({'mode': ''})
return context
def get_form_kwargs(self): def get_form_kwargs(self):
""" """
Pass extra kwargs to form, required for team_admin and organization querysets. Pass extra kwargs to form, required for team_admin and organization querysets.
...@@ -393,33 +420,140 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -393,33 +420,140 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
return kwargs return kwargs
def get_context_data(self, **kwargs): def get(self, request, *args, **kwargs):
context = super(CourseEditView, self).get_context_data(**kwargs) self.object = self.get_object()
history_id = self.request.GET.get('history_id', None) context = self.get_context_data()
try: return render(request, self.template_name, context)
history_object = self.object.history.get(history_id=history_id) if history_id else None
except: # pylint: disable=bare-except
history_object = None
context.update( def _get_misconfigured_couse_runs(self, course, price, mode):
{ misconfigured_seat_type_runs = set()
'is_internal_user': is_internal_user(self.request.user), misconfigured_price_runs = set()
'history_object': history_object for course_run in course.publisher_course_runs.filter(end__gt=datetime.now()):
} for seat in course_run.seats.all():
) if seat.type != mode:
return context misconfigured_seat_type_runs.add('{type} - {start}'.format(
type=seat.course_run.get_pacing_type_display(),
start=seat.course_run.start.strftime("%B %d, %Y")
))
if seat.price != price:
misconfigured_price_runs.add('{type} - {start}'.format(
type=seat.course_run.get_pacing_type_display(),
start=seat.course_run.start.strftime("%B %d, %Y")
))
return misconfigured_price_runs, misconfigured_seat_type_runs
def _create_or_update_course_entitlement(self, course, entitlement_form):
entitlement = course.entitlements.first()
if entitlement:
entitlement.price = entitlement_form.cleaned_data.get('price')
entitlement.mode = entitlement_form.cleaned_data.get('mode')
entitlement.save()
else:
entitlement_form.save(course=course)
def form_valid(self, form): def _render_post_error(self, request, ctx_overrides=None, status=400):
""" context = self.get_context_data()
If the form is valid, update organization and team_admin. if ctx_overrides:
""" context.update(ctx_overrides)
return render(request, self.template_name, context, status=status)
def _update_course(self, course_form, user, course_version):
course = course_form.save(commit=False)
course.changed_by = user
course.version = course_version
course.save()
return course
def _handle_entitlement_update(self, user, request, course_form):
entitlement_form = self.entitlement_form(request.POST)
entitlement_mode = entitlement_form.cleaned_data.get('mode')
entitlement_price = entitlement_form.cleaned_data.get('price')
if not entitlement_form.is_valid():
messages.error(
self.request,
_('The page could not be updated. Make sure that all values are correct, then try again.')
)
return self._render_post_error(request, ctx_overrides={
'course_form': course_form,
'entitlement_form': entitlement_form
})
# If the course is originally a SEAT_VERSION and it's now
# using entitlements check that there are no misconfigured runs
if self.object.version == Course.SEAT_VERSION:
if entitlement_mode:
type_misconfigurations, seat_misconfigurations = self._get_misconfigured_couse_runs(
self.object, entitlement_price, entitlement_mode
)
if type_misconfigurations:
# pylint: disable=no-member
error_message = _(
'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.'
).format(course_runs=', '.join(
str(course_run_start) for course_run_start in type_misconfigurations
))
messages.error(request, error_message)
if seat_misconfigurations:
# pylint: disable=no-member
error_message = _(
'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.'
).format(course_runs=', '.join(
str(course_run_start) for course_run_start in seat_misconfigurations
))
messages.error(request, error_message)
if seat_misconfigurations or type_misconfigurations:
return self._render_post_error(request, ctx_overrides={
'course_form': course_form,
'entitlement_form': entitlement_form
})
else:
with transaction.atomic():
course = self._update_course(course_form, user, Course.ENTITLEMENT_VERSION)
self._create_or_update_course_entitlement(course, entitlement_form)
else:
self._update_course(course_form, user, Course.SEAT_VERSION)
elif self.object.version == Course.ENTITLEMENT_VERSION:
if entitlement_mode:
with transaction.atomic():
course = self._update_course(course_form, user, Course.ENTITLEMENT_VERSION)
self._create_or_update_course_entitlement(course, entitlement_form)
else:
messages.error(request, _(
"Courses with a previously set Type and Price cannot be "
"changed to an Audit/Credit Course"
))
return self._render_post_error(request, ctx_overrides={
'course_form': course_form,
'entitlement_form': entitlement_form
})
def post(self, request, *args, **kwargs):
user = self.request.user user = self.request.user
self.object = form.save(commit=False) self.object = self.get_object()
self.object.changed_by = user course_form = self.get_form(self.form_class)
self.object.save()
organization = form.cleaned_data['organization'] if not course_form.is_valid():
messages.error(
self.request, _('The page could not be updated. Make sure that all values are correct, then try again.')
)
return self.render_to_response(self.get_context_data(form=course_form))
# If the switch is active handle the different Course versions, otherwise just save the form like normal
if waffle.switch_is_active(PUBLISHER_ENTITLEMENTS_WAFFLE_SWITCH):
error_response = self._handle_entitlement_update(user, request, course_form)
if error_response:
return error_response
else:
self._update_course(course_form, user, Course.SEAT_VERSION)
organization = course_form.cleaned_data['organization']
if self.object.organizations.first() != organization: if self.object.organizations.first() != organization:
organization_extension = get_object_or_404(OrganizationExtension, organization=organization) organization_extension = get_object_or_404(OrganizationExtension, organization=organization)
self.object.organizations.remove(self.object.organizations.first()) self.object.organizations.remove(self.object.organizations.first())
...@@ -448,7 +582,7 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -448,7 +582,7 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
if user_role not in (self.object.course_state.owner_role, PublisherUserRole.ProjectCoordinator): if user_role not in (self.object.course_state.owner_role, PublisherUserRole.ProjectCoordinator):
self.object.course_state.change_owner_role(user_role) self.object.course_state.change_owner_role(user_role)
team_admin = form.cleaned_data['team_admin'] team_admin = course_form.cleaned_data['team_admin']
if self.object.course_team_admin != team_admin: if self.object.course_team_admin != team_admin:
course_admin_role = get_object_or_404( course_admin_role = get_object_or_404(
CourseUserRole, course=self.object, role=PublisherUserRole.CourseTeam CourseUserRole, course=self.object, role=PublisherUserRole.CourseTeam
...@@ -457,16 +591,9 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView): ...@@ -457,16 +591,9 @@ class CourseEditView(mixins.PublisherPermissionMixin, UpdateView):
course_admin_role.user = team_admin course_admin_role.user = team_admin
course_admin_role.save() course_admin_role.save()
messages.success(self.request, _('Course updated successfully.')) messages.success(self.request, _('Course updated successfully.'))
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
# pylint: disable=no-member
messages.error(
self.request, _('The page could not be updated. Make sure that all values are correct, then try again.')
)
return self.render_to_response(self.get_context_data(form=form))
class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixin, DetailView): class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixin, DetailView):
""" Course Detail View.""" """ Course Detail View."""
......
...@@ -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