diff --git a/SoftLayer/CLI/account/item_detail.py b/SoftLayer/CLI/account/item_detail.py index 7a2c53df3..ddc2d31ed 100644 --- a/SoftLayer/CLI/account/item_detail.py +++ b/SoftLayer/CLI/account/item_detail.py @@ -1,52 +1,53 @@ -"""Gets some details about a specific billing item.""" -# :license: MIT, see LICENSE for more details. -import click - -from SoftLayer.CLI import environment -from SoftLayer.CLI import formatting -from SoftLayer.managers.account import AccountManager as AccountManager -from SoftLayer import utils - - -@click.command() -@click.argument('identifier') -@environment.pass_env -def cli(env, identifier): - """Gets detailed information about a billing item.""" - manager = AccountManager(env.client) - item = manager.get_billing_item(identifier) - env.fout(item_table(item)) - - -def item_table(item): - """Formats a table for billing items""" - - date_format = '%Y-%m-%d' - table = formatting.KeyValueTable(["Key", "Value"], title="{}".format(item.get('description', 'Billing Item'))) - table.add_row(['createDate', utils.clean_time(item.get('createDate'), date_format, date_format)]) - table.add_row(['cycleStartDate', utils.clean_time(item.get('cycleStartDate'), date_format, date_format)]) - table.add_row(['cancellationDate', utils.clean_time(item.get('cancellationDate'), date_format, date_format)]) - table.add_row(['description', item.get('description')]) - fqdn = "{}.{}".format(item.get('hostName'), item.get('domain')) - if fqdn != ".": - table.add_row(['FQDN', fqdn]) - - if item.get('hourlyFlag', False): - table.add_row(['hourlyRecurringFee', item.get('hourlyRecurringFee')]) - table.add_row(['hoursUsed', item.get('hoursUsed')]) - table.add_row(['currentHourlyCharge', item.get('currentHourlyCharge')]) - else: - table.add_row(['recurringFee', item.get('recurringFee')]) - - ordered_by = "IBM" - user = utils.lookup(item, 'orderItem', 'order', 'userRecord') - if user: - ordered_by = "{} ({})".format(user.get('displayName'), utils.lookup(user, 'userStatus', 'name')) - table.add_row(['Ordered By', ordered_by]) - table.add_row(['Notes', item.get('notes')]) - table.add_row(['Location', utils.lookup(item, 'location', 'name')]) - if item.get('children'): - for child in item.get('children'): - table.add_row([child.get('categoryCode'), child.get('description')]) - - return table +"""Gets some details about a specific billing item.""" +# :license: MIT, see LICENSE for more details. +import click + +from SoftLayer.CLI import environment +from SoftLayer.CLI import formatting +from SoftLayer.managers.account import AccountManager as AccountManager +from SoftLayer import utils + + +@click.command() +@click.argument('identifier') +@environment.pass_env +def cli(env, identifier): + """Gets detailed information about a billing item.""" + manager = AccountManager(env.client) + item = manager.get_item_detail(identifier) + env.fout(item_table(item)) + + +def item_table(item): + """Formats a table for billing items""" + + date_format = '%Y-%m-%d' + table = formatting.Table(["Key", "Value"], title="{}".format(item.get('description', 'Billing Item'))) + table.add_row(['createDate', utils.clean_time(item.get('createDate'), date_format, date_format)]) + table.add_row(['cycleStartDate', utils.clean_time(item.get('cycleStartDate'), date_format, date_format)]) + table.add_row(['cancellationDate', utils.clean_time(item.get('cancellationDate'), date_format, date_format)]) + table.add_row(['description', item.get('description')]) + table.align = 'l' + fqdn = "{}.{}".format(item.get('hostName'), item.get('domain')) + if fqdn != ".": + table.add_row(['FQDN', fqdn]) + + if item.get('hourlyFlag', False): + table.add_row(['hourlyRecurringFee', item.get('hourlyRecurringFee')]) + table.add_row(['hoursUsed', item.get('hoursUsed')]) + table.add_row(['currentHourlyCharge', item.get('currentHourlyCharge')]) + else: + table.add_row(['recurringFee', item.get('recurringFee')]) + + ordered_by = "IBM" + user = utils.lookup(item, 'orderItem', 'order', 'userRecord') + if user: + ordered_by = "{} ({})".format(user.get('displayName'), utils.lookup(user, 'userStatus', 'name')) + table.add_row(['Ordered By', ordered_by]) + table.add_row(['Notes', item.get('notes')]) + table.add_row(['Location', utils.lookup(item, 'location', 'name')]) + if item.get('children'): + for child in item.get('children'): + table.add_row([child.get('categoryCode'), child.get('description')]) + + return table diff --git a/SoftLayer/fixtures/SoftLayer_Billing_Invoice_Item.py b/SoftLayer/fixtures/SoftLayer_Billing_Invoice_Item.py new file mode 100644 index 000000000..4be040cbe --- /dev/null +++ b/SoftLayer/fixtures/SoftLayer_Billing_Invoice_Item.py @@ -0,0 +1,3 @@ +from SoftLayer.fixtures.SoftLayer_Billing_Item import getObject as billingItem + +getBillingItem = billingItem diff --git a/SoftLayer/managers/account.py b/SoftLayer/managers/account.py index 56f951c28..884e4335b 100644 --- a/SoftLayer/managers/account.py +++ b/SoftLayer/managers/account.py @@ -8,6 +8,7 @@ import logging +from SoftLayer import SoftLayerAPIError from SoftLayer import utils # Invalid names are ignored due to long method names and short argument names @@ -21,6 +22,11 @@ class AccountManager(utils.IdentifierMixin, object): :param SoftLayer.API.BaseClient client: the client instance """ + _DEFAULT_BILLING_ITEM_MASK = """mask[ + orderItem[id,order[id,userRecord[id,email,displayName,userStatus]]], + nextInvoiceTotalRecurringAmount, + location, hourlyFlag, children + ]""" def __init__(self, client): self.client = client @@ -205,20 +211,41 @@ def get_account_billing_items(self, mask=None): def get_billing_item(self, identifier, mask=None): """Gets details about a billing item - :param int identifier Billing_Item id + :param int identifier: Billing_Item id :param string mask: Object mask to use. :return: Billing_Item """ if mask is None: - mask = """mask[ - orderItem[id,order[id,userRecord[id,email,displayName,userStatus]]], - nextInvoiceTotalRecurringAmount, - location, hourlyFlag, children - ]""" + mask = self._DEFAULT_BILLING_ITEM_MASK return self.client.call('Billing_Item', 'getObject', id=identifier, mask=mask) + def get_billing_item_from_invoice(self, identifier, mask=None): + """Gets details about a billing item of a billing invoice item + + :param int identifier: Billing_Invoice_Item id + :param mask: Object mask to use. + :return: Billing_Item + """ + if mask is None: + mask = self._DEFAULT_BILLING_ITEM_MASK + return self.client.call('Billing_Invoice_Item', 'getBillingItem', id=identifier, mask=mask) + + def get_item_detail(self, identifier): + """Gets details about a billing item + + :param int identifier: Billing_Item id or Billing_Invoice_Item + :return: Billing_Item + """ + + try: + return self.get_billing_item(identifier) + except SoftLayerAPIError as exception: + if exception.faultCode == 404: + return self.get_billing_item_from_invoice(identifier) + raise + def cancel_item(self, identifier, reason="No longer needed", note=None): """Cancels a specific billing item with a reason diff --git a/tests/managers/account_tests.py b/tests/managers/account_tests.py index b47ec6abb..b051e5ee7 100644 --- a/tests/managers/account_tests.py +++ b/tests/managers/account_tests.py @@ -5,6 +5,7 @@ """ from SoftLayer.managers.account import AccountManager as AccountManager +from SoftLayer import SoftLayerAPIError from SoftLayer import testing @@ -133,3 +134,18 @@ def test_cancel_item(self): self.manager.cancel_item(12345, reason, note) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, reason, note), identifier=12345) + + def test_get_billing_item_from_invoice(self): + self.manager.get_billing_item_from_invoice(12345) + self.assert_called_with('SoftLayer_Billing_Invoice_Item', 'getBillingItem', identifier=12345) + + def test_get_item_details_with_billing_item_id(self): + self.manager.get_item_detail(12345) + self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier=12345) + + def test_get_item_details_with_invoice_item_id(self): + mock = self.set_mock('SoftLayer_Billing_Item', 'getObject') + mock.side_effect = SoftLayerAPIError(404, "Unable to find object with id of '123456'.") + self.manager.get_item_detail(123456) + self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier=123456) + self.assert_called_with('SoftLayer_Billing_Invoice_Item', 'getBillingItem', identifier=123456)