feat: bet and branch filters, admin company, customer wallet

This commit is contained in:
Samuel Tariku 2025-06-25 22:47:06 +03:00
parent 53ef3ee1f0
commit 4d5c90ab05
26 changed files with 615 additions and 117 deletions

View File

@ -286,7 +286,8 @@ CREATE VIEW branch_details AS
SELECT branches.*, SELECT branches.*,
CONCAT(users.first_name, ' ', users.last_name) AS manager_name, CONCAT(users.first_name, ' ', users.last_name) AS manager_name,
users.phone_number AS manager_phone_number, users.phone_number AS manager_phone_number,
wallets.balance wallets.balance,
wallets.is_active AS wallet_is_active
FROM branches FROM branches
LEFT JOIN users ON branches.branch_manager_id = users.id LEFT JOIN users ON branches.branch_manager_id = users.id
LEFT JOin wallets ON wallets.id = branches.wallet_id; LEFT JOin wallets ON wallets.id = branches.wallet_id;
@ -307,6 +308,25 @@ SELECT tickets.*,
FROM tickets FROM tickets
LEFT JOIN ticket_outcomes ON tickets.id = ticket_outcomes.ticket_id LEFT JOIN ticket_outcomes ON tickets.id = ticket_outcomes.ticket_id
GROUP BY tickets.id; GROUP BY tickets.id;
CREATE VIEW customer_wallet_details AS
SELECT cw.id,
cw.customer_id,
rw.id AS regular_id,
rw.balance AS regular_balance,
sw.id AS static_id,
sw.balance AS static_balance,
rw.is_active as regular_is_active,
sw.is_active as static_is_active,
rw.updated_at as regular_updated_at,
sw.updated_at as static_updated_at,
cw.created_at,
users.first_name,
users.last_name,
users.phone_number
FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id
JOIN users ON users.id = cw.customer_id;
-- Foreign Keys -- Foreign Keys
ALTER TABLE users ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email), ADD CONSTRAINT unique_email UNIQUE (email),

View File

