feat: add the bet winning to user wallet

This commit is contained in:
Samuel Tariku 2025-05-30 19:38:59 +03:00
parent 95eaed18ad
commit 542400bcaf
21 changed files with 291 additions and 72 deletions

View File

@ -47,6 +47,7 @@ CREATE TABLE IF NOT EXISTS bets (
status INT NOT NULL, status INT NOT NULL,
full_name VARCHAR(255) NOT NULL, full_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL,
company_id BIGINT,
branch_id BIGINT, branch_id BIGINT,
user_id BIGINT, user_id BIGINT,
cashed_out BOOLEAN DEFAULT FALSE NOT NULL, cashed_out BOOLEAN DEFAULT FALSE NOT NULL,
@ -341,7 +342,8 @@ INSERT INTO users (
created_at, created_at,
updated_at, updated_at,
suspended_at, suspended_at,
suspended suspended,
company_id
) )
VALUES ( VALUES (
'Test', 'Test',
@ -355,7 +357,8 @@ VALUES (
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP,
NULL, NULL,
FALSE FALSE,
1
); );
INSERT INTO users ( INSERT INTO users (
first_name, first_name,

View File

@ -8,9 +8,10 @@ INSERT INTO bets (
branch_id, branch_id,
user_id, user_id,
is_shop_bet, is_shop_bet,
cashout_id cashout_id,
company_id
) )
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *; RETURNING *;
-- name: CreateBetOutcome :copyfrom -- name: CreateBetOutcome :copyfrom
INSERT INTO bet_outcomes ( INSERT INTO bet_outcomes (
@ -45,7 +46,19 @@ VALUES (
); );
-- name: GetAllBets :many -- name: GetAllBets :many
SELECT * SELECT *
FROM bet_with_outcomes; FROM bet_with_outcomes
wHERE (
branch_id = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
AND (
user_id = $3
OR $3 IS NULL
);
-- name: GetBetByID :one -- name: GetBetByID :one
SELECT * SELECT *
FROM bet_with_outcomes FROM bet_with_outcomes

View File

@ -42,7 +42,19 @@ VALUES (
RETURNING *; RETURNING *;
-- name: GetAllTransactions :many -- name: GetAllTransactions :many
SELECT * SELECT *
FROM transactions; FROM transactions
wHERE (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
cashier_id = sqlc.narg('cashier_id')
OR sqlc.narg('cashier_id') IS NULL
);
-- name: GetTransactionByID :one -- name: GetTransactionByID :one
SELECT * SELECT *
FROM transactions FROM transactions

View File

@ -21,10 +21,11 @@ INSERT INTO bets (
branch_id, branch_id,
user_id, user_id,
is_shop_bet, is_shop_bet,
cashout_id cashout_id,
company_id
) )
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet RETURNING id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet
` `
type CreateBetParams struct { type CreateBetParams struct {
@ -37,6 +38,7 @@ type CreateBetParams struct {
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
IsShopBet bool `json:"is_shop_bet"` IsShopBet bool `json:"is_shop_bet"`
CashoutID string `json:"cashout_id"` CashoutID string `json:"cashout_id"`
CompanyID pgtype.Int8 `json:"company_id"`
} }
func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, error) { func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, error) {
@ -50,6 +52,7 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
arg.UserID, arg.UserID,
arg.IsShopBet, arg.IsShopBet,
arg.CashoutID, arg.CashoutID,
arg.CompanyID,
) )
var i Bet var i Bet
err := row.Scan( err := row.Scan(
@ -59,6 +62,7 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
&i.Status, &i.Status,
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.CompanyID,
&i.BranchID, &i.BranchID,
&i.UserID, &i.UserID,
&i.CashedOut, &i.CashedOut,
@ -107,12 +111,30 @@ func (q *Queries) DeleteBetOutcome(ctx context.Context, betID int64) error {
} }
const GetAllBets = `-- name: GetAllBets :many const GetAllBets = `-- name: GetAllBets :many
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes SELECT id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes
FROM bet_with_outcomes FROM bet_with_outcomes
wHERE (
branch_id = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
AND (
user_id = $3
OR $3 IS NULL
)
` `
func (q *Queries) GetAllBets(ctx context.Context) ([]BetWithOutcome, error) { type GetAllBetsParams struct {
rows, err := q.db.Query(ctx, GetAllBets) BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
UserID pgtype.Int8 `json:"user_id"`
}
func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWithOutcome, error) {
rows, err := q.db.Query(ctx, GetAllBets, arg.BranchID, arg.CompanyID, arg.UserID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -127,6 +149,7 @@ func (q *Queries) GetAllBets(ctx context.Context) ([]BetWithOutcome, error) {
&i.Status, &i.Status,
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.CompanyID,
&i.BranchID, &i.BranchID,
&i.UserID, &i.UserID,
&i.CashedOut, &i.CashedOut,
@ -147,7 +170,7 @@ func (q *Queries) GetAllBets(ctx context.Context) ([]BetWithOutcome, error) {
} }
const GetBetByBranchID = `-- name: GetBetByBranchID :many const GetBetByBranchID = `-- name: GetBetByBranchID :many
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes SELECT id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE branch_id = $1 WHERE branch_id = $1
` `
@ -168,6 +191,7 @@ func (q *Queries) GetBetByBranchID(ctx context.Context, branchID pgtype.Int8) ([
&i.Status, &i.Status,
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.CompanyID,
&i.BranchID, &i.BranchID,
&i.UserID, &i.UserID,
&i.CashedOut, &i.CashedOut,
@ -188,7 +212,7 @@ func (q *Queries) GetBetByBranchID(ctx context.Context, branchID pgtype.Int8) ([
} }
const GetBetByCashoutID = `-- name: GetBetByCashoutID :one const GetBetByCashoutID = `-- name: GetBetByCashoutID :one
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes SELECT id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE cashout_id = $1 WHERE cashout_id = $1
` `
@ -203,6 +227,7 @@ func (q *Queries) GetBetByCashoutID(ctx context.Context, cashoutID string) (BetW
&i.Status, &i.Status,
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.CompanyID,
&i.BranchID, &i.BranchID,
&i.UserID, &i.UserID,
&i.CashedOut, &i.CashedOut,
@ -216,7 +241,7 @@ func (q *Queries) GetBetByCashoutID(ctx context.Context, cashoutID string) (BetW
} }
const GetBetByID = `-- name: GetBetByID :one const GetBetByID = `-- name: GetBetByID :one
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes SELECT id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE id = $1 WHERE id = $1
` `
@ -231,6 +256,7 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
&i.Status, &i.Status,
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.CompanyID,
&i.BranchID, &i.BranchID,
&i.UserID, &i.UserID,
&i.CashedOut, &i.CashedOut,
@ -244,7 +270,7 @@ func (q *Queries) GetBetByID(ctx context.Context, id int64) (BetWithOutcome, err
} }
const GetBetByUserID = `-- name: GetBetByUserID :many const GetBetByUserID = `-- name: GetBetByUserID :many
SELECT id, amount, total_odds, status, full_name, phone_number, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes SELECT id, amount, total_odds, status, full_name, phone_number, company_id, branch_id, user_id, cashed_out, cashout_id, created_at, updated_at, is_shop_bet, outcomes
FROM bet_with_outcomes FROM bet_with_outcomes
WHERE user_id = $1 WHERE user_id = $1
` `
@ -265,6 +291,7 @@ func (q *Queries) GetBetByUserID(ctx context.Context, userID pgtype.Int8) ([]Bet
&i.Status, &i.Status,
&i.FullName, &i.FullName,
&i.PhoneNumber, &i.PhoneNumber,
&i.CompanyID,
&i.BranchID, &i.BranchID,
&i.UserID, &i.UserID,
&i.CashedOut, &i.CashedOut,

View File

@ -62,6 +62,7 @@ type Bet struct {
Status int32 `json:"status"` Status int32 `json:"status"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"` PhoneNumber string `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
BranchID pgtype.Int8 `json:"branch_id"` BranchID pgtype.Int8 `json:"branch_id"`
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
CashedOut bool `json:"cashed_out"` CashedOut bool `json:"cashed_out"`
@ -96,6 +97,7 @@ type BetWithOutcome struct {
Status int32 `json:"status"` Status int32 `json:"status"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"` PhoneNumber string `json:"phone_number"`
CompanyID pgtype.Int8 `json:"company_id"`
BranchID pgtype.Int8 `json:"branch_id"` BranchID pgtype.Int8 `json:"branch_id"`
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
CashedOut bool `json:"cashed_out"` CashedOut bool `json:"cashed_out"`
@ -382,6 +384,32 @@ type User struct {
ReferredBy pgtype.Text `json:"referred_by"` ReferredBy pgtype.Text `json:"referred_by"`
} }
type UserGameInteraction struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
GameID int64 `json:"game_id"`
InteractionType string `json:"interaction_type"`
Amount pgtype.Numeric `json:"amount"`
DurationSeconds pgtype.Int4 `json:"duration_seconds"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type VirtualGame struct {
ID int64 `json:"id"`
Name string `json:"name"`
Provider string `json:"provider"`
Category string `json:"category"`
MinBet pgtype.Numeric `json:"min_bet"`
MaxBet pgtype.Numeric `json:"max_bet"`
Volatility string `json:"volatility"`
Rtp pgtype.Numeric `json:"rtp"`
IsFeatured pgtype.Bool `json:"is_featured"`
PopularityScore pgtype.Int4 `json:"popularity_score"`
ThumbnailUrl pgtype.Text `json:"thumbnail_url"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type VirtualGameSession struct { type VirtualGameSession struct {
ID int64 `json:"id"` ID int64 `json:"id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`

View File

@ -130,10 +130,28 @@ func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionPa
const GetAllTransactions = `-- name: GetAllTransactions :many const GetAllTransactions = `-- name: GetAllTransactions :many
SELECT id, amount, branch_id, company_id, cashier_id, cashier_name, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, approved_by, approver_name, branch_location, branch_name, created_at, updated_at SELECT id, amount, branch_id, company_id, cashier_id, cashier_name, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, approved_by, approver_name, branch_location, branch_name, created_at, updated_at
FROM transactions FROM transactions
wHERE (
branch_id = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
AND (
cashier_id = $3
OR $3 IS NULL
)
` `
func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error) { type GetAllTransactionsParams struct {
rows, err := q.db.Query(ctx, GetAllTransactions) BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
}
func (q *Queries) GetAllTransactions(ctx context.Context, arg GetAllTransactionsParams) ([]Transaction, error) {
rows, err := q.db.Query(ctx, GetAllTransactions, arg.BranchID, arg.CompanyID, arg.CashierID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -48,6 +48,7 @@ type Bet struct {
FullName string FullName string
PhoneNumber string PhoneNumber string
BranchID ValidInt64 // Can Be Nullable BranchID ValidInt64 // Can Be Nullable
CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable UserID ValidInt64 // Can Be Nullable
IsShopBet bool IsShopBet bool
CashedOut bool CashedOut bool
@ -55,6 +56,12 @@ type Bet struct {
CreatedAt time.Time CreatedAt time.Time
} }
type BetFilter struct {
BranchID ValidInt64 // Can Be Nullable
CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable
}
type GetBet struct { type GetBet struct {
ID int64 ID int64
Amount Currency Amount Currency
@ -63,6 +70,7 @@ type GetBet struct {
FullName string FullName string
PhoneNumber string PhoneNumber string
BranchID ValidInt64 // Can Be Nullable BranchID ValidInt64 // Can Be Nullable
CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable UserID ValidInt64 // Can Be Nullable
IsShopBet bool IsShopBet bool
CashedOut bool CashedOut bool
@ -77,6 +85,7 @@ type CreateBet struct {
Status OutcomeStatus Status OutcomeStatus
FullName string FullName string
PhoneNumber string PhoneNumber string
CompanyID ValidInt64 // Can Be Nullable
BranchID ValidInt64 // Can Be Nullable BranchID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable UserID ValidInt64 // Can Be Nullable
IsShopBet bool IsShopBet bool
@ -164,3 +173,4 @@ func ConvertBet(bet GetBet) BetRes {
CreatedAt: bet.CreatedAt, CreatedAt: bet.CreatedAt,
} }
} }

View File

@ -47,3 +47,14 @@ func (m Currency) String() string {
x = x / 100 x = x / 100
return fmt.Sprintf("$%.2f", x) return fmt.Sprintf("$%.2f", x)
} }
func CalculateWinnings(amount Currency, totalOdds float32) Currency {
vat := amount.Float32() * 0.15
stakeAfterVat := amount.Float32() - vat
possibleWin := stakeAfterVat * totalOdds
incomeTax := possibleWin * 0.15
return ToCurrency(possibleWin - incomeTax)
}

View File

@ -47,6 +47,12 @@ type Transaction struct {
UpdatedAt time.Time UpdatedAt time.Time
CreatedAt time.Time CreatedAt time.Time
} }
type TransactionFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
CashierID ValidInt64
}
type CreateTransaction struct { type CreateTransaction struct {
Amount Currency Amount Currency
BranchID int64 BranchID int64

View File

@ -22,6 +22,10 @@ func convertDBBet(bet dbgen.Bet) domain.Bet {
Value: bet.BranchID.Int64, Value: bet.BranchID.Int64,
Valid: bet.BranchID.Valid, Valid: bet.BranchID.Valid,
}, },
CompanyID: domain.ValidInt64{
Value: bet.CompanyID.Int64,
Valid: bet.CompanyID.Valid,
},
UserID: domain.ValidInt64{ UserID: domain.ValidInt64{
Value: bet.UserID.Int64, Value: bet.UserID.Int64,
Valid: bet.UserID.Valid, Valid: bet.UserID.Valid,
@ -111,6 +115,7 @@ func convertCreateBet(bet domain.CreateBet) dbgen.CreateBetParams {
Status: int32(bet.Status), Status: int32(bet.Status),
FullName: bet.FullName, FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber, PhoneNumber: bet.PhoneNumber,
BranchID: pgtype.Int8{ BranchID: pgtype.Int8{
Int64: bet.BranchID.Value, Int64: bet.BranchID.Value,
Valid: bet.BranchID.Valid, Valid: bet.BranchID.Valid,
@ -168,8 +173,21 @@ func (s *Store) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet
return convertDBBetWithOutcomes(bet), nil return convertDBBetWithOutcomes(bet), nil
} }
func (s *Store) GetAllBets(ctx context.Context) ([]domain.GetBet, error) { func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, error) {
bets, err := s.queries.GetAllBets(ctx) bets, err := s.queries.GetAllBets(ctx, dbgen.GetAllBetsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
},
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
UserID: pgtype.Int8{
Int64: filter.UserID.Value,
Valid: filter.UserID.Valid,
},
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -84,8 +84,21 @@ func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transa
return convertDBTransaction(transaction), nil return convertDBTransaction(transaction), nil
} }
func (s *Store) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) { func (s *Store) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error) {
transaction, err := s.queries.GetAllTransactions(ctx) transaction, err := s.queries.GetAllTransactions(ctx, dbgen.GetAllTransactionsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
},
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
CashierID: pgtype.Int8{
Int64: filter.CashierID.Value,
Valid: filter.CashierID.Valid,
},
})
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -11,7 +11,7 @@ type BetStore interface {
CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBetOutcome) (int64, error)
GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error)
GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error)
GetAllBets(ctx context.Context) ([]domain.GetBet, error) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, error)
GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain.GetBet, error) GetBetByBranchID(ctx context.Context, BranchID int64) ([]domain.GetBet, error)
GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetBet, error)
GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]domain.BetOutcome, error) GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]domain.BetOutcome, error)

View File

@ -197,6 +197,11 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
Value: branch.ID, Value: branch.ID,
Valid: true, Valid: true,
} }
newBet.CompanyID = domain.ValidInt64{
Value: branch.CompanyID,
Valid: true,
}
newBet.UserID = domain.ValidInt64{ newBet.UserID = domain.ValidInt64{
Value: userID, Value: userID,
Valid: true, Valid: true,
@ -227,6 +232,10 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
Value: branch.ID, Value: branch.ID,
Valid: true, Valid: true,
} }
newBet.CompanyID = domain.ValidInt64{
Value: branch.CompanyID,
Valid: true,
}
newBet.UserID = domain.ValidInt64{ newBet.UserID = domain.ValidInt64{
Value: userID, Value: userID,
Valid: true, Valid: true,
@ -483,8 +492,8 @@ func (s *Service) GetBetByID(ctx context.Context, id int64) (domain.GetBet, erro
func (s *Service) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) { func (s *Service) GetBetByCashoutID(ctx context.Context, id string) (domain.GetBet, error) {
return s.betStore.GetBetByCashoutID(ctx, id) return s.betStore.GetBetByCashoutID(ctx, id)
} }
func (s *Service) GetAllBets(ctx context.Context) ([]domain.GetBet, error) { func (s *Service) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, error) {
return s.betStore.GetAllBets(ctx) return s.betStore.GetAllBets(ctx, filter)
} }
func (s *Service) GetBetByBranchID(ctx context.Context, branchID int64) ([]domain.GetBet, error) { func (s *Service) GetBetByBranchID(ctx context.Context, branchID int64) ([]domain.GetBet, error) {
@ -500,6 +509,41 @@ func (s *Service) UpdateCashOut(ctx context.Context, id int64, cashedOut bool) e
} }
func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error { func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error {
bet, err := s.GetBetByID(ctx, id)
if err != nil {
s.logger.Error("Failed to update bet status. Invalid bet id")
return err
}
if bet.IsShopBet ||
status == domain.OUTCOME_STATUS_ERROR ||
status == domain.OUTCOME_STATUS_PENDING ||
status == domain.OUTCOME_STATUS_LOSS {
return s.betStore.UpdateStatus(ctx, id, status)
}
customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, id)
if err != nil {
s.logger.Error("Failed to update bet status. Invalid customer wallet id")
return err
}
var amount domain.Currency
if status == domain.OUTCOME_STATUS_WIN {
amount = domain.CalculateWinnings(bet.Amount, bet.TotalOdds)
} else if status == domain.OUTCOME_STATUS_HALF {
amount = (domain.CalculateWinnings(bet.Amount, bet.TotalOdds)) / 2
} else {
amount = bet.Amount
}
err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount)
if err != nil {
s.logger.Error("Failed to update bet status. Failed to update user wallet")
return err
}
return s.betStore.UpdateStatus(ctx, id, status) return s.betStore.UpdateStatus(ctx, id, status)
} }

View File

@ -9,7 +9,7 @@ import (
type TransactionStore interface { type TransactionStore interface {
CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error)
GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error)
GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error)
GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error)
UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error
} }

