def test_create_run_enrollments_enroll_api_fail(mocker, user, keep_failed_enrollments, exception_cls, inner_exception): """ create_run_enrollments should log a message and still create local enrollment records when an enrollment exception is raised if a flag is set to true """ num_runs = 3 runs = CourseRunFactory.create_batch(num_runs) patched_edx_enroll = mocker.patch( "courses.api.enroll_in_edx_course_runs", side_effect=exception_cls(user, runs[2], inner_exception), ) patched_log_exception = mocker.patch("courses.api.log.exception") patched_send_enrollment_email = mocker.patch( "courses.api.mail_api.send_course_run_enrollment_email") successful_enrollments, edx_request_success = create_run_enrollments( user, runs, order=None, company=None, keep_failed_enrollments=keep_failed_enrollments, ) patched_edx_enroll.assert_called_once_with(user, runs) patched_log_exception.assert_called_once() patched_send_enrollment_email.assert_not_called() expected_enrollments = 0 if not keep_failed_enrollments else num_runs assert len(successful_enrollments) == expected_enrollments assert edx_request_success is False
def test_create_run_enrollments(mocker, user): """ create_run_enrollments should call the edX API to create enrollments, create or reactivate local enrollment records, and notify enrolled users via email """ num_runs = 3 order = OrderFactory.create() company = CompanyFactory.create() runs = CourseRunFactory.create_batch(num_runs) # Create an existing deactivate enrollment to test that it gets reactivated CourseRunEnrollmentFactory.create( user=user, run=runs[0], order=order, change_status=ENROLL_CHANGE_STATUS_REFUNDED, active=False, ) patched_edx_enroll = mocker.patch("courses.api.enroll_in_edx_course_runs") patched_send_enrollment_email = mocker.patch( "courses.api.mail_api.send_course_run_enrollment_email") successful_enrollments, edx_request_success = create_run_enrollments( user, runs, order=order, company=company) patched_edx_enroll.assert_called_once_with(user, runs) assert patched_send_enrollment_email.call_count == num_runs assert edx_request_success is True assert len(successful_enrollments) == num_runs enrollments = CourseRunEnrollment.objects.order_by("run__id").all() for (run, enrollment) in zip(runs, enrollments): assert enrollment.change_status is None assert enrollment.active is True assert enrollment.edx_enrolled is True assert enrollment.run == run patched_send_enrollment_email.assert_any_call(enrollment)
def enroll_user_in_order_items(order): """ Enroll the user in the CourseRuns associated with their Order, and create local records of their enrollments. Args: order (Order): An order """ order_line = Line.objects.prefetch_related("line_selections__run").get( order=order) runs = [ line_selection.run for line_selection in order_line.line_selections.all() ] programs = get_order_programs(order) company = get_company_affiliation(order) if programs and not runs: log.error( "An order is being completed for a program, but does not have any course run selections. " "(Order: %d, purchaser: '%s', program(s): %s)", order.id, order.purchaser.email, [program.readable_id for program in programs], ) successful_run_enrollments = [] if runs: successful_run_enrollments, _ = create_run_enrollments( order.purchaser, runs, order=order, company=company, keep_failed_enrollments=True, ) voucher = (order.purchaser.vouchers.filter(product_id__in=order.lines.all( ).values_list("product_version__product__id", flat=True)).order_by( "uploaded").last()) voucher_target = None if (voucher and voucher.is_redeemed() and voucher.product is not None and voucher.enrollment is None): voucher_target = voucher.product.content_object voucher_enrollment = first_or_none( (enrollment for enrollment in successful_run_enrollments if enrollment.run == voucher_target)) if voucher_enrollment is not None: voucher.enrollment = voucher_enrollment voucher.save() if programs: create_program_enrollments(order.purchaser, programs, order=order, company=company)
def test_create_run_enrollments_api_fail(mocker, user, exception_cls): """ create_run_enrollments should log a message and still create local enrollment records when certain exceptions are raised if a flag is set to true """ patched_edx_enroll = mocker.patch("courses.api.enroll_in_edx_course_runs", side_effect=exception_cls) patched_log_exception = mocker.patch("courses.api.log.exception") patched_send_enrollment_email = mocker.patch( "courses.api.mail_api.send_course_run_enrollment_email") run = CourseRunFactory.create() successful_enrollments, edx_request_success = create_run_enrollments( user, [run], order=None, company=None, keep_failed_enrollments=True) patched_edx_enroll.assert_called_once_with(user, [run]) patched_log_exception.assert_called_once() patched_send_enrollment_email.assert_not_called() assert len(successful_enrollments) == 1 assert edx_request_success is False
def test_create_run_enrollments_creation_fail(mocker, user): """ create_run_enrollments should log a message and send an admin email if there's an error during the creation of local enrollment records """ runs = CourseRunFactory.create_batch(2) enrollment = CourseRunEnrollmentFactory.build(run=runs[1]) mocker.patch( "courses.api.CourseRunEnrollment.all_objects.get_or_create", side_effect=[Exception(), (enrollment, True)], ) patched_edx_enroll = mocker.patch("courses.api.enroll_in_edx_course_runs") patched_log_exception = mocker.patch("courses.api.log.exception") patched_mail_api = mocker.patch("courses.api.mail_api") successful_enrollments, edx_request_success = create_run_enrollments( user, runs, order=None, company=None) patched_edx_enroll.assert_called_once_with(user, runs) patched_log_exception.assert_called_once() patched_mail_api.send_course_run_enrollment_email.assert_not_called() patched_mail_api.send_enrollment_failure_message.assert_called_once() assert successful_enrollments == [enrollment] assert edx_request_success is True
def handle(self, *args, **options): """Handle command execution""" user = fetch_user(options["user"]) run = CourseRun.objects.filter(courseware_id=options["run"]).first() if run is None: raise CommandError( "Could not find course run with courseware_id={}".format( options["run"])) product = Product.objects.filter( courseruns__courseware_id=options["run"]).first() if product is None: raise CommandError( "No product found for that course with courseware_id={}". format(options["run"])) coupon = Coupon.objects.filter(coupon_code=options["code"]).first() if not coupon: raise CommandError("That enrollment code {} does not exist".format( options["code"])) # Check if the coupon is valid for the product coupon_version = best_coupon_for_product(product, user, code=coupon.coupon_code) if coupon_version is None: raise CommandError({ "coupons": "Enrollment code {} is invalid for course run {}".format( options["code"], options["run"]) }) # Fetch the latest product version. product_version = latest_product_version(product) # Calculate the total paid price after applying coupon. total_price_paid = get_product_version_price_with_discount( coupon_version=coupon_version, product_version=product_version) with transaction.atomic(): # Create an order. order = Order.objects.create( status=Order.FULFILLED, purchaser=user, total_price_paid=total_price_paid, ) successful_enrollments, edx_request_success = create_run_enrollments( user, [run], keep_failed_enrollments=options["keep_failed_enrollments"], order=order, ) if not successful_enrollments: raise CommandError("Failed to create the enrollment record") ProductCouponAssignment.objects.filter( email__iexact=user.email, redeemed=False, product_coupon__coupon=coupon).update(redeemed=True) self.stdout.write( self.style.SUCCESS( "Enrollment created for user {} in {} (edX enrollment success: {})" .format(user, options["run"], edx_request_success))) line = Line.objects.create(order=order, product_version=product_version, quantity=1) redeem_coupon(coupon_version=coupon_version, order=order) self.stdout.write( self.style.SUCCESS( "Order {} with line {} is created for user {} ".format( order, line, user)))