@ -63,6 +63,19 @@ wHERE (
AND ( AND (
is_shop_bet = sqlc.narg('is_shop_bet') is_shop_bet = sqlc.narg('is_shop_bet')
OR sqlc.narg('is_shop_bet') IS NULL OR sqlc.narg('is_shop_bet') IS NULL
)
AND (
full_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
created_at > sqlc.narg('created_before')
OR sqlc.narg('created_before') IS NULL
)
AND (
created_at < sqlc.narg('created_after')
OR sqlc.narg('created_after') IS NULL
); );
-- name: GetBetByID :one -- name: GetBetByID :one
SELECT * SELECT *
@ -83,7 +96,13 @@ WHERE user_id = $1;
-- name: GetBetOutcomeByEventID :many -- name: GetBetOutcomeByEventID :many
SELECT * SELECT *
FROM bet_outcomes FROM bet_outcomes
WHERE event_id = $1; WHERE (event_id = $1)
AND (
status = sqlc.narg('filter_status')
OR sqlc.narg('filter_status') IS NULL
OR status = sqlc.narg('filter_status_2')
OR sqlc.narg('filter_status_2') IS NULL
);
-- name: GetBetOutcomeByBetID :many -- name: GetBetOutcomeByBetID :many
SELECT * SELECT *
FROM bet_outcomes FROM bet_outcomes
@ -103,6 +122,11 @@ UPDATE bet_outcomes
SET status = $1 SET status = $1
WHERE id = $2 WHERE id = $2
RETURNING *; RETURNING *;
-- name: UpdateBetOutcomeStatusByBetID :one
UPDATE bet_outcomes
SET status = $1
WHERE bet_id = $2
RETURNING *;
-- name: UpdateBetOutcomeStatusForEvent :many -- name: UpdateBetOutcomeStatusForEvent :many
UPDATE bet_outcomes UPDATE bet_outcomes
SEt status = $1 SEt status = $1
@ -119,5 +143,3 @@ WHERE id = $1;
-- name: DeleteBetOutcome :exec -- name: DeleteBetOutcome :exec
DELETE FROM bet_outcomes DELETE FROM bet_outcomes
WHERE bet_id = $1; WHERE bet_id = $1;

View File

@ -31,6 +31,23 @@ WHERE (
AND ( AND (
is_active = sqlc.narg('is_active') is_active = sqlc.narg('is_active')
OR sqlc.narg('is_active') IS NULL OR sqlc.narg('is_active') IS NULL
)
AND (
branch_manager_id = sqlc.narg('branch_manager_id')
OR sqlc.narg('branch_manager_id') IS NULL
)
AND (
name ILIKE '%' || sqlc.narg('query') || '%'
OR location ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
created_at > sqlc.narg('created_before')
OR sqlc.narg('created_before') IS NULL
)
AND (
created_at < sqlc.narg('created_after')
OR sqlc.narg('created_after') IS NULL
); );
-- name: GetBranchByID :one -- name: GetBranchByID :one
SELECT * SELECT *

View File

@ -26,20 +26,13 @@ WHERE id = $1;
SELECT * SELECT *
FROM wallets FROM wallets
WHERE user_id = $1; WHERE user_id = $1;
-- name: GetAllCustomerWallet :many
SELECT *
FROM customer_wallet_details;
-- name: GetCustomerWallet :one -- name: GetCustomerWallet :one
SELECT cw.id, SELECT *
cw.customer_id, FROM customer_wallet_details
rw.id AS regular_id, WHERE customer_id = $1;
rw.balance AS regular_balance,
sw.id AS static_id,
sw.balance AS static_balance,
rw.updated_at as regular_updated_at,
sw.updated_at as static_updated_at,
cw.created_at
FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id
WHERE cw.customer_id = $1;
-- name: GetAllBranchWallets :many -- name: GetAllBranchWallets :many
SELECT wallets.id, SELECT wallets.id,
wallets.balance, wallets.balance,

View File

@ -133,6 +133,19 @@ wHERE (
is_shop_bet = $4 is_shop_bet = $4
OR $4 IS NULL OR $4 IS NULL
) )
AND (
full_name ILIKE '%' || $5 || '%'
OR phone_number ILIKE '%' || $5 || '%'
OR $5 IS NULL
)
AND (
created_at > $6
OR $6 IS NULL
)
AND (
created_at < $7
OR $7 IS NULL
)
` `
type GetAllBetsParams struct { type GetAllBetsParams struct {
@ -140,6 +153,9 @@ type GetAllBetsParams struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
UserID pgtype.Int8 `json:"user_id"` UserID pgtype.Int8 `json:"user_id"`
IsShopBet pgtype.Bool `json:"is_shop_bet"` IsShopBet pgtype.Bool `json:"is_shop_bet"`
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
} }
func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWithOutcome, error) { func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWithOutcome, error) {
@ -148,6 +164,9 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
arg.CompanyID, arg.CompanyID,
arg.UserID, arg.UserID,
arg.IsShopBet, arg.IsShopBet,
arg.Query,
arg.CreatedBefore,
arg.CreatedAfter,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -394,11 +413,23 @@ func (q *Queries) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]BetO
const GetBetOutcomeByEventID = `-- name: GetBetOutcomeByEventID :many const GetBetOutcomeByEventID = `-- name: GetBetOutcomeByEventID :many
SELECT id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires SELECT id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires
FROM bet_outcomes FROM bet_outcomes
WHERE event_id = $1 WHERE (event_id = $1)
AND (
status = $2
OR $2 IS NULL
OR status = $3
OR $3 IS NULL
)
` `
func (q *Queries) GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]BetOutcome, error) { type GetBetOutcomeByEventIDParams struct {
rows, err := q.db.Query(ctx, GetBetOutcomeByEventID, eventID) EventID int64 `json:"event_id"`
FilterStatus pgtype.Int4 `json:"filter_status"`
FilterStatus2 pgtype.Int4 `json:"filter_status_2"`
}
func (q *Queries) GetBetOutcomeByEventID(ctx context.Context, arg GetBetOutcomeByEventIDParams) ([]BetOutcome, error) {
rows, err := q.db.Query(ctx, GetBetOutcomeByEventID, arg.EventID, arg.FilterStatus, arg.FilterStatus2)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -468,6 +499,41 @@ func (q *Queries) UpdateBetOutcomeStatus(ctx context.Context, arg UpdateBetOutco
return i, err return i, err
} }
const UpdateBetOutcomeStatusByBetID = `-- name: UpdateBetOutcomeStatusByBetID :one
UPDATE bet_outcomes
SET status = $1
WHERE bet_id = $2
RETURNING id, bet_id, sport_id, event_id, odd_id, home_team_name, away_team_name, market_id, market_name, odd, odd_name, odd_header, odd_handicap, status, expires
`
type UpdateBetOutcomeStatusByBetIDParams struct {
Status int32 `json:"status"`
BetID int64 `json:"bet_id"`
}
func (q *Queries) UpdateBetOutcomeStatusByBetID(ctx context.Context, arg UpdateBetOutcomeStatusByBetIDParams) (BetOutcome, error) {
row := q.db.QueryRow(ctx, UpdateBetOutcomeStatusByBetID, arg.Status, arg.BetID)
var i BetOutcome
err := row.Scan(
&i.ID,
&i.BetID,
&i.SportID,
&i.EventID,
&i.OddID,
&i.HomeTeamName,
&i.AwayTeamName,
&i.MarketID,
&i.MarketName,
&i.Odd,
&i.OddName,
&i.OddHeader,
&i.OddHandicap,
&i.Status,
&i.Expires,
)
return i, err
}
const UpdateBetOutcomeStatusForEvent = `-- name: UpdateBetOutcomeStatusForEvent :many const UpdateBetOutcomeStatusForEvent = `-- name: UpdateBetOutcomeStatusForEvent :many
UPDATE bet_outcomes UPDATE bet_outcomes
SEt status = $1 SEt status = $1

View File

@ -155,7 +155,7 @@ func (q *Queries) DeleteBranchOperation(ctx context.Context, arg DeleteBranchOpe
} }
const GetAllBranches = `-- name: GetAllBranches :many const GetAllBranches = `-- name: GetAllBranches :many
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active
FROM branch_details FROM branch_details
WHERE ( WHERE (
company_id = $1 company_id = $1
@ -165,15 +165,43 @@ WHERE (
is_active = $2 is_active = $2
OR $2 IS NULL OR $2 IS NULL
) )
AND (
branch_manager_id = $3
OR $3 IS NULL
)
AND (
name ILIKE '%' || $4 || '%'
OR location ILIKE '%' || $4 || '%'
OR $4 IS NULL
)
AND (
created_at > $5
OR $5 IS NULL
)
AND (
created_at < $6
OR $6 IS NULL
)
` `
type GetAllBranchesParams struct { type GetAllBranchesParams struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
IsActive pgtype.Bool `json:"is_active"` IsActive pgtype.Bool `json:"is_active"`
BranchManagerID pgtype.Int8 `json:"branch_manager_id"`
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
} }
func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams) ([]BranchDetail, error) { func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams) ([]BranchDetail, error) {
rows, err := q.db.Query(ctx, GetAllBranches, arg.CompanyID, arg.IsActive) rows, err := q.db.Query(ctx, GetAllBranches,
arg.CompanyID,
arg.IsActive,
arg.BranchManagerID,
arg.Query,
arg.CreatedBefore,
arg.CreatedAfter,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,6 +223,7 @@ func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams)
&i.ManagerName, &i.ManagerName,
&i.ManagerPhoneNumber, &i.ManagerPhoneNumber,
&i.Balance, &i.Balance,
&i.WalletIsActive,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -257,7 +286,7 @@ func (q *Queries) GetBranchByCashier(ctx context.Context, userID int64) (Branch,
} }
const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active
FROM branch_details FROM branch_details
WHERE company_id = $1 WHERE company_id = $1
` `
@ -285,6 +314,7 @@ func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
&i.ManagerName, &i.ManagerName,
&i.ManagerPhoneNumber, &i.ManagerPhoneNumber,
&i.Balance, &i.Balance,
&i.WalletIsActive,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -297,7 +327,7 @@ func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
} }
const GetBranchByID = `-- name: GetBranchByID :one const GetBranchByID = `-- name: GetBranchByID :one
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active
FROM branch_details FROM branch_details
WHERE id = $1 WHERE id = $1
` `
@ -319,12 +349,13 @@ func (q *Queries) GetBranchByID(ctx context.Context, id int64) (BranchDetail, er
&i.ManagerName, &i.ManagerName,
&i.ManagerPhoneNumber, &i.ManagerPhoneNumber,
&i.Balance, &i.Balance,
&i.WalletIsActive,
) )
return i, err return i, err
} }
const GetBranchByManagerID = `-- name: GetBranchByManagerID :many const GetBranchByManagerID = `-- name: GetBranchByManagerID :many
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active
FROM branch_details FROM branch_details
WHERE branch_manager_id = $1 WHERE branch_manager_id = $1
` `
@ -352,6 +383,7 @@ func (q *Queries) GetBranchByManagerID(ctx context.Context, branchManagerID int6
&i.ManagerName, &i.ManagerName,
&i.ManagerPhoneNumber, &i.ManagerPhoneNumber,
&i.Balance, &i.Balance,
&i.WalletIsActive,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -411,7 +443,7 @@ func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]Ge
} }
const SearchBranchByName = `-- name: SearchBranchByName :many const SearchBranchByName = `-- name: SearchBranchByName :many
SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance SELECT id, name, location, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active
FROM branch_details FROM branch_details
WHERE name ILIKE '%' || $1 || '%' WHERE name ILIKE '%' || $1 || '%'
` `
@ -439,6 +471,7 @@ func (q *Queries) SearchBranchByName(ctx context.Context, dollar_1 pgtype.Text)
&i.ManagerName, &i.ManagerName,
&i.ManagerPhoneNumber, &i.ManagerPhoneNumber,
&i.Balance, &i.Balance,
&i.WalletIsActive,
); err != nil { ); err != nil {
return nil, err return nil, err
} }

