Financial Management Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
fgedik
Product and Topic Expert
Product and Topic Expert
835

In modern business environments, subscription billing systems are pivotal in managing complex billing scenarios, especially for industries offering services based on usage. SAP Subscription Billing provides businesses with a robust platform to manage subscriptions, track usage data, and handle bills. However, managing large amounts of usage data, including filtering out unassigned or irrelevant data, can quickly become overwhelming.

Unassigned usage data — that is, usage data which is not associated with a valid or active subscription — can clutter the system, leading to inefficiencies and potential errors in the billing process. To streamline this process and ensure clean, accurate data, automating the filtering of unassigned usage records becomes crucial.

 In this blog post, we will walk through how to create a Python script that can automatically discard unassigned usage data in SAP Subscription Billing, providing your business with cleaner datasets, smoother operations, and improved billing accuracy. By using Python's powerful libraries, we can easily connect to SAP's APIs, process the usage data, and automate this critical task.

The final code will look like this:

fgedik_0-1732281960145.png

This guide is perfect for those who are new to coding and APIs, as I’ll walk you through every step, including detailed explanations of the code. If you're an expert, feel free to skip the descriptive sections and jump straight to the code snippets.

Let’s break it down step by step.

IMPORT

First, let's create a new file. In this example, we will use a blank Notepad++ sheet. After creating the file, save it with the name "script.py" (or any other name with the .py extension). Now, let's begin implementing the logic:


We will need the requests and time libraries for the script. Let’s briefly go over why these are necessary:

requests: This library is essential for making HTTP requests to the SAP Subscription Billing API (or any other API). It simplifies the process of sending requests, handling responses, and managing authentication. By using requests, we can easily fetch usage data from the system and interact with SAP SB’s endpoints.

time: This library provides time-related functions. In this script, we might use it to handle delays between requests, control execution intervals, or measure elapsed time for operations like data fetching and processing. If we need to implement pauses or time-based actions, the time library will be crucial.

Let’s import both libraries at the top of our script:

import requests

import time

 

CONFIGURATION

 In this section, we define some important constants and settings that will be used throughout the script. Let’s go through each one:

BASE_URL = "https://eu10.revenue.cloud.sap/api/usage-record/v1/usage-records"

This is the base URL for the SAP Subscription Billing API’s usage records endpoint to retrieve the list with all unassigned usage data. We will use a filter to get only unassigned usage data in the next steps.

 DISCARD_URL_TEMPLATE = "https://eu10.revenue.cloud.sap/api/usage-record/v1/usage-records/{id}/discard"

This is the URL for discarding a specific usage record. The {id} placeholder will be replaced with the actual ID of the usage record we want to discard. This allows us to dynamically generate the correct URL for each record we need to delete. For example, if a usage record with ID "12345" needs to be discarded, this template would generate the URL "https://eu10.revenue.cloud.sap/api/usage-record/v1/usage-records/12345/discard".

BEARER_TOKEN = "xx"  # Replace with your actual Bearer Token

The BEARER_TOKEN is an authorization token required to authenticate API requests to SAP Subscription Billing. This token ensures that only authorized users can interact with the system. You should replace "xx" with your actual bearer token, which you can obtain from your SAP Subscription Billing subaccount. Note: Always keep this token secure, as it grants access to your data. Also make sure, that the token has the following Scope: usage_discard. If you don’t know how to generate the bearer token, you can also check the SAP Subscription Billing documentation:
https://help.sap.com/docs/subscription-billing/apis-events/generate-access-token?locale=en-US

 HEADERS = {"Authorization": f"Bearer {BEARER_TOKEN}"}

This sets up the necessary headers for our HTTP requests. The Authorization header is used to include the Bearer Token when making API calls. This is how the SAP Subscription Billing API identifies and authenticates our requests. By passing the BEARER_TOKEN as part of the Authorization header, we ensure that the API knows we have the correct permissions.

 

GET LIST WITH UNASSIGNED USAGE DATA

 get_unassigned_entries Function

This function is responsible for fetching the usage records from SAP Subscription Billing that are unassigned and not discarded. This allows us to filter out records that are either assigned to an active subscription or have already been discarded.

 Let’s break down the code inside the function:

 def get_unassigned_entries()::

This is the function definition. The name get_unassigned_entries suggests it will retrieve the unassigned usage records from the SAP API.

 Printing a Log Message

print("Fetching unassigned usage records (excluding discarded ones)...")

This line simply prints a message to the console, indicating that the function is attempting to fetch unassigned usage records.

 Setting Up the Filter Parameters

params = {"filter": "isAssigned eq false and discarded eq false"}

params: This is a dictionary containing the filter parameters for the API request. We are telling the SAP Subscription Billing API to filter usage records by two criteria:

isAssigned eq false: Only retrieve records that are unassigned.

discarded eq false: Only retrieve records that have not been marked as discarded.

This filter ensures that only relevant records are returned — unassigned usage records that haven’t been discarded yet.

 Making the API Request

response = requests.get(BASE_URL, headers=HEADERS, params=params)

This makes a GET request to the API. The request includes:

BASE_URL: The base URL for usage data in SAP Subscription Billing API.

headers=HEADERS: The headers containing the authorization token (as set up earlier).

params=params: The filter parameters to fetch unassigned and non-discarded usage records.

The response from this request will contain the usage records that match the filter criteria.

 Handling the Response

if response.status_code == 200:

        print("Successfully fetched unassigned entries.")

        return response.json()  # Since the response is a list, not a dict

if response.status_code == 200:: This checks if the request was successful by examining the status code of the response. A 200 status code indicates that the request was successful.

print("Successfully fetched unassigned entries."): If the request is successful, it prints a success message to the console.

return response.json(): This returns the JSON response from the API.

Handling Errors

else:

        print(f"Error fetching unassigned entries: {response.status_code} - {response.text}")

        return []

else:: If the request fails (i.e., the status code is not 200), the error block is executed.

print(f"Error fetching unassigned entries: {response.status_code} - {response.text}"): This prints an error message along with the status code and any additional error details returned by the API.

return []: In case of an error, the function returns an empty list. This ensures that the script does not crash and can continue execution, even if there is an issue fetching the data.

 

DISCARD UNASSIGNED USAGE DATA

 discard_entry Function

This function is responsible for discarding a specific usage record from the SAP Subscription Billing system. Here the script will process the unassigned usage records from the retrieved list.

 

Here’s a detailed breakdown of the code:

 Function Definition

def discard_entry(entry_id)::  This is the function definition. The function takes a single parameter, entry_id, which is the unique identifier of the usage record that needs to be discarded. The function will use this ID to target the correct record.

Constructing the Discard URL

url = DISCARD_URL_TEMPLATE.format(id=entry_id): This line uses the DISCARD_URL_TEMPLATE from the configuration section and dynamically fills in the {id} placeholder with the entry_id provided to the function. This creates the correct URL for the discard API endpoint for that specific usage record. For example, if entry_id is 12345, this line will construct the URL https://eu10.revenue.cloud.sap/api/usage-record/v1/usage-records/12345/discard.

Sending the Discard Request

requests.post(url, headers=HEADERS): This sends an HTTP POST request to the URL constructed earlier. The POST method is used because we are asking the API to take an action rather than retrieve data. We also pass the headers=HEADERS, which include the authorization Bearer token, to authenticate the request.

Handling the Response

if response.status_code == 200:

        print(f"Successfully discarded usage record with ID: {entry_id}")

else:

        print(f"Failed to discard usage record {entry_id}: {response.status_code} - {response.text}")

if response.status_code == 200:: This checks if the request was successful by examining the status code. A 200 status code means the request was successful, and the usage record has been discarded.

print(f"Successfully discarded usage record with ID: {entry_id}"): If the request is successful, this message is printed to the console, confirming that the usage record with the given entry_id was discarded.

else:: If the request fails (i.e., the status code is not 200), the else block is executed.

print(f"Failed to discard usage record {entry_id}: {response.status_code} - {response.text}"): This prints an error message with the status code and the response content, which can provide details about why the discard operation failed.

MAIN FUNCTION

 main Function

This is the entry point of the script, where everything comes together. The main function coordinates the flow of the program by calling the necessary functions to fetch unassigned usage records and discard them if needed.

 Let’s break it down:

 Function Definition

def main():: This defines the main function. It's typically used as the starting point of the script when executed, and it handles the overall process.

 Fetching Unassigned Entries

python

Code kopieren

unassigned_entries = get_unassigned_entries()

    if not unassigned_entries:

        print("No unassigned usage records to discard.")

        return

unassigned_entries = get_unassigned_entries(): This calls the get_unassigned_entries function to fetch the list of unassigned usage records from the SAP Subscription Billing API. The result is stored in the unassigned_entries variable.

if not unassigned_entries:: This checks if the unassigned_entries list is empty (i.e., there were no unassigned records found).

print("No unassigned usage records to discard."): If no unassigned records are found, it prints a message and exits the function early with return, meaning no further actions will be taken.

 Processing Unassigned Entries

    print(f"Found {len(unassigned_entries)} unassigned usage records to discard.")

    for entry in unassigned_entries:

        entry_id = entry.get("id")

        if entry_id:

            discard_entry(entry_id)

            time.sleep(0.5)

        else:

            print("Skipping entry with missing ID.")

print(f"Found {len(unassigned_entries)} unassigned usage records to discard."): If there are unassigned entries, it prints the total number of unassigned records found.

for entry in unassigned_entries:: This starts a loop to process each unassigned entry.

entry_id = entry.get("id"): For each entry, it attempts to get the id field. This is necessary for the discard_entry function to know which record to discard.

if entry_id:: If the entry has a valid id, it proceeds to discard it.

discard_entry(entry_id): This calls the discard_entry function to discard the record with the given entry_id.

time.sleep(0.5): This adds a short delay (0.5 seconds) between requests. This is useful for avoiding overwhelming the server with too many requests in a short period. It also helps prevent hitting API rate limits.

else:: If the entry does not have an id, it prints a message and skips that entry.

 Entry Point Check

if __name__ == "__main__":: This is a standard Python construct that ensures the main function is executed only when the script is run directly (not when it's imported as a module in another script).

main(): If the script is run directly, the main function is called, starting the process of fetching and discarding unassigned usage records.

 

RUN THE SCRIPT

 Before running the script, let's review some unassigned data in the SAP Subscription Billing system.

fgedik_1-1732281960160.png

 

As you can see, there are multiple unassigned usage data records in the system. Now let’s run the script to discard them.

Open Command Prompt and navigate to the folder, where you have saved the script:

 

fgedik_2-1732281960161.png

If the run was successful, the console should look like this:

fgedik_3-1732281960164.png

Now let’s validate in the SAP Subscription Billing system:

Copy the first ID from the console and look for it in the system:

fgedik_4-1732281960172.png

 

As you can see the unassigned usage data was discarded successfully.

 With just a few modifications to fit your specific SAP Subscription Billing setup, this script can be a powerful tool to maintain clean and accurate usage data in your environment. Whether you're managing a large-scale subscription service or simply maintaining data integrity, automating the cleanup process can save you time and improve the efficiency of your operations.

 Let me know if you have any questions or need further assistance as you implement this solution.