View File

@ -22,8 +22,8 @@ func (s *Service) CreateTransaction(ctx context.Context, transaction domain.Crea
func (s *Service) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) { func (s *Service) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
return s.transactionStore.GetTransactionByID(ctx, id) return s.transactionStore.GetTransactionByID(ctx, id)
} }
func (s *Service) GetAllTransactions(ctx context.Context) ([]domain.Transaction, error) { func (s *Service) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error) {
return s.transactionStore.GetAllTransactions(ctx) return s.transactionStore.GetAllTransactions(ctx, filter)
} }
func (s *Service) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) { func (s *Service) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
return s.transactionStore.GetTransactionByBranch(ctx, id) return s.transactionStore.GetTransactionByBranch(ctx, id)

View File

@ -21,22 +21,22 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
spec string spec string
task func() task func()
}{ }{
{ // {
spec: "0 0 * * * *", // Every 1 hour // spec: "0 0 * * * *", // Every 1 hour
task: func() { // task: func() {
if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { // if err := eventService.FetchUpcomingEvents(context.Background()); err != nil {
log.Printf("FetchUpcomingEvents error: %v", err) // log.Printf("FetchUpcomingEvents error: %v", err)
} // }
}, // },
}, // },
{ // {
spec: "0 */15 * * * *", // Every 15 minutes // spec: "0 */15 * * * *", // Every 15 minutes
task: func() { // task: func() {
if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil { // if err := oddsService.FetchNonLiveOdds(context.Background()); err != nil {
log.Printf("FetchNonLiveOdds error: %v", err) // log.Printf("FetchNonLiveOdds error: %v", err)
} // }
}, // },
}, // },
{ {
spec: "0 */15 * * * *", // Every 15 Minutes spec: "0 */15 * * * *", // Every 15 Minutes
task: func() { task: func() {

View File

@ -150,7 +150,13 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
// @Router /bet [get] // @Router /bet [get]
func (h *Handler) GetAllBet(c *fiber.Ctx) error { func (h *Handler) GetAllBet(c *fiber.Ctx) error {
bets, err := h.betSvc.GetAllBets(c.Context()) companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
BranchID: branchID,
CompanyID: companyID,
})
if err != nil { if err != nil {
h.logger.Error("Failed to get bets", "error", err) h.logger.Error("Failed to get bets", "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets") return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets")

View File

@ -37,7 +37,7 @@ type CreateCashierReq struct {
func (h *Handler) CreateCashier(c *fiber.Ctx) error { func (h *Handler) CreateCashier(c *fiber.Ctx) error {
// Get user_id from middleware // Get user_id from middleware
companyID := c.Locals("company_id").(domain.ValidInt64) // companyID := c.Locals("company_id").(domain.ValidInt64)
var req CreateCashierReq var req CreateCashierReq
if err := c.BodyParser(&req); err != nil { if err := c.BodyParser(&req); err != nil {
@ -48,6 +48,13 @@ func (h *Handler) CreateCashier(c *fiber.Ctx) error {
if !ok { if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
} }
// Cashiers inherit the company id from the branch id
branch, err := h.branchSvc.GetBranchByID(c.Context(), req.BranchID)
if err != nil {
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID is invalid", nil, nil)
}
userRequest := domain.CreateUserReq{ userRequest := domain.CreateUserReq{
FirstName: req.FirstName, FirstName: req.FirstName,
LastName: req.LastName, LastName: req.LastName,
@ -56,7 +63,10 @@ func (h *Handler) CreateCashier(c *fiber.Ctx) error {
Password: req.Password, Password: req.Password,
Role: string(domain.RoleCashier), Role: string(domain.RoleCashier),
Suspended: req.Suspended, Suspended: req.Suspended,
CompanyID: companyID, CompanyID: domain.ValidInt64{
Value: branch.CompanyID,
Valid: true,
},
} }
fmt.Print(req.Suspended) fmt.Print(req.Suspended)
newUser, err := h.userSvc.CreateUser(c.Context(), userRequest, true) newUser, err := h.userSvc.CreateUser(c.Context(), userRequest, true)

View File

@ -225,35 +225,18 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
// @Router /transaction [get] // @Router /transaction [get]
func (h *Handler) GetAllTransactions(c *fiber.Ctx) error { func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// Get user_id from middleware // Get user_id from middleware
userID := c.Locals("user_id").(int64) // userID := c.Locals("user_id").(int64)
// role := c.Locals("role").(domain.Role)
// Fetch user details companyID := c.Locals("company_id").(domain.ValidInt64)
user, err := h.userSvc.GetUserByID(c.Context(), userID) branchID := c.Locals("branch_id").(domain.ValidInt64)
if err != nil {
h.logger.Error("Failed to fetch user details", "user_id", userID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve user details", err, nil)
}
var transactions []domain.Transaction var transactions []domain.Transaction
// Check user role and fetch transactions accordingly // Check user role and fetch transactions accordingly
// TODO: filtering by the user role transactions, err := h.transactionSvc.GetAllTransactions(c.Context(), domain.TransactionFilter{
switch user.Role { CompanyID: companyID,
case domain.RoleSuperAdmin: BranchID: branchID,
// Admin can fetch all transactions })
transactions, err = h.transactionSvc.GetAllTransactions(c.Context())
case domain.RoleAdmin:
// Admins can fetch transaction for company branches
transactions, err = h.transactionSvc.GetAllTransactions(c.Context())
case domain.RoleBranchManager, domain.RoleCashier:
// Branch Manager or Cashier can fetch transactions for their branches
// transactions, err = transactionSvc.GetTransactionByBranch(c.Context(), user.BranchID)
transactions, err = h.transactionSvc.GetAllTransactions(c.Context())
default:
// Unauthorized role
return response.WriteJSON(c, fiber.StatusForbidden, "Unauthorized", nil, nil)
}
if err != nil { if err != nil {
h.logger.Error("Failed to get transactions", "error", err) h.logger.Error("Failed to get transactions", "error", err)

View File

@ -53,6 +53,22 @@ func (a *App) authMiddleware(c *fiber.Ctx) error {
c.Locals("company_id", claim.CompanyID) c.Locals("company_id", claim.CompanyID)
c.Locals("refresh_token", refreshToken) c.Locals("refresh_token", refreshToken)
var branchID domain.ValidInt64
if claim.Role == domain.RoleCashier {
branch, err := a.branchSvc.GetBranchByCashier(c.Context(), claim.UserId)
if err != nil {
a.logger.Error("Failed to get branch id for bet", "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to branch id for bet")
}
branchID = domain.ValidInt64{
Value: branch.ID,
Valid: true,
}
}
c.Locals("branch_id", branchID)
return c.Next() return c.Next()
} }

View File

@ -49,6 +49,7 @@ db-up:
.PHONY: db-down .PHONY: db-down
db-down: db-down:
@docker volume rm fortunebet-backend_postgres_data
@docker compose down @docker compose down
postgres: postgres:
@docker exec -it fortunebet-backend-postgres-1 psql -U root -d gh @docker exec -it fortunebet-backend-postgres-1 psql -U root -d gh