Bet insert and verification fixed

parent adf01f7a
#!/usr/bin/env python3
"""
Script to check database schema and table structure
"""
import sqlite3
from pathlib import Path
# Database configuration
DATABASE_PATH = "data/mbetterclient.db"
def check_database_schema():
"""Check database schema and table structure"""
try:
print("Checking database schema...")
# Connect to the database directly
conn = sqlite3.connect(DATABASE_PATH)
cursor = conn.cursor()
# Get all tables in the database
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
tables = cursor.fetchall()
print(f"Found {len(tables)} tables:")
for table in tables:
print(f" - {table[0]}")
# Check if bet_details table exists
bet_details_exists = any(table[0] == 'bet_details' for table in tables)
print(f"\nbet_details table exists: {bet_details_exists}")
# Get schema for bets table
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='bets';")
bets_schema = cursor.fetchone()
if bets_schema:
print(f"\nBets table schema:")
print(bets_schema[0])
# Get schema for bet_details table if it exists
if bet_details_exists:
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='bet_details';")
bet_details_schema = cursor.fetchone()
if bet_details_schema:
print(f"\nBet details table schema:")
print(bet_details_schema[0])
# Check if there are any bets in the database
cursor.execute("SELECT COUNT(*) FROM bets;")
bet_count = cursor.fetchone()[0]
print(f"\nTotal bets in database: {bet_count}")
# Check if there are any bet_details in the database
if bet_details_exists:
cursor.execute("SELECT COUNT(*) FROM bet_details;")
details_count = cursor.fetchone()[0]
print(f"Total bet details in database: {details_count}")
conn.close()
print("\nDatabase schema check completed successfully")
except Exception as e:
print(f"Database schema check failed: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
check_database_schema()
\ No newline at end of file
...@@ -54,11 +54,13 @@ class DatabaseManager: ...@@ -54,11 +54,13 @@ class DatabaseManager:
# Configure SQLite for better performance and reliability # Configure SQLite for better performance and reliability
with self.engine.connect() as conn: with self.engine.connect() as conn:
# Use WAL mode with proper checkpoint configuration to fix bet insertion issues
conn.execute(text("PRAGMA journal_mode=WAL")) conn.execute(text("PRAGMA journal_mode=WAL"))
conn.execute(text("PRAGMA synchronous=NORMAL")) conn.execute(text("PRAGMA synchronous=FULL")) # Ensure data is written synchronously
conn.execute(text("PRAGMA cache_size=10000")) conn.execute(text("PRAGMA cache_size=10000"))
conn.execute(text("PRAGMA temp_store=MEMORY")) conn.execute(text("PRAGMA temp_store=MEMORY"))
conn.execute(text("PRAGMA mmap_size=268435456")) # 256MB conn.execute(text("PRAGMA mmap_size=268435456")) # 256MB
conn.execute(text("PRAGMA foreign_keys=ON")) # Enable foreign key constraints
conn.commit() conn.commit()
# Create session factory # Create session factory
...@@ -128,11 +130,13 @@ class DatabaseManager: ...@@ -128,11 +130,13 @@ class DatabaseManager:
# Configure SQLite for better performance and reliability # Configure SQLite for better performance and reliability
with self.engine.connect() as conn: with self.engine.connect() as conn:
# Use WAL mode with proper checkpoint configuration to fix bet insertion issues
conn.execute(text("PRAGMA journal_mode=WAL")) conn.execute(text("PRAGMA journal_mode=WAL"))
conn.execute(text("PRAGMA synchronous=NORMAL")) conn.execute(text("PRAGMA synchronous=FULL")) # Ensure data is written synchronously
conn.execute(text("PRAGMA cache_size=10000")) conn.execute(text("PRAGMA cache_size=10000"))
conn.execute(text("PRAGMA temp_store=MEMORY")) conn.execute(text("PRAGMA temp_store=MEMORY"))
conn.execute(text("PRAGMA mmap_size=268435456")) # 256MB conn.execute(text("PRAGMA mmap_size=268435456")) # 256MB
conn.execute(text("PRAGMA foreign_keys=ON")) # Enable foreign key constraints
conn.commit() conn.commit()
# Create session factory # Create session factory
...@@ -176,7 +180,9 @@ class DatabaseManager: ...@@ -176,7 +180,9 @@ class DatabaseManager:
"""Get database session""" """Get database session"""
if not self._initialized: if not self._initialized:
raise RuntimeError("Database manager not initialized") raise RuntimeError("Database manager not initialized")
return self.Session() session = self.Session()
logger.debug(f"DEBUG: Database manager returning session for database: {self.db_path}")
return session
def close(self): def close(self):
"""Close database connections""" """Close database connections"""
......
...@@ -4,7 +4,7 @@ SQLAlchemy database models for MbetterClient ...@@ -4,7 +4,7 @@ SQLAlchemy database models for MbetterClient
import json import json
import hashlib import hashlib
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from typing import Dict, Any, Optional, List from typing import Dict, Any, Optional, List
from sqlalchemy import ( from sqlalchemy import (
Column, Integer, String, Text, DateTime, Boolean, Float, Column, Integer, String, Text, DateTime, Boolean, Float,
......
...@@ -217,8 +217,8 @@ def format_bet_id_for_barcode(bet_uuid: str, standard: str) -> str: ...@@ -217,8 +217,8 @@ def format_bet_id_for_barcode(bet_uuid: str, standard: str) -> str:
clean_uuid = bet_uuid.replace('-', '').upper() clean_uuid = bet_uuid.replace('-', '').upper()
if standard in ['code128', 'code39']: if standard in ['code128', 'code39']:
# These support alphanumeric, use first 16 characters # These support alphanumeric, use full UUID for maximum uniqueness
return clean_uuid[:16] return clean_uuid
elif standard in ['ean13', 'ean8', 'upca', 'upce', 'itf', 'codabar']: elif standard in ['ean13', 'ean8', 'upca', 'upce', 'itf', 'codabar']:
# These require numeric data # These require numeric data
......
This diff is collapsed.
This diff is collapsed.
...@@ -167,6 +167,15 @@ ...@@ -167,6 +167,15 @@
<dt class="text-muted">Bet UUID</dt> <dt class="text-muted">Bet UUID</dt>
<dd class="font-monospace">{{ bet.uuid }}</dd> <dd class="font-monospace">{{ bet.uuid }}</dd>
<dt class="text-muted">Barcode ID</dt>
<dd class="font-monospace">
{% if bet.barcode_data %}
{{ bet.barcode_data }}
{% else %}
<span class="text-muted">Not available</span>
{% endif %}
</dd>
<dt class="text-muted">Created</dt> <dt class="text-muted">Created</dt>
<dd>{{ bet.bet_datetime.strftime('%Y-%m-%d %H:%M') }}</dd> <dd>{{ bet.bet_datetime.strftime('%Y-%m-%d %H:%M') }}</dd>
......
...@@ -473,6 +473,7 @@ function updateBetsTable(data, container) { ...@@ -473,6 +473,7 @@ function updateBetsTable(data, container) {
<thead class="table-dark"> <thead class="table-dark">
<tr> <tr>
<th><i class="fas fa-hashtag me-1"></i>Bet ID</th> <th><i class="fas fa-hashtag me-1"></i>Bet ID</th>
<th><i class="fas fa-barcode me-1"></i>Barcode</th>
<th><i class="fas fa-clock me-1"></i>Date & Time</th> <th><i class="fas fa-clock me-1"></i>Date & Time</th>
<th><i class="fas fa-list-ol me-1"></i>Details</th> <th><i class="fas fa-list-ol me-1"></i>Details</th>
<th><i class="fas fa-hashtag me-1"></i>Match</th> <th><i class="fas fa-hashtag me-1"></i>Match</th>
...@@ -522,6 +523,7 @@ function updateBetsTable(data, container) { ...@@ -522,6 +523,7 @@ function updateBetsTable(data, container) {
tableHTML += ` tableHTML += `
<tr> <tr>
<td><strong>${bet.uuid.substring(0, 8)}...</strong></td> <td><strong>${bet.uuid.substring(0, 8)}...</strong></td>
<td>${bet.barcode_data ? bet.barcode_data.substring(0, 16) + '...' : 'N/A'}</td>
<td>${betDateTime}</td> <td>${betDateTime}</td>
<td>${bet.details ? bet.details.length : 0} selections</td> <td>${bet.details ? bet.details.length : 0} selections</td>
<td>${matchNumbers.length > 0 ? matchNumbers.join(', ') : 'N/A'}</td> <td>${matchNumbers.length > 0 ? matchNumbers.join(', ') : 'N/A'}</td>
...@@ -544,13 +546,11 @@ function updateBetsTable(data, container) { ...@@ -544,13 +546,11 @@ function updateBetsTable(data, container) {
title="Print Bet Receipt Directly"> title="Print Bet Receipt Directly">
<i class="fas fa-print"></i> <i class="fas fa-print"></i>
</button> </button>
${overallStatus === 'pending' ? ` <button class="btn btn-sm btn-outline-danger ms-1 btn-delete-bet"
<button class="btn btn-sm btn-outline-danger ms-1 btn-cancel-bet"
data-bet-id="${bet.uuid}" data-bet-id="${bet.uuid}"
title="Cancel Bet"> title="Delete Bet">
<i class="fas fa-ban"></i> <i class="fas fa-trash"></i>
</button> </button>
` : ''}
</td> </td>
</tr> </tr>
`; `;
...@@ -564,12 +564,12 @@ function updateBetsTable(data, container) { ...@@ -564,12 +564,12 @@ function updateBetsTable(data, container) {
container.innerHTML = tableHTML; container.innerHTML = tableHTML;
// Add event listeners for cancel buttons // Add event listeners for delete buttons
container.querySelectorAll('.btn-cancel-bet').forEach(button => { container.querySelectorAll('.btn-delete-bet').forEach(button => {
button.addEventListener('click', function() { button.addEventListener('click', function() {
const betId = this.getAttribute('data-bet-id'); const betId = this.getAttribute('data-bet-id');
if (confirm('Are you sure you want to cancel this bet? This action cannot be undone.')) { if (confirm('Are you sure you want to permanently delete this bet? This action cannot be undone and will remove all bet data from the database.')) {
cancelBet(betId); deleteBet(betId);
} }
}); });
}); });
...@@ -603,8 +603,8 @@ function updateBettingStats(stats) { ...@@ -603,8 +603,8 @@ function updateBettingStats(stats) {
document.getElementById('pending-bets').textContent = stats.pending_bets || 0; document.getElementById('pending-bets').textContent = stats.pending_bets || 0;
} }
function cancelBet(betId) { function deleteBet(betId) {
fetch(`/api/cashier/bets/${betId}`, { fetch(`/api/bets/${betId}`, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -615,13 +615,13 @@ function cancelBet(betId) { ...@@ -615,13 +615,13 @@ function cancelBet(betId) {
if (data.success) { if (data.success) {
// Refresh the bets table // Refresh the bets table
loadBets(); loadBets();
showNotification('Bet cancelled successfully!', 'success'); showNotification('Bet deleted successfully!', 'success');
} else { } else {
showNotification('Failed to cancel bet: ' + (data.error || 'Unknown error'), 'error'); showNotification('Failed to delete bet: ' + (data.error || 'Unknown error'), 'error');
} }
}) })
.catch(error => { .catch(error => {
showNotification('Error cancelling bet: ' + error.message, 'error'); showNotification('Error deleting bet: ' + error.message, 'error');
}); });
} }
......
...@@ -167,6 +167,15 @@ ...@@ -167,6 +167,15 @@
<dt class="text-muted">Bet UUID</dt> <dt class="text-muted">Bet UUID</dt>
<dd class="font-monospace">{{ bet.uuid }}</dd> <dd class="font-monospace">{{ bet.uuid }}</dd>
<dt class="text-muted">Barcode ID</dt>
<dd class="font-monospace">
{% if bet.barcode_data %}
{{ bet.barcode_data }}
{% else %}
<span class="text-muted">Not available</span>
{% endif %}
</dd>
<dt class="text-muted">Created</dt> <dt class="text-muted">Created</dt>
<dd>{{ bet.bet_datetime.strftime('%Y-%m-%d %H:%M') }}</dd> <dd>{{ bet.bet_datetime.strftime('%Y-%m-%d %H:%M') }}</dd>
......
...@@ -473,6 +473,7 @@ function updateBetsTable(data, container) { ...@@ -473,6 +473,7 @@ function updateBetsTable(data, container) {
<thead class="table-dark"> <thead class="table-dark">
<tr> <tr>
<th><i class="fas fa-hashtag me-1"></i>Bet ID</th> <th><i class="fas fa-hashtag me-1"></i>Bet ID</th>
<th><i class="fas fa-barcode me-1"></i>Barcode</th>
<th><i class="fas fa-clock me-1"></i>Date & Time</th> <th><i class="fas fa-clock me-1"></i>Date & Time</th>
<th><i class="fas fa-list-ol me-1"></i>Details</th> <th><i class="fas fa-list-ol me-1"></i>Details</th>
<th><i class="fas fa-hashtag me-1"></i>Match</th> <th><i class="fas fa-hashtag me-1"></i>Match</th>
...@@ -522,6 +523,7 @@ function updateBetsTable(data, container) { ...@@ -522,6 +523,7 @@ function updateBetsTable(data, container) {
tableHTML += ` tableHTML += `
<tr> <tr>
<td><strong>${bet.uuid.substring(0, 8)}...</strong></td> <td><strong>${bet.uuid.substring(0, 8)}...</strong></td>
<td>${bet.barcode_data ? bet.barcode_data.substring(0, 16) + '...' : 'N/A'}</td>
<td>${betDateTime}</td> <td>${betDateTime}</td>
<td>${bet.details ? bet.details.length : 0} selections</td> <td>${bet.details ? bet.details.length : 0} selections</td>
<td>${matchNumbers.length > 0 ? matchNumbers.join(', ') : 'N/A'}</td> <td>${matchNumbers.length > 0 ? matchNumbers.join(', ') : 'N/A'}</td>
......
...@@ -560,14 +560,17 @@ function loadAvailableMatches() { ...@@ -560,14 +560,17 @@ function loadAvailableMatches() {
}) })
.then(data => { .then(data => {
console.log('📦 API response data:', data); console.log('📦 API response data:', data);
console.log('📦 Number of matches returned:', data.matches ? data.matches.length : 0);
if (data.success) { if (data.success) {
// Update count badge // Update count badge
countBadge.textContent = data.total; countBadge.textContent = data.total;
countBadge.className = data.total > 0 ? 'badge bg-success ms-2' : 'badge bg-warning ms-2'; countBadge.className = data.total > 0 ? 'badge bg-success ms-2' : 'badge bg-warning ms-2';
console.log('✅ Updating available matches display');
updateAvailableMatchesDisplay(data, container); updateAvailableMatchesDisplay(data, container);
} else { } else {
console.error('❌ API returned success=false:', data.error);
container.innerHTML = ` container.innerHTML = `
<div class="text-center text-danger"> <div class="text-center text-danger">
<i class="fas fa-exclamation-triangle me-2"></i>Error loading matches: ${data.error || 'Unknown error'} <i class="fas fa-exclamation-triangle me-2"></i>Error loading matches: ${data.error || 'Unknown error'}
...@@ -730,41 +733,51 @@ function updateAvailableMatchesDisplay(data, container) { ...@@ -730,41 +733,51 @@ function updateAvailableMatchesDisplay(data, container) {
} }
function updateBetSummary() { function updateBetSummary() {
console.log('🔄 updateBetSummary() called');
const summaryContent = document.getElementById('bet-summary-content'); const summaryContent = document.getElementById('bet-summary-content');
const totalSection = document.getElementById('bet-total-section'); const totalSection = document.getElementById('bet-total-section');
const totalAmountElement = document.getElementById('bet-total-amount'); const totalAmountElement = document.getElementById('bet-total-amount');
const submitButton = document.getElementById('btn-submit-bet'); const submitButton = document.getElementById('btn-submit-bet');
// Clear previous selections // Clear previous selections
selectedOutcomes.clear(); selectedOutcomes.clear();
console.log('🧹 Cleared selectedOutcomes');
let totalAmount = 0; let totalAmount = 0;
let hasSelections = false; let hasSelections = false;
let summaryHTML = ''; let summaryHTML = '';
// Collect all amount inputs with values > 0 // Collect all amount inputs with values > 0
document.querySelectorAll('.amount-input').forEach(input => { const amountInputs = document.querySelectorAll('.amount-input');
console.log('📊 Found', amountInputs.length, 'amount inputs');
amountInputs.forEach((input, index) => {
const amount = parseFloat(input.value) || 0; const amount = parseFloat(input.value) || 0;
console.log(`💰 Input ${index}: value="${input.value}", parsed amount=${amount}`);
if (amount > 0) { if (amount > 0) {
const matchId = input.getAttribute('data-match-id'); const matchId = input.getAttribute('data-match-id');
const outcome = input.getAttribute('data-outcome'); const outcome = input.getAttribute('data-outcome');
console.log(`✅ Adding selection: matchId=${matchId}, outcome=${outcome}, amount=${amount}`);
hasSelections = true; hasSelections = true;
totalAmount += amount; totalAmount += amount;
// Store selection // Store selection
if (!selectedOutcomes.has(matchId)) { if (!selectedOutcomes.has(matchId)) {
selectedOutcomes.set(matchId, { outcomes: [], amounts: [] }); selectedOutcomes.set(matchId, { outcomes: [], amounts: [] });
console.log(`📝 Created new entry for match ${matchId}`);
} }
const matchSelections = selectedOutcomes.get(matchId); const matchSelections = selectedOutcomes.get(matchId);
matchSelections.outcomes.push(outcome); matchSelections.outcomes.push(outcome);
matchSelections.amounts.push(amount); matchSelections.amounts.push(amount);
// Get match info for display // Get match info for display
const matchCard = input.closest('.match-card'); const matchCard = input.closest('.match-card');
const matchTitle = matchCard.querySelector('h6').textContent.trim(); const matchTitle = matchCard.querySelector('h6').textContent.trim();
summaryHTML += ` summaryHTML += `
<div class="mb-2 p-2 bg-light rounded"> <div class="mb-2 p-2 bg-light rounded">
<small class="fw-bold d-block">${matchTitle.split(':')[1]}</small> <small class="fw-bold d-block">${matchTitle.split(':')[1]}</small>
...@@ -776,13 +789,18 @@ function updateBetSummary() { ...@@ -776,13 +789,18 @@ function updateBetSummary() {
`; `;
} }
}); });
console.log('📋 Final selectedOutcomes:', selectedOutcomes);
console.log('💵 Total amount:', totalAmount, 'hasSelections:', hasSelections);
if (hasSelections) { if (hasSelections) {
console.log('✅ Enabling submit button and showing summary');
summaryContent.innerHTML = summaryHTML; summaryContent.innerHTML = summaryHTML;
totalSection.style.display = 'block'; totalSection.style.display = 'block';
totalAmountElement.textContent = formatCurrency(totalAmount); totalAmountElement.textContent = formatCurrency(totalAmount);
submitButton.disabled = false; submitButton.disabled = false;
} else { } else {
console.log('❌ No selections, disabling submit button');
summaryContent.innerHTML = ` summaryContent.innerHTML = `
<div class="text-center text-muted"> <div class="text-center text-muted">
<i class="fas fa-info-circle me-2"></i> <i class="fas fa-info-circle me-2"></i>
...@@ -795,17 +813,23 @@ function updateBetSummary() { ...@@ -795,17 +813,23 @@ function updateBetSummary() {
} }
function submitBet() { function submitBet() {
console.log('🎯 submitBet() called');
console.log('🎯 selectedOutcomes.size:', selectedOutcomes.size);
console.log('🎯 selectedOutcomes:', selectedOutcomes);
if (selectedOutcomes.size === 0) { if (selectedOutcomes.size === 0) {
console.log('❌ No outcomes selected, showing error notification');
showNotification('Please select at least one outcome with an amount', 'error'); showNotification('Please select at least one outcome with an amount', 'error');
return; return;
} }
// Prepare bet data // Prepare bet data
const betData = { const betData = {
bet_details: [] bet_details: []
}; };
selectedOutcomes.forEach((selections, matchId) => { selectedOutcomes.forEach((selections, matchId) => {
console.log('📋 Processing match', matchId, 'with selections:', selections);
selections.outcomes.forEach((outcome, index) => { selections.outcomes.forEach((outcome, index) => {
betData.bet_details.push({ betData.bet_details.push({
match_id: parseInt(matchId), match_id: parseInt(matchId),
...@@ -814,10 +838,12 @@ function submitBet() { ...@@ -814,10 +838,12 @@ function submitBet() {
}); });
}); });
}); });
console.log('📤 Submitting bet data:', betData); console.log('📤 Submitting bet data:', betData);
console.log('📤 Bet data JSON:', JSON.stringify(betData));
// Submit to API // Submit to API
console.log('🌐 Making fetch request to /api/cashier/bets');
fetch('/api/cashier/bets', { fetch('/api/cashier/bets', {
method: 'POST', method: 'POST',
headers: { headers: {
...@@ -825,8 +851,13 @@ function submitBet() { ...@@ -825,8 +851,13 @@ function submitBet() {
}, },
body: JSON.stringify(betData) body: JSON.stringify(betData)
}) })
.then(response => response.json()) .then(response => {
console.log('📡 API response status:', response.status);
console.log('📡 API response headers:', response.headers);
return response.json();
})
.then(data => { .then(data => {
console.log('📦 API response data:', data);
if (data.success) { if (data.success) {
showNotification('Bet submitted successfully!', 'success'); showNotification('Bet submitted successfully!', 'success');
setTimeout(() => { setTimeout(() => {
...@@ -834,9 +865,11 @@ function submitBet() { ...@@ -834,9 +865,11 @@ function submitBet() {
}, 1500); }, 1500);
} else { } else {
showNotification('Failed to submit bet: ' + (data.error || 'Unknown error'), 'error'); showNotification('Failed to submit bet: ' + (data.error || 'Unknown error'), 'error');
console.error('❌ Bet submission failed:', data);
} }
}) })
.catch(error => { .catch(error => {
console.error('❌ Error submitting bet:', error);
showNotification('Error submitting bet: ' + error.message, 'error'); showNotification('Error submitting bet: ' + error.message, 'error');
}); });
} }
......
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