View File

@ -143,6 +143,7 @@ type BranchDetail struct {
ManagerName interface{} `json:"manager_name"` ManagerName interface{} `json:"manager_name"`
ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"` ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"`
Balance pgtype.Int8 `json:"balance"` Balance pgtype.Int8 `json:"balance"`
WalletIsActive pgtype.Bool `json:"wallet_is_active"`
} }
type BranchOperation struct { type BranchOperation struct {
@ -181,6 +182,23 @@ type CustomerWallet struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type CustomerWalletDetail struct {
ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"`
RegularID int64 `json:"regular_id"`
RegularBalance int64 `json:"regular_balance"`
StaticID int64 `json:"static_id"`
StaticBalance int64 `json:"static_balance"`
RegularIsActive bool `json:"regular_is_active"`
StaticIsActive bool `json:"static_is_active"`
RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"`
StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"`
CreatedAt pgtype.Timestamp `json:"created_at"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
PhoneNumber pgtype.Text `json:"phone_number"`
}
type Event struct { type Event struct {
ID string `json:"id"` ID string `json:"id"`
SportID pgtype.Int4 `json:"sport_id"` SportID pgtype.Int4 `json:"sport_id"`

View File

@ -143,6 +143,46 @@ func (q *Queries) GetAllBranchWallets(ctx context.Context) ([]GetAllBranchWallet
return items, nil return items, nil
} }
const GetAllCustomerWallet = `-- name: GetAllCustomerWallet :many
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
FROM customer_wallet_details
`
func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDetail, error) {
rows, err := q.db.Query(ctx, GetAllCustomerWallet)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CustomerWalletDetail
for rows.Next() {
var i CustomerWalletDetail
if err := rows.Scan(
&i.ID,
&i.CustomerID,
&i.RegularID,
&i.RegularBalance,
&i.StaticID,
&i.StaticBalance,
&i.RegularIsActive,
&i.StaticIsActive,
&i.RegularUpdatedAt,
&i.StaticUpdatedAt,
&i.CreatedAt,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetAllWallets = `-- name: GetAllWallets :many const GetAllWallets = `-- name: GetAllWallets :many
SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at, currency, bonus_balance, cash_balance SELECT id, balance, is_withdraw, is_bettable, is_transferable, user_id, is_active, created_at, updated_at, currency, bonus_balance, cash_balance
FROM wallets FROM wallets
@ -182,36 +222,14 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
} }
const GetCustomerWallet = `-- name: GetCustomerWallet :one const GetCustomerWallet = `-- name: GetCustomerWallet :one
SELECT cw.id, SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
cw.customer_id, FROM customer_wallet_details
rw.id AS regular_id, WHERE customer_id = $1
rw.balance AS regular_balance,
sw.id AS static_id,
sw.balance AS static_balance,
rw.updated_at as regular_updated_at,
sw.updated_at as static_updated_at,
cw.created_at
FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id
WHERE cw.customer_id = $1
` `
type GetCustomerWalletRow struct { func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (CustomerWalletDetail, error) {
ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"`
RegularID int64 `json:"regular_id"`
RegularBalance int64 `json:"regular_balance"`
StaticID int64 `json:"static_id"`
StaticBalance int64 `json:"static_balance"`
RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"`
StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"`
CreatedAt pgtype.Timestamp `json:"created_at"`
}
func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (GetCustomerWalletRow, error) {
row := q.db.QueryRow(ctx, GetCustomerWallet, customerID) row := q.db.QueryRow(ctx, GetCustomerWallet, customerID)
var i GetCustomerWalletRow var i CustomerWalletDetail
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CustomerID, &i.CustomerID,
@ -219,9 +237,14 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (GetC
&i.RegularBalance, &i.RegularBalance,
&i.StaticID, &i.StaticID,
&i.StaticBalance, &i.StaticBalance,
&i.RegularIsActive,
&i.StaticIsActive,
&i.RegularUpdatedAt, &i.RegularUpdatedAt,
&i.StaticUpdatedAt, &i.StaticUpdatedAt,
&i.CreatedAt, &i.CreatedAt,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
) )
return i, err return i, err
} }

