diff options
Diffstat (limited to 'tests/models/test_finance.py')
| -rw-r--r-- | tests/models/test_finance.py | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/tests/models/test_finance.py b/tests/models/test_finance.py new file mode 100644 index 0000000..888bf49 --- /dev/null +++ b/tests/models/test_finance.py @@ -0,0 +1,929 @@ +from datetime import timezone, timedelta +from itertools import product as iter_product +from random import randint +from uuid import uuid4 + +import pandas as pd +import pytest + +# noinspection PyUnresolvedReferences +from distributed.utils_test import ( + gen_cluster, + client_no_amm, + loop, + loop_in_thread, + cleanup, + cluster_fixture, + client, +) +from faker import Faker + +from generalresearch.incite.schemas.mergers.pop_ledger import ( + numerical_col_names, +) +from generalresearch.models.thl.finance import ( + POPFinancial, + ProductBalances, + BusinessBalances, +) +from test_utils.conftest import delete_df_collection +from test_utils.incite.collections.conftest import ledger_collection +from test_utils.incite.mergers.conftest import pop_ledger_merge +from test_utils.managers.ledger.conftest import ( + create_main_accounts, + session_with_tx_factory, +) + +fake = Faker() + + +class TestProductBalanceInitialize: + + def test_unknown_fields(self): + with pytest.raises(expected_exception=ValueError): + ProductBalances.model_validate( + { + "bp_payment.DEBIT": 1, + } + ) + + def test_payout(self): + val = randint(1, 1_000) + instance = ProductBalances.model_validate({"bp_payment.CREDIT": val}) + assert instance.payout == val + + def test_adjustment(self): + instance = ProductBalances.model_validate( + {"bp_adjustment.CREDIT": 90, "bp_adjustment.DEBIT": 147} + ) + + assert -57 == instance.adjustment + + def test_plug(self): + instance = ProductBalances.model_validate( + { + "bp_adjustment.CREDIT": 1000, + "bp_adjustment.DEBIT": 200, + "plug.DEBIT": 50, + } + ) + assert 750 == instance.adjustment + + instance = ProductBalances.model_validate( + { + "bp_payment.CREDIT": 789, + "bp_adjustment.CREDIT": 23, + "bp_adjustment.DEBIT": 101, + "plug.DEBIT": 17, + } + ) + assert 694 == instance.net + assert 694 == instance.balance + + def test_expense(self): + instance = ProductBalances.model_validate( + {"user_bonus.CREDIT": 0, "user_bonus.DEBIT": 999} + ) + + assert -999 == instance.expense + + def test_payment(self): + instance = ProductBalances.model_validate( + {"bp_payout.CREDIT": 1, "bp_payout.DEBIT": 100} + ) + + assert 99 == instance.payment + + def test_balance(self): + instance = ProductBalances.model_validate( + { + # Payouts from surveys: 1000 + "bp_payment.CREDIT": 1000, + # Adjustments: -200 + "bp_adjustment.CREDIT": 100, + "bp_adjustment.DEBIT": 300, + # Expense: -50 + "user_bonus.CREDIT": 0, + "user_bonus.DEBIT": 50, + # Prior supplier Payouts = 99 + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 100, + } + ) + + # Supplier payments aren't considered in the net + assert 750 == instance.net + + # Confirm any Supplier payments are taken out of their balance + assert 651 == instance.balance + + def test_retainer(self): + instance = ProductBalances.model_validate( + { + "bp_payment.CREDIT": 1000, + } + ) + + assert 1000 == instance.balance + assert 250 == instance.retainer + + instance = ProductBalances.model_validate( + { + "bp_payment.CREDIT": 1000, + # 1001 worth of adjustments, making it negative + "bp_adjustment.DEBIT": 1001, + } + ) + + assert -1 == instance.balance + assert 0 == instance.retainer + + def test_available_balance(self): + instance = ProductBalances.model_validate( + { + "bp_payment.CREDIT": 1000, + } + ) + + assert 750 == instance.available_balance + + instance = ProductBalances.model_validate( + { + # Payouts from surveys: $188.37 + "bp_payment.CREDIT": 18_837, + # Adjustments: -$7.53 + $.17 + "bp_adjustment.CREDIT": 17, + "bp_adjustment.DEBIT": 753, + # $.15 of those marketplace Failure >> Completes were never + # actually paid out, so plug those positive adjustments + "plug.DEBIT": 15, + # Expense: -$27.45 + "user_bonus.CREDIT": 0, + "user_bonus.DEBIT": 2_745, + # Prior supplier Payouts = $100 + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 10_001, + } + ) + + assert 18837 == instance.payout + assert -751 == instance.adjustment + assert 15341 == instance.net + + # Confirm any Supplier payments are taken out of their balance + assert 5341 == instance.balance + assert 1335 == instance.retainer + assert 4006 == instance.available_balance + + def test_json_schema(self): + instance = ProductBalances.model_validate( + { + # Payouts from surveys: 1000 + "bp_payment.CREDIT": 1000, + # Adjustments: -200 + "bp_adjustment.CREDIT": 100, + "bp_adjustment.DEBIT": 300, + # $.80 of those marketplace Failure >> Completes were never + # actually paid out, so plug those positive adjustments + "plug.DEBIT": 80, + # Expense: -50 + "user_bonus.CREDIT": 0, + "user_bonus.DEBIT": 50, + # Prior supplier Payouts = 99 + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 100, + } + ) + + assert isinstance(instance.model_json_schema(), dict) + openapi_fields = list(instance.model_json_schema()["properties"].keys()) + + # Ensure the SkipJsonSchema is working.. + assert "mp_payment_credit" not in openapi_fields + assert "mp_payment_debit" not in openapi_fields + assert "mp_adjustment_credit" not in openapi_fields + assert "mp_adjustment_debit" not in openapi_fields + assert "bp_payment_debit" not in openapi_fields + assert "plug_credit" not in openapi_fields + assert "plug_debit" not in openapi_fields + + # Confirm the @property computed fields show up in openapi. I don't + # know how to do that yet... so this is check to confirm they're + # known computed fields for now + computed_fields = list(instance.model_computed_fields.keys()) + assert "payout" in computed_fields + assert "adjustment" in computed_fields + assert "expense" in computed_fields + assert "payment" in computed_fields + assert "net" in computed_fields + assert "balance" in computed_fields + assert "retainer" in computed_fields + assert "available_balance" in computed_fields + + def test_repr(self): + instance = ProductBalances.model_validate( + { + # Payouts from surveys: 1000 + "bp_payment.CREDIT": 1000, + # Adjustments: -200 + "bp_adjustment.CREDIT": 100, + "bp_adjustment.DEBIT": 300, + # $.80 of those marketplace Failure >> Completes were never + # actually paid out, so plug those positive adjustments + "plug.DEBIT": 80, + # Expense: -50 + "user_bonus.CREDIT": 0, + "user_bonus.DEBIT": 50, + # Prior supplier Payouts = 99 + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 100, + } + ) + + assert "Total Adjustment: -$2.80" in str(instance) + + +class TestBusinessBalanceInitialize: + + def test_validate_product_ids(self): + instance1 = ProductBalances.model_validate( + {"bp_payment.CREDIT": 500, "bp_adjustment.DEBIT": 40} + ) + + instance2 = ProductBalances.model_validate( + {"bp_payment.CREDIT": 500, "bp_adjustment.DEBIT": 40} + ) + + with pytest.raises(expected_exception=ValueError) as cm: + BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert "'product_id' must be set for BusinessBalance children" in str(cm.value) + + # Confirm that once you add them, it successfully initializes + instance1.product_id = uuid4().hex + instance2.product_id = uuid4().hex + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert isinstance(instance, BusinessBalances) + + def test_payout(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.DEBIT": 40, + } + ) + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.DEBIT": 40, + } + ) + + # Confirm the base payouts are as expected. + assert instance1.payout == 500 + assert instance2.payout == 500 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.payout == 1_000 + + def test_adjustment(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + } + ) + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + } + ) + + # Confirm the base adjustment are as expected. + assert instance1.adjustment == -30 + assert instance2.adjustment == -30 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.adjustment == -60 + + def test_expense(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + } + ) + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + } + ) + + # Confirm the base adjustment are as expected. + assert instance1.expense == -4 + assert instance2.expense == -4 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.expense == -8 + + def test_net(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + } + ) + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + } + ) + + # Confirm the simple net + assert instance1.net == 466 + assert instance2.net == 466 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.net == 466 * 2 + + def test_payment(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 10_001, + } + ) + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 500, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 40, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 10_001, + } + ) + assert instance1.payment == 10_000 + assert instance2.payment == 10_000 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.payment == 20_000 + + def test_balance(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 500, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 10_001, + } + ) + assert instance1.balance == 39_506 + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 40_000, + "bp_adjustment.CREDIT": 2_000, + "bp_adjustment.DEBIT": 400, + "plug.DEBIT": 983, + "user_bonus.DEBIT": 392, + "user_bonus.CREDIT": 0, + "bp_payout.CREDIT": 0, + "bp_payout.DEBIT": 8_000, + } + ) + assert instance2.balance == 32_225 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.balance == 39_506 + 32_225 + + def test_retainer(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 500, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 10_001, + } + ) + assert instance1.balance == 39_506 + assert instance1.retainer == 9_876 + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 40_000, + "bp_adjustment.CREDIT": 2_000, + "bp_adjustment.DEBIT": 400, + "plug.DEBIT": 983, + "user_bonus.DEBIT": 392, + "user_bonus.CREDIT": 0, + "bp_payout.CREDIT": 0, + "bp_payout.DEBIT": 8_000, + } + ) + assert instance2.balance == 32_225 + assert instance2.retainer == 8_056 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.retainer == 9_876 + 8_056 + + def test_available_balance(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.CREDIT": 20, + "bp_adjustment.DEBIT": 500, + "plug.DEBIT": 10, + "user_bonus.DEBIT": 5, + "user_bonus.CREDIT": 1, + "bp_payout.CREDIT": 1, + "bp_payout.DEBIT": 10_001, + } + ) + assert instance1.balance == 39_506 + assert instance1.retainer == 9_876 + assert instance1.available_balance == 39_506 - 9_876 + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 40_000, + "bp_adjustment.CREDIT": 2_000, + "bp_adjustment.DEBIT": 400, + "plug.DEBIT": 983, + "user_bonus.DEBIT": 392, + "user_bonus.CREDIT": 0, + "bp_payout.CREDIT": 0, + "bp_payout.DEBIT": 8_000, + } + ) + assert instance2.balance == 32_225 + assert instance2.retainer == 8_056 + assert instance2.available_balance == 32_225 - 8_056 + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert instance.retainer == 9_876 + 8_056 + assert instance.available_balance == instance.balance - (9_876 + 8_056) + + def test_negative_net(self): + instance1 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.DEBIT": 50_001, + "bp_payout.DEBIT": 4_999, + } + ) + assert 50_000 == instance1.payout + assert -50_001 == instance1.adjustment + assert 4_999 == instance1.payment + + assert -1 == instance1.net + assert -5_000 == instance1.balance + assert 0 == instance1.available_balance + + instance2 = ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.DEBIT": 10_000, + "bp_payout.DEBIT": 10_000, + } + ) + assert 50_000 == instance2.payout + assert -10_000 == instance2.adjustment + assert 10_000 == instance2.payment + + assert 40_000 == instance2.net + assert 30_000 == instance2.balance + assert 22_500 == instance2.available_balance + + # Now confirm that they're correct in the BusinessBalance + instance = BusinessBalances.model_validate( + {"product_balances": [instance1, instance2]} + ) + assert 100_000 == instance.payout + assert -60_001 == instance.adjustment + assert 14_999 == instance.payment + + assert 39_999 == instance.net + assert 25_000 == instance.balance + + # Compare the retainers together. We can't just calculate the retainer + # on the Business.balance because it'll be "masked" by any Products + # that have a negative balance and actually reduce the Business's + # retainer as a whole. Therefore, we need to sum together each of the + # retainers from the child Products + assert 0 == instance1.retainer + assert 7_500 == instance2.retainer + assert 6_250 == instance.balance * 0.25 + assert 6_250 != instance.retainer + assert 7_500 == instance.retainer + assert 25_000 - 7_500 == instance.available_balance + + def test_str(self): + instance = BusinessBalances.model_validate( + { + "product_balances": [ + ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.DEBIT": 50_001, + "bp_payout.DEBIT": 4_999, + } + ), + ProductBalances.model_validate( + { + "product_id": uuid4().hex, + "bp_payment.CREDIT": 50_000, + "bp_adjustment.DEBIT": 10_000, + "bp_payout.DEBIT": 10_000, + } + ), + ] + } + ) + + assert "Products: 2" in str(instance) + assert "Total Adjustment: -$600.01" in str(instance) + assert "Available Balance: $175.00" in str(instance) + + def test_from_json(self): + s = '{"product_balances":[{"product_id":"7485124190274248bc14132755c8fc3b","bp_payment_credit":1184,"adjustment_credit":0,"adjustment_debit":0,"supplier_credit":0,"supplier_debit":0,"user_bonus_credit":0,"user_bonus_debit":0,"payout":1184,"adjustment":0,"expense":0,"net":1184,"payment":0,"balance":1184,"retainer":296,"available_balance":888,"adjustment_percent":0.0}],"payout":1184,"adjustment":0,"expense":0,"net":1184,"payment":0,"balance":1184,"retainer":296,"available_balance":888,"adjustment_percent":0.0}' + instance = BusinessBalances.model_validate_json(s) + + assert instance.payout == 1184 + assert instance.available_balance == 888 + assert instance.retainer == 296 + assert len(instance.product_balances) == 1 + assert instance.adjustment_percent == 0.0 + assert instance.expense == 0 + + p = instance.product_balances[0] + assert p.payout == 1184 + assert p.available_balance == 888 + assert p.retainer == 296 + + +@pytest.mark.parametrize( + argnames="offset, duration", + argvalues=list( + iter_product( + ["12h", "2D"], + [timedelta(days=2), timedelta(days=5)], + ) + ), +) +class TestProductFinanceData: + + def test_base( + self, + client_no_amm, + ledger_collection, + pop_ledger_merge, + mnt_filepath, + session_with_tx_factory, + product, + user_factory, + start, + duration, + delete_df_collection, + thl_lm, + create_main_accounts, + ): + from generalresearch.models.thl.user import User + + # -- Build & Setup + # assert ledger_collection.start is None + # assert ledger_collection.offset is None + u: User = user_factory(product=product, created=ledger_collection.start) + + for item in ledger_collection.items: + + for s_idx in range(3): + rand_item_time = fake.date_time_between( + start_date=item.start, + end_date=item.finish, + tzinfo=timezone.utc, + ) + session_with_tx_factory(started=rand_item_time, user=u) + + item.initial_load(overwrite=True) + + # Confirm any of the items are archived + assert ledger_collection.progress.has_archive.eq(True).all() + + pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) + # assert pop_ledger_merge.progress.has_archive.eq(True).all() + + item_finishes = [i.finish for i in ledger_collection.items] + item_finishes.sort(reverse=True) + last_item_finish = item_finishes[0] + + # -- + account = thl_lm.get_account_or_create_bp_wallet(product=u.product) + + ddf = pop_ledger_merge.ddf( + force_rr_latest=False, + include_partial=True, + columns=numerical_col_names + ["time_idx", "account_id"], + filters=[ + ("account_id", "==", account.uuid), + ("time_idx", ">=", start), + ("time_idx", "<", start + duration), + ], + ) + + df: pd.DataFrame = client_no_amm.compute(collections=ddf, sync=True) + + assert isinstance(df, pd.DataFrame) + assert not df.empty + + # -- + + df = df.groupby([pd.Grouper(key="time_idx", freq="D"), "account_id"]).sum() + res = POPFinancial.list_from_pandas(df, accounts=[account]) + + assert isinstance(res, list) + assert isinstance(res[0], POPFinancial) + + # On this, we can assert all products are the same, and that there are + # no overlapping time intervals + assert 1 == len(set(list([i.product_id for i in res]))) + assert len(res) == len(set(list([i.time for i in res]))) + + +@pytest.mark.parametrize( + argnames="offset, duration", + argvalues=list( + iter_product( + ["12h", "2D"], + [timedelta(days=2), timedelta(days=5)], + ) + ), +) +class TestPOPFinancialData: + + def test_base( + self, + client_no_amm, + ledger_collection, + pop_ledger_merge, + mnt_filepath, + user_factory, + product, + start, + duration, + create_main_accounts, + session_with_tx_factory, + session_manager, + thl_lm, + delete_df_collection, + delete_ledger_db, + ): + # -- Build & Setup + delete_ledger_db() + create_main_accounts() + delete_df_collection(coll=ledger_collection) + # assert ledger_collection.start is None + # assert ledger_collection.offset is None + + users = [] + for idx in range(5): + u = user_factory(product=product) + + for item in ledger_collection.items: + rand_item_time = fake.date_time_between( + start_date=item.start, + end_date=item.finish, + tzinfo=timezone.utc, + ) + + session_with_tx_factory(started=rand_item_time, user=u) + item.initial_load(overwrite=True) + + users.append(u) + + # Confirm any of the items are archived + assert ledger_collection.progress.has_archive.eq(True).all() + + pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) + # assert pop_ledger_merge.progress.has_archive.eq(True).all() + + item_finishes = [i.finish for i in ledger_collection.items] + item_finishes.sort(reverse=True) + last_item_finish = item_finishes[0] + + accounts = [] + for user in users: + account = thl_lm.get_account_or_create_bp_wallet(product=u.product) + accounts.append(account) + account_ids = [a.uuid for a in accounts] + + # -- + + ddf = pop_ledger_merge.ddf( + force_rr_latest=False, + include_partial=True, + columns=numerical_col_names + ["time_idx", "account_id"], + filters=[ + ("account_id", "in", account_ids), + ("time_idx", ">=", start), + ("time_idx", "<", last_item_finish), + ], + ) + df: pd.DataFrame = client_no_amm.compute(collections=ddf, sync=True) + + df = df.groupby([pd.Grouper(key="time_idx", freq="D"), "account_id"]).sum() + res = POPFinancial.list_from_pandas(df, accounts=accounts) + + assert isinstance(res, list) + for i in res: + assert isinstance(i, POPFinancial) + + # This does not return the AccountID, it's the Product ID + assert i.product_id in [u.product_id for u in users] + + # 1 Product, multiple Users + assert len(users) == len(accounts) + + # We group on days, and duration is a parameter to parametrize + assert isinstance(duration, timedelta) + + # -- Teardown + delete_df_collection(ledger_collection) + + +@pytest.mark.parametrize( + argnames="offset, duration", + argvalues=list( + iter_product( + ["12h", "1D"], + [timedelta(days=2), timedelta(days=3)], + ) + ), +) +class TestBusinessBalanceData: + def test_from_pandas( + self, + client_no_amm, + ledger_collection, + pop_ledger_merge, + user_factory, + product, + create_main_accounts, + session_factory, + thl_lm, + session_manager, + start, + thl_web_rr, + duration, + delete_df_collection, + delete_ledger_db, + session_with_tx_factory, + offset, + rm_ledger_collection, + ): + from generalresearch.models.thl.user import User + from generalresearch.models.thl.ledger import LedgerAccount + + delete_ledger_db() + create_main_accounts() + delete_df_collection(coll=ledger_collection) + rm_ledger_collection() + + for idx in range(5): + u: User = user_factory(product=product, created=ledger_collection.start) + + for item in ledger_collection.items: + item_time = fake.date_time_between( + start_date=item.start, + end_date=item.finish, + tzinfo=timezone.utc, + ) + session_with_tx_factory(started=item_time, user=u) + item.initial_load(overwrite=True) + + # Confirm any of the items are archived + assert ledger_collection.progress.has_archive.eq(True).all() + pop_ledger_merge.build(client=client_no_amm, ledger_coll=ledger_collection) + # assert pop_ledger_merge.progress.has_archive.eq(True).all() + + account: LedgerAccount = thl_lm.get_account_or_create_bp_wallet(product=product) + + ddf = pop_ledger_merge.ddf( + force_rr_latest=False, + include_partial=True, + columns=numerical_col_names + ["account_id"], + filters=[("account_id", "in", [account.uuid])], + ) + ddf = ddf.groupby("account_id").sum() + df: pd.DataFrame = client_no_amm.compute(collections=ddf, sync=True) + + assert isinstance(df, pd.DataFrame) + + instance = BusinessBalances.from_pandas( + input_data=df, accounts=[account], thl_pg_config=thl_web_rr + ) + balance: int = thl_lm.get_account_balance(account=account) + + assert instance.balance == balance + assert instance.net == balance + assert instance.payout == balance + + assert instance.payment == 0 + assert instance.adjustment == 0 + assert instance.adjustment_percent == 0.0 + + assert instance.expense == 0 + + # Cleanup + delete_ledger_db() + delete_df_collection(coll=ledger_collection) |
