fix: added message to wallet_transfers and gave it a detailed view

This commit is contained in:
Samuel Tariku 2025-06-29 21:51:23 +03:00
parent 74941bc535
commit 3e4e9eead7
28 changed files with 324 additions and 142 deletions

View File

@ -354,6 +354,13 @@ FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id JOIN wallets sw ON cw.static_wallet_id = sw.id
JOIN users ON users.id = cw.customer_id; JOIN users ON users.id = cw.customer_id;
CREATE VIEW wallet_transfer_details AS
SELECT wt.*,
users.first_name,
users.last_name,
users.phone_number
FROM wallet_transfer wt
LEFT JOIN users ON users.id = wt.cashier_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

@ -14,7 +14,8 @@ FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id JOIN users ON branch_cashiers.user_id = users.id
JOIN branches ON branches.id = branch_id JOIN branches ON branches.id = branch_id
WHERE ( WHERE (
full_name ILIKE '%' || sqlc.narg('query') || '%' first_name ILIKE '%' || sqlc.narg('query') || '%'
OR last_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%' OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL OR sqlc.narg('query') IS NULL
) )

View File

@ -15,19 +15,19 @@ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *; RETURNING *;
-- name: GetAllTransfers :many -- name: GetAllTransfers :many
SELECT * SELECT *
FROM wallet_transfer; FROM wallet_transfer_details;
-- name: GetTransfersByWallet :many -- name: GetTransfersByWallet :many
SELECT * SELECT *
FROM wallet_transfer FROM wallet_transfer_details
WHERE receiver_wallet_id = $1 WHERE receiver_wallet_id = $1
OR sender_wallet_id = $1; OR sender_wallet_id = $1;
-- name: GetTransferByID :one -- name: GetTransferByID :one
SELECT * SELECT *
FROM wallet_transfer FROM wallet_transfer_details
WHERE id = $1; WHERE id = $1;
-- name: GetTransferByReference :one -- name: GetTransferByReference :one
SELECT * SELECT *
FROM wallet_transfer FROM wallet_transfer_details
WHERE reference_number = $1; WHERE reference_number = $1;
-- name: UpdateTransferVerification :exec -- name: UpdateTransferVerification :exec
UPDATE wallet_transfer UPDATE wallet_transfer

View File

@ -67,7 +67,8 @@ wHERE (
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
full_name ILIKE '%' || sqlc.narg('query') || '%' first_name ILIKE '%' || sqlc.narg('query') || '%'
OR last_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%' OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL OR sqlc.narg('query') IS NULL
) )

View File

@ -21,7 +21,8 @@ FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id JOIN users ON branch_cashiers.user_id = users.id
JOIN branches ON branches.id = branch_id JOIN branches ON branches.id = branch_id
WHERE ( WHERE (
full_name ILIKE '%' || $1 || '%' first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%'
OR phone_number ILIKE '%' || $1 || '%' OR phone_number ILIKE '%' || $1 || '%'
OR $1 IS NULL OR $1 IS NULL
) )

View File

@ -597,3 +597,22 @@ type WalletTransfer struct {
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"` UpdatedAt pgtype.Timestamp `json:"updated_at"`
} }
type WalletTransferDetail struct {
ID int64 `json:"id"`
Amount pgtype.Int8 `json:"amount"`
Message string `json:"message"`
Type pgtype.Text `json:"type"`
ReceiverWalletID pgtype.Int8 `json:"receiver_wallet_id"`
SenderWalletID pgtype.Int8 `json:"sender_wallet_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
Verified pgtype.Bool `json:"verified"`
ReferenceNumber string `json:"reference_number"`
Status pgtype.Text `json:"status"`
PaymentMethod pgtype.Text `json:"payment_method"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
FirstName pgtype.Text `json:"first_name"`
LastName pgtype.Text `json:"last_name"`
PhoneNumber pgtype.Text `json:"phone_number"`
}

View File

