Skip to content

Commit

Permalink
Fixed #2201 Added invoice item rollup to 'account invoice-detail' to …
Browse files Browse the repository at this point in the history
…better match invoices as dispalyed in the portal
  • Loading branch information
allmightyspiff committed Dec 17, 2024
1 parent e7de42b commit 413fa88
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 10 deletions.
40 changes: 37 additions & 3 deletions SoftLayer/CLI/account/invoice_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
help="Shows a very detailed list of charges")
@environment.pass_env
def cli(env, identifier, details):
"""Invoice details"""
"""Invoice details
Will display the top level invoice items for a given invoice. The cost displayed is the sum of the item's
cost along with all its child items.
The --details option will display any child items a top level item may have. Parent items will appear
in this list as well to display their specific cost.
"""

manager = AccountManager(env.client)
top_items = manager.get_billing_items(identifier)
Expand Down Expand Up @@ -49,16 +55,31 @@ def get_invoice_table(identifier, top_items, details):
description = nice_string(item.get('description'))
if fqdn != '.':
description = "%s (%s)" % (item.get('description'), fqdn)
total_recur, total_single = sum_item_charges(item)
table.add_row([
item.get('id'),
category,
nice_string(description),
"$%.2f" % float(item.get('oneTimeAfterTaxAmount')),
"$%.2f" % float(item.get('recurringAfterTaxAmount')),
f"${total_single:,.2f}",
f"${total_recur:,.2f}",
utils.clean_time(item.get('createDate'), out_format="%Y-%m-%d"),
utils.lookup(item, 'location', 'name')
])
if details:
# This item has children, so we want to print out the parent item too. This will match the
# invoice from the portal. https://github.com/softlayer/softlayer-python/issues/2201
if len(item.get('children')) > 0:
single = float(item.get('oneTimeAfterTaxAmount', 0.0))
recurring = float(item.get('recurringAfterTaxAmount', 0.0))
table.add_row([
'>>>',
category,
nice_string(description),
f"${single:,.2f}",
f"${recurring:,.2f}",
'---',
'---'
])
for child in item.get('children', []):
table.add_row([
'>>>',
Expand All @@ -70,3 +91,16 @@ def get_invoice_table(identifier, top_items, details):
'---'
])
return table


def sum_item_charges(item: dict) -> (float, float):
"""Takes a billing Item, sums up its child items and returns recurring, one_time prices"""

# API returns floats as strings in this case
single = float(item.get('oneTimeAfterTaxAmount', 0.0))
recurring = float(item.get('recurringAfterTaxAmount', 0.0))
for child in item.get('children', []):
single = single + float(child.get('oneTimeAfterTaxAmount', 0.0))
recurring = recurring + float(child.get('recurringAfterTaxAmount', 0.0))

return (recurring, single)
11 changes: 8 additions & 3 deletions SoftLayer/testing/xmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
print(f"Server running on http://{my_server.server_name}:{my_server.server_port}")
---
$> python quick-server.py
$> curl -X POST -d "<?xml version='1.0' encoding='iso-8859-1'?><methodCall><methodName>getInvoiceTopLevelItems</methodName><params><param><value><struct><member><name>headers</name><value><struct><member><name>SoftLayer_Billing_InvoiceInitParameters</name><value><struct><member><name>id</name><value><string>1234</string></value></member></struct></value></member></struct></value></member></struct></value></param></params></methodCall>" http://127.0.0.1:4321/SoftLayer_Billing_Invoice
$> curl -X POST -d "<?xml version='1.0' encoding='iso-8859-1'?><methodCall><methodName> \
getInvoiceTopLevelItems</methodName><params><param><value><struct><member><name>headers</name> \
<value><struct><member><name>SoftLayer_Billing_InvoiceInitParameters</name><value><struct> \
<member><name>id</name><value><string>1234</string></value></member></struct></value></member> \
</struct></value></member></struct></value></param></params></methodCall>" \
http://127.0.0.1:4321/SoftLayer_Billing_Invoice
:license: MIT, see LICENSE for more details.
"""
Expand Down Expand Up @@ -99,10 +104,10 @@ def do_POST(self):
self.end_headers()
response_body = '''<error>OverflowError in XML response.</error>'''
self.wfile.write(response_body.encode('utf-8'))
logging.exception(f"Error while handling request: {str(ex)}")
logging.exception("Error while handling request: %s", ex)
except Exception as ex:
self.send_response(500)
logging.exception(f"Error while handling request: {str(ex)}")
logging.exception("Error while handling request: %s", ex)

def log_message(self, fmt, *args):
"""Override log_message."""
Expand Down
17 changes: 13 additions & 4 deletions tests/CLI/modules/account_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,11 @@ def test_event_jsonraw_output(self):
command = '--format jsonraw account events'
command_params = command.split()
result = self.run_command(command_params)

json_text_tables = result.stdout.split('\n')
print(f"RESULT: {result.output}")
# removing an extra item due to an additional Newline at the end of the output
json_text_tables.pop()
# each item in the json_text_tables should be a list
for json_text_table in json_text_tables:
print(f"TESTING THIS: \n{json_text_table}\n")
json_table = json.loads(json_text_table)
self.assertIsInstance(json_table, list)

Expand All @@ -66,6 +63,18 @@ def test_invoice_detail_details(self):
self.assert_no_fail(result)
self.assert_called_with('SoftLayer_Billing_Invoice', 'getInvoiceTopLevelItems', identifier='1234')

def test_invoice_detail_sum_children(self):
result = self.run_command(['--format=json', 'account', 'invoice-detail', '1234', '--details'])
self.assert_no_fail(result)
json_out = json.loads(result.output)
self.assertEqual(len(json_out), 7)
self.assertEqual(json_out[0]['Item Id'], 724951323)
self.assertEqual(json_out[0]['Single'], '$55.50')
self.assertEqual(json_out[0]['Monthly'], '$0.10')
self.assertEqual(json_out[3]['Item Id'], 1111222)
self.assertEqual(json_out[3]['Single'], '$0.00')
self.assertEqual(json_out[3]['Monthly'], '$30.36')

def test_invoice_detail_csv_output_format(self):
result = self.run_command(["--format", "csv", 'account', 'invoice-detail', '1234'])
result_output = result.output.replace('\r', '').split('\n')
Expand All @@ -74,7 +83,7 @@ def test_invoice_detail_csv_output_format(self):
'"Create Date","Location"')
self.assertEqual(result_output[1], '724951323,"Private (only) Secondary VLAN IP Addresses",'
'"64 Portable Private IP Addresses (bleg.beh.com)",'
'"$0.00","$0.00","2018-04-04","fra02"')
'"$55.50","$0.10","2018-04-04","fra02"')

# slcli account invoices
def test_invoices(self):
Expand Down

0 comments on commit 413fa88

Please sign in to comment.