fix: added message to transfer and more filter routes

This commit is contained in:
Samuel Tariku 2025-06-29 18:00:23 +03:00
parent 56bcdeffaa
commit 10a49b4571
29 changed files with 453 additions and 126 deletions

View File

@ -119,15 +119,20 @@ CREATE TABLE IF NOT EXISTS banks (
name VARCHAR(255) NOT NULL,
acct_length INT NOT NULL,
country_id INT NOT NULL,
is_mobilemoney INT, -- nullable integer (0 or 1)
is_active INT NOT NULL, -- 0 or 1
is_rtgs INT NOT NULL, -- 0 or 1
active INT NOT NULL, -- 0 or 1
is_24hrs INT, -- nullable integer (0 or 1)
is_mobilemoney INT,
-- nullable integer (0 or 1)
is_active INT NOT NULL,
-- 0 or 1
is_rtgs INT NOT NULL,
-- 0 or 1
active INT NOT NULL,
-- 0 or 1
is_24hrs INT,
-- nullable integer (0 or 1)
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
currency VARCHAR(10) NOT NULL,
bank_logo TEXT -- URL or base64 string
bank_logo TEXT -- URL or base64 string
);
CREATE TABLE IF NOT EXISTS wallets (
id BIGSERIAL PRIMARY KEY,
@ -151,6 +156,7 @@ CREATE TABLE IF NOT EXISTS customer_wallets (
CREATE TABLE IF NOT EXISTS wallet_transfer (
id BIGSERIAL PRIMARY KEY,
amount BIGINT,
message VARCHAR(255) NOT NULL,
type VARCHAR(255),
receiver_wallet_id BIGINT,
sender_wallet_id BIGINT,

View File

@ -12,7 +12,20 @@ SELECT users.*,
branches.location As branch_location
FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id
JOIN branches ON branches.id = branch_id;
JOIN branches ON branches.id = branch_id
WHERE (
full_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
users.created_at > sqlc.narg('created_before')
OR sqlc.narg('created_before') IS NULL
)
AND (
users.created_at < sqlc.narg('created_after')
OR sqlc.narg('created_after') IS NULL
);
-- name: GetCashierByID :one
SELECT users.*,
branch_id,

View File

@ -54,6 +54,19 @@ wHERE (
AND (
cashier_id = sqlc.narg('cashier_id')
OR sqlc.narg('cashier_id') 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: GetTransactionByID :one
SELECT *

View File

@ -1,6 +1,7 @@
-- name: CreateTransfer :one
INSERT INTO wallet_transfer (
amount,
message,
type,
receiver_wallet_id,
sender_wallet_id,
@ -10,7 +11,7 @@ INSERT INTO wallet_transfer (
status,
payment_method
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING *;
-- name: GetAllTransfers :many
SELECT *
@ -33,18 +34,15 @@ UPDATE wallet_transfer
SET verified = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2;
-- name: UpdateTransferStatus :exec
UPDATE wallet_transfer
SET status = $1,
updated_at = CURRENT_TIMESTAMP
WHERE id = $2;
-- name: GetWalletTransactionsInRange :many
SELECT type, COUNT(*) as count, SUM(amount) as total_amount
SELECT type,
COUNT(*) as count,
SUM(amount) as total_amount
FROM wallet_transfer
WHERE created_at BETWEEN $1 AND $2
GROUP BY type;
GROUP BY type;

View File

@ -1,22 +1,32 @@
-- name: CreateUser :one
INSERT INTO users (
first_name,
last_name,
email,
phone_number,
role,
password,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
first_name,
last_name,
email,
phone_number,
role,
password,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
$1, $2, $3, $4, $5, $6,
$7, $8, $9, $10, $11, $12
)
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
RETURNING id,
first_name,
last_name,
@ -29,7 +39,6 @@ RETURNING id,
updated_at,
suspended,
company_id;
-- name: GetUserByID :one
SELECT *
FROM users
@ -57,6 +66,19 @@ wHERE (
company_id = $2
OR $2 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
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
-- name: GetTotalUsers :one
SELECT COUNT(*)

View File

@ -20,8 +20,27 @@ SELECT users.id, users.first_name, users.last_name, users.email, users.phone_num
FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id
JOIN branches ON branches.id = branch_id
WHERE (
full_name ILIKE '%' || $1 || '%'
OR phone_number ILIKE '%' || $1 || '%'
OR $1 IS NULL
)
AND (
users.created_at > $2
OR $2 IS NULL
)
AND (
users.created_at < $3
OR $3 IS NULL
)
`
type GetAllCashiersParams struct {
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamptz `json:"created_before"`
CreatedAfter pgtype.Timestamptz `json:"created_after"`
}
type GetAllCashiersRow struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
@ -45,8 +64,8 @@ type GetAllCashiersRow struct {
BranchLocation string `json:"branch_location"`
}
func (q *Queries) GetAllCashiers(ctx context.Context) ([]GetAllCashiersRow, error) {
rows, err := q.db.Query(ctx, GetAllCashiers)
func (q *Queries) GetAllCashiers(ctx context.Context, arg GetAllCashiersParams) ([]GetAllCashiersRow, error) {
rows, err := q.db.Query(ctx, GetAllCashiers, arg.Query, arg.CreatedBefore, arg.CreatedAfter)
if err != nil {
return nil, err
}

View File

@ -580,6 +580,7 @@ type WalletThresholdNotification struct {
type WalletTransfer 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"`

View File

@ -142,16 +142,39 @@ wHERE (
cashier_id = $3
OR $3 IS NULL
)
AND (
full_name ILIKE '%' || $4 || '%'
OR phone_number ILIKE '%' || $4 || '%'
OR $4 IS NULL
)
AND (
created_at > $5
OR $5 IS NULL
)
AND (
created_at < $6
OR $6 IS NULL
)
`
type GetAllTransactionsParams struct {
BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
}
func (q *Queries) GetAllTransactions(ctx context.Context, arg GetAllTransactionsParams) ([]Transaction, error) {
rows, err := q.db.Query(ctx, GetAllTransactions, arg.BranchID, arg.CompanyID, arg.CashierID)
rows, err := q.db.Query(ctx, GetAllTransactions,
arg.BranchID,
arg.CompanyID,
arg.CashierID,
arg.Query,
arg.CreatedBefore,
arg.CreatedAfter,
)
if err != nil {
return nil, err
}

View File

@ -14,6 +14,7 @@ import (
const CreateTransfer = `-- name: CreateTransfer :one
INSERT INTO wallet_transfer (
amount,
message,
type,
receiver_wallet_id,
sender_wallet_id,
@ -23,12 +24,13 @@ INSERT INTO wallet_transfer (
status,
payment_method
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, amount, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id, amount, message, type, receiver_wallet_id, sender_wallet_id, cashier_id, verified, reference_number, status, payment_method, created_at, updated_at
`
type CreateTransferParams struct {
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"`
@ -42,6 +44,7 @@ type CreateTransferParams struct {
func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams) (WalletTransfer, error) {
row := q.db.QueryRow(ctx, CreateTransfer,
arg.Amount,
arg.Message,
arg.Type,
arg.ReceiverWalletID,
arg.SenderWalletID,
@ -55,6 +58,7 @@ func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams)
err := row.Scan(
&i.ID,
&i.Amount,
&i.Message,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
@ -70,7 +74,7 @@ func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams)
}
const GetAllTransfers = `-- name: GetAllTransfers :many
SELECT id, amount, 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
FROM wallet_transfer
`
@ -86,6 +90,7 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.Message,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
@ -108,7 +113,7 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
}
const GetTransferByID = `-- name: GetTransferByID :one
SELECT id, amount, 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
FROM wallet_transfer
WHERE id = $1
`
@ -119,6 +124,7 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
err := row.Scan(
&i.ID,
&i.Amount,
&i.Message,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
@ -134,7 +140,7 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
}
const GetTransferByReference = `-- name: GetTransferByReference :one
SELECT id, amount, 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
FROM wallet_transfer
WHERE reference_number = $1
`
@ -145,6 +151,7 @@ func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber st
err := row.Scan(
&i.ID,
&i.Amount,
&i.Message,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
@ -160,7 +167,7 @@ func (q *Queries) GetTransferByReference(ctx context.Context, referenceNumber st
}
const GetTransfersByWallet = `-- name: GetTransfersByWallet :many
SELECT id, amount, 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
FROM wallet_transfer
WHERE receiver_wallet_id = $1
OR sender_wallet_id = $1
@ -178,6 +185,7 @@ func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgt
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.Message,
&i.Type,
&i.ReceiverWalletID,
&i.SenderWalletID,
@ -200,7 +208,9 @@ func (q *Queries) GetTransfersByWallet(ctx context.Context, receiverWalletID pgt
}
const GetWalletTransactionsInRange = `-- name: GetWalletTransactionsInRange :many
SELECT type, COUNT(*) as count, SUM(amount) as total_amount
SELECT type,
COUNT(*) as count,
SUM(amount) as total_amount
FROM wallet_transfer
WHERE created_at BETWEEN $1 AND $2
GROUP BY type

View File

@ -45,23 +45,33 @@ func (q *Queries) CheckPhoneEmailExist(ctx context.Context, arg CheckPhoneEmailE
const CreateUser = `-- name: CreateUser :one
INSERT INTO users (
first_name,
last_name,
email,
phone_number,
role,
password,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
first_name,
last_name,
email,
phone_number,
role,
password,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
company_id
)
VALUES (
$1, $2, $3, $4, $5, $6,
$7, $8, $9, $10, $11, $12
)
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
RETURNING id,
first_name,
last_name,
@ -172,14 +182,30 @@ wHERE (
company_id = $2
OR $2 IS NULL
)
LIMIT $4 OFFSET $3
AND (
full_name ILIKE '%' || $3 || '%'
OR phone_number ILIKE '%' || $3 || '%'
OR $3 IS NULL
)
AND (
created_at > $4
OR $4 IS NULL
)
AND (
created_at < $5
OR $5 IS NULL
)
LIMIT $7 OFFSET $6
`
type GetAllUsersParams struct {
Role string `json:"role"`
CompanyID pgtype.Int8 `json:"company_id"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
Role string `json:"role"`
CompanyID pgtype.Int8 `json:"company_id"`
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamptz `json:"created_before"`
CreatedAfter pgtype.Timestamptz `json:"created_after"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetAllUsersRow struct {
@ -202,6 +228,9 @@ func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]Get
rows, err := q.db.Query(ctx, GetAllUsers,
arg.Role,
arg.CompanyID,
arg.Query,
arg.CreatedBefore,
arg.CreatedAfter,
arg.Offset,
arg.Limit,
)

View File

@ -49,9 +49,12 @@ type Transaction struct {
}
type TransactionFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
CashierID ValidInt64
CompanyID ValidInt64
BranchID ValidInt64
CashierID ValidInt64
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type CreateTransaction struct {
Amount Currency
@ -63,13 +66,13 @@ type CreateTransaction struct {
PaymentOption PaymentOption
FullName string
PhoneNumber string
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
BranchName string
BranchLocation string
CompanyID int64
CashierName string
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
BranchName string
BranchLocation string
CompanyID int64
CashierName string
}

View File

@ -36,6 +36,7 @@ type Transfer 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"`
@ -50,6 +51,7 @@ type Transfer struct {
type CreateTransfer struct {
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"`

View File

@ -30,6 +30,20 @@ type User struct {
CompanyID ValidInt64
}
type UserFilter struct {
Role string
CompanyID ValidInt64
Page ValidInt
PageSize ValidInt
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type ValidRole struct {
Value Role
Valid bool
}
type RegisterUserReq struct {
FirstName string
LastName string

View File

@ -99,6 +99,18 @@ func (s *Store) GetAllTransactions(ctx context.Context, filter domain.Transactio
Int64: filter.CashierID.Value,
Valid: filter.CashierID.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 {

View File

@ -36,8 +36,9 @@ func convertDBTransfer(transfer dbgen.WalletTransfer) domain.Transfer {
func convertCreateTransfer(transfer domain.CreateTransfer) dbgen.CreateTransferParams {
return dbgen.CreateTransferParams{
Amount: pgtype.Int8{Int64: int64(transfer.Amount), Valid: true},
Type: pgtype.Text{String: string(transfer.Type), Valid: true},
Message: transfer.Message,
Amount: pgtype.Int8{Int64: int64(transfer.Amount), Valid: true},
Type: pgtype.Text{String: string(transfer.Type), Valid: true},
ReceiverWalletID: pgtype.Int8{
Int64: transfer.ReceiverWalletID.Value,
Valid: transfer.ReceiverWalletID.Valid,

View File

@ -9,7 +9,6 @@ import (
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/jackc/pgx/v5/pgtype"
)
@ -88,7 +87,7 @@ func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error)
},
}, nil
}
func (s *Store) GetAllUsers(ctx context.Context, filter user.Filter) ([]domain.User, int64, error) {
func (s *Store) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) {
users, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{
Role: filter.Role,
CompanyID: pgtype.Int8{
@ -103,6 +102,18 @@ func (s *Store) GetAllUsers(ctx context.Context, filter user.Filter) ([]domain.U
Int32: int32(filter.Page.Value),
Valid: filter.Page.Valid,
},
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 {
return nil, 0, err

View File

@ -275,7 +275,8 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
branch.WalletID, domain.ToCurrency(deductedAmount), domain.BranchWalletType, domain.ValidInt64{
Value: userID,
Valid: true,
}, domain.TRANSFER_DIRECT)
}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", deductedAmount))
if err != nil {
s.mongoLogger.Error("failed to deduct from wallet",
zap.Int64("wallet_id", branch.WalletID),
@ -311,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{
Value: userID,
Valid: true,
}, domain.TRANSFER_DIRECT)
}, domain.TRANSFER_DIRECT, fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", deductedAmount))
if err != nil {
s.mongoLogger.Error("wallet deduction failed",
zap.Int64("wallet_id", branch.WalletID),
@ -337,7 +338,8 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
}
if req.Amount < wallets.RegularBalance.Float32() {
_, err = s.walletSvc.DeductFromWallet(ctx, wallets.RegularID,
domain.ToCurrency(req.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
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))
if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer regular wallet",
zap.Int64("customer_id", wallets.CustomerID),
@ -355,7 +357,8 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
}
// Empty the regular balance
_, 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()))
if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer regular wallet",
zap.Int64("customer_id", wallets.CustomerID),
@ -369,7 +372,8 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
// 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)
remainingAmount, domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing bet", remainingAmount.Float32()))
if err != nil {
s.mongoLogger.Error("wallet deduction failed for customer static wallet",
zap.Int64("customer_id", wallets.CustomerID),
@ -754,7 +758,8 @@ func (s *Service) UpdateStatus(ctx context.Context, id int64, status domain.Outc
amount = bet.Amount
}
_, err = s.walletSvc.AddToWallet(ctx, customerWallet.RegularID, amount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
_, 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()))
if err != nil {
s.mongoLogger.Error("failed to add winnings to wallet",

View File

@ -83,6 +83,7 @@ func (s *Service) InitiateDeposit(ctx context.Context, userID int64, amount doma
// Create payment record
transfer := domain.CreateTransfer{
Message: fmt.Sprintf("Depositing %d into wallet using chapa. Reference Number %s", amount, reference),
Amount: amount,
Type: domain.DEPOSIT,
PaymentMethod: domain.TRANSFER_CHAPA,
@ -167,8 +168,9 @@ func (s *Service) InitiateWithdrawal(ctx context.Context, userID int64, req doma
reference := uuid.New().String()
createTransfer := domain.CreateTransfer{
Amount: domain.Currency(amount),
Type: domain.WITHDRAW,
Message: fmt.Sprintf("Withdrawing %d into wallet using chapa. Reference Number %s", amount, reference),
Amount: domain.Currency(amount),
Type: domain.WITHDRAW,
SenderWalletID: domain.ValidInt64{
Value: withdrawWallet.ID,
Valid: true,
@ -265,7 +267,10 @@ func (s *Service) ManuallyVerify(ctx context.Context, txRef string) (*domain.Cha
}
// Credit wallet
if _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{}); err != nil {
_, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value,
transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_CHAPA, domain.PaymentDetails{},
fmt.Sprintf("Added %d to wallet using Chapa", transfer.Amount.Float32()))
if err != nil {
return nil, fmt.Errorf("failed to credit wallet: %w", err)
}
}
@ -325,7 +330,7 @@ func (s *Service) HandleVerifyDepositWebhook(ctx context.Context, transfer domai
ReferenceNumber: domain.ValidString{
Value: transfer.Reference,
},
}); err != nil {
}, fmt.Sprintf("Added %d to wallet using Chapa")); err != nil {
return fmt.Errorf("failed to credit user wallet: %w", err)
}
}
@ -361,7 +366,10 @@ func (s *Service) HandleVerifyWithdrawWebhook(ctx context.Context, payment domai
return fmt.Errorf("failed to update payment status: %w", err)
} // If payment is completed, credit user's walle
} else {
if _, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}); err != nil {
_, err := s.walletStore.AddToWallet(ctx, transfer.SenderWalletID.Value, transfer.Amount, domain.ValidInt64{},
domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Added %d to wallet by system because chapa withdraw is unsuccessful"))
if err != nil {
return fmt.Errorf("failed to credit user wallet: %w", err)
}
}

View File

@ -43,22 +43,9 @@ func (s *Service) DeleteUser(ctx context.Context, id int64) error {
return s.userStore.DeleteUser(ctx, id)
}
type Filter struct {
Role string
CompanyID domain.ValidInt64
Page domain.ValidInt
PageSize domain.ValidInt
}
type ValidRole struct {
Value domain.Role
Valid bool
}
type ValidBranchId struct {
Value int64
Valid bool
}
func (s *Service) GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, int64, error) {
func (s *Service) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) {
// Get all Users
return s.userStore.GetAllUsers(ctx, filter)
}

View File

@ -206,8 +206,10 @@ func (s *service) ProcessBet(ctx context.Context, req *domain.PopOKBetRequest) (
if err != nil {
return &domain.PopOKBetResponse{}, fmt.Errorf("Failed to read user wallets")
}
if _, err := s.walletSvc.DeductFromWallet(ctx, claims.UserID, domain.Currency(amountCents), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT); err != nil {
_, err = s.walletSvc.DeductFromWallet(ctx, claims.UserID, domain.Currency(amountCents),
domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducted %d amount from wallet by system while placing virtual game bet", amountCents))
if err != nil {
return nil, fmt.Errorf("insufficient balance")
}

View File

@ -42,7 +42,6 @@ func (s *Service) UpdateTransferStatus(ctx context.Context, id int64, status str
return s.transferStore.UpdateTransferStatus(ctx, id, status)
}
func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64,
amount domain.Currency, paymentMethod domain.PaymentMethod,
cashierID domain.ValidInt64) (domain.Transfer, error) {
@ -85,6 +84,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
transfer, err := s.CreateTransfer(ctx, domain.CreateTransfer{
Message: fmt.Sprintf("Transferring %d to another wallet", amount),
SenderWalletID: domain.ValidInt64{
Value: senderID,
Valid: true,
@ -119,7 +119,7 @@ func (s *Service) SendTransferNotification(ctx context.Context, senderWallet dom
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: "Wallet has been deducted",
Message: fmt.Sprintf(`%s %d has been transferred from your wallet`,senderWallet.Currency, amount),
Message: fmt.Sprintf(`%s %d has been transferred from your wallet`, senderWallet.Currency, amount),
},
Priority: 2,
Metadata: []byte(fmt.Sprintf(`{
@ -148,7 +148,7 @@ func (s *Service) SendTransferNotification(ctx context.Context, senderWallet dom
DeliveryChannel: domain.DeliveryChannelInApp,
Payload: domain.NotificationPayload{
Headline: "Wallet has been credited",
Message: fmt.Sprintf(`%s %d has been transferred to your wallet`,receiverWallet.Currency, amount),
Message: fmt.Sprintf(`%s %d has been transferred to your wallet`, receiverWallet.Currency, amount),
},
Priority: 2,
Metadata: []byte(fmt.Sprintf(`{

View File

@ -84,7 +84,7 @@ func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Cu
}
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, message string) (domain.Transfer, error) {
wallet, err := s.GetWalletByID(ctx, id)
if err != nil {
return domain.Transfer{}, err
@ -99,6 +99,7 @@ func (s *Service) AddToWallet(
// Log the transfer here for reference
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
Message: message,
Amount: amount,
Verified: true,
ReceiverWalletID: domain.ValidInt64{
@ -114,7 +115,7 @@ func (s *Service) AddToWallet(
return newTransfer, err
}
func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.Currency, walletType domain.WalletType, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod) (domain.Transfer, error) {
func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.Currency, walletType domain.WalletType, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, message string) (domain.Transfer, error) {
wallet, err := s.GetWalletByID(ctx, id)
if err != nil {
return domain.Transfer{}, err
@ -138,6 +139,7 @@ func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.
// Log the transfer here for reference
newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{
Message: message,
Amount: amount,
Verified: true,
SenderWalletID: domain.ValidInt64{

View File

@ -66,7 +66,7 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S
}
for _, job := range schedule {
// job.task()
job.task()
if _, err := c.AddFunc(job.spec, job.task); err != nil {
log.Fatalf("Failed to schedule cron job: %v", err)
}

View File

@ -6,7 +6,6 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"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/gofiber/fiber/v2"
"go.uber.org/zap"
@ -155,7 +154,41 @@ type AdminRes struct {
// @Failure 500 {object} response.APIResponse
// @Router /admin [get]
func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
filter := user.Filter{
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,
}
}
filter := domain.UserFilter{
Role: string(domain.RoleAdmin),
CompanyID: domain.ValidInt64{
Value: int64(c.QueryInt("company_id")),
@ -169,6 +202,9 @@ func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
valErrs, ok := h.validator.Validate(c, filter)

View File

@ -124,7 +124,40 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
if role != domain.RoleSuperAdmin && !companyId.Valid {
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID")
}
filter := user.Filter{
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,
}
}
filter := domain.UserFilter{
Role: string(domain.RoleCashier),
CompanyID: companyId,
Page: domain.ValidInt{
@ -135,6 +168,9 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
valErrs, ok := h.validator.Validate(c, filter)

View File

@ -111,12 +111,47 @@ type ManagersRes struct {
func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role)
companyId := c.Locals("company_id").(domain.ValidInt64)
// Checking to make sure that admin user has a company id in the token
if role != domain.RoleSuperAdmin && !companyId.Valid {
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID")
}
filter := user.Filter{
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,
}
}
filter := domain.UserFilter{
Role: string(domain.RoleBranchManager),
CompanyID: companyId,
Page: domain.ValidInt{
@ -127,6 +162,9 @@ func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
valErrs, ok := h.validator.Validate(c, filter)
if !ok {

View File

@ -230,12 +230,47 @@ func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
var transactions []domain.Transaction
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,
}
}
// Check user role and fetch transactions accordingly
transactions, err := h.transactionSvc.GetAllTransactions(c.Context(), domain.TransactionFilter{
CompanyID: companyID,
BranchID: branchID,
CompanyID: companyID,
BranchID: branchID,
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil {

View File

@ -233,7 +233,7 @@ func (h *Handler) RefillWallet(c *fiber.Ctx) error {
c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{
Value: userID,
Valid: true,
}, domain.TRANSFER_BANK, domain.PaymentDetails{})
}, domain.TRANSFER_BANK, domain.PaymentDetails{}, fmt.Sprintf("Added %d to wallet directly by super-admin", req.Amount))
if !ok {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil)

View File

@ -189,7 +189,8 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
// TODO: Remove later
_, err = h.walletSvc.AddToWallet(
c.Context(), newWallet.RegularID, 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{},
"Added 100.0 to wallet only as test for deployment")
if err != nil {
h.logger.Error("Failed to update wallet for user", "userID", newUser.ID, "error", err)