How to - APIs
General Principles
Recursive Contract Construction
Constructing objects in streamOS is recursive, start with the lowest in the tree and continue building upwards through the hierarchy. Every reference to a UUID in a POST is an id of an object returned from another POST statement. streamOS maintains a factory that it references for all of these objects and links them together on run time.
The Parent Contract is your ultimate tie in to the customer, so until the Parent Contract is created - contracts are orphans.
Plans and Contracts can be reused - so once created you can use them over and over in other create objects for free, by just referencing the id.
Overall Setup Flow
The overall flow of creation follows this pattern:
- Create Customers: Create all the customers in streamOS appropriately and store the streamOS customer_ids
- Create Products: Create all the customers in streamOS appropriately and store the streamOS customer_ids
- Create Contracts: Create the appropriate contract structure that represent your contracts, verify using the UI if required
Operational Flow
- Structure Usage: Ensure your usage flow is of the format specified in the /contracts/org_id/usage endpoint
- Get Charges: Every update of usage or a contract upload / update triggers a run of the contracts - use the charges endpoint to get the results.
- Force a contract run: if you want to force a contract run trigger this
Modifications & Updates
Modifying Contracts: Modifying contracts in streamOS is tantamount to creating a new contract. You would provide the same information as you did during contract creation to the same endpoint, except for the information that you are changing. Ensure you use the same id as the object you are replacing so that the system knows to replace existing references to it.
Modifying Plans: Plans are modified in a similar fashion - post the modified information to the same endpoint with the same id to replace a plan definition. However, now that a plan has been changed - call the refresh endpoint to ensure that all contracts that reference this plan update their local copies of the plan.
Sample Code
import pytest
from flask import json
from app import app
from schema import input, output
import uuid
import os
org_id = <YOUR_ORG_ID>
auth_token = None
@pytest.fixture
def client():
global auth_token
app.config['TESTING'] = True
with app.test_client() as client:
if auth_token is None:
response = client.post('/login', json={'username':<USERNAME>, 'password': <PASSWORD>})
auth_token = response.json['access_token']
client.environ_base['HTTP_AUTHORIZATION'] = f'Bearer {auth_token}'
yield client
#test cases for the /plans/fixedfees endpoint
def test_fixed_fee_no_input(client):
response = client.post(f'/contracts/{org_id}/plans/fixedfee', json={})
assert response.status_code == 400
def test_fixed_fee_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/plans/fixedfee', json=invalid_input)
assert response.status_code == 422
# Further assertions can be added based on specific validation error messages
def test_fixed_fee_success(client, prefix=''):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Fixed Fee",
"amount": 100.0,
"period": None
# Add other required fields according to your FixedFeeSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/plans/fixedfee', json=valid_input)
assert response.status_code == 201
assert 'amount' in response.json
assert 'id' in response.json
assert 'display_name' in response.json
assert response.json['amount'] == 100.0
assert response.json['display_name'] == 'Test Fixed Fee'
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
# Test cases for the /plans/tier endpoint
def test_tier_no_input(client):
response = client.post(f'/contracts/{org_id}/plans/tier', json={})
assert response.status_code == 400
def test_tier_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/plans/tier', json=invalid_input)
assert response.status_code == 422
# Further assertions can be added based on specific validation error messages
def test_tier_invalid_start_value(client):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Tier",
"tiers": [[1, 100, 0.1], [101, None, 0.2]],
"pricing_type": 1,
"period": "1D"
# Add other required fields according to your TierSchema
}
response = client.post(f'/contracts/{org_id}/plans/tier', json=valid_input)
assert response.status_code == 400, f'Received: {response.json}'
def test_tier_invalid_end_value(client):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Tier",
"tiers": [[0, 100, 0.1], [101, 201, 0.2]],
"pricing_type": 1,
"period": "1D"
# Add other required fields according to your TierSchema
}
response = client.post(f'/contracts/{org_id}/plans/tier', json=valid_input)
assert response.status_code == 400
def test_tier_success(client, prefix=''):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Tier",
"tiers": [[0, 100, 0.1], [101, None, 0.2]],
"pricing_type": 1
# Add other required fields according to your TierSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/plans/tier', json=valid_input)
assert response.status_code == 201
# Assuming the response includes a UUID
assert 'id' in response.json
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
# Test cases for the /modifiers/discounts endpoint
def test_discount_no_input(client):
response = client.post(f'/contracts/{org_id}/modifiers/discounts', json={})
assert response.status_code == 400
def test_discount_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/modifiers/discounts', json=invalid_input)
assert response.status_code == 422
# Further assertions can be added based on specific validation error messages
def test_discount_success(client, prefix=''):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Discount",
"percentage": 10.0,
"threshold": 100.0
# Add other required fields according to your DiscountSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/modifiers/discounts', json=valid_input)
assert response.status_code == 201
# Assuming the response includes a UUID
assert 'id' in response.json
assert 'percentage' in response.json
assert 'threshold' in response.json
# Further assertions can be added to validate the UUID format or other
return response.json['id']
def test_billing_frequency_no_input(client):
response = client.post(f'/contracts/{org_id}/modifiers/billing_frequency', json={})
assert response.status_code == 400
def test_billing_frequency_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/modifiers/billing_frequency', json=invalid_input)
assert response.status_code == 422
# Further assertions can be added based on specific validation error messages
def test_billing_frequency_success(client, prefix=''):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Billing Frequency",
"type": 1,
"interval": 1,
"frequency": "D",
"anchor": "S",
"eq": False
# Add other required fields according to your BillingFrequencySchema
}
response = client.post(f'{prefix}/contracts/{org_id}/modifiers/billing_frequency', json=valid_input)
assert response.status_code == 201
# Assuming the response includes a UUID
assert 'id' in response.json
assert 'display_name' in response.json
assert 'type' in response.json
assert 'interval' in response.json
assert 'frequency' in response.json
assert 'anchor' in response.json
assert 'eq' in response.json
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
def test_billing_frequency_sim_dates(client):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Billing Frequency",
"type": 1,
"interval": 1,
"frequency": "M",
"anchor": "S",
"eq": False,
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-12-31T23:59:59"
# Add other required fields according to your BillingFrequencySchema
}
response = client.post(f'/contracts/{org_id}/modifiers/billing_frequency/sim_dates', json=valid_input)
assert response.status_code == 200, f'Received: {response.json}'
# Assuming the response includes a UUID
assert 'dates' in response.json, f'Received: {response.json}'
def test_minimum_no_input(client):
response = client.post(f'/contracts/{org_id}/modifiers/minimums', json={})
assert response.status_code == 400
def test_minimum_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/modifiers/minimums', json=invalid_input)
assert response.status_code == 422
# Further assertions can be added based on specific validation error messages
def test_minimum_success(client, prefix=''):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Minimum",
"amount": 100.0
# Add other required fields according to your MinimumSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/modifiers/minimums', json=valid_input)
assert response.status_code == 201
# Assuming the response includes a UUID
assert 'id' in response.json
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
#test cases for the /contracts/productcontract endpoint
def test_product_pricing_no_input(client):
response = client.post(f'/contracts/{org_id}/productcontract', json={})
assert response.status_code == 400
assert response.json == {'message': 'No input data provided'}
def test_product_pricing_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/productcontract', json=invalid_input)
assert response.status_code == 422, f'Received: {response.json}'
# Further assertions can be added based on specific validation error messages
def test_product_pricing_success(client, prefix=''):
# Assuming valid input structure for demonstration
tier_uuid = test_tier_success(client, prefix)
billing_uuid = test_billing_frequency_success(client, prefix)
valid_input = {
"display_name": "Test Product Contract",
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-12-31T23:59:59",
"billing_frequency": billing_uuid,
"tiers": tier_uuid,
"product_id": uuid.uuid4()
# Add other required fields according to your ProductContractSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/productcontract', json=valid_input)
assert response.status_code == 201, f'Received: {response.json}'
# Assuming the response includes a UUID
assert 'id' in response.json, f'Received: {response.json}'
assert 'billing_frequency' in response.json, f'Received: {response.json}'
assert 'tiers' in response.json, f'Received: {response.json}'
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
#testcases for the /contracts/subcontract endpoint
def test_subcontract_no_input(client):
response = client.post(f'/contracts/{org_id}/subcontract', json={})
assert response.status_code == 400
assert response.json == {'message': 'No input data provided'}
def test_subcontract_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/subcontract', json=invalid_input)
assert response.status_code == 422, f'Received: {response.json}'
# Further assertions can be added based on specific validation error messages
def test_subcontract_bad_objects(client):
# Assuming valid input structure for demonstration
billing_frequency_uuid = test_billing_frequency_success(client)
valid_input = {
"display_name": "Test Subcontract",
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-12-31T23:59:59",
"billing_frequency": billing_frequency_uuid,
"minimums": uuid.uuid4(),
"discounts": uuid.uuid4(),
"product_contracts": [uuid.uuid4()],
"fixed_fees": [uuid.uuid4()],
"sub_contracts": [uuid.uuid4()],
"override_underlying": False
# Add other required fields according to your ContractSchema
}
response = client.post(f'/contracts/{org_id}/subcontract', json=valid_input)
assert response.status_code == 422, f'Received: {response.json}'
def test_subcontract_success(client, prefix=''):
# Assuming valid input structure for demonstration
minimum_uuid = test_minimum_success(client, prefix)
discount_uuid = test_discount_success(client, prefix)
product_contract_uuid = test_product_pricing_success(client, prefix)
fixed_fee_uuid = test_fixed_fee_success(client, prefix)
billing_frequency_uuid = test_billing_frequency_success(client, prefix)
credit_uuid = test_credit_success(client, prefix)
valid_input = {
"display_name": "Test Subcontract",
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-12-31T23:59:59",
"billing_frequency": billing_frequency_uuid,
"minimums": minimum_uuid,
"discounts": discount_uuid,
"product_contracts": [product_contract_uuid],
"fixed_fees": [fixed_fee_uuid],
"credits": credit_uuid,
"override_underlying": False
# Add other required fields according to your ContractSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/subcontract', json=valid_input)
assert response.status_code == 201, f'Received: {response.json}'
# Assuming the response includes a UUID
assert 'id' in response.json, f'Received: {response.json}'
assert 'billing_frequency' in response.json, f'Received: {response.json}'
assert 'minimums' in response.json, f'Received: {response.json}'
assert 'discounts' in response.json, f'Received: {response.json}'
assert 'product_contracts' in response.json, f'Received: {response.json}'
assert 'fixed_fees' in response.json, f'Received: {response.json}'
assert 'credits' in response.json, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Subcontract', f'Received: {response.json}'
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
#testcases for the /contracts/parentcontract endpoint
def test_parent_contract_no_input(client):
response = client.post(f'/contracts/{org_id}/parentcontract', json={})
assert response.status_code == 400
assert response.json == {'message': 'No input data provided'}
def test_parent_contract_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/parentcontract', json=invalid_input)
assert response.status_code == 422, f'Received: {response.json}'
# Further assertions can be added based on specific validation error messages
def test_parent_contract_success(client, prefix=''):
# Assuming valid input structure for demonstration
product_contract_uuid = test_product_pricing_success(client, prefix)
fixed_fee_uuid = test_fixed_fee_success(client, prefix)
sub_contract_uuid = test_subcontract_success(client, prefix)
billing_frequency_uuid = test_billing_frequency_success(client, prefix)
credit_uuid = test_credit_success(client, prefix)
valid_input = {
"organization_id": org_id,
"customer_id": uuid.uuid4(),
"display_name": "Test Parent Contract",
"start_date": "2023-01-01T00:00:00",
"end_date": "2023-12-31T23:59:59",
"billing_frequency": billing_frequency_uuid,
"product_contracts": [product_contract_uuid],
"credits": credit_uuid,
"fixed_fees": [fixed_fee_uuid],
"sub_contracts": [sub_contract_uuid],
"override_underlying": False
# Add other required fields according to your ParentContractSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/parentcontract', json=valid_input)
assert response.status_code == 201, f'Received: {response.json}'
# Assuming the response includes a UUID
assert 'id' in response.json, f'Received: {response.json}'
assert 'billing_frequency' in response.json, f'Received: {response.json}'
assert 'product_contracts' in response.json, f'Received: {response.json}'
assert 'fixed_fees' in response.json, f'Received: {response.json}'
assert 'sub_contracts' in response.json, f'Received: {response.json}'
assert 'credits' in response.json, f'Received: {response.json}'
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
def test_credit_no_input(client):
response = client.post(f'/contracts/{org_id}/modifiers/credits', json={})
assert response.status_code == 400
assert response.json == {'message': 'No input data provided'}
def test_credit_invalid_input(client):
# Assuming invalid input structure for demonstration
invalid_input = {"invalidField": "invalidValue"}
response = client.post(f'/contracts/{org_id}/modifiers/credits', json=invalid_input)
assert response.status_code == 422, f'Received: {response.json}'
# Further assertions can be added based on specific validation error messages
def test_credit_success(client, prefix=''):
# Assuming valid input structure for demonstration
valid_input = {
"display_name": "Test Credit",
"amount": 100.0,
"expiry": "1Y"
# Add other required fields according to your CreditSchema
}
response = client.post(f'{prefix}/contracts/{org_id}/modifiers/credits', json=valid_input)
assert response.status_code == 201, f'Received: {response.json}'
# Assuming the response includes a UUID
assert 'id' in response.json, f'Received: {response.json}'
assert 'expiry' in response.json, f'Received: {response.json}'
assert 'display_name' in response.json, f'Received: {response.json}'
assert 'amount' in response.json, f'Received: {response.json}'
# Further assertions can be added to validate the UUID format or other response details
return response.json['id']
def test_get_credits(client):
credit_uuid = test_credit_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/credits')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 1, f'Received: {response.json}'
assert (response.json[0])['id'] == credit_uuid, f'Received: {response.json}'
def test_get_credit_success(client):
credit_uuid = test_credit_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/credit/{credit_uuid}')
assert response.status_code == 200
assert response.json['id'] == credit_uuid, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Credit', f'Received: {response.json}'
assert response.json['amount'] == 100.0, f'Received: {response.json}'
assert response.json['expiry'] == '1Y', f'Received: {response.json}'
def test_tiers(client):
tier_uuid = test_tier_success(client)
response = client.get(f'/contracts/{org_id}/plans/tiers')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 1, f'Received: {response.json}'
assert (response.json[0])['id'] == tier_uuid, f'Received: {response.json}'
def test_nonexistent_tier(client):
uuid = '00000000-0000-0000-0000-000000000000'
response = client.get(f'/contracts/{org_id}/plans/tier/{uuid}')
assert response.status_code == 404
def test_get_tier_success(client):
tier_ = test_tier_success(client)
response = client.get(f'/contracts/{org_id}/plans/tier/{tier_}')
assert response.status_code == 200
assert response.json['id'] == tier_, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Tier', f'Received: {response.json}'
assert response.json['tier'] == [[0, 100, 0.1], [101, None, 0.2]], f'Received: {response.json}'
assert response.json['pricing_type'] == 1, f'Received: {response.json}'
#test cases for the /plans/fixedfees endpoint GET method
def test_get_empty_fixed_fees(client):
response = client.get(f'/contracts/{org_id}/plans/fixedfees')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 0, f'Received: {response.json}'
def test_nonexistent_fixed_fee(client):
uuid = '00000000-0000-0000-0000-000000000000'
response = client.get(f'/contracts/{org_id}/plans/fixedfee/{uuid}')
assert response.status_code == 404
def test_get_fixed_fee_success(client):
fixed_fee_uuid = test_fixed_fee_success(client)
response = client.get(f'/contracts/{org_id}/plans/fixedfee/{fixed_fee_uuid}')
assert response.status_code == 200
assert response.json['id'] == fixed_fee_uuid, f'Received: {response.json}'
assert response.json['amount'] == 100.0, f'Received: {response.json}'
assert response.json['period'] == None, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Fixed Fee', f'Received: {response.json}'
def test_fixed_fees(client):
fixed_fee_uuid = test_fixed_fee_success(client)
response = client.get(f'/contracts/{org_id}/plans/fixedfees')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 1, f'Received: {response.json}'
assert (response.json[0])['id'] == fixed_fee_uuid, f'Received: {response.json}'
def test_get_discount_success(client):
discount_uuid = test_discount_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/discount/{discount_uuid}')
assert response.status_code == 200
assert response.json['id'] == discount_uuid, f'Received: {response.json}'
assert response.json['percentage'] == 10.0, f'Received: {response.json}'
assert response.json['threshold'] == 100.0, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Discount', f'Received: {response.json}'
def test_discounts(client):
discount_uuid = test_discount_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/discounts')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 1, f'Received: {response.json}'
assert (response.json[0])['id'] == discount_uuid, f'Received: {response.json}'
#test cases for the /modifiers/minimums endpoint GET method
def test_get_empty_minimums(client):
response = client.get(f'/contracts/{org_id}/modifiers/minimums')
assert response.status_code == 200
assert not response.json, f'Received: {response.json}'
def test_get_minimum_success(client):
minimum_uuid = test_minimum_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/minimum/{minimum_uuid}')
assert response.status_code == 200
assert response.json['id'] == minimum_uuid, f'Received: {response.json}'
assert response.json['amount'] == 100.0, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Minimum', f'Received: {response.json}'
def test_get_billing_frequency_success(client):
billing_frequency_uuid = test_billing_frequency_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/billing_frequency/{billing_frequency_uuid}')
assert response.status_code == 200
assert response.json['id'] == billing_frequency_uuid, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Billing Frequency', f'Received: {response.json}'
assert response.json['type'] == 1, f'Received: {response.json}'
assert response.json['interval'] == 1, f'Received: {response.json}'
assert response.json['frequency'] == 'D', f'Received: {response.json}'
assert response.json['anchor'] == 'S', f'Received: {response.json}'
assert response.json['eq'] == False, f'Received: {response.json}'
def test_billing_frequencies(client):
billing_frequency_uuid = test_billing_frequency_success(client)
response = client.get(f'/contracts/{org_id}/modifiers/billing_frequencies')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 1, f'Received: {response.json}'
assert (response.json[0])['id'] == billing_frequency_uuid, f'Received: {response.json}'
def test_get_contract_success(client):
parent_contract_uuid = test_parent_contract_success(client)
response = client.get(f'/contracts/{org_id}/contract/{parent_contract_uuid}')
assert response.status_code == 200
assert response.json['id'] == parent_contract_uuid, f'Received: {response.json}'
assert response.json['display_name'] == 'Test Parent Contract', f'Received: {response.json}'
assert response.json['override_underlying'] == False, f'Received: {response.json}'
assert all(k in response.json for k in ['organization_id' ,'customer_id', 'minimums', 'discounts', 'product_contracts', 'fixed_fees', 'sub_contracts']), f'Received: {response.json}'
def test_get_contracts_by_customer_success(client):
parent_contract_uuid = test_parent_contract_success(client)
response = client.get(f'/contracts/{org_id}/contract/{parent_contract_uuid}')
customer_id = (response.json)['customer_id']
response = client.get(f'/contracts/{org_id}/customer/{customer_id}')
assert response.status_code == 200
assert isinstance(response.json, list) and len(response.json) == 1, f'Received: {response.json}'
assert (response.json[0])['id'] == parent_contract_uuid, f'Received: {response.json}'
def test_factory_run(client):
response = client.post(f'/contracts/{org_id}/factory/run', json={})
assert response.status_code == 200, f'Received: {response.json}'
def test_customer_create_success(client):
response = client.post(f'/contracts/{org_id}/customers', json={'customer_name': 'Test Customer', 'customer_email': 'test@test.com'})
assert response.status_code == 201
assert 'customer_id' in response.json
assert 'customer_name' in response.json
assert response.json['customer_name'] == 'Test Customer'
return response.json['customer_id']
def test_customer_get_success(client):
customer_id = test_customer_create_success(client)
response = client.get(f'/contracts/{org_id}/customers/{customer_id}')
assert response.status_code == 200
assert response.json['customer_id'] == customer_id
assert response.json['customer_name'] == 'Test Customer'
assert response.json['customer_email'] == 'test@test.com'
def test_customer_update_success(client):
customer_id = test_customer_create_success(client)
response = client.put(f'/contracts/{org_id}/customers/{customer_id}', json={'customer_name': 'Updated Customer', 'customer_email': 'test@test.com'})
assert response.status_code == 200
assert response.json['customer_id'] == customer_id
def test_sim_factory_run(client):
parent_contract_uuid = test_parent_contract_success(client, prefix='/sim')
response = client.get(f'/sim/contracts/{org_id}/contract/{parent_contract_uuid}')
product_id = response.json['product_contracts'][0]['product_id']
product_contract_id = response.json['product_contracts'][0]['id']
response = client.post(f'/sim/contracts/{org_id}/factory/run', json={'usage': [{'date': '2024-02-29', 'product_id': product_id, 'contract_id': product_contract_id, 'quantity': 100}] })
assert response.status_code == 200, f'Received: {response.json}'
response = client.get(f'/sim/contracts/{org_id}/factory/charges')
assert response.status_code == 200, f'Received: {response.json}'
assert isinstance(response.json, list), f'Received: {response.json}'