Skip to content

Commit b8f4ca3

Browse files
Merge pull request #1354 from ATGE/issues/1340
Order lookup
2 parents efbdf59 + afb20da commit b8f4ca3

File tree

9 files changed

+171
-13
lines changed

9 files changed

+171
-13
lines changed

SoftLayer/CLI/account/invoice_detail.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ def cli(env, identifier, details):
1919

2020
manager = AccountManager(env.client)
2121
top_items = manager.get_billing_items(identifier)
22+
table = get_invoice_table(identifier, top_items, details)
23+
env.fout(table)
24+
25+
26+
def nice_string(ugly_string, limit=100):
27+
"""Format and trims strings"""
28+
return (ugly_string[:limit] + '..') if len(ugly_string) > limit else ugly_string
29+
30+
31+
def get_invoice_table(identifier, top_items, details):
32+
"""Formats a table for invoice top level items.
33+
34+
:param int identifier: Invoice identifier.
35+
:param list top_items: invoiceTopLevelItems.
36+
:param bool details: To add very detailed list of charges.
37+
"""
2238

2339
title = "Invoice %s" % identifier
2440
table = formatting.Table(["Item Id", "Category", "Description", "Single",
@@ -52,10 +68,4 @@ def cli(env, identifier, details):
5268
'---',
5369
'---'
5470
])
55-
56-
env.fout(table)
57-
58-
59-
def nice_string(ugly_string, limit=100):
60-
"""Format and trims strings"""
61-
return (ugly_string[:limit] + '..') if len(ugly_string) > limit else ugly_string
71+
return table

SoftLayer/CLI/account/orders.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Order list account"""
1+
"""Lists account orders."""
22
# :license: MIT, see LICENSE for more details.
33

44
import click
@@ -16,7 +16,7 @@
1616
show_default=True)
1717
@environment.pass_env
1818
def cli(env, limit):
19-
"""Order list account."""
19+
"""Lists account orders. Use `slcli order lookup <ID>` to find more details about a specific order."""
2020
manager = AccountManager(env.client)
2121
orders = manager.get_account_all_billing_orders(limit)
2222

SoftLayer/CLI/order/lookup.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""Provides some details related to the order."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
from SoftLayer.CLI.account.invoice_detail import get_invoice_table
7+
8+
from SoftLayer.CLI import environment
9+
from SoftLayer.CLI import formatting
10+
from SoftLayer.managers import ordering
11+
from SoftLayer import utils
12+
13+
14+
@click.command()
15+
@click.argument('identifier')
16+
@click.option('--details', is_flag=True, default=False, show_default=True,
17+
help="Shows a very detailed list of charges")
18+
@environment.pass_env
19+
def cli(env, identifier, details):
20+
"""Provides some details related to order owner, date order, cost information, initial invoice."""
21+
22+
manager = ordering.OrderingManager(env.client)
23+
order = manager.get_order_detail(identifier)
24+
order_table = get_order_table(order)
25+
26+
invoice = order.get('initialInvoice', {})
27+
top_items = invoice.get('invoiceTopLevelItems', [])
28+
invoice_id = invoice.get('id')
29+
invoice_table = get_invoice_table(invoice_id, top_items, details)
30+
31+
order_table.add_row(['Initial Invoice', invoice_table])
32+
33+
env.fout(order_table)
34+
35+
36+
def get_order_table(order):
37+
"""Formats a table for billing order"""
38+
39+
title = "Order {id}".format(id=order.get('id'))
40+
date_format = '%Y-%m-%d'
41+
table = formatting.Table(["Key", "Value"], title=title)
42+
table.align = 'l'
43+
44+
ordered_by = "IBM"
45+
user = order.get('userRecord', None)
46+
if user:
47+
ordered_by = "{} ({})".format(user.get('displayName'), utils.lookup(user, 'userStatus', 'name'))
48+
table.add_row(['Ordered By', ordered_by])
49+
50+
table.add_row(['Create Date', utils.clean_time(order.get('createDate'), date_format, date_format)])
51+
table.add_row(['Modify Date', utils.clean_time(order.get('modifyDate'), date_format, date_format)])
52+
table.add_row(['Order Approval Date', utils.clean_time(order.get('orderApprovalDate'), date_format, date_format)])
53+
table.add_row(['Status', order.get('status')])
54+
table.add_row(['Order Total Amount', "{price:.2f}".format(price=float(order.get('orderTotalAmount', '0')))])
55+
table.add_row(['Invoice Total Amount', "{price:.2f}".
56+
format(price=float(order.get('initialInvoice', {}).get('invoiceTotalAmount', '0')))])
57+
58+
items = order.get('items', [])
59+
item_table = formatting.Table(["Item Description"])
60+
item_table.align['description'] = 'l'
61+
62+
for item in items:
63+
item_table.add_row([item.get('description')])
64+
65+
table.add_row(['Items', item_table])
66+
67+
return table

SoftLayer/CLI/routes.py

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@
226226
('order:quote-list', 'SoftLayer.CLI.order.quote_list:cli'),
227227
('order:quote-detail', 'SoftLayer.CLI.order.quote_detail:cli'),
228228
('order:quote', 'SoftLayer.CLI.order.quote:cli'),
229+
('order:lookup', 'SoftLayer.CLI.order.lookup:cli'),
229230

230231
('rwhois', 'SoftLayer.CLI.rwhois'),
231232
('rwhois:edit', 'SoftLayer.CLI.rwhois.edit:cli'),

SoftLayer/fixtures/SoftLayer_Billing_Order.py

+25
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,28 @@
4545
'orderApprovalDate': '2019-09-15T13:13:13-06:00',
4646
'orderTotalAmount': '0'
4747
}]
48+
49+
getObject = {
50+
'accountId': 1234,
51+
'createDate': '2020-09-23T16:22:30-06:00',
52+
'id': 6543210,
53+
'impersonatingUserRecordId': None,
54+
'initialInvoice': {
55+
'amount': '0',
56+
'id': 60012345,
57+
'invoiceTotalAmount': '0'
58+
},
59+
'items': [{
60+
'description': 'Dual Intel Xeon Silver 4210 (20 Cores, 2.20 GHz)'
61+
}],
62+
'modifyDate': '2020-09-23T16:22:32-06:00',
63+
'orderQuoteId': None,
64+
'orderTypeId': 11,
65+
'presaleEventId': None,
66+
'privateCloudOrderFlag': False,
67+
'status': 'APPROVED',
68+
'userRecord': {
69+
'displayName': 'testUser'
70+
},
71+
'userRecordId': 7654321,
72+
}

SoftLayer/managers/ordering.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from SoftLayer import exceptions
1313

14-
1514
CATEGORY_MASK = '''id, isRequired, itemCategory[id, name, categoryCode]'''
1615

1716
ITEM_MASK = '''id, keyName, description, itemCategory, categories, prices'''
@@ -61,6 +60,28 @@ def get_packages_of_type(self, package_types, mask=None):
6160
packages = self.filter_outlet_packages(packages)
6261
return packages
6362

63+
def get_order_detail(self, order_id, mask=None):
64+
"""Get order details.
65+
66+
:param int order_id: to specify the order that we want to retrieve.
67+
:param string mask: Mask to specify the properties we want to retrieve.
68+
"""
69+
_default_mask = (
70+
'mask[orderTotalAmount,orderApprovalDate,'
71+
'initialInvoice[id,amount,invoiceTotalAmount,'
72+
'invoiceTopLevelItems[id, description, hostName, domainName, oneTimeAfterTaxAmount,'
73+
'recurringAfterTaxAmount, createDate,'
74+
'categoryCode,'
75+
'category[name],'
76+
'location[name],'
77+
'children[id, category[name], description, oneTimeAfterTaxAmount,recurringAfterTaxAmount]]],'
78+
'items[description],userRecord[displayName,userStatus]]')
79+
80+
mask = _default_mask if mask is None else mask
81+
82+
order = self.billing_svc.getObject(mask=mask, id=order_id)
83+
return order
84+
6485
@staticmethod
6586
def filter_outlet_packages(packages):
6687
"""Remove packages designated as OUTLET.

docs/cli/ordering.rst

+6
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,9 @@ Quotes
135135
.. click:: SoftLayer.CLI.order.place_quote:cli
136136
:prog: order place-quote
137137
:show-nested:
138+
139+
Lookup
140+
======
141+
.. click:: SoftLayer.CLI.order.lookup:cli
142+
:prog: order lookup
143+
:show-nested:

tests/CLI/modules/order_tests.py

+7
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,13 @@ def _get_verified_order_return(self):
406406
'recurringFee': '150'}
407407
return {'orderContainers': [{'prices': [price1, price2]}]}
408408

409+
def test_order_lookup(self):
410+
result = self.run_command(['order', 'lookup', '12345'])
411+
self.assert_no_fail(result)
412+
self.assert_called_with('SoftLayer_Billing_Order', 'getObject', identifier='12345')
413+
self.assertIn('Ordered By', result.output)
414+
self.assertIn('Initial Invoice', result.output)
415+
409416

410417
def _get_all_packages():
411418
package_type = {'keyName': 'BARE_METAL_CPU'}

tests/managers/ordering_tests.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ def test_get_item_capacity_core(self):
735735
"capacity": "1",
736736
"id": 10201,
737737
"keyName": "GUEST_CORE_1_DEDICATED",
738-
}]
738+
}]
739739

740740
item_capacity = self.ordering.get_item_capacity(items, ['GUEST_CORE_1_DEDICATED', 'OS_RHEL_7_X_LAMP_64_BIT'])
741741

@@ -752,7 +752,7 @@ def test_get_item_capacity_storage(self):
752752
"capacity": "1",
753753
"id": 10201,
754754
"keyName": "READHEAVY_TIER",
755-
}]
755+
}]
756756

757757
item_capacity = self.ordering.get_item_capacity(items, ['READHEAVY_TIER', 'STORAGE_SPACE_FOR_2_IOPS_PER_GB'])
758758

@@ -770,7 +770,7 @@ def test_get_item_capacity_intel(self):
770770
"capacity": "1",
771771
"id": 10201,
772772
"keyName": "GUEST_CORE_1_DEDICATED",
773-
}]
773+
}]
774774

775775
item_capacity = self.ordering.get_item_capacity(items, ['INTEL_XEON_2690_2_60', 'BANDWIDTH_20000_GB'])
776776

@@ -809,3 +809,24 @@ def test_get_item_prices_by_location(self):
809809

810810
self.assertEqual(options[0]['item']['keyName'], item_prices[0]['item']['keyName'])
811811
self.assertEqual(options[0]['hourlyRecurringFee'], item_prices[0]['hourlyRecurringFee'])
812+
813+
def test_get_oder_detail_mask(self):
814+
order_id = 12345
815+
test_mask = 'mask[id]'
816+
self.ordering.get_order_detail(order_id, mask=test_mask)
817+
self.assert_called_with('SoftLayer_Billing_Order', 'getObject', identifier=order_id, mask=test_mask)
818+
819+
def test_get_oder_detail_default_mask(self):
820+
order_id = 12345
821+
_default_mask = (
822+
'mask[orderTotalAmount,orderApprovalDate,'
823+
'initialInvoice[id,amount,invoiceTotalAmount,'
824+
'invoiceTopLevelItems[id, description, hostName, domainName, oneTimeAfterTaxAmount,'
825+
'recurringAfterTaxAmount, createDate,'
826+
'categoryCode,'
827+
'category[name],'
828+
'location[name],'
829+
'children[id, category[name], description, oneTimeAfterTaxAmount,recurringAfterTaxAmount]]],'
830+
'items[description],userRecord[displayName,userStatus]]')
831+
self.ordering.get_order_detail(order_id)
832+
self.assert_called_with('SoftLayer_Billing_Order', 'getObject', identifier=order_id, mask=_default_mask)

0 commit comments

Comments
 (0)