@ -74,19 +74,19 @@ func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams)
} }
const GetAllTransfers = `-- name: GetAllTransfers :many const GetAllTransfers = `-- name: GetAllTransfers :many
SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at, first_name, last_name, phone_number
FROM wallet_transfer FROM wallet_transfer_details
` `
func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error) { func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransferDetail, error) {
rows, err := q.db.Query(ctx, GetAllTransfers) rows, err := q.db.Query(ctx, GetAllTransfers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []WalletTransfer var items []WalletTransferDetail
for rows.Next() { for rows.Next() {
var i WalletTransfer var i WalletTransferDetail
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Amount, &i.Amount,
@ -101,6 +101,9 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
&i.PaymentMethod, &i.PaymentMethod,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -113,14 +116,14 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
} }
const GetTransferByID = `-- name: GetTransferByID :one const GetTransferByID = `-- name: GetTransferByID :one
SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at, first_name, last_name, phone_number
FROM wallet_transfer FROM wallet_transfer_details
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer, error) { func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransferDetail, error) {
row := q.db.QueryRow(ctx, GetTransferByID, id) row := q.db.QueryRow(ctx, GetTransferByID, id)
var i WalletTransfer var i WalletTransferDetail
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Amount, &i.Amount,
@ -135,19 +138,22 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
&i.PaymentMethod, &i.PaymentMethod,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
) )
return i, err return i, err
} }
const GetTransferByReference = `-- name: GetTransferByReference :one const GetTransferByReference = `-- name: GetTransferByReference :one
SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at, first_name, last_name, phone_number
FROM wallet_transfer FROM wallet_transfer_details
WHERE reference_number = $1 WHERE reference_number = $1
` `
func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber string) (WalletTransfer, error) { func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber string) (WalletTransferDetail, error) {
row := q.db.QueryRow(ctx, GetTransferByReference, referenceNumber) row := q.db.QueryRow(ctx, GetTransferByReference, referenceNumber)
var i WalletTransfer var i WalletTransferDetail
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Amount, &i.Amount,
@ -162,26 +168,29 @@ func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber st
&i.PaymentMethod, &i.PaymentMethod,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
) )
return i, err return i, err
} }
const GetTransfersByWallet = `-- name: GetTransfersByWallet :many const GetTransfersByWallet = `-- name: GetTransfersByWallet :many
SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at SELECT id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at, first_name, last_name, phone_number
FROM wallet_transfer FROM wallet_transfer_details
WHERE receiver_wallet_id = $1 WHERE receiver_wallet_id = $1
OR sender_wallet_id = $1 OR sender_wallet_id = $1
` `
func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgtype.Int8) ([]WalletTransfer, error) { func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgtype.Int8) ([]WalletTransferDetail, error) {
rows, err := q.db.Query(ctx, GetTransfersByWallet, receiverWalletID) rows, err := q.db.Query(ctx, GetTransfersByWallet, receiverWalletID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []WalletTransfer var items []WalletTransferDetail
for rows.Next() { for rows.Next() {
var i WalletTransfer var i WalletTransferDetail
if err := rows.Scan( if err := rows.Scan(
&i.ID, &i.ID,
&i.Amount, &i.Amount,
@ -196,6 +205,9 @@ func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgt
&i.PaymentMethod, &i.PaymentMethod,
&i.CreatedAt, &i.CreatedAt,
&i.UpdatedAt, &i.UpdatedAt,
&i.FirstName,
&i.LastName,
&i.PhoneNumber,
); err != nil { ); err != nil {
return nil, err return nil, err
} }

View File

@ -183,7 +183,8 @@ wHERE (
OR $2 IS NULL OR $2 IS NULL
) )
AND ( AND (
full_name ILIKE '%' || $3 || '%' first_name ILIKE '%' || $3 || '%'
OR last_name ILIKE '%' || $3 || '%'
OR phone_number ILIKE '%' || $3 || '%' OR phone_number ILIKE '%' || $3 || '%'
OR $3 IS NULL OR $3 IS NULL
) )

View File

@ -18,8 +18,7 @@ const (
BANK BANK
) )
// Transaction only represents when the user cashes out a bet in the shop // Transaction only represents branch transactions
// It probably would be better to call it a CashOut or ShopWithdrawal
type Transaction struct { type Transaction struct {
ID int64 ID int64
Amount Currency Amount Currency

View File

@ -43,7 +43,25 @@ type Transfer struct {
SenderWalletID ValidInt64 `json:"sender_wallet_id"` SenderWalletID ValidInt64 `json:"sender_wallet_id"`
ReferenceNumber string `json:"reference_number"` // <-- needed ReferenceNumber string `json:"reference_number"` // <-- needed
Status string `json:"status"` Status string `json:"status"`
CashierID ValidInt64 `json:"cashier_id"` DepositorID ValidInt64 `json:"depositor_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type TransferDetail struct {
ID int64 `json:"id"`
Amount Currency `json:"amount"`
Verified bool `json:"verified"`
Message string `json:"message"`
Type TransferType `json:"type"`
PaymentMethod PaymentMethod `json:"payment_method"`
ReceiverWalletID ValidInt64 `json:"receiver_wallet_id"`
SenderWalletID ValidInt64 `json:"sender_wallet_id"`
ReferenceNumber string `json:"reference_number"` // <-- needed
Status string `json:"status"`
DepositorID ValidInt64 `json:"depositor_id"`
DepositorFirstName string `json:"depositor_first_name"`
DepositorLastName string `json:"depositor_last_name"`
DepositorPhoneNumber string `json:"depositor_phone_number"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }

View File

@ -8,12 +8,13 @@ import (
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
func convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer { func convertDBTransferDetail(transfer dbgen.WalletTransferDetail) domain.TransferDetail {
return domain.Transfer{ return domain.TransferDetail{
ID: transfer.ID, ID: transfer.ID,
Amount: domain.Currency(transfer.Amount.Int64), Amount: domain.Currency(transfer.Amount.Int64),
Type: domain.TransferType(transfer.Type.String), Type: domain.TransferType(transfer.Type.String),
Verified: transfer.Verified.Bool, Verified: transfer.Verified.Bool,
Message: transfer.Message,
ReceiverWalletID: domain.ValidInt64{ ReceiverWalletID: domain.ValidInt64{
Value: transfer.ReceiverWalletID.Int64, Value: transfer.ReceiverWalletID.Int64,
Valid: transfer.ReceiverWalletID.Valid, Valid: transfer.ReceiverWalletID.Valid,
@ -22,7 +23,36 @@ func convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer {
Value: transfer.SenderWalletID.Int64, Value: transfer.SenderWalletID.Int64,
Valid: transfer.SenderWalletID.Valid, Valid: transfer.SenderWalletID.Valid,
}, },
CashierID: domain.ValidInt64{ DepositorID: domain.ValidInt64{
Value: transfer.CashierID.Int64,
Valid: transfer.CashierID.Valid,
},
DepositorFirstName: transfer.FirstName.String,
DepositorLastName: transfer.LastName.String,
DepositorPhoneNumber: transfer.PhoneNumber.String,
PaymentMethod: domain.PaymentMethod(transfer.PaymentMethod.String),
ReferenceNumber: transfer.ReferenceNumber,
Status: transfer.Status.String,
CreatedAt: transfer.CreatedAt.Time,
UpdatedAt: transfer.UpdatedAt.Time,
}
}
func convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer {
return domain.Transfer{
ID: transfer.ID,
Amount: domain.Currency(transfer.Amount.Int64),
Type: domain.TransferType(transfer.Type.String),
Verified: transfer.Verified.Bool,
Message: transfer.Message,
ReceiverWalletID: domain.ValidInt64{
Value: transfer.ReceiverWalletID.Int64,
Valid: transfer.ReceiverWalletID.Valid,
},
SenderWalletID: domain.ValidInt64{
Value: transfer.SenderWalletID.Int64,
Valid: transfer.SenderWalletID.Valid,
},
DepositorID: domain.ValidInt64{
Value: transfer.CashierID.Int64, Value: transfer.CashierID.Int64,
Valid: transfer.CashierID.Valid, Valid: transfer.CashierID.Valid,
}, },
@ -54,6 +84,10 @@ func convertCreateTransfer(transfer domain.CreateTransfer) dbgen.CreateTransferP
ReferenceNumber: string(transfer.ReferenceNumber), ReferenceNumber: string(transfer.ReferenceNumber),
PaymentMethod: pgtype.Text{String: string(transfer.PaymentMethod), Valid: true}, PaymentMethod: pgtype.Text{String: string(transfer.PaymentMethod), Valid: true},
Verified: pgtype.Bool{
Bool: transfer.Verified,
Valid: true,
},
} }
} }
@ -65,47 +99,47 @@ func (s *Store) CreateTransfer(ctx context.Context, transfer domain.CreateTransf
return convertDBTransfer(newTransfer), nil return convertDBTransfer(newTransfer), nil
} }
func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) { func (s *Store) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error) {
transfers, err := s.queries.GetAllTransfers(ctx) transfers, err := s.queries.GetAllTransfers(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var result []domain.Transfer = make([]domain.Transfer, 0, len(transfers)) var result []domain.TransferDetail = make([]domain.TransferDetail, 0, len(transfers))
for _, transfer := range transfers { for _, transfer := range transfers {
result = append(result, convertDBTransfer(transfer)) result = append(result, convertDBTransferDetail(transfer))
} }
return result, nil return result, nil
} }
func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) { func (s *Store) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.TransferDetail, error) {
transfers, err := s.queries.GetTransfersByWallet(ctx, pgtype.Int8{Int64: walletID, Valid: true}) transfers, err := s.queries.GetTransfersByWallet(ctx, pgtype.Int8{Int64: walletID, Valid: true})
if err != nil { if err != nil {
return nil, err return nil, err
} }
var result []domain.Transfer = make([]domain.Transfer, 0, len(transfers)) var result []domain.TransferDetail = make([]domain.TransferDetail, 0, len(transfers))
for _, transfer := range transfers { for _, transfer := range transfers {
result = append(result, convertDBTransfer(transfer)) result = append(result, convertDBTransferDetail(transfer))
} }
return result, nil return result, nil
} }
func (s *Store) GetTransferByReference(ctx context.Context, reference string) (domain.Transfer, error) { func (s *Store) GetTransferByReference(ctx context.Context, reference string) (domain.TransferDetail, error) {
transfer, err := s.queries.GetTransferByReference(ctx, reference) transfer, err := s.queries.GetTransferByReference(ctx, reference)
if err != nil { if err != nil {
return domain.Transfer{}, nil return domain.TransferDetail{}, nil
} }
return convertDBTransfer(transfer), nil return convertDBTransferDetail(transfer), nil
} }
func (s *Store) GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) { func (s *Store) GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error) {
transfer, err := s.queries.GetTransferByID(ctx, id) transfer, err := s.queries.GetTransferByID(ctx, id)
if err != nil { if err != nil {
return domain.Transfer{}, nil return domain.TransferDetail{}, nil
} }
return convertDBTransfer(transfer), nil return convertDBTransferDetail(transfer), nil
} }
func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error { func (s *Store) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error {

View File

@ -149,8 +149,21 @@ func (s *Store) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]do
return userList, totalCount, nil return userList, totalCount, nil
} }
func (s *Store) GetAllCashiers(ctx context.Context) ([]domain.GetCashier, int64, error) { func (s *Store) GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error) {
users, err := s.queries.GetAllCashiers(ctx) users, err := s.queries.GetAllCashiers(ctx, dbgen.GetAllCashiersParams{
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
CreatedBefore: pgtype.Timestamptz{
Time: filter.CreatedBefore.Value,
Valid: filter.CreatedBefore.Valid,
},
CreatedAfter: pgtype.Timestamptz{
Time: filter.CreatedAfter.Value,
Valid: filter.CreatedAfter.Valid,
},
})
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }

