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:
# Configure SQLite for better performance and reliability
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 synchronous=NORMAL"))
conn.execute(text("PRAGMA synchronous=FULL")) # Ensure data is written synchronously
conn.execute(text("PRAGMA cache_size=10000"))
conn.execute(text("PRAGMA temp_store=MEMORY"))
conn.execute(text("PRAGMA mmap_size=268435456")) # 256MB
conn.execute(text("PRAGMA foreign_keys=ON")) # Enable foreign key constraints
conn.commit()
# Create session factory
......@@ -128,11 +130,13 @@ class DatabaseManager:
# Configure SQLite for better performance and reliability
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 synchronous=NORMAL"))
conn.execute(text("PRAGMA synchronous=FULL")) # Ensure data is written synchronously
conn.execute(text("PRAGMA cache_size=10000"))
conn.execute(text("PRAGMA temp_store=MEMORY"))
conn.execute(text("PRAGMA mmap_size=268435456")) # 256MB
conn.execute(text("PRAGMA foreign_keys=ON")) # Enable foreign key constraints
conn.commit()
# Create session factory
......@@ -176,7 +180,9 @@ class DatabaseManager:
"""Get database session"""
if not self._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):
"""Close database connections"""
......
......@@ -4,7 +4,7 @@ SQLAlchemy database models for MbetterClient
import json
import hashlib
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from typing import Dict, Any, Optional, List
from sqlalchemy import (
Column, Integer, String, Text, DateTime, Boolean, Float,
......
......@@ -217,8 +217,8 @@ def format_bet_id_for_barcode(bet_uuid: str, standard: str) -> str:
clean_uuid = bet_uuid.replace('-', '').upper()
if standard in ['code128', 'code39']:
# These support alphanumeric, use first 16 characters
return clean_uuid[:16]
# These support alphanumeric, use full UUID for maximum uniqueness
return clean_uuid
elif standard in ['ean13', 'ean8', 'upca', 'upce', 'itf', 'codabar']:
# These require numeric data
......
This diff is collapsed.
This diff is collapsed.
......@@ -167,6 +167,15 @@
<dt class="text-muted">Bet UUID</dt>
<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>
<dd>{{ bet.bet_datetime.strftime('%Y-%m-%d %H:%M') }}</dd>
......
......@@ -473,6 +473,7 @@ function updateBetsTable(data, container) {
<thead class="table-dark">
<tr>
<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-list-ol me-1"></i>Details</th>
<th><i class="fas fa-hashtag me-1"></i>Match</th>
......@@ -522,6 +523,7 @@ function updateBetsTable(data, container) {
tableHTML += `
<tr>
<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>${bet.details ? bet.details.length : 0} selections</td>
<td>${matchNumbers.length > 0 ? matchNumbers.join(', ') : 'N/A'}</td>
......@@ -544,13 +546,11 @@ function updateBetsTable(data, container) {
title="Print Bet Receipt Directly">
<i class="fas fa-print"></i>
</button>
${overallStatus === 'pending' ? `
<button class="btn btn-sm btn-outline-danger ms-1 btn-cancel-bet"
<button class="btn btn-sm btn-outline-danger ms-1 btn-delete-bet"
data-bet-id="${bet.uuid}"
title="Cancel Bet">
<i class="fas fa-ban"></i>
title="Delete Bet">
<i class="fas fa-trash"></i>
</button>
` : ''}
</td>
</tr>
`;
......@@ -564,12 +564,12 @@ function updateBetsTable(data, container) {
container.innerHTML = tableHTML;
// Add event listeners for cancel buttons
container.querySelectorAll('.btn-cancel-bet').forEach(button => {
// Add event listeners for delete buttons
container.querySelectorAll('.btn-delete-bet').forEach(button => {
button.addEventListener('click', function() {
const betId = this.getAttribute('data-bet-id');
if (confirm('Are you sure you want to cancel this bet? This action cannot be undone.')) {
cancelBet(betId);
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.')) {
deleteBet(betId);
}
});
});
......@@ -603,8 +603,8 @@ function updateBettingStats(stats) {
document.getElementById('pending-bets').textContent = stats.pending_bets || 0;
}
function cancelBet(betId) {
fetch(`/api/cashier/bets/${betId}`, {
function deleteBet(betId) {
fetch(`/api/bets/${betId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
......@@ -615,13 +615,13 @@ function cancelBet(betId) {
if (data.success) {
// Refresh the bets table
loadBets();
showNotification('Bet cancelled successfully!', 'success');
showNotification('Bet deleted successfully!', 'success');
} else {
showNotification('Failed to cancel bet: ' + (data.error || 'Unknown error'), 'error');
showNotification('Failed to delete bet: ' + (data.error || 'Unknown error'), 'error');
}
})
.catch(error => {
showNotification('Error cancelling bet: ' + error.message, 'error');
showNotification('Error deleting bet: ' + error.message, 'error');
});
}
......
......@@ -167,6 +167,15 @@
<dt class="text-muted">Bet UUID</dt>
<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>
<dd>{{ bet.bet_datetime.strftime('%Y-%m-%d %H:%M') }}</dd>
......
......@@ -473,6 +473,7 @@ function updateBetsTable(data, container) {
<thead class="table-dark">
<tr>
<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-list-ol me-1"></i>Details</th>
<th><i class="fas fa-hashtag me-1"></i>Match</th>
......@@ -522,6 +523,7 @@ function updateBetsTable(data, container) {
tableHTML += `
<tr>
<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>${bet.details ? bet.details.length : 0} selections</td>
<td>${matchNumbers.length > 0 ? matchNumbers.join(', ') : 'N/A'}</td>
......
......@@ -560,14 +560,17 @@ function loadAvailableMatches() {
})
.then(data => {
console.log('📦 API response data:', data);
console.log('📦 Number of matches returned:', data.matches ? data.matches.length : 0);
if (data.success) {
// Update count badge
countBadge.textContent = data.total;
countBadge.className = data.total > 0 ? 'badge bg-success ms-2' : 'badge bg-warning ms-2';
console.log('✅ Updating available matches display');
updateAvailableMatchesDisplay(data, container);
} else {
console.error('❌ API returned success=false:', data.error);
container.innerHTML = `
<div class="text-center text-danger">
<i class="fas fa-exclamation-triangle me-2"></i>Error loading matches: ${data.error || 'Unknown error'}
......@@ -730,41 +733,51 @@ function updateAvailableMatchesDisplay(data, container) {
}
function updateBetSummary() {
console.log('🔄 updateBetSummary() called');
const summaryContent = document.getElementById('bet-summary-content');
const totalSection = document.getElementById('bet-total-section');
const totalAmountElement = document.getElementById('bet-total-amount');
const submitButton = document.getElementById('btn-submit-bet');
// Clear previous selections
selectedOutcomes.clear();
console.log('🧹 Cleared selectedOutcomes');
let totalAmount = 0;
let hasSelections = false;
let summaryHTML = '';
// 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;
console.log(`💰 Input ${index}: value="${input.value}", parsed amount=${amount}`);
if (amount > 0) {
const matchId = input.getAttribute('data-match-id');
const outcome = input.getAttribute('data-outcome');
console.log(`✅ Adding selection: matchId=${matchId}, outcome=${outcome}, amount=${amount}`);
hasSelections = true;
totalAmount += amount;
// Store selection
if (!selectedOutcomes.has(matchId)) {
selectedOutcomes.set(matchId, { outcomes: [], amounts: [] });
console.log(`📝 Created new entry for match ${matchId}`);
}
const matchSelections = selectedOutcomes.get(matchId);
matchSelections.outcomes.push(outcome);
matchSelections.amounts.push(amount);
// Get match info for display
const matchCard = input.closest('.match-card');
const matchTitle = matchCard.querySelector('h6').textContent.trim();
summaryHTML += `
<div class="mb-2 p-2 bg-light rounded">
<small class="fw-bold d-block">${matchTitle.split(':')[1]}</small>
......@@ -776,13 +789,18 @@ function updateBetSummary() {
`;
}
});
console.log('📋 Final selectedOutcomes:', selectedOutcomes);
console.log('💵 Total amount:', totalAmount, 'hasSelections:', hasSelections);
if (hasSelections) {
console.log('✅ Enabling submit button and showing summary');
summaryContent.innerHTML = summaryHTML;
totalSection.style.display = 'block';
totalAmountElement.textContent = formatCurrency(totalAmount);
submitButton.disabled = false;
} else {
console.log('❌ No selections, disabling submit button');
summaryContent.innerHTML = `
<div class="text-center text-muted">
<i class="fas fa-info-circle me-2"></i>
......@@ -795,17 +813,23 @@ function updateBetSummary() {
}
function submitBet() {
console.log('🎯 submitBet() called');
console.log('🎯 selectedOutcomes.size:', selectedOutcomes.size);
console.log('🎯 selectedOutcomes:', selectedOutcomes);
if (selectedOutcomes.size === 0) {
console.log('❌ No outcomes selected, showing error notification');
showNotification('Please select at least one outcome with an amount', 'error');
return;
}
// Prepare bet data
const betData = {
bet_details: []
};
selectedOutcomes.forEach((selections, matchId) => {
console.log('📋 Processing match', matchId, 'with selections:', selections);
selections.outcomes.forEach((outcome, index) => {
betData.bet_details.push({
match_id: parseInt(matchId),
......@@ -814,10 +838,12 @@ function submitBet() {
});
});
});
console.log('📤 Submitting bet data:', betData);
console.log('📤 Bet data JSON:', JSON.stringify(betData));
// Submit to API
console.log('🌐 Making fetch request to /api/cashier/bets');
fetch('/api/cashier/bets', {
method: 'POST',
headers: {
......@@ -825,8 +851,13 @@ function submitBet() {
},
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 => {
console.log('📦 API response data:', data);
if (data.success) {
showNotification('Bet submitted successfully!', 'success');
setTimeout(() => {
......@@ -834,9 +865,11 @@ function submitBet() {
}, 1500);
} else {
showNotification('Failed to submit bet: ' + (data.error || 'Unknown error'), 'error');
console.error('❌ Bet submission failed:', data);
}
})
.catch(error => {
console.error('❌ Error submitting bet:', 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