diff --git a/common/djangoapps/terrain/stubs/ecommerce.py b/common/djangoapps/terrain/stubs/ecommerce.py
index 082b09e6cf..6db67537fa 100644
--- a/common/djangoapps/terrain/stubs/ecommerce.py
+++ b/common/djangoapps/terrain/stubs/ecommerce.py
@@ -38,18 +38,14 @@ class StubEcommerceServiceHandler(StubHttpRequestHandler): # pylint: disable=mi
{
'status': 'Complete',
'number': 'Edx-123',
- 'total_excl_tax': '100.0',
+ 'total_excl_tax': '100.00',
'date_placed': '2016-04-21T23:14:23Z',
'lines': [
{
'title': 'Test Course',
+ 'line_price_excl_tax': '100.00',
'product': {
- 'attribute_values': [
- {
- 'name': 'certificate_type',
- 'value': 'verified'
- }
- ]
+ 'product_class': 'Seat'
}
}
],
diff --git a/common/test/acceptance/pages/lms/account_settings.py b/common/test/acceptance/pages/lms/account_settings.py
index 793f714b45..a65461e3cc 100644
--- a/common/test/acceptance/pages/lms/account_settings.py
+++ b/common/test/acceptance/pages/lms/account_settings.py
@@ -72,7 +72,7 @@ class AccountSettingsPage(FieldsMixin, PageObject):
def get_value_of_order_history_row_item(self, field_id, field_name):
""" Return the text value of the provided order field name."""
query = self.q(css='.u-field-{} .u-field-order-{}'.format(field_id, field_name))
- return query.text[0] if query.present else None
+ return query.text if query.present else None
def order_button_is_visible(self, field_id):
""" Check that if hovering over the order history row shows the
diff --git a/common/test/acceptance/tests/lms/test_account_settings.py b/common/test/acceptance/tests/lms/test_account_settings.py
index ff5f0992a9..cd96e5af8c 100644
--- a/common/test/acceptance/tests/lms/test_account_settings.py
+++ b/common/test/acceptance/tests/lms/test_account_settings.py
@@ -489,15 +489,25 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, AcceptanceTest):
# verify that we are on correct tab
self.assertTrue(self.account_settings_page.is_order_history_tab_visible)
- expected_order_data = {
- 'title': 'Test Course',
+ expected_order_data_first_row = {
+ 'number': 'Order Number:\nEdx-123',
'date': 'Date Placed:\nApr 21, 2016',
- 'price': 'Cost:\n$100.0',
- 'number': 'Order Number:\nEdx-123'
+ 'price': 'Cost:\n$100.00',
}
- for field_name, value in expected_order_data.iteritems():
+ expected_order_data_second_row = {
+ 'number': 'Product Name:\nTest Course',
+ 'date': 'Date Placed:\nApr 21, 2016',
+ 'price': 'Cost:\n$100.00',
+ }
+
+ for field_name, value in expected_order_data_first_row.iteritems():
self.assertEqual(
- self.account_settings_page.get_value_of_order_history_row_item('order-Edx-123', field_name), value
+ self.account_settings_page.get_value_of_order_history_row_item('order-Edx-123', field_name)[0], value
+ )
+
+ for field_name, value in expected_order_data_second_row.iteritems():
+ self.assertEqual(
+ self.account_settings_page.get_value_of_order_history_row_item('order-Edx-123', field_name)[1], value
)
self.assertTrue(self.account_settings_page.order_button_is_visible('order-Edx-123'))
diff --git a/lms/djangoapps/commerce/tests/mocks.py b/lms/djangoapps/commerce/tests/mocks.py
index d4a4489f40..8435fc040b 100644
--- a/lms/djangoapps/commerce/tests/mocks.py
+++ b/lms/djangoapps/commerce/tests/mocks.py
@@ -164,6 +164,22 @@ class mock_get_orders(mock_ecommerce_api_endpoint):
)])
)
]
+ ),
+ factories.OrderFactory(
+ lines=[
+ factories.OrderLineFactory(
+ product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory(
+ name='certificate_type',
+ value='verified'
+ )])
+ ),
+ factories.OrderLineFactory(
+ product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory(
+ name='certificate_type',
+ value='verified'
+ )])
+ ),
+ ]
)
]
}
diff --git a/lms/djangoapps/commerce/utils.py b/lms/djangoapps/commerce/utils.py
index e7282a5019..114e307c96 100644
--- a/lms/djangoapps/commerce/utils.py
+++ b/lms/djangoapps/commerce/utils.py
@@ -39,11 +39,10 @@ class EcommerceService(object):
Receipt page for the specified Order.
"""
ecommerce_receipt_page_url = configuration_helpers.get_value('ECOMMERCE_RECEIPT_PAGE_URL')
+
if ecommerce_receipt_page_url:
- receipt_page_url = self.get_absolute_ecommerce_url(ecommerce_receipt_page_url)
- else:
- receipt_page_url = self.config.receipt_page
- return receipt_page_url + order_number
+ return self.get_absolute_ecommerce_url(ecommerce_receipt_page_url + order_number)
+ return self.config.receipt_page + order_number
def is_enabled(self, user):
"""
diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py
index 71b0ae0c60..7dc4049a55 100644
--- a/lms/djangoapps/student_account/test/test_views.py
+++ b/lms/djangoapps/student_account/test/test_views.py
@@ -634,20 +634,21 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
self.assertContains(response, '
')
def test_commerce_order_detail(self):
+ """
+ Verify that get_user_orders returns the correct order data.
+ """
with mock_get_orders():
order_detail = get_user_orders(self.user)
- user_order = mock_get_orders.default_response['results'][0]
- expected = [
- {
- 'number': user_order['number'],
- 'price': user_order['total_excl_tax'],
- 'title': user_order['lines'][0]['title'],
+ for i, order in enumerate(mock_get_orders.default_response['results']):
+ expected = {
+ 'number': order['number'],
+ 'price': order['total_excl_tax'],
'order_date': 'Jan 01, 2016',
- 'receipt_url': '/commerce/checkout/receipt/?orderNum=' + user_order['number']
+ 'receipt_url': '/commerce/checkout/receipt/?orderNum=' + order['number'],
+ 'lines': order['lines'],
}
- ]
- self.assertEqual(order_detail, expected)
+ self.assertEqual(order_detail[i], expected)
def test_commerce_order_detail_exception(self):
with mock_get_orders(exception=exceptions.HttpNotFoundError):
@@ -673,26 +674,6 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
self.assertEqual(order_detail, [])
- def test_honor_course_order_detail(self):
- response = {
- 'results': [
- factories.OrderFactory(
- lines=[
- factories.OrderLineFactory(
- product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory(
- name='certificate_type',
- value='honor'
- )])
- )
- ]
- )
- ]
- }
- with mock_get_orders(response=response):
- order_detail = get_user_orders(self.user)
-
- self.assertEqual(order_detail, [])
-
def test_order_history_with_no_product(self):
response = {
'results': [
@@ -716,6 +697,24 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
self.assertEqual(len(order_detail), 1)
+ def test_order_history_with_coupon(self):
+ """
+ Verify that get_order_details returns empty receipt_url for coupon product.
+ """
+ response = {
+ 'results': [
+ factories.OrderFactory(
+ lines=[
+ factories.OrderLineFactory(product=factories.ProductFactory(product_class='Coupon'))
+ ]
+ )
+ ]
+ }
+ with mock_get_orders(response=response):
+ order_detail = get_user_orders(self.user)
+
+ self.assertEqual(order_detail[0]['receipt_url'], '')
+
@override_settings(SITE_NAME=settings.MICROSITE_LOGISTRATION_HOSTNAME)
class MicrositeLogistrationTests(TestCase):
diff --git a/lms/djangoapps/student_account/views.py b/lms/djangoapps/student_account/views.py
index 1e6b46b822..806f898068 100644
--- a/lms/djangoapps/student_account/views.py
+++ b/lms/djangoapps/student_account/views.py
@@ -331,7 +331,6 @@ def get_user_orders(user):
"""
no_data = []
user_orders = []
- allowed_course_modes = ['professional', 'verified', 'credit']
commerce_configuration = CommerceConfiguration.current()
user_query = {'username': user.username}
@@ -344,26 +343,25 @@ def get_user_orders(user):
for order in commerce_user_orders:
if order['status'].lower() == 'complete':
- for line in order['lines']:
- product = line.get('product')
- if product:
- for attribute in product['attribute_values']:
- if attribute['name'] == 'certificate_type' and attribute['value'] in allowed_course_modes:
- try:
- date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ")
- order_data = {
- 'number': order['number'],
- 'price': order['total_excl_tax'],
- 'title': order['lines'][0]['title'],
- 'order_date': strftime_localized(
- date_placed.replace(tzinfo=pytz.UTC), 'SHORT_DATE'
- ),
- 'receipt_url': EcommerceService().get_receipt_page_url(order['number'])
- }
- user_orders.append(order_data)
- except KeyError:
- log.exception('Invalid order structure: %r', order)
- return no_data
+ date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ")
+ order_data = {
+ 'number': order['number'],
+ 'price': order['total_excl_tax'],
+ 'order_date': strftime_localized(date_placed, 'SHORT_DATE'),
+ 'receipt_url': EcommerceService().get_receipt_page_url(order['number']),
+ 'lines': order['lines'],
+ }
+
+ # If the order lines contain a product that is not a Seat
+ # we do not want to display the Order Details button. It
+ # will break the receipt page if used.
+ for order_line in order['lines']:
+ product = order_line.get('product')
+
+ if product and product.get('product_class') != 'Seat':
+ order_data['receipt_url'] = ''
+ break
+ user_orders.append(order_data)
return user_orders
diff --git a/lms/static/js/student_account/views/account_settings_factory.js b/lms/static/js/student_account/views/account_settings_factory.js
index 60461e233e..c8a7f0c22f 100644
--- a/lms/static/js/student_account/views/account_settings_factory.js
+++ b/lms/static/js/student_account/views/account_settings_factory.js
@@ -229,12 +229,12 @@
}
return {
'view': new AccountSettingsFieldViews.OrderHistoryFieldView({
- title: order.title,
totalPrice: order.price,
orderId: order.number,
orderDate: order.order_date,
receiptUrl: order.receipt_url,
- valueAttribute: 'order-' + orderNumber
+ valueAttribute: 'order-' + orderNumber,
+ lines: order.lines
})
};
})
diff --git a/lms/static/js/student_account/views/account_settings_fields.js b/lms/static/js/student_account/views/account_settings_fields.js
index 2da3447e30..6e54d5fe14 100644
--- a/lms/static/js/student_account/views/account_settings_fields.js
+++ b/lms/static/js/student_account/views/account_settings_fields.js
@@ -325,12 +325,12 @@
render: function() {
HtmlUtils.setHtml(this.$el, this.template({
- title: this.options.title,
totalPrice: this.options.totalPrice,
orderId: this.options.orderId,
orderDate: this.options.orderDate,
receiptUrl: this.options.receiptUrl,
- valueAttribute: this.options.valueAttribute
+ valueAttribute: this.options.valueAttribute,
+ lines: this.options.lines
}));
this.delegateEvents();
return this;
diff --git a/lms/static/sass/views/_account-settings.scss b/lms/static/sass/views/_account-settings.scss
index 6a7fe19802..03b7e7922d 100644
--- a/lms/static/sass/views/_account-settings.scss
+++ b/lms/static/sass/views/_account-settings.scss
@@ -193,35 +193,37 @@
display: flex;
align-items: center;
font-size: em(16);
- color: $gray;
+ font-weight: 600;
+ color: $dark-gray;
width: 100%;
padding-top: $baseline;
padding-bottom: $baseline;
line-height: normal;
+ flex-direction: row;
+ flex-wrap: wrap;
span {
padding: $baseline;
}
- .u-field-order-title {
+ .u-field-order-number {
@include float(left);
- width: 26%;
- font-size: em(20);
- padding-left: ($baseline*2);
- }
-
- .u-field-order-value {
- @include float(left);
- width: 12%;
+ width: 30%;
}
.u-field-order-date {
@include float(left);
- width: 16%;
+ padding-left: 30px;
+ width: 20%;
+ }
+
+ .u-field-order-price {
+ @include float(left);
+ width: 15%;
}
.u-field-order-link {
- width: 15%;
+ width: 10%;
padding: 0;
.u-field-link {
@@ -237,6 +239,16 @@
}
}
+ .u-field-order-lines {
+ @extend .u-field-order;
+ padding: 5px 0 0;
+ font-weight: 100;
+
+ .u-field-order-number {
+ padding: 20px 10px 20px 30px;
+ }
+ }
+
.social-field-linked {
background: $m-gray-l4;
box-shadow: 0 1px 2px 1px $shadow-l2;
@@ -290,7 +302,7 @@
font-weight: $font-semibold;
}
}
-
+
.u-field-message {
position: relative;
padding: 24px 0 0 ($baseline*5);
diff --git a/lms/templates/fields/field_order_history.underscore b/lms/templates/fields/field_order_history.underscore
index 4d2daedce2..5d1aed8233 100644
--- a/lms/templates/fields/field_order_history.underscore
+++ b/lms/templates/fields/field_order_history.underscore
@@ -1,11 +1,17 @@
role="group" aria-labelledby="order-title-<%- orderId %>" <% } else { %> aria-hidden="true" <% } %>>
-
id="order-title-<%- orderId %>" <% } %>><%- title %>
-
Date Placed: <%- orderDate %>
-
Cost: <% if (!isNaN(parseFloat(totalPrice))) { %>$<% } %><%- totalPrice %>
-
Order Number: <%- orderId %>
+
<%- gettext('Order Number') %>: <%- orderId %>
+
<%- gettext('Date Placed') %>: <%- orderDate %>
+
<%- gettext('Cost') %>: <% if (!isNaN(parseFloat(totalPrice))) { %>$<% } %><%- totalPrice %>
<% if (receiptUrl) { %>
- <%- gettext('Order Details') %> for <%- orderId %>
+ <%- gettext('Order Details') %> <%- gettext('for') %> <%- orderId %>
<% } %>
+ <% _.each(lines, function(item){ %>
+
+ <%- gettext('Product Name') %>: <%- item.title %>
+ <%- gettext('Date Placed') %>: <%- orderDate %>
+ <%- gettext('Cost') %>: <% if (!isNaN(parseFloat(item.line_price_excl_tax))) { %>$<% } %><%- item.line_price_excl_tax %>
+
+ <% }); %>