View File

@ -61,6 +61,9 @@ type BetFilter struct {
CompanyID ValidInt64 // Can Be Nullable CompanyID ValidInt64 // Can Be Nullable
UserID ValidInt64 // Can Be Nullable UserID ValidInt64 // Can Be Nullable
IsShopBet ValidBool IsShopBet ValidBool
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
} }
type GetBet struct { type GetBet struct {

View File

@ -7,13 +7,17 @@ type Branch struct {
WalletID int64 WalletID int64
BranchManagerID int64 BranchManagerID int64
CompanyID int64 CompanyID int64
IsSuspended bool IsActive bool
IsSelfOwned bool IsSelfOwned bool
} }
type BranchFilter struct { type BranchFilter struct {
CompanyID ValidInt64 CompanyID ValidInt64
IsSuspended ValidBool IsActive ValidBool
BranchManagerID ValidInt64
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
} }
type BranchDetail struct { type BranchDetail struct {
@ -24,10 +28,11 @@ type BranchDetail struct {
Balance Currency Balance Currency
BranchManagerID int64 BranchManagerID int64
CompanyID int64 CompanyID int64
IsSuspended bool IsActive bool
IsSelfOwned bool IsSelfOwned bool
ManagerName string ManagerName string
ManagerPhoneNumber string ManagerPhoneNumber string
WalletIsActive bool
} }
type SupportedOperation struct { type SupportedOperation struct {

View File

@ -15,6 +15,13 @@ type Wallet struct {
CreatedAt time.Time CreatedAt time.Time
} }
type WalletFilter struct {
IsActive ValidBool
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type CustomerWallet struct { type CustomerWallet struct {
ID int64 ID int64
RegularID int64 RegularID int64
@ -28,9 +35,14 @@ type GetCustomerWallet struct {
StaticID int64 StaticID int64
StaticBalance Currency StaticBalance Currency
CustomerID int64 CustomerID int64
RegularIsActive bool
StaticIsActive bool
RegularUpdatedAt time.Time RegularUpdatedAt time.Time
StaticUpdatedAt time.Time StaticUpdatedAt time.Time
CreatedAt time.Time CreatedAt time.Time
FirstName string
LastName string
PhoneNumber string
} }
type BranchWallet struct { type BranchWallet struct {

View File

@ -213,6 +213,18 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
Bool: filter.IsShopBet.Value, Bool: filter.IsShopBet.Value,
Valid: filter.IsShopBet.Valid, Valid: filter.IsShopBet.Valid,
}, },
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
CreatedBefore: pgtype.Timestamp{
Time: filter.CreatedBefore.Value,
Valid: filter.CreatedBefore.Valid,
},
CreatedAfter: pgtype.Timestamp{
Time: filter.CreatedAfter.Value,
Valid: filter.CreatedAfter.Valid,
},
}) })
if err != nil { if err != nil {
domain.MongoDBLogger.Error("failed to get all bets", domain.MongoDBLogger.Error("failed to get all bets",
@ -312,8 +324,19 @@ func (s *Store) UpdateStatus(ctx context.Context, id int64, status domain.Outcom
return err return err
} }
func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64) ([]domain.BetOutcome, error) { func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_filtered bool) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, eventID)
outcomes, err := s.queries.GetBetOutcomeByEventID(ctx, dbgen.GetBetOutcomeByEventIDParams{
EventID: eventID,
FilterStatus: pgtype.Int4{
Int32: int32(domain.OUTCOME_STATUS_PENDING),
Valid: is_filtered,
},
FilterStatus2: pgtype.Int4{
Int32: int32(domain.OUTCOME_STATUS_ERROR),
Valid: is_filtered,
},
})
if err != nil { if err != nil {
domain.MongoDBLogger.Error("failed to get bet outcomes by event ID", domain.MongoDBLogger.Error("failed to get bet outcomes by event ID",
zap.Int64("event_id", eventID), zap.Int64("event_id", eventID),
@ -364,6 +387,24 @@ func (s *Store) UpdateBetOutcomeStatus(ctx context.Context, id int64, status dom
return res, nil return res, nil
} }
func (s *Store) UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) {
update, err := s.queries.UpdateBetOutcomeStatusByBetID(ctx, dbgen.UpdateBetOutcomeStatusByBetIDParams{
Status: int32(status),
BetID: id,
})
if err != nil {
domain.MongoDBLogger.Error("failed to update bet outcome status",
zap.Int64("id", id),
zap.Int32("status", int32(status)),
zap.Error(err),
)
return domain.BetOutcome{}, err
}
res := convertDBBetOutcomes(update)
return res, nil
}
func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) { func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) {
outcomes, err := s.queries.UpdateBetOutcomeStatusForEvent(ctx, dbgen.UpdateBetOutcomeStatusForEventParams{ outcomes, err := s.queries.UpdateBetOutcomeStatusForEvent(ctx, dbgen.UpdateBetOutcomeStatusForEventParams{
EventID: eventID, EventID: eventID,
@ -386,10 +427,6 @@ func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int6
return result, nil return result, nil
} }
func (s *Store) DeleteBet(ctx context.Context, id int64) error {
return s.queries.DeleteBet(ctx, id)
}
// GetBetSummary returns aggregated bet statistics // GetBetSummary returns aggregated bet statistics
func (s *Store) GetBetSummary(ctx context.Context, filter domain.ReportFilter) ( func (s *Store) GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
totalStakes domain.Currency, totalStakes domain.Currency,

View File

@ -32,6 +32,8 @@ func convertDBBranchDetail(dbBranch dbgen.BranchDetail) domain.BranchDetail {
ManagerName: dbBranch.ManagerName.(string), ManagerName: dbBranch.ManagerName.(string),
ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String, ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
Balance: domain.Currency(dbBranch.Balance.Int64), Balance: domain.Currency(dbBranch.Balance.Int64),
IsActive: dbBranch.IsActive,
WalletIsActive: dbBranch.WalletIsActive.Bool,
} }
} }
@ -140,6 +142,22 @@ func (s *Store) GetAllBranches(ctx context.Context, filter domain.BranchFilter)
Int64: filter.CompanyID.Value, Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid, Valid: filter.CompanyID.Valid,
}, },
BranchManagerID: pgtype.Int8{
Int64: filter.BranchManagerID.Value,
Valid: filter.BranchManagerID.Valid,
},
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
CreatedBefore: pgtype.Timestamp{
Time: filter.CreatedBefore.Value,
Valid: filter.CreatedBefore.Valid,
},
CreatedAfter: pgtype.Timestamp{
Time: filter.CreatedAfter.Value,
Valid: filter.CreatedAfter.Valid,
},
}) })
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -47,7 +47,7 @@ func convertCreateCustomerWallet(customerWallet domain.CreateCustomerWallet) dbg
} }
} }
func convertDBGetCustomerWallet(customerWallet dbgen.GetCustomerWalletRow) domain.GetCustomerWallet { func convertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) domain.GetCustomerWallet {
return domain.GetCustomerWallet{ return domain.GetCustomerWallet{
ID: customerWallet.ID, ID: customerWallet.ID,
RegularID: customerWallet.RegularID, RegularID: customerWallet.RegularID,
@ -55,9 +55,14 @@ func convertDBGetCustomerWallet(customerWallet dbgen.GetCustomerWalletRow) domai
StaticID: customerWallet.StaticID, StaticID: customerWallet.StaticID,
StaticBalance: domain.Currency(customerWallet.StaticBalance), StaticBalance: domain.Currency(customerWallet.StaticBalance),
CustomerID: customerWallet.CustomerID, CustomerID: customerWallet.CustomerID,
RegularIsActive: customerWallet.RegularIsActive,
StaticIsActive: customerWallet.StaticIsActive,
RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time, RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time,
StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time, StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time,
CreatedAt: customerWallet.CreatedAt.Time, CreatedAt: customerWallet.CreatedAt.Time,
FirstName: customerWallet.FirstName,
LastName: customerWallet.LastName,
PhoneNumber: customerWallet.PhoneNumber.String,
} }
} }
@ -115,6 +120,19 @@ func (s *Store) GetWalletsByUser(ctx context.Context, userID int64) ([]domain.Wa
return result, nil return result, nil
} }
func (s *Store) GetAllCustomerWallets(ctx context.Context) ([]domain.GetCustomerWallet, error) {
customerWallets, err := s.queries.GetAllCustomerWallet(ctx)
if err != nil {
return nil, err
}
var result []domain.GetCustomerWallet = make([]domain.GetCustomerWallet, 0, len(customerWallets))
for _, wallet := range customerWallets {
result = append(result, convertDBGetCustomerWallet(wallet))
}
return result, nil
}
func (s *Store) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) { func (s *Store) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) {
customerWallet, err := s.queries.GetCustomerWallet(ctx, customerID) customerWallet, err := s.queries.GetCustomerWallet(ctx, customerID)

View File

@ -15,14 +15,14 @@ type BetStore interface {
GetAllBets(ctx context.Context, filter domain.BetFilter) ([]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, is_filtered bool) ([]domain.BetOutcome, error)
GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain.BetOutcome, error)
GetBetCount(ctx context.Context, userID int64, outcomesHash string) (int64, error) GetBetCount(ctx context.Context, userID int64, outcomesHash string) (int64, error)
UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error UpdateCashOut(ctx context.Context, id int64, cashedOut bool) error
UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error UpdateStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error
UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error) UpdateBetOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, status domain.OutcomeStatus) (domain.BetOutcome, error)
UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int64, status domain.OutcomeStatus) ([]domain.BetOutcome, error)
DeleteBet(ctx context.Context, id int64) error
GetBetSummary(ctx context.Context, filter domain.ReportFilter) ( GetBetSummary(ctx context.Context, filter domain.ReportFilter) (
totalStakes domain.Currency, totalStakes domain.Currency,

View File

@ -34,6 +34,7 @@ var (
ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid") ErrRawOddInvalid = errors.New("Prematch Raw Odd is Invalid")
ErrBranchIDRequired = errors.New("Branch ID required for this role") ErrBranchIDRequired = errors.New("Branch ID required for this role")
ErrOutcomeLimit = errors.New("Too many outcomes on a single bet") ErrOutcomeLimit = errors.New("Too many outcomes on a single bet")
ErrTotalBalanceNotEnough = errors.New("Total Wallet balance is insufficient to create bet")
) )
type Service struct { type Service struct {
@ -326,7 +327,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
newBet.IsShopBet = true newBet.IsShopBet = true
case domain.RoleCustomer: case domain.RoleCustomer:
wallets, err := s.walletSvc.GetWalletsByUser(ctx, userID) wallets, err := s.walletSvc.GetCustomerWallet(ctx, userID)
if err != nil { if err != nil {
s.mongoLogger.Error("failed to get customer wallets", s.mongoLogger.Error("failed to get customer wallets",
zap.Int64("user_id", userID), zap.Int64("user_id", userID),
@ -334,18 +335,53 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
) )
return domain.CreateBetRes{}, err return domain.CreateBetRes{}, err
} }
if req.Amount < wallets.RegularBalance.Float32() {
userWallet := wallets[0] _, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID,
_, err = s.walletSvc.DeductFromWallet(ctx, userWallet.ID,
domain.ToCurrency(req.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT) domain.ToCurrency(req.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
if err != nil { if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer", s.mongoLogger.Error("wallet deduction failed for customer regular wallet",
zap.Int64("wallet_id", userWallet.ID), zap.Int64("customer_id", wallets.CustomerID),
zap.Int64("customer_wallet_id", wallets.ID),
zap.Int64("regular wallet_id", wallets.RegularID),
zap.Float32("amount", req.Amount), zap.Float32("amount", req.Amount),
zap.Error(err), zap.Error(err),
) )
return domain.CreateBetRes{}, err return domain.CreateBetRes{}, err
} }
} else {
combinedBalance := wallets.RegularBalance + wallets.StaticBalance
if req.Amount > combinedBalance.Float32() {
return domain.CreateBetRes{}, ErrTotalBalanceNotEnough
}
// Empty the regular balance
_, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID,
wallets.RegularBalance, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer regular wallet",
zap.Int64("customer_id", wallets.CustomerID),
zap.Int64("customer_wallet_id", wallets.ID),
zap.Int64("regular wallet_id", wallets.RegularID),
zap.Float32("amount", req.Amount),
zap.Error(err),
)
return domain.CreateBetRes{}, err
}
// Empty remaining from static balance
remainingAmount := wallets.RegularBalance - domain.Currency(req.Amount)
_, err = s.walletSvc.DeductFromWallet(ctx, wallets.StaticID,
remainingAmount, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer static wallet",
zap.Int64("customer_id", wallets.CustomerID),
zap.Int64("customer_wallet_id", wallets.ID),
zap.Int64("static wallet_id", wallets.StaticID),
zap.Float32("amount", req.Amount),
zap.Error(err),
)
return domain.CreateBetRes{}, err
}
}
newBet.UserID = domain.ValidInt64{Value: userID, Valid: true} newBet.UserID = domain.ValidInt64{Value: userID, Valid: true}
newBet.IsShopBet = false newBet.IsShopBet = false
@ -838,8 +874,23 @@ func (s *Service) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID in
return outcomes, nil return outcomes, nil
} }
func (s *Service) DeleteBet(ctx context.Context, id int64) error { func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error {
return s.betStore.DeleteBet(ctx, id) _, err := s.betStore.UpdateBetOutcomeStatusByBetID(ctx, id, domain.OUTCOME_STATUS_VOID)
if err != nil {
s.mongoLogger.Error("failed to update bet outcome to void", zap.Int64("id", id),
zap.Error(err),
)
return err
}
err = s.betStore.UpdateStatus(ctx, id, domain.OUTCOME_STATUS_VOID)
if err != nil {
s.mongoLogger.Error("failed to update bet to void", zap.Int64("id", id),
zap.Error(err),
)
return err
}
return nil
} }
func generateOutcomeHash(outcomes []domain.CreateBetOutcome) (string, error) { func generateOutcomeHash(outcomes []domain.CreateBetOutcome) (string, error) {

View File

@ -52,7 +52,7 @@ var (
func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, resultRes json.RawMessage, sportID int64) error { func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, resultRes json.RawMessage, sportID int64) error {
// TODO: Optimize this since there could be many outcomes with the same event_id and market_id that could be updated the same time // TODO: Optimize this since there could be many outcomes with the same event_id and market_id that could be updated the same time
outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID) outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, true)
if err != nil { if err != nil {
s.logger.Error("Failed to get pending bet outcomes", "error", err) s.logger.Error("Failed to get pending bet outcomes", "error", err)
return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err) return fmt.Errorf("failed to get pending bet outcomes for event %d: %w", eventID, err)
@ -108,7 +108,7 @@ func (s *Service) UpdateResultForOutcomes(ctx context.Context, eventID int64, re
} }
func (s *Service) RefundAllOutcomes(ctx context.Context, eventID int64) error { func (s *Service) RefundAllOutcomes(ctx context.Context, eventID int64) error {
outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID) outcomes, err := s.repo.GetBetOutcomeByEventID(ctx, eventID, false)
if err != nil { if err != nil {
s.logger.Error("Failed to get pending bet outcomes", "error", err) s.logger.Error("Failed to get pending bet outcomes", "error", err)

View File

@ -12,6 +12,7 @@ type WalletStore interface {
GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error)
GetAllWallets(ctx context.Context) ([]domain.Wallet, error) GetAllWallets(ctx context.Context) ([]domain.Wallet, error)
GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error)
GetAllCustomerWallets(ctx context.Context) ([]domain.GetCustomerWallet, error)
GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error)
GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error)
UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error

View File

@ -57,6 +57,9 @@ func (s *Service) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wall
return s.walletStore.GetWalletsByUser(ctx, id) return s.walletStore.GetWalletsByUser(ctx, id)
} }
func (s *Service) GetAllCustomerWallet(ctx context.Context) ([]domain.GetCustomerWallet, error) {
return s.walletStore.GetAllCustomerWallets(ctx)
}
func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) { func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) {
return s.walletStore.GetCustomerWallet(ctx, customerID) return s.walletStore.GetCustomerWallet(ctx, customerID)
} }
@ -70,7 +73,7 @@ func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Cu
} }
func (s *Service) AddToWallet( func (s *Service) AddToWallet(
ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, paymentDetails domain. PaymentDetails) (domain.Transfer, error) { ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, paymentDetails domain.PaymentDetails) (domain.Transfer, error) {
wallet, err := s.GetWalletByID(ctx, id) wallet, err := s.GetWalletByID(ctx, id)
if err != nil { if err != nil {
return domain.Transfer{}, err return domain.Transfer{}, err

View File

@ -209,13 +209,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 {
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64) companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64) branchID := c.Locals("branch_id").(domain.ValidInt64)
var isShopBet domain.ValidBool var isShopBet domain.ValidBool
isShopBetQuery := c.Query("is_shop") isShopBetQuery := c.Query("is_shop")
if isShopBetQuery != "" && role == domain.RoleSuperAdmin {
if isShopBetQuery != "" {
isShopBetParse, err := strconv.ParseBool(isShopBetQuery) isShopBetParse, err := strconv.ParseBool(isShopBetQuery)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to parse is_shop_bet", h.mongoLoggerSvc.Error("Failed to parse is_shop_bet",
@ -231,10 +231,47 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
} }
} }
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{ bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
BranchID: branchID, BranchID: branchID,
CompanyID: companyID, CompanyID: companyID,
IsShopBet: isShopBet, IsShopBet: isShopBet,
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}) })
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to get bets", h.mongoLoggerSvc.Error("Failed to get bets",
@ -432,7 +469,7 @@ func (h *Handler) DeleteBet(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID") return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID")
} }
err = h.betSvc.DeleteBet(c.Context(), id) err = h.betSvc.SetBetToRemoved(c.Context(), id)
if err != nil { if err != nil {
h.mongoLoggerSvc.Error("Failed to delete bet by ID", h.mongoLoggerSvc.Error("Failed to delete bet by ID",
zap.Int64("betID", id), zap.Int64("betID", id),

View File

@ -3,6 +3,7 @@ package handlers
import ( import (
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
@ -56,6 +57,7 @@ type BranchRes struct {
BranchManagerID int64 `json:"branch_manager_id" example:"1"` BranchManagerID int64 `json:"branch_manager_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"` CompanyID int64 `json:"company_id" example:"1"`
IsSelfOwned bool `json:"is_self_owned" example:"false"` IsSelfOwned bool `json:"is_self_owned" example:"false"`
IsActive bool `json:"is_active" example:"false"`
} }
type BranchDetailRes struct { type BranchDetailRes struct {
@ -69,6 +71,8 @@ type BranchDetailRes struct {
ManagerName string `json:"manager_name" example:"John Smith"` ManagerName string `json:"manager_name" example:"John Smith"`
ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"` ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"`
Balance float32 `json:"balance" example:"100.5"` Balance float32 `json:"balance" example:"100.5"`
IsActive bool `json:"is_active" example:"false"`
WalletIsActive bool `json:"is_wallet_active" example:"false"`
} }
func convertBranch(branch domain.Branch) BranchRes { func convertBranch(branch domain.Branch) BranchRes {
@ -80,6 +84,7 @@ func convertBranch(branch domain.Branch) BranchRes {
BranchManagerID: branch.BranchManagerID, BranchManagerID: branch.BranchManagerID,
CompanyID: branch.CompanyID, CompanyID: branch.CompanyID,
IsSelfOwned: branch.IsSelfOwned, IsSelfOwned: branch.IsSelfOwned,
IsActive: branch.IsActive,
} }
} }
@ -95,6 +100,8 @@ func convertBranchDetail(branch domain.BranchDetail) BranchDetailRes {
ManagerName: branch.ManagerName, ManagerName: branch.ManagerName,
ManagerPhoneNumber: branch.ManagerPhoneNumber, ManagerPhoneNumber: branch.ManagerPhoneNumber,
Balance: branch.Balance.Float32(), Balance: branch.Balance.Float32(),
IsActive: branch.IsActive,
WalletIsActive: branch.WalletIsActive,
} }
} }
@ -391,13 +398,63 @@ func (h *Handler) GetAllBranches(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid is_active param", err, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid is_active param", err, nil)
} }
branchManagerQuery := c.Query("branch_manager_id")
var branchManagerID domain.ValidInt64
if branchManagerQuery != "" {
parseManagerID, err := strconv.ParseInt(branchManagerQuery, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse branch_manager_id")
}
branchManagerID = domain.ValidInt64{
Value: parseManagerID,
Valid: true,
}
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.logger.Error("invalid start_time format", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
branches, err := h.branchSvc.GetAllBranches(c.Context(), branches, err := h.branchSvc.GetAllBranches(c.Context(),
domain.BranchFilter{ domain.BranchFilter{
CompanyID: companyID, CompanyID: companyID,
IsSuspended: domain.ValidBool{ IsActive: domain.ValidBool{
Value: isActive, Value: isActive,
Valid: isActiveValid, Valid: isActiveValid,
}, },
BranchManagerID: branchManagerID,
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}) })
if err != nil { if err != nil {
h.logger.Error("Failed to get branches", "error", err) h.logger.Error("Failed to get branches", "error", err)

View File

@ -184,7 +184,34 @@ func (h *Handler) GetCompanyByID(c *fiber.Ctx) error {
res := convertGetCompany(company) res := convertGetCompany(company)
return response.WriteJSON(c, fiber.StatusOK, "Company retrieved successfully", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Company retrieved successfully", res, nil)
}
// GetCompanyForAdmin godoc
// @Summary Gets company by id
// @Description Gets a single company by id
// @Tags company
// @Accept json
// @Produce json
// @Success 200 {object} CompanyRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin-company [get]
func (h *Handler) GetCompanyForAdmin(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Invalid company ID", nil, nil)
}
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value)
if err != nil {
h.logger.Error("Failed to get company by ID", "companyID", companyID.Value, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to company branch", err, nil)
}
res := convertGetCompany(company)
return response.WriteJSON(c, fiber.StatusOK, "Company retrieved successfully", res, nil)
} }
// GetAllCompanies godoc // GetAllCompanies godoc

View File

@ -2,6 +2,7 @@ package handlers
import ( import (
"context" "context"
"os"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -20,7 +21,7 @@ import (
// @Router /api/v1/logs [get] // @Router /api/v1/logs [get]
func GetLogsHandler(appCtx context.Context) fiber.Handler { func GetLogsHandler(appCtx context.Context) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
client, err := mongo.Connect(appCtx, options.Client().ApplyURI("mongodb://root:secret@mongo:27017/?authSource=admin")) client, err := mongo.Connect(appCtx, options.Client().ApplyURI(os.Getenv("MONGODB_URL")))
if err != nil { if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "MongoDB connection failed: "+err.Error()) return fiber.NewError(fiber.StatusInternalServerError, "MongoDB connection failed: "+err.Error())
} }

View File

@ -174,11 +174,7 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Unknown Error") return fiber.NewError(fiber.StatusInternalServerError, "Unknown Error")
} }
newWallet, err := h.walletSvc.CreateWallet(c.Context(), domain.CreateWallet{ newWallet, err := h.walletSvc.CreateCustomerWallet(c.Context(), newUser.ID)
UserID: newUser.ID,
IsWithdraw: true,
IsBettable: true,
})
if err != nil { if err != nil {
h.logger.Error("Failed to create wallet for user", "userID", newUser.ID, "error", err) h.logger.Error("Failed to create wallet for user", "userID", newUser.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create user wallet") return fiber.NewError(fiber.StatusInternalServerError, "Failed to create user wallet")
@ -193,7 +189,7 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
// TODO: Remove later // TODO: Remove later
_, err = h.walletSvc.AddToWallet( _, err = h.walletSvc.AddToWallet(
c.Context(), newWallet.ID, domain.ToCurrency(100.0), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) c.Context(), newWallet.RegularID, domain.ToCurrency(100.0), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
if err != nil { if err != nil {
h.logger.Error("Failed to update wallet for user", "userID", newUser.ID, "error", err) h.logger.Error("Failed to update wallet for user", "userID", newUser.ID, "error", err)

View File

@ -9,9 +9,6 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
type UpdateWalletActiveReq struct {
IsActive bool `json:"is_active" validate:"required" example:"true"`
}
type WalletRes struct { type WalletRes struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
Balance float32 `json:"amount" example:"100.0"` Balance float32 `json:"amount" example:"100.0"`
@ -45,9 +42,14 @@ type CustomerWalletRes struct {
StaticID int64 `json:"static_id" example:"1"` StaticID int64 `json:"static_id" example:"1"`
StaticBalance float32 `json:"static_balance" example:"100.0"` StaticBalance float32 `json:"static_balance" example:"100.0"`
CustomerID int64 `json:"customer_id" example:"1"` CustomerID int64 `json:"customer_id" example:"1"`
RegularIsActive bool `json:"regular_is_active" example:"true"`
StaticIsActive bool `json:"static_is_active" example:"true"`
RegularUpdatedAt time.Time `json:"regular_updated_at"` RegularUpdatedAt time.Time `json:"regular_updated_at"`
StaticUpdatedAt time.Time `json:"static_updated_at"` StaticUpdatedAt time.Time `json:"static_updated_at"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
} }
func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes { func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
@ -58,9 +60,14 @@ func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
StaticID: wallet.StaticID, StaticID: wallet.StaticID,
StaticBalance: wallet.StaticBalance.Float32(), StaticBalance: wallet.StaticBalance.Float32(),
CustomerID: wallet.CustomerID, CustomerID: wallet.CustomerID,
RegularIsActive: wallet.RegularIsActive,
StaticIsActive: wallet.StaticIsActive,
RegularUpdatedAt: wallet.RegularUpdatedAt, RegularUpdatedAt: wallet.RegularUpdatedAt,
StaticUpdatedAt: wallet.StaticUpdatedAt, StaticUpdatedAt: wallet.StaticUpdatedAt,
CreatedAt: wallet.CreatedAt, CreatedAt: wallet.CreatedAt,
FirstName: wallet.FirstName,
LastName: wallet.LastName,
PhoneNumber: wallet.PhoneNumber,
} }
} }
@ -173,7 +180,38 @@ func (h *Handler) GetAllBranchWallets(c *fiber.Ctx) error {
} }
return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil) return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil)
}
// GetAllCustomerWallets godoc
// @Summary Get all customer wallets
// @Description Retrieve all customer wallets
// @Tags wallet
// @Accept json
// @Produce json
// @Success 200 {array} CustomerWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /customerWallet [get]
func (h *Handler) GetAllCustomerWallets(c *fiber.Ctx) error {
wallets, err := h.walletSvc.GetAllCustomerWallet(c.Context())
if err != nil {
h.logger.Error("Failed to get wallets", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve wallets", err, nil)
}
var res []CustomerWalletRes = make([]CustomerWalletRes, 0, len(wallets))
for _, wallet := range wallets {
res = append(res, ConvertCustomerWallet(wallet))
}
return response.WriteJSON(c, fiber.StatusOK, "All Wallets retrieved", res, nil)
}
type UpdateWalletActiveReq struct {
IsActive bool `json:"is_active" validate:"required" example:"true"`
} }
// UpdateWalletActive godoc // UpdateWalletActive godoc
@ -255,13 +293,13 @@ func (h *Handler) GetCustomerWallet(c *fiber.Ctx) error {
// h.logger.Info("Fetching customer wallet", "userID", userID) // h.logger.Info("Fetching customer wallet", "userID", userID)
wallet, err := h.walletSvc.GetWalletsByUser(c.Context(), userID) wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), userID)
if err != nil { if err != nil {
h.logger.Error("Failed to get customer wallet", "userID", userID, "error", err) h.logger.Error("Failed to get customer wallet", "userID", userID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallet") return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve wallet")
} }
res := convertWallet(wallet[0]) res := ConvertCustomerWallet(wallet)
return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Wallet retrieved successfully", res, nil)
} }

View File

@ -174,6 +174,7 @@ func (a *App) initAppRoutes() {
a.fiber.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany) a.fiber.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany)
a.fiber.Get("/company/:id/branch", a.authMiddleware, h.GetBranchByCompanyID) a.fiber.Get("/company/:id/branch", a.authMiddleware, h.GetBranchByCompanyID)
a.fiber.Get("/search/company", a.authMiddleware, h.SearchCompany) a.fiber.Get("/search/company", a.authMiddleware, h.SearchCompany)
a.fiber.Get("/admin-company", a.authMiddleware, h.GetCompanyForAdmin)
// Ticket Routes // Ticket Routes
a.fiber.Post("/ticket", h.CreateTicket) a.fiber.Post("/ticket", h.CreateTicket)
@ -195,6 +196,7 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/wallet/:id", h.GetWalletByID) a.fiber.Get("/wallet/:id", h.GetWalletByID)
a.fiber.Put("/wallet/:id", h.UpdateWalletActive) a.fiber.Put("/wallet/:id", h.UpdateWalletActive)
a.fiber.Get("/branchWallet", a.authMiddleware, h.GetAllBranchWallets) a.fiber.Get("/branchWallet", a.authMiddleware, h.GetAllBranchWallets)
a.fiber.Get("/customerWallet", a.authMiddleware, h.GetAllCustomerWallets)
a.fiber.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier) a.fiber.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier)
// Transfer // Transfer