View File

@ -276,7 +276,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
Value: userID, Value: userID,
Valid: true, Valid: true,
}, domain.TRANSFER_DIRECT, }, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", deductedAmount)) fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", deductedAmount))
if err != nil { if err != nil {
s.mongoLogger.Error("failed to deduct from wallet", s.mongoLogger.Error("failed to deduct from wallet",
zap.Int64("wallet_id", branch.WalletID), zap.Int64("wallet_id", branch.WalletID),
@ -312,7 +312,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
_, err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount), domain.BranchWalletType, domain.ValidInt64{ _, err = s.walletSvc.DeductFromWallet(ctx, branch.WalletID, domain.ToCurrency(deductedAmount), domain.BranchWalletType, domain.ValidInt64{
Value: userID, Value: userID,
Valid: true, Valid: true,
}, domain.TRANSFER_DIRECT, fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", deductedAmount)) }, domain.TRANSFER_DIRECT, fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", deductedAmount))
if err != nil { if err != nil {
s.mongoLogger.Error("wallet deduction failed", s.mongoLogger.Error("wallet deduction failed",
zap.Int64("wallet_id", branch.WalletID), zap.Int64("wallet_id", branch.WalletID),
@ -339,7 +339,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
if req.Amount < wallets.RegularBalance.Float32() { if req.Amount < wallets.RegularBalance.Float32() {
_, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID, _, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID,
domain.ToCurrency(req.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.ToCurrency(req.Amount), domain.CustomerWalletType, domain.ValidInt64{},
domain.TRANSFER_DIRECT, fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", req.Amount)) domain.TRANSFER_DIRECT, fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", req.Amount))
if err != nil { if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer regular wallet", s.mongoLogger.Error("wallet deduction failed for customer regular wallet",
zap.Int64("customer_id", wallets.CustomerID), zap.Int64("customer_id", wallets.CustomerID),
@ -358,7 +358,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
// Empty the regular balance // Empty the regular balance
_, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID, _, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID,
wallets.RegularBalance, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT, wallets.RegularBalance, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", wallets.RegularBalance.Float32())) fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", wallets.RegularBalance.Float32()))
if err != nil { if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer regular wallet", s.mongoLogger.Error("wallet deduction failed for customer regular wallet",
zap.Int64("customer_id", wallets.CustomerID), zap.Int64("customer_id", wallets.CustomerID),
@ -373,7 +373,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
remainingAmount := wallets.RegularBalance - domain.Currency(req.Amount) remainingAmount := wallets.RegularBalance - domain.Currency(req.Amount)
_, err = s.walletSvc.DeductFromWallet(ctx, wallets.StaticID, _, err = s.walletSvc.DeductFromWallet(ctx, wallets.StaticID,
remainingAmount, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT, remainingAmount, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", remainingAmount.Float32())) fmt.Sprintf("Deducted %v amount from wallet by system while placing bet", remainingAmount.Float32()))
if err != nil { if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer static wallet", s.mongoLogger.Error("wallet deduction failed for customer static wallet",
zap.Int64("customer_id", wallets.CustomerID), zap.Int64("customer_id", wallets.CustomerID),
@ -759,7 +759,7 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc
} }
_, err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount, domain.ValidInt64{}, _, err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount, domain.ValidInt64{},
domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %d to wallet by system for winning a bet", amount.Float32())) domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet by system for winning a bet", amount.Float32()))
if err != nil { if err != nil {
s.mongoLogger.Error("failed to add winnings to wallet", s.mongoLogger.Error("failed to add winnings to wallet",

View File

@ -83,7 +83,7 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
// Create payment record // Create payment record
transfer := domain.CreateTransfer{ transfer := domain.CreateTransfer{
Message: fmt.Sprintf("Depositing %d into wallet using chapa. Reference Number %s", amount, reference), Message: fmt.Sprintf("Depositing %v into wallet using chapa. Reference Number %v", amount.Float32(), reference),
Amount: amount, Amount: amount,
Type: domain.DEPOSIT, Type: domain.DEPOSIT,
PaymentMethod: domain.TRANSFER_CHAPA, PaymentMethod: domain.TRANSFER_CHAPA,
@ -269,7 +269,7 @@ func (s *Service) ManuallyVerify(ctx context.Context, txRef string) (*domain.Cha
// Credit wallet // Credit wallet
_, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value,
transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{}, transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{},
fmt.Sprintf("Added %d to wallet using Chapa", transfer.Amount.Float32())) fmt.Sprintf("Added %v to wallet using Chapa", transfer.Amount.Float32()))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err) return nil, fmt.Errorf("failed to credit wallet: %w", err)
} }
@ -330,7 +330,7 @@ func (s *Service) HandleVerifyDepositWebhook(ctx context.Context, transfer domai
ReferenceNumber: domain.ValidString{ ReferenceNumber: domain.ValidString{
Value: transfer.Reference, Value: transfer.Reference,
}, },
}, fmt.Sprintf("Added %d to wallet using Chapa")); err != nil { }, fmt.Sprintf("Added %v to wallet using Chapa")); err != nil {
return fmt.Errorf("failed to credit user wallet: %w", err) return fmt.Errorf("failed to credit user wallet: %w", err)
} }
} }
@ -368,7 +368,7 @@ func (s *Service) HandleVerifyWithdrawWebhook(ctx context.Context, payment domai
} else { } else {
_, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{},
domain.TRANSFER_DIRECT, domain.PaymentDetails{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %d to wallet by system because chapa withdraw is unsuccessful")) fmt.Sprintf("Added %v to wallet by system because chapa withdraw is unsuccessful"))
if err != nil { if err != nil {
return fmt.Errorf("failed to credit user wallet: %w", err) return fmt.Errorf("failed to credit user wallet: %w", err)
} }

View File

@ -127,7 +127,10 @@ func (s *Service) ProcessReferral(ctx context.Context, referredPhone, referralCo
return err return err
} }
_, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, domain.ToCurrency(float32(referral.RewardAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID,
domain.ToCurrency(float32(referral.RewardAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to static wallet because of referral ID %v", referral.RewardAmount, referrerId),
)
if err != nil { if err != nil {
s.logger.Error("Failed to add referral reward to static wallet", "walletID", wallets.StaticID, "referrer phone number", referredPhone, "error", err) s.logger.Error("Failed to add referral reward to static wallet", "walletID", wallets.StaticID, "referrer phone number", referredPhone, "error", err)
return err return err
@ -165,7 +168,9 @@ func (s *Service) ProcessDepositBonus(ctx context.Context, userPhone string, amo
walletID := wallets[0].ID walletID := wallets[0].ID
bonus := amount * (settings.CashbackPercentage / 100) bonus := amount * (settings.CashbackPercentage / 100)
currentBonus := float64(wallets[0].Balance) currentBonus := float64(wallets[0].Balance)
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBonus+bonus)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBonus+bonus)), domain.ValidInt64{},
domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to static wallet because of Deposit Cashback Bonus %d", currentBonus+bonus, bonus))
if err != nil { if err != nil {
s.logger.Error("Failed to add deposit bonus to wallet", "walletID", walletID, "userID", userID, "bonus", bonus, "error", err) s.logger.Error("Failed to add deposit bonus to wallet", "walletID", walletID, "userID", userID, "bonus", bonus, "error", err)
return err return err
@ -219,7 +224,9 @@ func (s *Service) ProcessBetReferral(ctx context.Context, userPhone string, betA
walletID := wallets[0].ID walletID := wallets[0].ID
currentBalance := float64(wallets[0].Balance) currentBalance := float64(wallets[0].Balance)
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{},
domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to static wallet because of bet referral", referral.RewardAmount))
if err != nil { if err != nil {
s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referrerID, "bonus", bonus, "error", err) s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referrerID, "bonus", bonus, "error", err)
return err return err

View File

@ -58,8 +58,8 @@ func (s *Service) GetCashiersByBranch(ctx context.Context, branchID int64) ([]do
return s.userStore.GetCashiersByBranch(ctx, branchID) return s.userStore.GetCashiersByBranch(ctx, branchID)
} }
func (s *Service) GetAllCashiers(ctx context.Context) ([]domain.GetCashier, int64, error){ func (s *Service) GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error){
return s.userStore.GetAllCashiers(ctx) return s.userStore.GetAllCashiers(ctx, filter)
} }
func (s *Service) GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error) { func (s *Service) GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error) {

View File

@ -10,8 +10,8 @@ type UserStore interface {
CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error) CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error)
CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error) CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error)
GetUserByID(ctx context.Context, id int64) (domain.User, error) GetUserByID(ctx context.Context, id int64) (domain.User, error)
GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, int64, error) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error)
GetAllCashiers(ctx context.Context) ([]domain.GetCashier, int64, error) GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error)
GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error) GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error)
GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error) GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error)
UpdateUser(ctx context.Context, user domain.UpdateUserReq) error UpdateUser(ctx context.Context, user domain.UpdateUserReq) error

