Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mbetter
MBetterc
Commits
cbbd607f
Commit
cbbd607f
authored
Dec 18, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bet insert and verification fixed
parent
adf01f7a
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
714 additions
and
275 deletions
+714
-275
check_database_schema.py
check_database_schema.py
+68
-0
manager.py
mbetterclient/database/manager.py
+9
-3
models.py
mbetterclient/database/models.py
+1
-1
barcode_utils.py
mbetterclient/utils/barcode_utils.py
+2
-2
auth.py
mbetterclient/web_dashboard/auth.py
+178
-89
routes.py
mbetterclient/web_dashboard/routes.py
+374
-151
admin_bet_details.html
.../web_dashboard/templates/dashboard/admin_bet_details.html
+9
-0
admin_bets.html
...rclient/web_dashboard/templates/dashboard/admin_bets.html
+14
-14
bet_details.html
...client/web_dashboard/templates/dashboard/bet_details.html
+9
-0
bets.html
mbetterclient/web_dashboard/templates/dashboard/bets.html
+2
-0
new_bet.html
mbetterclient/web_dashboard/templates/dashboard/new_bet.html
+48
-15
No files found.
check_database_schema.py
0 → 100644
View file @
cbbd607f
#!/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
"
\n
bet_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
"
\n
Bets 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
"
\n
Bet 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
"
\n
Total 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
(
"
\n
Database 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
mbetterclient/database/manager.py
View file @
cbbd607f
...
...
@@ -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"""
...
...
mbetterclient/database/models.py
View file @
cbbd607f
...
...
@@ -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
,
...
...
mbetterclient/utils/barcode_utils.py
View file @
cbbd607f
...
...
@@ -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 f
irst 16 character
s
return
clean_uuid
[:
16
]
# These support alphanumeric, use f
ull UUID for maximum uniquenes
s
return
clean_uuid
elif
standard
in
[
'ean13'
,
'ean8'
,
'upca'
,
'upce'
,
'itf'
,
'codabar'
]:
# These require numeric data
...
...
mbetterclient/web_dashboard/auth.py
View file @
cbbd607f
This diff is collapsed.
Click to expand it.
mbetterclient/web_dashboard/routes.py
View file @
cbbd607f
This diff is collapsed.
Click to expand it.
mbetterclient/web_dashboard/templates/dashboard/admin_bet_details.html
View file @
cbbd607f
...
...
@@ -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>
...
...
mbetterclient/web_dashboard/templates/dashboard/admin_bets.html
View file @
cbbd607f
...
...
@@ -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 undon
e.'
))
{
cancel
Bet
(
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 databas
e.'
))
{
delete
Bet
(
betId
);
}
});
});
...
...
@@ -603,8 +603,8 @@ function updateBettingStats(stats) {
document
.
getElementById
(
'pending-bets'
).
textContent
=
stats
.
pending_bets
||
0
;
}
function
cancel
Bet
(
betId
)
{
fetch
(
`/api/
cashier/
bets/
${
betId
}
`
,
{
function
delete
Bet
(
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
cancell
ed successfully!'
,
'success'
);
showNotification
(
'Bet
delet
ed 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
cancell
ing bet: '
+
error
.
message
,
'error'
);
showNotification
(
'Error
delet
ing bet: '
+
error
.
message
,
'error'
);
});
}
...
...
mbetterclient/web_dashboard/templates/dashboard/bet_details.html
View file @
cbbd607f
...
...
@@ -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>
...
...
mbetterclient/web_dashboard/templates/dashboard/bets.html
View file @
cbbd607f
...
...
@@ -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>
...
...
mbetterclient/web_dashboard/templates/dashboard/new_bet.html
View file @
cbbd607f
...
...
@@ -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'
);
});
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment