From 7e39bb160a4c68a57cce5350239f8fab5eb16052 Mon Sep 17 00:00:00 2001 From: padreug Date: Sat, 5 Jul 2025 15:35:58 +0200 Subject: [PATCH] Enhance client balance summary retrieval: Updated the get_client_balance_summary function to accept an optional as_of_time parameter for temporal accuracy in balance calculations. Adjusted SQL queries to filter results based on the specified time, ensuring accurate balance summaries. Added logging for cases where as_of_time is utilized. Updated transaction processing to leverage this new functionality for improved accuracy in client balance calculations. --- crud.py | 33 ++++++++++++++++++++++++--------- transaction_processor.py | 12 +++++++++++- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/crud.py b/crud.py index 8798ae7..4b04ef2 100644 --- a/crud.py +++ b/crud.py @@ -221,32 +221,47 @@ async def get_payments_by_lamassu_transaction(lamassu_transaction_id: str) -> Li # Balance and Summary Operations -async def get_client_balance_summary(client_id: str) -> ClientBalanceSummary: - # Get total confirmed deposits +async def get_client_balance_summary(client_id: str, as_of_time: Optional[datetime] = None) -> ClientBalanceSummary: + """Get client balance summary, optionally as of a specific point in time""" + + # Build time filter for temporal accuracy + time_filter = "" + params = {"client_id": client_id} + + if as_of_time is not None: + time_filter = "AND confirmed_at <= :as_of_time" + params["as_of_time"] = as_of_time + + # Get total confirmed deposits (only those confirmed before the cutoff time) total_deposits_result = await db.fetchone( - """ + f""" SELECT COALESCE(SUM(amount), 0) as total, currency FROM satoshimachine.dca_deposits - WHERE client_id = :client_id AND status = 'confirmed' + WHERE client_id = :client_id AND status = 'confirmed' {time_filter} GROUP BY currency """, - {"client_id": client_id} + params ) - # Get total payments made + # Get total payments made (only those created before the cutoff time) total_payments_result = await db.fetchone( - """ + f""" SELECT COALESCE(SUM(amount_fiat), 0) as total FROM satoshimachine.dca_payments - WHERE client_id = :client_id AND status = 'confirmed' + WHERE client_id = :client_id AND status = 'confirmed' {time_filter} """, - {"client_id": client_id} + params ) total_deposits = total_deposits_result["total"] if total_deposits_result else 0 total_payments = total_payments_result["total"] if total_payments_result else 0 currency = total_deposits_result["currency"] if total_deposits_result else "GTQ" + # Log temporal filtering if as_of_time was used + if as_of_time is not None: + from lnbits.core.services import logger + logger.info(f"Client {client_id[:8]}... balance as of {as_of_time}: {total_deposits - total_payments} centavos remaining") + return ClientBalanceSummary( client_id=client_id, total_deposits=total_deposits, diff --git a/transaction_processor.py b/transaction_processor.py index b9ff64b..f842911 100644 --- a/transaction_processor.py +++ b/transaction_processor.py @@ -608,6 +608,7 @@ class LamassuTransactionProcessor: fiat_amount = transaction.get("fiat_amount") # Actual fiat dispensed (principal only) commission_percentage = transaction.get("commission_percentage") # Already stored as decimal (e.g., 0.045) discount = transaction.get("discount") # Discount percentage + transaction_time = transaction.get("transaction_time") # ATM transaction timestamp for temporal accuracy # Validate required fields if crypto_atoms is None: @@ -622,6 +623,10 @@ class LamassuTransactionProcessor: if discount is None: logger.info(f"Missing discount in transaction: {transaction}, defaulting to 0") discount = 0.0 + if transaction_time is None: + logger.warning(f"Missing transaction_time in transaction: {transaction}") + # Could use current time as fallback, but this indicates a data issue + # transaction_time = datetime.now(timezone.utc) # Calculate effective commission percentage after discount (following the reference logic) if commission_percentage > 0: @@ -642,13 +647,18 @@ class LamassuTransactionProcessor: logger.info(f"Transaction - Total crypto: {crypto_atoms} sats") logger.info(f"Commission: {commission_percentage*100:.1f}% - {discount:.1f}% discount = {effective_commission*100:.1f}% effective ({commission_amount_sats} sats)") logger.info(f"Base for DCA: {base_crypto_atoms} sats, Fiat dispensed: {fiat_amount}, Exchange rate: {exchange_rate:.2f} sats/fiat_unit") + if transaction_time: + logger.info(f"Calculating balances as of transaction time: {transaction_time}") + else: + logger.warning("No transaction time available - using current balances (may be inaccurate)") # Get balance summaries for all clients to calculate proportions client_balances = {} total_confirmed_deposits = 0 for client in flow_clients: - balance = await get_client_balance_summary(client.id) + # Get balance as of the transaction time for temporal accuracy + balance = await get_client_balance_summary(client.id, as_of_time=transaction_time) if balance.remaining_balance > 0: # Only include clients with remaining balance client_balances[client.id] = balance.remaining_balance total_confirmed_deposits += balance.remaining_balance