diff --git a/db/migrations/000001_fortune.up.sql b/db/migrations/000001_fortune.up.sql index 6ed5000..622b628 100644 --- a/db/migrations/000001_fortune.up.sql +++ b/db/migrations/000001_fortune.up.sql @@ -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, diff --git a/db/query/cashier.sql b/db/query/cashier.sql index ad885a0..974222e 100644 --- a/db/query/cashier.sql +++ b/db/query/cashier.sql @@ -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, diff --git a/db/query/transactions.sql b/db/query/transactions.sql index e63d5a9..199e987 100644 --- a/db/query/transactions.sql +++ b/db/query/transactions.sql @@ -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 * diff --git a/db/query/transfer.sql b/db/query/transfer.sql index b4cc137..13f7658 100644 --- a/db/query/transfer.sql +++ b/db/query/transfer.sql @@ -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; \ No newline at end of file diff --git a/db/query/user.sql b/db/query/user.sql index 515bfe8..235d88d 100644 --- a/db/query/user.sql +++ b/db/query/user.sql @@ -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(*) diff --git a/gen/db/cashier.sql.go b/gen/db/cashier.sql.go index 113771c..e055e5e 100644 --- a/gen/db/cashier.sql.go +++ b/gen/db/cashier.sql.go @@ -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 } diff --git a/gen/db/models.go b/gen/db/models.go index 3ba6c5e..a03a537 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -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"` diff --git a/gen/db/transactions.sql.go b/gen/db/transactions.sql.go index cbd5743..6bf08fc 100644 --- a/gen/db/transactions.sql.go +++ b/gen/db/transactions.sql.go @@ -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 } diff --git a/gen/db/transfer.sql.go b/gen/db/transfer.sql.go index 5055d84..236ea5a 100644 --- a/gen/db/transfer.sql.go +++ b/gen/db/transfer.sql.go @@ -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 diff --git a/gen/db/user.sql.go b/gen/db/user.sql.go index 89051b2..b838343 100644 --- a/gen/db/user.sql.go +++ b/gen/db/user.sql.go @@ -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, ) diff --git a/internal/domain/transaction.go b/internal/domain/transaction.go index 427ec81..195369e 100644 --- a/internal/domain/transaction.go +++ b/internal/domain/transaction.go @@ -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 } diff --git a/internal/domain/transfer.go b/internal/domain/transfer.go index a518aec..8524cd4 100644 --- a/internal/domain/transfer.go +++ b/internal/domain/transfer.go @@ -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"` diff --git a/internal/domain/user.go b/internal/domain/user.go index 4bb3ef4..eb49ec6 100644 --- a/internal/domain/user.go +++ b/internal/domain/user.go @@ -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 diff --git a/internal/repository/transaction.go b/internal/repository/transaction.go index edc8184..48fd0ce 100644 --- a/internal/repository/transaction.go +++ b/internal/repository/transaction.go @@ -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 { diff --git a/internal/repository/transfer.go b/internal/repository/transfer.go index c432f9d..5b8821a 100644 --- a/internal/repository/transfer.go +++ b/internal/repository/transfer.go @@ -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, diff --git a/internal/repository/user.go b/internal/repository/user.go index da3ed81..0f31933 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -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 diff --git a/internal/services/bet/service.go b/internal/services/bet/service.go index fb52bc0..8f7d23d 100644 --- a/internal/services/bet/service.go +++ b/internal/services/bet/service.go @@ -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", diff --git a/internal/services/chapa/service.go b/internal/services/chapa/service.go index 96d5145..e718f45 100644 --- a/internal/services/chapa/service.go +++ b/internal/services/chapa/service.go @@ -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) } } diff --git a/internal/services/user/direct.go b/internal/services/user/direct.go index f8bd8f9..b8217b9 100644 --- a/internal/services/user/direct.go +++ b/internal/services/user/direct.go @@ -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) } diff --git a/internal/services/virtualGame/service.go b/internal/services/virtualGame/service.go index e413993..44ea049 100644 --- a/internal/services/virtualGame/service.go +++ b/internal/services/virtualGame/service.go @@ -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") } diff --git a/internal/services/wallet/transfer.go b/internal/services/wallet/transfer.go index b9e269e..cb7ad67 100644 --- a/internal/services/wallet/transfer.go +++ b/internal/services/wallet/transfer.go @@ -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(`{ diff --git a/internal/services/wallet/wallet.go b/internal/services/wallet/wallet.go index eab19f1..a5c8ba3 100644 --- a/internal/services/wallet/wallet.go +++ b/internal/services/wallet/wallet.go @@ -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{ diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 58e9e24..7e39391 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -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) } diff --git a/internal/web_server/handlers/admin.go b/internal/web_server/handlers/admin.go index 8e282f1..23b86e7 100644 --- a/internal/web_server/handlers/admin.go +++ b/internal/web_server/handlers/admin.go @@ -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) diff --git a/internal/web_server/handlers/cashier.go b/internal/web_server/handlers/cashier.go index 6dc18e7..0155f45 100644 --- a/internal/web_server/handlers/cashier.go +++ b/internal/web_server/handlers/cashier.go @@ -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) diff --git a/internal/web_server/handlers/manager.go b/internal/web_server/handlers/manager.go index 02c1496..3fec76f 100644 --- a/internal/web_server/handlers/manager.go +++ b/internal/web_server/handlers/manager.go @@ -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 { diff --git a/internal/web_server/handlers/transaction_handler.go b/internal/web_server/handlers/transaction_handler.go index 77189dd..817f6b0 100644 --- a/internal/web_server/handlers/transaction_handler.go +++ b/internal/web_server/handlers/transaction_handler.go @@ -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 { diff --git a/internal/web_server/handlers/transfer_handler.go b/internal/web_server/handlers/transfer_handler.go index 428ff5a..54d2454 100644 --- a/internal/web_server/handlers/transfer_handler.go +++ b/internal/web_server/handlers/transfer_handler.go @@ -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) diff --git a/internal/web_server/handlers/user.go b/internal/web_server/handlers/user.go index a088698..853781f 100644 --- a/internal/web_server/handlers/user.go +++ b/internal/web_server/handlers/user.go @@ -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)