From 551f690adecf7869c0cd9eb59e5cb631201f06c2 Mon Sep 17 00:00:00 2001 From: Waheed Ahmed Date: Thu, 26 Sep 2019 15:20:00 +0500 Subject: [PATCH] Use order date_placed enrollment attribute for refund. Use order date_placed enrollment attribute for refund if exist otherwise fetch it from ecommerce. PROD-454 --- common/djangoapps/student/models.py | 70 +++++++++++-------- .../djangoapps/student/tests/test_refunds.py | 28 +++++++- 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 9a7e521eb8..648bda9a80 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -1765,8 +1765,45 @@ class CourseEnrollment(models.Model): # NOTE: This is here to avoid circular references from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, ECOMMERCE_DATE_FORMAT + date_placed = self.get_order_attribute_value('date_placed') + + if not date_placed: + order_number = self.get_order_attribute_value('order_number') + if not order_number: + return None + + try: + order = ecommerce_api_client(self.user).orders(order_number).get() + date_placed = order['date_placed'] + except HttpClientError: + log.warning( + u"Encountered HttpClientError while getting order details from ecommerce. " + u"Order={number} and user {user}".format(number=order_number, user=self.user.id)) + return None + + except HttpServerError: + log.warning( + u"Encountered HttpServerError while getting order details from ecommerce. " + u"Order={number} and user {user}".format(number=order_number, user=self.user.id)) + return None + + except SlumberBaseException: + log.warning( + u"Encountered an error while getting order details from ecommerce. " + u"Order={number} and user {user}".format(number=order_number, user=self.user.id)) + return None + + refund_window_start_date = max( + datetime.strptime(date_placed, ECOMMERCE_DATE_FORMAT), + self.course_overview.start.replace(tzinfo=None) + ) + + return refund_window_start_date.replace(tzinfo=UTC) + EnrollmentRefundConfiguration.current().refund_window + + def get_order_attribute_value(self, attr_name): + """ Get and return course enrollment order attribute's value.""" try: - attribute = self.attributes.get(namespace='order', name='order_number') + attribute = self.attributes.get(namespace='order', name=attr_name) except ObjectDoesNotExist: return None except MultipleObjectsReturned: @@ -1777,36 +1814,9 @@ class CourseEnrollment(models.Model): self.user.id, enrollment_id ) - attribute = self.attributes.filter(namespace='order', name='order_number').last() + attribute = self.attributes.filter(namespace='order', name=attr_name).last() - order_number = attribute.value - try: - order = ecommerce_api_client(self.user).orders(order_number).get() - - except HttpClientError: - log.warning( - u"Encountered HttpClientError while getting order details from ecommerce. " - u"Order={number} and user {user}".format(number=order_number, user=self.user.id)) - return None - - except HttpServerError: - log.warning( - u"Encountered HttpServerError while getting order details from ecommerce. " - u"Order={number} and user {user}".format(number=order_number, user=self.user.id)) - return None - - except SlumberBaseException: - log.warning( - u"Encountered an error while getting order details from ecommerce. " - u"Order={number} and user {user}".format(number=order_number, user=self.user.id)) - return None - - refund_window_start_date = max( - datetime.strptime(order['date_placed'], ECOMMERCE_DATE_FORMAT), - self.course_overview.start.replace(tzinfo=None) - ) - - return refund_window_start_date.replace(tzinfo=UTC) + EnrollmentRefundConfiguration.current().refund_window + return attribute.value @property def username(self): diff --git a/common/djangoapps/student/tests/test_refunds.py b/common/djangoapps/student/tests/test_refunds.py index 53a3d15e92..2214946d6f 100644 --- a/common/djangoapps/student/tests/test_refunds.py +++ b/common/djangoapps/student/tests/test_refunds.py @@ -22,10 +22,11 @@ from six.moves import range # These imports refer to lms djangoapps. # Their testcases are only run under lms. from course_modes.tests.factories import CourseModeFactory + from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory from openedx.core.djangoapps.commerce.utils import ECOMMERCE_DATE_FORMAT -from student.models import CourseEnrollment +from student.models import CourseEnrollment, EnrollmentRefundConfiguration from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -169,6 +170,31 @@ class RefundableTest(SharedModuleStoreTestCase): """ Assert that the None is returned when no order number attribute is found.""" self.assertIsNone(self.enrollment.refund_cutoff_date()) + @patch('openedx.core.djangoapps.commerce.utils.ecommerce_api_client') + def test_refund_cutoff_date_with_date_placed_attr(self, mock_ecommerce_api_client): + """ + Assert that the refund_cutoff_date returns order placement date if order:date_placed + attribute exist without calling ecommerce. + """ + now = datetime.now(pytz.UTC).replace(microsecond=0) + order_date = now + timedelta(days=2) + course_start = now + timedelta(days=1) + + self.enrollment.course_overview.start = course_start + self.enrollment.attributes.create( + enrollment=self.enrollment, + namespace='order', + name='date_placed', + value=order_date.strftime(ECOMMERCE_DATE_FORMAT) + ) + + refund_config = EnrollmentRefundConfiguration.current() + self.assertEqual( + self.enrollment.refund_cutoff_date(), + order_date + refund_config.refund_window + ) + mock_ecommerce_api_client.assert_not_called() + @httpretty.activate @override_settings(ECOMMERCE_API_URL=TEST_API_URL) def test_multiple_refunds_dashbaord_page_error(self):