Commit 03d2e1c8 authored by Your Name's avatar Your Name

Fix settlement and report sync accounting

parent b57de374
......@@ -1746,6 +1746,7 @@ class ReportsSyncResponseHandler(ResponseHandler):
total_payin = 0.0
total_payout = 0.0
total_bets = 0
unique_match_ids = set()
for bet in bets:
# Ensure bet_details are loaded (handle lazy loading)
......@@ -1756,15 +1757,15 @@ class ReportsSyncResponseHandler(ResponseHandler):
if bet_details:
total_payin += sum(d.amount for d in bet_details)
total_bets += len(bet_details)
# Add win amounts for winning bets (result is 'win', not 'won')
total_payout += sum(d.win_amount for d in bet_details if d.result == 'win')
unique_match_ids.update(d.match_id for d in bet_details if getattr(d, 'match_id', None) is not None)
return {
'total_payin': float(total_payin),
'total_payout': float(total_payout),
'net_profit': float(total_payin - total_payout),
'total_bets': total_bets,
'total_matches': len(stats)
'total_matches': len(unique_match_ids)
}
def _calculate_backoff_time(self, retry_count: int) -> int:
......@@ -3183,4 +3184,4 @@ class APIClient(ThreadedComponent):
return shlex.join(cmd_parts)
except AttributeError:
# Fallback for Python < 3.8
return ' '.join(shlex.quote(arg) for arg in cmd_parts)
\ No newline at end of file
return ' '.join(shlex.quote(arg) for arg in cmd_parts)
This diff is collapsed.
from pathlib import Path
from types import SimpleNamespace
from mbetterclient.api_client.client import ReportsSyncResponseHandler
from mbetterclient.core.message_bus import MessageBus
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.database.models import BetDetailModel, BetModel
def _build_handler(tmp_path: Path):
db_path = tmp_path / "reports_sync_summary.db"
db_manager = DatabaseManager(str(db_path))
assert db_manager.initialize()
handler = ReportsSyncResponseHandler(
db_manager=db_manager,
user_data_dir=str(tmp_path),
api_client=SimpleNamespace(settings=SimpleNamespace(rustdesk_id=None)),
message_bus=MessageBus(),
)
return handler, db_manager
def test_calculate_summary_uses_settled_rows_and_unique_matches(tmp_path):
handler, db_manager = _build_handler(tmp_path)
session = db_manager.get_session()
try:
bet = BetModel(uuid='bet-1', fixture_id='fixture-1', paid=True, paid_out=False)
session.add(bet)
session.flush()
session.add_all([
BetDetailModel(bet_id='bet-1', match_id=10, match_number=1, outcome='UNDER', amount=10.0, win_amount=20.0, result='win'),
BetDetailModel(bet_id='bet-1', match_id=10, match_number=1, outcome='OVER', amount=15.0, win_amount=0.0, result='lost'),
BetDetailModel(bet_id='bet-1', match_id=11, match_number=2, outcome='WIN1', amount=25.0, win_amount=75.0, result='win'),
BetDetailModel(bet_id='bet-1', match_id=11, match_number=2, outcome='KO1', amount=5.0, win_amount=0.0, result='cancelled'),
])
session.commit()
loaded_bet = session.query(BetModel).filter_by(uuid='bet-1').first()
summary = handler._calculate_summary([loaded_bet], stats=[])
assert summary['total_payin'] == 50.0
assert summary['total_payout'] == 95.0
assert summary['net_profit'] == -45.0
assert summary['total_bets'] == 3
assert summary['total_matches'] == 2
finally:
session.close()
db_manager.close()
from pathlib import Path
from mbetterclient.core.games_thread import GamesThread
from mbetterclient.core.message_bus import MessageBus
from mbetterclient.database.manager import DatabaseManager
from mbetterclient.database.models import BetDetailModel, MatchModel, MatchOutcomeModel
def _build_games_thread(tmp_path: Path):
db_path = tmp_path / "settlement_consistency.db"
db_manager = DatabaseManager(str(db_path))
assert db_manager.initialize()
message_bus = MessageBus()
games_thread = GamesThread("test_games_thread", message_bus, db_manager)
return games_thread, db_manager
def _seed_match(session):
match = MatchModel(
match_number=1,
fighter1_township='A',
fighter2_township='B',
venue_kampala_township='Kampala',
filename='fixture.txt',
file_sha1sum='fixture-sha1',
fixture_id='fixture-1',
active_status=True,
status='done',
result='WIN1',
under_over_result='UNDER',
winning_outcomes='["WIN1", "KO1"]',
)
session.add(match)
session.flush()
session.add_all([
MatchOutcomeModel(match_id=match.id, column_name='UNDER', float_value=2.0),
MatchOutcomeModel(match_id=match.id, column_name='OVER', float_value=3.0),
MatchOutcomeModel(match_id=match.id, column_name='WIN1', float_value=4.0),
MatchOutcomeModel(match_id=match.id, column_name='KO1', float_value=5.0),
MatchOutcomeModel(match_id=match.id, column_name='WIN2', float_value=6.0),
])
session.add_all([
BetDetailModel(bet_id='bet-under', match_id=match.id, match_number=1, outcome='UNDER', amount=10.0, result='pending'),
BetDetailModel(bet_id='bet-over', match_id=match.id, match_number=1, outcome='OVER', amount=11.0, result='pending'),
BetDetailModel(bet_id='bet-win1', match_id=match.id, match_number=1, outcome='WIN1', amount=12.0, result='pending'),
BetDetailModel(bet_id='bet-ko1', match_id=match.id, match_number=1, outcome='KO1', amount=13.0, result='pending'),
BetDetailModel(bet_id='bet-win2', match_id=match.id, match_number=1, outcome='WIN2', amount=14.0, result='pending'),
])
session.commit()
return match.id
def test_ensure_all_bets_resolved_settles_two_extraction_channels(tmp_path):
games_thread, db_manager = _build_games_thread(tmp_path)
session = db_manager.get_session()
try:
match_id = _seed_match(session)
finally:
session.close()
games_thread._ensure_all_bets_resolved(match_id, 'WIN1')
session = db_manager.get_session()
try:
rows = {
row.outcome: row
for row in session.query(BetDetailModel).filter_by(match_id=match_id).all()
}
assert rows['UNDER'].result == 'win'
assert rows['UNDER'].win_amount == 20.0
assert rows['OVER'].result == 'lost'
assert rows['OVER'].win_amount == 0.0
assert rows['WIN1'].result == 'win'
assert rows['WIN1'].win_amount == 48.0
assert rows['KO1'].result == 'win'
assert rows['KO1'].win_amount == 65.0
assert rows['WIN2'].result == 'lost'
assert rows['WIN2'].win_amount == 0.0
assert session.query(BetDetailModel).filter_by(match_id=match_id, result='pending').count() == 0
finally:
session.close()
db_manager.close()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment