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
27663e61
Commit
27663e61
authored
Dec 12, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix barcode bet verification
parent
9ed29d92
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
728 additions
and
216 deletions
+728
-216
migrations.py
mbetterclient/database/migrations.py
+68
-0
models.py
mbetterclient/database/models.py
+9
-2
routes.py
mbetterclient/web_dashboard/routes.py
+120
-5
admin_bet_details.html
.../web_dashboard/templates/dashboard/admin_bet_details.html
+239
-102
bet_details.html
...client/web_dashboard/templates/dashboard/bet_details.html
+238
-101
verify_bet.html
...rclient/web_dashboard/templates/dashboard/verify_bet.html
+27
-3
verify_bet_mobile.html
.../web_dashboard/templates/dashboard/verify_bet_mobile.html
+27
-3
No files found.
mbetterclient/database/migrations.py
View file @
27663e61
...
@@ -2548,6 +2548,73 @@ class Migration_032_FixExtractionAssociationDefaults(DatabaseMigration):
...
@@ -2548,6 +2548,73 @@ class Migration_032_FixExtractionAssociationDefaults(DatabaseMigration):
return
False
return
False
class
Migration_033_AddBarcodeFieldsToBets
(
DatabaseMigration
):
"""Add barcode_standard and barcode_data fields to bets table for barcode verification"""
def
__init__
(
self
):
super
()
.
__init__
(
"033"
,
"Add barcode_standard and barcode_data fields to bets table"
)
def
up
(
self
,
db_manager
)
->
bool
:
"""Add barcode fields to bets table"""
try
:
with
db_manager
.
engine
.
connect
()
as
conn
:
# Check if columns already exist
result
=
conn
.
execute
(
text
(
"PRAGMA table_info(bets)"
))
columns
=
[
row
[
1
]
for
row
in
result
.
fetchall
()]
if
'barcode_standard'
not
in
columns
:
# Add barcode_standard column
conn
.
execute
(
text
(
"""
ALTER TABLE bets
ADD COLUMN barcode_standard VARCHAR(50)
"""
))
logger
.
info
(
"barcode_standard column added to bets table"
)
else
:
logger
.
info
(
"barcode_standard column already exists in bets table"
)
if
'barcode_data'
not
in
columns
:
# Add barcode_data column
conn
.
execute
(
text
(
"""
ALTER TABLE bets
ADD COLUMN barcode_data VARCHAR(255)
"""
))
logger
.
info
(
"barcode_data column added to bets table"
)
else
:
logger
.
info
(
"barcode_data column already exists in bets table"
)
# Add indexes for barcode fields
conn
.
execute
(
text
(
"""
CREATE INDEX IF NOT EXISTS ix_bets_barcode_data ON bets(barcode_data)
"""
))
conn
.
execute
(
text
(
"""
CREATE INDEX IF NOT EXISTS ix_bets_barcode_standard ON bets(barcode_standard)
"""
))
# Add unique constraint for barcode data per standard
try
:
conn
.
execute
(
text
(
"""
CREATE UNIQUE INDEX IF NOT EXISTS uq_bets_barcode ON bets(barcode_data, barcode_standard)
"""
))
except
Exception
as
e
:
logger
.
warning
(
f
"Could not create unique constraint on barcode fields: {e}"
)
conn
.
commit
()
logger
.
info
(
"Barcode fields added to bets table successfully"
)
return
True
except
Exception
as
e
:
logger
.
error
(
f
"Failed to add barcode fields to bets table: {e}"
)
return
False
def
down
(
self
,
db_manager
)
->
bool
:
"""Remove barcode fields - SQLite doesn't support DROP COLUMN easily"""
logger
.
warning
(
"SQLite doesn't support DROP COLUMN - barcode fields will remain"
)
return
True
# Registry of all migrations in order
# Registry of all migrations in order
# Registry of all migrations in order
# Registry of all migrations in order
...
@@ -2584,6 +2651,7 @@ MIGRATIONS: List[DatabaseMigration] = [
...
@@ -2584,6 +2651,7 @@ MIGRATIONS: List[DatabaseMigration] = [
Migration_030_AddZipValidationStatus
(),
Migration_030_AddZipValidationStatus
(),
Migration_031_AddWinningOutcomesFields
(),
Migration_031_AddWinningOutcomesFields
(),
Migration_032_FixExtractionAssociationDefaults
(),
Migration_032_FixExtractionAssociationDefaults
(),
Migration_033_AddBarcodeFieldsToBets
(),
]
]
...
...
mbetterclient/database/models.py
View file @
27663e61
...
@@ -678,15 +678,22 @@ class BetModel(BaseModel):
...
@@ -678,15 +678,22 @@ class BetModel(BaseModel):
Index
(
'ix_bets_uuid'
,
'uuid'
),
Index
(
'ix_bets_uuid'
,
'uuid'
),
Index
(
'ix_bets_fixture_id'
,
'fixture_id'
),
Index
(
'ix_bets_fixture_id'
,
'fixture_id'
),
Index
(
'ix_bets_created_at'
,
'created_at'
),
Index
(
'ix_bets_created_at'
,
'created_at'
),
Index
(
'ix_bets_barcode_data'
,
'barcode_data'
),
Index
(
'ix_bets_barcode_standard'
,
'barcode_standard'
),
UniqueConstraint
(
'uuid'
,
name
=
'uq_bets_uuid'
),
UniqueConstraint
(
'uuid'
,
name
=
'uq_bets_uuid'
),
UniqueConstraint
(
'barcode_data'
,
'barcode_standard'
,
name
=
'uq_bets_barcode'
),
)
)
uuid
=
Column
(
String
(
1024
),
nullable
=
False
,
unique
=
True
,
comment
=
'Unique identifier for the bet'
)
uuid
=
Column
(
String
(
1024
),
nullable
=
False
,
unique
=
True
,
comment
=
'Unique identifier for the bet'
)
fixture_id
=
Column
(
String
(
255
),
nullable
=
False
,
comment
=
'Reference to fixture_id from matches table'
)
fixture_id
=
Column
(
String
(
255
),
nullable
=
False
,
comment
=
'Reference to fixture_id from matches table'
)
bet_datetime
=
Column
(
DateTime
,
default
=
datetime
.
utcnow
,
nullable
=
False
,
comment
=
'Bet creation timestamp'
)
bet_datetime
=
Column
(
DateTime
,
default
=
datetime
.
utcnow
,
nullable
=
False
,
comment
=
'Bet creation timestamp'
)
paid
=
Column
(
Boolean
,
default
=
False
,
nullable
=
False
,
comment
=
'Payment status (True if payment received)'
)
paid
=
Column
(
Boolean
,
default
=
False
,
nullable
=
False
,
comment
=
'Payment status (True if payment received)'
)
paid_out
=
Column
(
Boolean
,
default
=
False
,
nullable
=
False
,
comment
=
'Payout status (True if winnings paid out)'
)
paid_out
=
Column
(
Boolean
,
default
=
False
,
nullable
=
False
,
comment
=
'Payout status (True if winnings paid out)'
)
# Barcode fields for verification
barcode_standard
=
Column
(
String
(
50
),
comment
=
'Barcode standard used (ean13, code128, etc.)'
)
barcode_data
=
Column
(
String
(
255
),
comment
=
'Barcode data for verification'
)
# Relationships
# Relationships
bet_details
=
relationship
(
'BetDetailModel'
,
back_populates
=
'bet'
,
cascade
=
'all, delete-orphan'
)
bet_details
=
relationship
(
'BetDetailModel'
,
back_populates
=
'bet'
,
cascade
=
'all, delete-orphan'
)
...
...
mbetterclient/web_dashboard/routes.py
View file @
27663e61
...
@@ -176,11 +176,19 @@ def bet_details(bet_id):
...
@@ -176,11 +176,19 @@ def bet_details(bet_id):
except
(
json
.
JSONDecodeError
,
TypeError
):
except
(
json
.
JSONDecodeError
,
TypeError
):
winning_outcomes
=
[]
winning_outcomes
=
[]
# Get odds for this outcome
odds
=
0.0
if
match
:
outcomes_dict
=
match
.
get_outcomes_dict
()
odds
=
outcomes_dict
.
get
(
detail
.
outcome
,
0.0
)
detail_dict
=
{
detail_dict
=
{
'id'
:
detail
.
id
,
'id'
:
detail
.
id
,
'match_id'
:
detail
.
match_id
,
'match_id'
:
detail
.
match_id
,
'outcome'
:
detail
.
outcome
,
'outcome'
:
detail
.
outcome
,
'amount'
:
float
(
detail
.
amount
),
'amount'
:
float
(
detail
.
amount
),
'odds'
:
float
(
odds
),
'potential_winning'
:
float
(
detail
.
amount
)
*
float
(
odds
),
'result'
:
detail
.
result
,
'result'
:
detail
.
result
,
'match'
:
{
'match'
:
{
'match_number'
:
match
.
match_number
if
match
else
'Unknown'
,
'match_number'
:
match
.
match_number
if
match
else
'Unknown'
,
...
@@ -625,11 +633,19 @@ def cashier_bet_details(bet_id):
...
@@ -625,11 +633,19 @@ def cashier_bet_details(bet_id):
except
(
json
.
JSONDecodeError
,
TypeError
):
except
(
json
.
JSONDecodeError
,
TypeError
):
winning_outcomes
=
[]
winning_outcomes
=
[]
# Get odds for this outcome
odds
=
0.0
if
match
:
outcomes_dict
=
match
.
get_outcomes_dict
()
odds
=
outcomes_dict
.
get
(
detail
.
outcome
,
0.0
)
detail_dict
=
{
detail_dict
=
{
'id'
:
detail
.
id
,
'id'
:
detail
.
id
,
'match_id'
:
detail
.
match_id
,
'match_id'
:
detail
.
match_id
,
'outcome'
:
detail
.
outcome
,
'outcome'
:
detail
.
outcome
,
'amount'
:
float
(
detail
.
amount
),
'amount'
:
float
(
detail
.
amount
),
'odds'
:
float
(
odds
),
'potential_winning'
:
float
(
detail
.
amount
)
*
float
(
odds
),
'result'
:
detail
.
result
,
'result'
:
detail
.
result
,
'match'
:
{
'match'
:
{
'match_number'
:
match
.
match_number
if
match
else
'Unknown'
,
'match_number'
:
match
.
match_number
if
match
else
'Unknown'
,
...
@@ -4140,6 +4156,33 @@ def create_cashier_bet():
...
@@ -4140,6 +4156,33 @@ def create_cashier_bet():
)
)
session
.
add
(
bet_detail
)
session
.
add
(
bet_detail
)
# Generate and store barcode data if enabled
try
:
from
..utils.barcode_utils
import
format_bet_id_for_barcode
# Get barcode configuration
if
api_bp
.
db_manager
:
barcode_enabled
=
api_bp
.
db_manager
.
get_config_value
(
'barcode.enabled'
,
False
)
barcode_standard
=
api_bp
.
db_manager
.
get_config_value
(
'barcode.standard'
,
'none'
)
if
barcode_enabled
and
barcode_standard
!=
'none'
:
# Generate barcode data for the bet
barcode_data
=
format_bet_id_for_barcode
(
bet_uuid
,
barcode_standard
)
# Update the bet with barcode information
new_bet
.
barcode_standard
=
barcode_standard
new_bet
.
barcode_data
=
barcode_data
session
.
commit
()
logger
.
info
(
f
"Generated barcode data for bet {bet_uuid}: {barcode_standard} -> {barcode_data}"
)
else
:
logger
.
debug
(
f
"Barcode generation disabled or not configured for bet {bet_uuid}"
)
else
:
logger
.
warning
(
"Database manager not available for barcode generation"
)
except
Exception
as
barcode_e
:
logger
.
error
(
f
"Failed to generate barcode data for bet {bet_uuid}: {barcode_e}"
)
# Don't fail the bet creation if barcode generation fails
session
.
commit
()
session
.
commit
()
logger
.
info
(
f
"Created bet {bet_uuid} with {len(bet_details)} details"
)
logger
.
info
(
f
"Created bet {bet_uuid} with {len(bet_details)} details"
)
...
@@ -4449,7 +4492,7 @@ def verify_bet_details(bet_id):
...
@@ -4449,7 +4492,7 @@ def verify_bet_details(bet_id):
"""Get bet details for verification - no authentication required"""
"""Get bet details for verification - no authentication required"""
try
:
try
:
from
..database.models
import
BetModel
,
BetDetailModel
,
MatchModel
from
..database.models
import
BetModel
,
BetDetailModel
,
MatchModel
bet_uuid
=
str
(
bet_id
)
bet_uuid
=
str
(
bet_id
)
session
=
api_bp
.
db_manager
.
get_session
()
session
=
api_bp
.
db_manager
.
get_session
()
try
:
try
:
...
@@ -4464,12 +4507,84 @@ def verify_bet_details(bet_id):
...
@@ -4464,12 +4507,84 @@ def verify_bet_details(bet_id):
# Get bet details with match information
# Get bet details with match information
bet_details
=
session
.
query
(
BetDetailModel
)
.
filter_by
(
bet_id
=
bet_uuid
)
.
all
()
bet_details
=
session
.
query
(
BetDetailModel
)
.
filter_by
(
bet_id
=
bet_uuid
)
.
all
()
details_data
=
[]
details_data
=
[]
for
detail
in
bet_details
:
detail_data
=
detail
.
to_dict
()
# Get match information
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
if
match
:
detail_data
[
'match'
]
=
{
'match_number'
:
match
.
match_number
,
'fighter1_township'
:
match
.
fighter1_township
,
'fighter2_township'
:
match
.
fighter2_township
,
'venue_kampala_township'
:
match
.
venue_kampala_township
,
'status'
:
match
.
status
,
'result'
:
match
.
result
}
else
:
detail_data
[
'match'
]
=
None
details_data
.
append
(
detail_data
)
bet_data
[
'details'
]
=
details_data
bet_data
[
'details_count'
]
=
len
(
details_data
)
# Calculate total amount
total_amount
=
sum
(
float
(
detail
.
amount
)
for
detail
in
bet_details
)
bet_data
[
'total_amount'
]
=
total_amount
return
jsonify
({
"success"
:
True
,
"bet"
:
bet_data
})
finally
:
session
.
close
()
except
Exception
as
e
:
logger
.
error
(
f
"API verify bet details error: {e}"
)
return
jsonify
({
"error"
:
str
(
e
)}),
500
@
api_bp
.
route
(
'/verify-barcode'
)
def
verify_barcode
():
"""Get bet details for verification by barcode data - no authentication required"""
try
:
from
..database.models
import
BetModel
,
BetDetailModel
,
MatchModel
# Get barcode data from query parameters
barcode_data
=
request
.
args
.
get
(
'data'
,
''
)
.
strip
()
barcode_standard
=
request
.
args
.
get
(
'standard'
,
''
)
.
strip
()
if
not
barcode_data
:
return
jsonify
({
"error"
:
"Barcode data is required"
}),
400
session
=
api_bp
.
db_manager
.
get_session
()
try
:
# Look up bet by barcode data and standard
query
=
session
.
query
(
BetModel
)
.
filter_by
(
barcode_data
=
barcode_data
)
# If standard is provided, also filter by it
if
barcode_standard
:
query
=
query
.
filter_by
(
barcode_standard
=
barcode_standard
)
bet
=
query
.
first
()
if
not
bet
:
return
jsonify
({
"error"
:
"Bet not found for this barcode"
}),
404
bet_data
=
bet
.
to_dict
()
bet_data
[
'paid'
]
=
bet
.
paid
# Include paid status
# Get bet details with match information
bet_details
=
session
.
query
(
BetDetailModel
)
.
filter_by
(
bet_id
=
bet
.
uuid
)
.
all
()
details_data
=
[]
total_amount
=
0.0
total_amount
=
0.0
for
detail
in
bet_details
:
for
detail
in
bet_details
:
detail_data
=
detail
.
to_dict
()
detail_data
=
detail
.
to_dict
()
total_amount
+=
float
(
detail
.
amount
)
total_amount
+=
float
(
detail
.
amount
)
# Get match information
# Get match information
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
if
match
:
if
match
:
...
@@ -4498,7 +4613,7 @@ def verify_bet_details(bet_id):
...
@@ -4498,7 +4613,7 @@ def verify_bet_details(bet_id):
'cancelled'
:
0
,
'cancelled'
:
0
,
'winnings'
:
0.0
'winnings'
:
0.0
}
}
overall_status
=
'pending'
overall_status
=
'pending'
for
detail
in
bet_details
:
for
detail
in
bet_details
:
if
detail
.
result
==
'pending'
:
if
detail
.
result
==
'pending'
:
...
@@ -4510,7 +4625,7 @@ def verify_bet_details(bet_id):
...
@@ -4510,7 +4625,7 @@ def verify_bet_details(bet_id):
results
[
'lost'
]
+=
1
results
[
'lost'
]
+=
1
elif
detail
.
result
==
'cancelled'
:
elif
detail
.
result
==
'cancelled'
:
results
[
'cancelled'
]
+=
1
results
[
'cancelled'
]
+=
1
# Determine overall status
# Determine overall status
if
results
[
'pending'
]
==
0
:
if
results
[
'pending'
]
==
0
:
if
results
[
'won'
]
>
0
and
results
[
'lost'
]
==
0
:
if
results
[
'won'
]
>
0
and
results
[
'lost'
]
==
0
:
...
@@ -4519,7 +4634,7 @@ def verify_bet_details(bet_id):
...
@@ -4519,7 +4634,7 @@ def verify_bet_details(bet_id):
overall_status
=
'lost'
overall_status
=
'lost'
elif
results
[
'cancelled'
]
>
0
:
elif
results
[
'cancelled'
]
>
0
:
overall_status
=
'cancelled'
overall_status
=
'cancelled'
bet_data
[
'overall_status'
]
=
overall_status
bet_data
[
'overall_status'
]
=
overall_status
bet_data
[
'results'
]
=
results
bet_data
[
'results'
]
=
results
...
...
mbetterclient/web_dashboard/templates/dashboard/admin_bet_details.html
View file @
27663e61
This diff is collapsed.
Click to expand it.
mbetterclient/web_dashboard/templates/dashboard/bet_details.html
View file @
27663e61
This diff is collapsed.
Click to expand it.
mbetterclient/web_dashboard/templates/dashboard/verify_bet.html
View file @
27663e61
...
@@ -501,14 +501,15 @@ function handleBarcodeInput(event) {
...
@@ -501,14 +501,15 @@ function handleBarcodeInput(event) {
function
processBarcodeInput
()
{
function
processBarcodeInput
()
{
const
input
=
document
.
getElementById
(
'barcode-input'
);
const
input
=
document
.
getElementById
(
'barcode-input'
);
const
barcodeData
=
input
.
value
.
trim
();
const
barcodeData
=
input
.
value
.
trim
();
if
(
!
barcodeData
)
{
if
(
!
barcodeData
)
{
showScannerStatus
(
'Please enter a barcode value'
,
'warning'
);
showScannerStatus
(
'Please enter a barcode value'
,
'warning'
);
return
;
return
;
}
}
console
.
log
(
'Barcode input detected:'
,
barcodeData
);
console
.
log
(
'Barcode input detected:'
,
barcodeData
);
handleCodeDetected
(
barcodeData
,
'Barcode'
);
// For manual barcode input, we don't know the standard, so try to verify directly
verifyBarcode
(
barcodeData
);
}
}
function
extractUuidFromBarcode
(
barcodeData
)
{
function
extractUuidFromBarcode
(
barcodeData
)
{
...
@@ -550,6 +551,29 @@ function verifyBet(betUuid) {
...
@@ -550,6 +551,29 @@ function verifyBet(betUuid) {
});
});
}
}
function
verifyBarcode
(
barcodeData
,
barcodeStandard
=
null
)
{
const
params
=
new
URLSearchParams
({
data
:
barcodeData
});
if
(
barcodeStandard
)
{
params
.
append
(
'standard'
,
barcodeStandard
);
}
fetch
(
`/api/verify-barcode?
${
params
}
`
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
displayBetDetails
(
data
.
bet
);
}
else
{
showScannerStatus
(
'Bet not found: '
+
(
data
.
error
||
'Unknown error'
),
'danger'
);
}
})
.
catch
(
error
=>
{
console
.
error
(
'Error verifying barcode:'
,
error
);
showScannerStatus
(
'Error verifying barcode: '
+
error
.
message
,
'danger'
);
});
}
function
displayBetDetails
(
bet
)
{
function
displayBetDetails
(
bet
)
{
const
modalContent
=
document
.
getElementById
(
'bet-details-content'
);
const
modalContent
=
document
.
getElementById
(
'bet-details-content'
);
...
...
mbetterclient/web_dashboard/templates/dashboard/verify_bet_mobile.html
View file @
27663e61
...
@@ -482,14 +482,15 @@
...
@@ -482,14 +482,15 @@
function
processBarcodeInput
()
{
function
processBarcodeInput
()
{
const
input
=
document
.
getElementById
(
'barcode-input'
);
const
input
=
document
.
getElementById
(
'barcode-input'
);
const
barcodeData
=
input
.
value
.
trim
();
const
barcodeData
=
input
.
value
.
trim
();
if
(
!
barcodeData
)
{
if
(
!
barcodeData
)
{
showScannerStatus
(
'Please enter a barcode value'
,
'warning'
);
showScannerStatus
(
'Please enter a barcode value'
,
'warning'
);
return
;
return
;
}
}
console
.
log
(
'Barcode input detected:'
,
barcodeData
);
console
.
log
(
'Barcode input detected:'
,
barcodeData
);
handleCodeDetected
(
barcodeData
,
'Barcode'
);
// For manual barcode input, we don't know the standard, so try to verify directly
verifyBarcode
(
barcodeData
);
}
}
function
extractUuidFromBarcode
(
barcodeData
)
{
function
extractUuidFromBarcode
(
barcodeData
)
{
...
@@ -531,6 +532,29 @@
...
@@ -531,6 +532,29 @@
});
});
}
}
function
verifyBarcode
(
barcodeData
,
barcodeStandard
=
null
)
{
const
params
=
new
URLSearchParams
({
data
:
barcodeData
});
if
(
barcodeStandard
)
{
params
.
append
(
'standard'
,
barcodeStandard
);
}
fetch
(
`/api/verify-barcode?
${
params
}
`
)
.
then
(
response
=>
response
.
json
())
.
then
(
data
=>
{
if
(
data
.
success
)
{
displayBetDetails
(
data
.
bet
);
}
else
{
showScannerStatus
(
'Bet not found: '
+
(
data
.
error
||
'Unknown error'
),
'danger'
);
}
})
.
catch
(
error
=>
{
console
.
error
(
'Error verifying barcode:'
,
error
);
showScannerStatus
(
'Error verifying barcode. Please check your connection.'
,
'danger'
);
});
}
function
displayBetDetails
(
bet
)
{
function
displayBetDetails
(
bet
)
{
const
modalContent
=
document
.
getElementById
(
'bet-details-content'
);
const
modalContent
=
document
.
getElementById
(
'bet-details-content'
);
...
...
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