View File

@ -127,8 +127,9 @@ func (s *AleaPlayService) processTransaction(ctx context.Context, tx *domain.Vir
return errors.New("no wallet available for user") return errors.New("no wallet available for user")
} }
tx.WalletID = wallets[0].ID tx.WalletID = wallets[0].ID
_, err = s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
if _, err := s.walletSvc.AddToWallet(ctx, tx.WalletID, domain.Currency(tx.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil { fmt.Sprintf("Added %v to wallet for winning AleaPlay Virtual Bet", tx.Amount))
if err != nil {
return fmt.Errorf("wallet update failed: %w", err) return fmt.Errorf("wallet update failed: %w", err)
} }

View File

@ -138,7 +138,9 @@ func (s *service) HandleCallback(ctx context.Context, callback *domain.PopOKCall
return errors.New("unknown transaction type") return errors.New("unknown transaction type")
} }
_, err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.Currency(amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to wallet for winning PopOkBet", amount),
)
if err != nil { if err != nil {
s.logger.Error("Failed to update wallet", "walletID", walletID, "userID", session.UserID, "amount", amount, "error", err) s.logger.Error("Failed to update wallet", "walletID", walletID, "userID", session.UserID, "amount", amount, "error", err)
return err return err
@ -208,7 +210,7 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
} }
_, err = s.walletSvc.DeductFromWallet(ctx, claims.UserID, domain.Currency(amountCents), _, err = s.walletSvc.DeductFromWallet(ctx, claims.UserID, domain.Currency(amountCents),
domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing virtual game bet", amountCents)) fmt.Sprintf("Deducted %v amount from wallet by system while placing virtual game bet", amountCents))
if err != nil { if err != nil {
return nil, fmt.Errorf("insufficient balance") return nil, fmt.Errorf("insufficient balance")
} }
@ -274,8 +276,11 @@ func (s *service) ProcessWin(ctx context.Context, req *domain.PopOKWinRequest) (
amountCents := int64(req.Amount * 100) amountCents := int64(req.Amount * 100)
// 4. Credit to wallet // 4. Credit to wallet
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{},
if _, err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil { domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to wallet for winning PopOkBet", req.Amount),
)
if err != nil {
s.logger.Error("Failed to credit wallet", "userID", claims.UserID, "error", err) s.logger.Error("Failed to credit wallet", "userID", claims.UserID, "error", err)
return nil, fmt.Errorf("wallet credit failed") return nil, fmt.Errorf("wallet credit failed")
} }
@ -342,7 +347,9 @@ func (s *service) ProcessTournamentWin(ctx context.Context, req *domain.PopOKWin
amountCents := int64(req.Amount * 100) amountCents := int64(req.Amount * 100)
// 4. Credit user wallet // 4. Credit user wallet
if _, err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil { _, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to wallet for winning Popok Tournament", req.Amount))
if err != nil {
s.logger.Error("Failed to credit wallet for tournament", "userID", claims.UserID, "error", err) s.logger.Error("Failed to credit wallet for tournament", "userID", claims.UserID, "error", err)
return nil, fmt.Errorf("wallet credit failed") return nil, fmt.Errorf("wallet credit failed")
} }
@ -403,8 +410,9 @@ func (s *service) ProcessPromoWin(ctx context.Context, req *domain.PopOKWinReque
} }
amountCents := int64(req.Amount * 100) amountCents := int64(req.Amount * 100)
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{},
if _, err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil { domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet for winning PopOk Promo Win", req.Amount))
if err != nil {
s.logger.Error("Failed to credit wallet for promo", "userID", claims.UserID, "error", err) s.logger.Error("Failed to credit wallet for promo", "userID", claims.UserID, "error", err)
return nil, fmt.Errorf("wallet credit failed") return nil, fmt.Errorf("wallet credit failed")
} }
@ -509,8 +517,10 @@ func (s *service) ProcessCancel(ctx context.Context, req *domain.PopOKCancelRequ
// 5. Refund the bet amount (absolute value since bet amount is negative) // 5. Refund the bet amount (absolute value since bet amount is negative)
refundAmount := -originalBet.Amount refundAmount := -originalBet.Amount
_, err = s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(refundAmount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
if _, err := s.walletSvc.AddToWallet(ctx, claims.UserID, domain.Currency(refundAmount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil { fmt.Sprintf("Added %v to wallet as refund for cancelling PopOk bet", refundAmount),
)
if err != nil {
s.logger.Error("Failed to refund bet", "userID", claims.UserID, "error", err) s.logger.Error("Failed to refund bet", "userID", claims.UserID, "error", err)
return nil, fmt.Errorf("refund failed") return nil, fmt.Errorf("refund failed")
} }

View File

@ -26,10 +26,10 @@ type WalletStore interface {
type TransferStore interface { type TransferStore interface {
CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error)
GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error)
GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.TransferDetail, error)
GetTransferByReference(ctx context.Context, reference string) (domain.Transfer, error) GetTransferByReference(ctx context.Context, reference string) (domain.TransferDetail, error)
GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error)
UpdateTransferVerification(ctx context.Context, id int64, verified bool) error UpdateTransferVerification(ctx context.Context, id int64, verified bool) error
UpdateTransferStatus(ctx context.Context, id int64, status string) error UpdateTransferStatus(ctx context.Context, id int64, status string) error
} }

View File

@ -9,7 +9,8 @@ import (
) )
var ( var (
ErrWalletNotTransferable = errors.New("wallet is not transferable") ErrSenderWalletNotTransferable = errors.New("sender wallet is not transferable")
ErrReceiverWalletNotTransferable = errors.New("receiver wallet is not transferable")
ErrInsufficientBalance = errors.New("wallet balance is insufficient") ErrInsufficientBalance = errors.New("wallet balance is insufficient")
) )
@ -18,19 +19,19 @@ func (s *Service) CreateTransfer(ctx context.Context, transfer domain.CreateTran
return s.transferStore.CreateTransfer(ctx, transfer) return s.transferStore.CreateTransfer(ctx, transfer)
} }
func (s *Service) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) { func (s *Service) GetAllTransfers(ctx context.Context) ([]domain.TransferDetail, error) {
return s.transferStore.GetAllTransfers(ctx) return s.transferStore.GetAllTransfers(ctx)
} }
func (s *Service) GetTransferByReference(ctx context.Context, reference string) (domain.Transfer, error) { func (s *Service) GetTransferByReference(ctx context.Context, reference string) (domain.TransferDetail, error) {
return s.transferStore.GetTransferByReference(ctx, reference) return s.transferStore.GetTransferByReference(ctx, reference)
} }
func (s *Service) GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) { func (s *Service) GetTransferByID(ctx context.Context, id int64) (domain.TransferDetail, error) {
return s.transferStore.GetTransferByID(ctx, id) return s.transferStore.GetTransferByID(ctx, id)
} }
func (s *Service) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) { func (s *Service) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.TransferDetail, error) {
return s.transferStore.GetTransfersByWallet(ctx, walletID) return s.transferStore.GetTransfersByWallet(ctx, walletID)
} }
@ -44,15 +45,17 @@ func (s *Service) UpdateTransferStatus(ctx context.Context, id int64, status str
func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64, func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64,
amount domain.Currency, paymentMethod domain.PaymentMethod, amount domain.Currency, paymentMethod domain.PaymentMethod,
cashierID domain.ValidInt64) (domain.Transfer, error) { cashierID domain.ValidInt64, message string) (domain.Transfer, error) {
senderWallet, err := s.GetWalletByID(ctx, senderID) senderWallet, err := s.GetWalletByID(ctx, senderID)
if err != nil { if err != nil {
return domain.Transfer{}, err return domain.Transfer{}, err
} }
if senderWallet.IsTransferable { if !senderWallet.IsTransferable {
return domain.Transfer{}, ErrWalletNotTransferable fmt.Printf("Error: %d Sender Wallet is not transferable \n", senderWallet.ID)
return domain.Transfer{}, ErrSenderWalletNotTransferable
} }
receiverWallet, err := s.GetWalletByID(ctx, receiverID) receiverWallet, err := s.GetWalletByID(ctx, receiverID)
@ -60,8 +63,9 @@ func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiver
return domain.Transfer{}, err return domain.Transfer{}, err
} }
if receiverWallet.IsTransferable { if !receiverWallet.IsTransferable {
return domain.Transfer{}, ErrWalletNotTransferable fmt.Printf("Error: %d Receiver Wallet is not transferable \n", senderWallet.ID)
return domain.Transfer{}, ErrReceiverWalletNotTransferable
} }
// Deduct from sender // Deduct from sender
@ -84,7 +88,7 @@ func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiver
// Log the transfer so that if there is a mistake, it can be reverted // Log the transfer so that if there is a mistake, it can be reverted
transfer, err := s.CreateTransfer(ctx, domain.CreateTransfer{ transfer, err := s.CreateTransfer(ctx, domain.CreateTransfer{
Message: fmt.Sprintf("Transferring %d to another wallet", amount), Message: message,
SenderWalletID: domain.ValidInt64{ SenderWalletID: domain.ValidInt64{
Value: senderID, Value: senderID,
Valid: true, Valid: true,

View File

@ -23,22 +23,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 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events) // spec: "0 0 * * * *", // Every 1 hour (since its takes that long to fetch all the events)
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 */5 * * * *", // Every 5 Minutes spec: "0 */5 * * * *", // Every 5 Minutes
task: func() { task: func() {
@ -66,7 +66,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
} }
for _, job := range schedule { for _, job := range schedule {
job.task() // job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil { if _, err := c.AddFunc(job.spec, job.task); err != nil {
log.Fatalf("Failed to schedule cron job: %v", err) log.Fatalf("Failed to schedule cron job: %v", err)
} }

View File

@ -7,7 +7,6 @@ import (
"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"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -178,7 +177,11 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
} }
cashiers, total, err := h.userSvc.GetAllCashiers(c.Context()) cashiers, total, err := h.userSvc.GetAllCashiers(c.Context(), domain.UserFilter{
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil { if err != nil {
h.logger.Error("GetAllCashiers failed", "error", err) h.logger.Error("GetAllCashiers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get cashiers", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get cashiers", nil, nil)

View File

@ -66,7 +66,9 @@ func (h *Handler) InitiateDeposit(c *fiber.Ctx) error {
multiplier = bonusMultiplier[0].Multiplier multiplier = bonusMultiplier[0].Multiplier
} }
_, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, domain.ToCurrency(float32(amount)*multiplier), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}) _, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, domain.ToCurrency(float32(amount)*multiplier), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %v to static wallet because of deposit bonus using multiplier %v", float32(amount)*multiplier, multiplier),
)
if err != nil { if err != nil {
h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err) h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err)
return err return err

View File

@ -6,7 +6,6 @@ import (
"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"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -173,7 +172,7 @@ func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
managers, total, err := h.userSvc.GetAllUsers(c.Context(), filter) managers, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
if err != nil { if err != nil {
h.logger.Error("GetAllManagers failed", "error", err) h.logger.Error("GetAllManagers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Managers", nil, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Managers", err, nil)
} }
var result []ManagersRes = make([]ManagersRes, len(managers)) var result []ManagersRes = make([]ManagersRes, len(managers))

View File

@ -9,7 +9,6 @@ import (
"strconv" "strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/ws"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor" "github.com/gofiber/fiber/v2/middleware/adaptor"
@ -183,7 +182,7 @@ func (h *Handler) CreateAndSendNotification(c *fiber.Ctx) error {
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"message": "Single notification sent successfully", "notification_id": notification.ID}) return c.Status(fiber.StatusCreated).JSON(fiber.Map{"message": "Single notification sent successfully", "notification_id": notification.ID})
case domain.NotificationDeliverySchemeBulk: case domain.NotificationDeliverySchemeBulk:
recipients, _, err := h.userSvc.GetAllUsers(context.Background(), user.Filter{ recipients, _, err := h.userSvc.GetAllUsers(context.Background(), domain.UserFilter{
Role: string(req.Reciever), Role: string(req.Reciever),
}) })
if err != nil { if err != nil {

View File

@ -8,17 +8,22 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go.uber.org/zap"
) )
type TransferWalletRes struct { type TransferWalletRes struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Amount float32 `json:"amount"` Amount float32 `json:"amount"`
Verified bool `json:"verified"` Verified bool `json:"verified"`
Message string `json:"message"`
Type string `json:"type"` Type string `json:"type"`
PaymentMethod string `json:"payment_method"` PaymentMethod string `json:"payment_method"`
ReceiverWalletID *int64 `json:"receiver_wallet_id,omitempty"` ReceiverWalletID *int64 `json:"receiver_wallet_id,omitempty"`
SenderWalletID *int64 `json:"sender_wallet_id,omitempty"` SenderWalletID *int64 `json:"sender_wallet_id,omitempty"`
CashierID *int64 `json:"cashier_id,omitempty"` DepositorID *int64 `json:"depositor_id,omitempty"`
DepositorFirstName string `json:"depositor_first_name"`
DepositorLastName string `json:"depositor_last_name"`
DepositorPhoneNumber string `json:"depositor_phone_number"`
ReferenceNumber string `json:"reference_number"` // ← Add this ReferenceNumber string `json:"reference_number"` // ← Add this
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
@ -48,20 +53,55 @@ func convertTransfer(t domain.Transfer) TransferWalletRes {
senderID = &t.SenderWalletID.Value senderID = &t.SenderWalletID.Value
} }
var cashierID *int64 var depositorID *int64
if t.CashierID.Valid { if t.DepositorID.Valid {
cashierID = &t.CashierID.Value depositorID = &t.DepositorID.Value
} }
return TransferWalletRes{ return TransferWalletRes{
ID: t.ID, ID: t.ID,
Amount: float32(t.Amount), Amount: t.Amount.Float32(),
Verified: t.Verified, Verified: t.Verified,
Message: t.Message,
Type: string(t.Type), Type: string(t.Type),
PaymentMethod: string(t.PaymentMethod), PaymentMethod: string(t.PaymentMethod),
ReceiverWalletID: receiverID, ReceiverWalletID: receiverID,
SenderWalletID: senderID, SenderWalletID: senderID,
CashierID: cashierID, DepositorID: depositorID,
ReferenceNumber: t.ReferenceNumber,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,
}
}
func convertTransferDetail(t domain.TransferDetail) TransferWalletRes {
var receiverID *int64
if t.ReceiverWalletID.Valid {
receiverID = &t.ReceiverWalletID.Value
}
var senderID *int64
if t.SenderWalletID.Valid {
senderID = &t.SenderWalletID.Value
}
var depositorID *int64
if t.DepositorID.Valid {
depositorID = &t.DepositorID.Value
}
return TransferWalletRes{
ID: t.ID,
Amount: t.Amount.Float32(),
Verified: t.Verified,
Message: t.Message,
Type: string(t.Type),
PaymentMethod: string(t.PaymentMethod),
ReceiverWalletID: receiverID,
SenderWalletID: senderID,
DepositorID: depositorID,
DepositorFirstName: t.DepositorFirstName,
DepositorLastName: t.DepositorLastName,
DepositorPhoneNumber: t.DepositorPhoneNumber,
ReferenceNumber: t.ReferenceNumber, ReferenceNumber: t.ReferenceNumber,
CreatedAt: t.CreatedAt, CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt, UpdatedAt: t.UpdatedAt,
@ -107,7 +147,7 @@ func (h *Handler) GetTransfersByWallet(c *fiber.Ctx) error {
var transferResponses []TransferWalletRes var transferResponses []TransferWalletRes
for _, transfer := range transfers { for _, transfer := range transfers {
transferResponses = append(transferResponses, convertTransfer(transfer)) transferResponses = append(transferResponses, convertTransferDetail(transfer))
} }
return response.WriteJSON(c, fiber.StatusOK, "Transfers retrieved successfully", transferResponses, nil) return response.WriteJSON(c, fiber.StatusOK, "Transfers retrieved successfully", transferResponses, nil)
@ -140,16 +180,15 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role) role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64) companyID := c.Locals("company_id").(domain.ValidInt64)
fmt.Printf("\n\nCompant ID: %v\n\n", companyID.Value) fmt.Printf("\n\nCompany ID: %v\n\n", companyID.Value)
var senderID int64 var senderID int64
//TODO: check to make sure that the cashiers aren't transferring TO branch wallet //TODO: check to make sure that the cashiers aren't transferring TO branch wallet
switch role { switch role {
case domain.RoleCustomer: case domain.RoleCustomer:
h.logger.Error("Unauthorized access", "userID", userID, "role", role) h.logger.Error("Unauthorized access", "userID", userID, "role", role)
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil) return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
case domain.RoleBranchManager, domain.RoleAdmin, domain.RoleSuperAdmin: case domain.RoleAdmin:
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value) company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value)
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
@ -160,13 +199,20 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
} }
senderID = company.WalletID senderID = company.WalletID
h.logger.Error("Will", "userID", userID, "role", role) h.logger.Error("Will", "userID", userID, "role", role)
default:
case domain.RoleSuperAdmin:
return response.WriteJSON(c, fiber.StatusBadRequest, "Super Admin does not have a wallet", err, nil)
case domain.RoleBranchManager:
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch Manager does not have a wallet", err, nil)
case domain.RoleCashier:
cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID) cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
if err != nil { if err != nil {
h.logger.Error("Failed to get branch", "user ID", userID, "error", err) h.logger.Error("Failed to get branch", "user ID", userID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier branch", err, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier branch", err, nil)
} }
senderID = cashierBranch.WalletID senderID = cashierBranch.WalletID
default:
return response.WriteJSON(c, fiber.StatusInternalServerError, "Unknown Role", err, nil)
} }
var req CreateTransferReq var req CreateTransferReq
@ -181,9 +227,14 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
} }
transfer, err := h.walletSvc.TransferToWallet(c.Context(), senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod), domain.ValidInt64{Value: userID, Valid: true}) transfer, err := h.walletSvc.TransferToWallet(c.Context(),
senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod),
domain.ValidInt64{Value: userID, Valid: true},
fmt.Sprintf("Transferred %v from wallet to another", req.Amount),
)
if !ok { if err != nil {
h.mongoLoggerSvc.Error("Failed to transfer money to wallet", zap.Error(err))
return response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil)
} }
@ -233,7 +284,7 @@ func (h *Handler) RefillWallet(c *fiber.Ctx) error {
c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{ c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{
Value: userID, Value: userID,
Valid: true, Valid: true,
}, domain.TRANSFER_BANK, domain.PaymentDetails{}, fmt.Sprintf("Added %d to wallet directly by super-admin", req.Amount)) }, domain.TRANSFER_BANK, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet directly by super-admin", req.Amount))
if !ok { if !ok {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil) return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil)

View File

@ -50,7 +50,7 @@ swagger:
.PHONY: db-up .PHONY: db-up
db-up: db-up:
@docker compose up -d postgres migrate mongo @docker compose up -d postgres migrate mongo redis
.PHONY: db-down .PHONY: db-down
db-down: db-down: