Commit b682c43d by Ivan Ivic

Update Order History page to reflect bundled purchase

LEARNER-381
parent d9f17435
...@@ -38,18 +38,14 @@ class StubEcommerceServiceHandler(StubHttpRequestHandler): # pylint: disable=mi ...@@ -38,18 +38,14 @@ class StubEcommerceServiceHandler(StubHttpRequestHandler): # pylint: disable=mi
{ {
'status': 'Complete', 'status': 'Complete',
'number': 'Edx-123', 'number': 'Edx-123',
'total_excl_tax': '100.0', 'total_excl_tax': '100.00',
'date_placed': '2016-04-21T23:14:23Z', 'date_placed': '2016-04-21T23:14:23Z',
'lines': [ 'lines': [
{ {
'title': 'Test Course', 'title': 'Test Course',
'line_price_excl_tax': '100.00',
'product': { 'product': {
'attribute_values': [ 'product_class': 'Seat'
{
'name': 'certificate_type',
'value': 'verified'
}
]
} }
} }
], ],
......
...@@ -72,7 +72,7 @@ class AccountSettingsPage(FieldsMixin, PageObject): ...@@ -72,7 +72,7 @@ class AccountSettingsPage(FieldsMixin, PageObject):
def get_value_of_order_history_row_item(self, field_id, field_name): def get_value_of_order_history_row_item(self, field_id, field_name):
""" Return the text value of the provided order 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)) 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): def order_button_is_visible(self, field_id):
""" Check that if hovering over the order history row shows the """ Check that if hovering over the order history row shows the
......
...@@ -489,15 +489,25 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, AcceptanceTest): ...@@ -489,15 +489,25 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, AcceptanceTest):
# verify that we are on correct tab # verify that we are on correct tab
self.assertTrue(self.account_settings_page.is_order_history_tab_visible) self.assertTrue(self.account_settings_page.is_order_history_tab_visible)
expected_order_data = { expected_order_data_first_row = {
'title': 'Test Course', 'number': 'Order Number:\nEdx-123',
'date': 'Date Placed:\nApr 21, 2016', 'date': 'Date Placed:\nApr 21, 2016',
'price': 'Cost:\n$100.0', 'price': 'Cost:\n$100.00',
'number': 'Order Number:\nEdx-123'
} }
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)[0], value
)
for field_name, value in expected_order_data_second_row.iteritems():
self.assertEqual( 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)[1], value
) )
self.assertTrue(self.account_settings_page.order_button_is_visible('order-Edx-123')) self.assertTrue(self.account_settings_page.order_button_is_visible('order-Edx-123'))
......
...@@ -164,6 +164,22 @@ class mock_get_orders(mock_ecommerce_api_endpoint): ...@@ -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'
)])
),
]
) )
] ]
} }
......
...@@ -39,11 +39,10 @@ class EcommerceService(object): ...@@ -39,11 +39,10 @@ class EcommerceService(object):
Receipt page for the specified Order. Receipt page for the specified Order.
""" """
ecommerce_receipt_page_url = configuration_helpers.get_value('ECOMMERCE_RECEIPT_PAGE_URL') ecommerce_receipt_page_url = configuration_helpers.get_value('ECOMMERCE_RECEIPT_PAGE_URL')
if ecommerce_receipt_page_url: if ecommerce_receipt_page_url:
receipt_page_url = self.get_absolute_ecommerce_url(ecommerce_receipt_page_url) return self.get_absolute_ecommerce_url(ecommerce_receipt_page_url + order_number)
else: return self.config.receipt_page + order_number
receipt_page_url = self.config.receipt_page
return receipt_page_url + order_number
def is_enabled(self, user): def is_enabled(self, user):
""" """
......
...@@ -634,20 +634,21 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf ...@@ -634,20 +634,21 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
self.assertContains(response, '<li class="item nav-global-01">') self.assertContains(response, '<li class="item nav-global-01">')
def test_commerce_order_detail(self): def test_commerce_order_detail(self):
"""
Verify that get_user_orders returns the correct order data.
"""
with mock_get_orders(): with mock_get_orders():
order_detail = get_user_orders(self.user) order_detail = get_user_orders(self.user)
user_order = mock_get_orders.default_response['results'][0] for i, order in enumerate(mock_get_orders.default_response['results']):
expected = [ expected = {
{ 'number': order['number'],
'number': user_order['number'], 'price': order['total_excl_tax'],
'price': user_order['total_excl_tax'],
'title': user_order['lines'][0]['title'],
'order_date': 'Jan 01, 2016', '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[i], expected)
self.assertEqual(order_detail, expected)
def test_commerce_order_detail_exception(self): def test_commerce_order_detail_exception(self):
with mock_get_orders(exception=exceptions.HttpNotFoundError): with mock_get_orders(exception=exceptions.HttpNotFoundError):
...@@ -673,15 +674,18 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf ...@@ -673,15 +674,18 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
self.assertEqual(order_detail, []) self.assertEqual(order_detail, [])
def test_honor_course_order_detail(self): def test_order_history_with_no_product(self):
response = { response = {
'results': [ 'results': [
factories.OrderFactory( factories.OrderFactory(
lines=[ lines=[
factories.OrderLineFactory( factories.OrderLineFactory(
product=None
),
factories.OrderLineFactory(
product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory( product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory(
name='certificate_type', name='certificate_type',
value='honor' value='verified'
)]) )])
) )
] ]
...@@ -691,22 +695,17 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf ...@@ -691,22 +695,17 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
with mock_get_orders(response=response): with mock_get_orders(response=response):
order_detail = get_user_orders(self.user) order_detail = get_user_orders(self.user)
self.assertEqual(order_detail, []) self.assertEqual(len(order_detail), 1)
def test_order_history_with_no_product(self): def test_order_history_with_coupon(self):
"""
Verify that get_order_details returns empty receipt_url for coupon product.
"""
response = { response = {
'results': [ 'results': [
factories.OrderFactory( factories.OrderFactory(
lines=[ lines=[
factories.OrderLineFactory( factories.OrderLineFactory(product=factories.ProductFactory(product_class='Coupon'))
product=None
),
factories.OrderLineFactory(
product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory(
name='certificate_type',
value='verified'
)])
)
] ]
) )
] ]
...@@ -714,7 +713,7 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf ...@@ -714,7 +713,7 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
with mock_get_orders(response=response): with mock_get_orders(response=response):
order_detail = get_user_orders(self.user) order_detail = get_user_orders(self.user)
self.assertEqual(len(order_detail), 1) self.assertEqual(order_detail[0]['receipt_url'], '')
@override_settings(SITE_NAME=settings.MICROSITE_LOGISTRATION_HOSTNAME) @override_settings(SITE_NAME=settings.MICROSITE_LOGISTRATION_HOSTNAME)
......
...@@ -331,7 +331,6 @@ def get_user_orders(user): ...@@ -331,7 +331,6 @@ def get_user_orders(user):
""" """
no_data = [] no_data = []
user_orders = [] user_orders = []
allowed_course_modes = ['professional', 'verified', 'credit']
commerce_configuration = CommerceConfiguration.current() commerce_configuration = CommerceConfiguration.current()
user_query = {'username': user.username} user_query = {'username': user.username}
...@@ -344,26 +343,25 @@ def get_user_orders(user): ...@@ -344,26 +343,25 @@ def get_user_orders(user):
for order in commerce_user_orders: for order in commerce_user_orders:
if order['status'].lower() == 'complete': if order['status'].lower() == 'complete':
for line in order['lines']: date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ")
product = line.get('product') order_data = {
if product: 'number': order['number'],
for attribute in product['attribute_values']: 'price': order['total_excl_tax'],
if attribute['name'] == 'certificate_type' and attribute['value'] in allowed_course_modes: 'order_date': strftime_localized(date_placed, 'SHORT_DATE'),
try: 'receipt_url': EcommerceService().get_receipt_page_url(order['number']),
date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ") 'lines': order['lines'],
order_data = { }
'number': order['number'],
'price': order['total_excl_tax'], # If the order lines contain a product that is not a Seat
'title': order['lines'][0]['title'], # we do not want to display the Order Details button. It
'order_date': strftime_localized( # will break the receipt page if used.
date_placed.replace(tzinfo=pytz.UTC), 'SHORT_DATE' for order_line in order['lines']:
), product = order_line.get('product')
'receipt_url': EcommerceService().get_receipt_page_url(order['number'])
} if product and product.get('product_class') != 'Seat':
user_orders.append(order_data) order_data['receipt_url'] = ''
except KeyError: break
log.exception('Invalid order structure: %r', order) user_orders.append(order_data)
return no_data
return user_orders return user_orders
......
...@@ -229,12 +229,12 @@ ...@@ -229,12 +229,12 @@
} }
return { return {
'view': new AccountSettingsFieldViews.OrderHistoryFieldView({ 'view': new AccountSettingsFieldViews.OrderHistoryFieldView({
title: order.title,
totalPrice: order.price, totalPrice: order.price,
orderId: order.number, orderId: order.number,
orderDate: order.order_date, orderDate: order.order_date,
receiptUrl: order.receipt_url, receiptUrl: order.receipt_url,
valueAttribute: 'order-' + orderNumber valueAttribute: 'order-' + orderNumber,
lines: order.lines
}) })
}; };
}) })
......
...@@ -325,12 +325,12 @@ ...@@ -325,12 +325,12 @@
render: function() { render: function() {
HtmlUtils.setHtml(this.$el, this.template({ HtmlUtils.setHtml(this.$el, this.template({
title: this.options.title,
totalPrice: this.options.totalPrice, totalPrice: this.options.totalPrice,
orderId: this.options.orderId, orderId: this.options.orderId,
orderDate: this.options.orderDate, orderDate: this.options.orderDate,
receiptUrl: this.options.receiptUrl, receiptUrl: this.options.receiptUrl,
valueAttribute: this.options.valueAttribute valueAttribute: this.options.valueAttribute,
lines: this.options.lines
})); }));
this.delegateEvents(); this.delegateEvents();
return this; return this;
......
...@@ -193,35 +193,37 @@ ...@@ -193,35 +193,37 @@
display: flex; display: flex;
align-items: center; align-items: center;
font-size: em(16); font-size: em(16);
color: $gray; font-weight: 600;
color: $dark-gray;
width: 100%; width: 100%;
padding-top: $baseline; padding-top: $baseline;
padding-bottom: $baseline; padding-bottom: $baseline;
line-height: normal; line-height: normal;
flex-direction: row;
flex-wrap: wrap;
span { span {
padding: $baseline; padding: $baseline;
} }
.u-field-order-title { .u-field-order-number {
@include float(left); @include float(left);
width: 26%; width: 30%;
font-size: em(20);
padding-left: ($baseline*2);
} }
.u-field-order-value { .u-field-order-date {
@include float(left); @include float(left);
width: 12%; padding-left: 30px;
width: 20%;
} }
.u-field-order-date { .u-field-order-price {
@include float(left); @include float(left);
width: 16%; width: 15%;
} }
.u-field-order-link { .u-field-order-link {
width: 15%; width: 10%;
padding: 0; padding: 0;
.u-field-link { .u-field-link {
...@@ -237,6 +239,16 @@ ...@@ -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 { .social-field-linked {
background: $m-gray-l4; background: $m-gray-l4;
box-shadow: 0 1px 2px 1px $shadow-l2; box-shadow: 0 1px 2px 1px $shadow-l2;
...@@ -290,7 +302,7 @@ ...@@ -290,7 +302,7 @@
font-weight: $font-semibold; font-weight: $font-semibold;
} }
} }
.u-field-message { .u-field-message {
position: relative; position: relative;
padding: 24px 0 0 ($baseline*5); padding: 24px 0 0 ($baseline*5);
......
<div class="field u-field-order" <% if (receiptUrl) { %> role="group" aria-labelledby="order-title-<%- orderId %>" <% } else { %> aria-hidden="true" <% } %>> <div class="field u-field-order" <% if (receiptUrl) { %> role="group" aria-labelledby="order-title-<%- orderId %>" <% } else { %> aria-hidden="true" <% } %>>
<span class="u-field-order-title" <% if (receiptUrl) { %> id="order-title-<%- orderId %>" <% } %>><%- title %></span> <span class="u-field-order-number"><span class="sr"><%- gettext('Order Number') %>: </span><%- orderId %></span>
<span class="u-field-order-date"><span class="sr">Date Placed: </span><%- orderDate %></span> <span class="u-field-order-date"><span class="sr"><%- gettext('Date Placed') %>: </span><%- orderDate %></span>
<span class="u-field-order-value u-field-order-price"><span class="sr">Cost: </span><% if (!isNaN(parseFloat(totalPrice))) { %>$<% } %><%- totalPrice %></span> <span class="u-field-order-price"><span class="sr"><%- gettext('Cost') %>: </span><% if (!isNaN(parseFloat(totalPrice))) { %>$<% } %><%- totalPrice %></span>
<span class="u-field-order-value u-field-order-number"><span class="sr">Order Number: </span><%- orderId %></span>
<span class="u-field-order-link"> <span class="u-field-order-link">
<% if (receiptUrl) { %> <% if (receiptUrl) { %>
<a class="u-field-link" target="_blank" href="<%- receiptUrl %>"><%- gettext('Order Details') %><span class="sr"> for <%- orderId %></span></a> <a class="u-field-link" target="_blank" href="<%- receiptUrl %>"><%- gettext('Order Details') %><span class="sr"> <%- gettext('for') %> <%- orderId %></span></a>
<% } %> <% } %>
</span> </span>
<% _.each(lines, function(item){ %>
<div class="field u-field-order-lines">
<span class="u-field-order-number"><span class="sr"><%- gettext('Product Name') %>: </span><%- item.title %></span>
<span class="u-field-order-date"><span class="sr"><%- gettext('Date Placed') %>: </span><%- orderDate %></span>
<span class="u-field-order-price"><span class="sr"><%- gettext('Cost') %>: </span><% if (!isNaN(parseFloat(item.line_price_excl_tax))) { %>$<% } %><%- item.line_price_excl_tax %></span>
</div>
<% }); %>
</div> </div>
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