From c4328dedf0101ac46850ea8f1da829c22a9524ff Mon Sep 17 00:00:00 2001 From: Samuel Tariku Date: Wed, 2 Jul 2025 17:48:16 +0300 Subject: [PATCH] fix: get bet using cashout id --- cmd/main.go | 2 +- db/query/shop_transactions.sql | 4 + gen/db/shop_transactions.sql.go | 32 ++ gen/db/transactions.sql.go | 334 ------------------- internal/repository/shop_bet.go | 8 + internal/repository/user.go | 1 + internal/services/transaction/port.go | 1 + internal/services/transaction/shop_bet.go | 10 +- internal/web_server/handlers/shop_handler.go | 83 ++++- internal/web_server/routes.go | 2 + 10 files changed, 137 insertions(+), 340 deletions(-) delete mode 100644 gen/db/transactions.sql.go diff --git a/cmd/main.go b/cmd/main.go index 7ffe4dc..495954d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -152,7 +152,7 @@ func main() { cfg.FIXER_API_KEY, cfg.FIXER_BASE_URL, ) - transactionSvc := transaction.NewService(store, *branchSvc) + transactionSvc := transaction.NewService(store, *branchSvc, *betSvc, *walletSvc) reportSvc := report.NewService( bet.BetStore(store), diff --git a/db/query/shop_transactions.sql b/db/query/shop_transactions.sql index 80d9848..4896008 100644 --- a/db/query/shop_transactions.sql +++ b/db/query/shop_transactions.sql @@ -113,6 +113,10 @@ WHERE id = $1; SELECT * FROM shop_bet_detail WHERE bet_id = $1; +-- name: GetShopBetByCashoutID :one +SELECT * +FROM shop_bet_detail +WHERE cashout_id = $1; -- name: GetShopBetByShopTransactionID :one SELECT * FROM shop_bet_detail diff --git a/gen/db/shop_transactions.sql.go b/gen/db/shop_transactions.sql.go index 84f4c87..6018736 100644 --- a/gen/db/shop_transactions.sql.go +++ b/gen/db/shop_transactions.sql.go @@ -448,6 +448,38 @@ func (q *Queries) GetShopBetByBetID(ctx context.Context, betID int64) (ShopBetDe return i, err } +const GetShopBetByCashoutID = `-- name: GetShopBetByCashoutID :one +SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes +FROM shop_bet_detail +WHERE cashout_id = $1 +` + +func (q *Queries) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (ShopBetDetail, error) { + row := q.db.QueryRow(ctx, GetShopBetByCashoutID, cashoutID) + var i ShopBetDetail + err := row.Scan( + &i.ID, + &i.ShopTransactionID, + &i.CashoutID, + &i.CashedOutBy, + &i.BetID, + &i.NumberOfOutcomes, + &i.CashedOut, + &i.CreatedAt, + &i.UpdatedAt, + &i.CustomerFullName, + &i.CustomerPhoneNumber, + &i.BranchID, + &i.CompanyID, + &i.Amount, + &i.TransactionVerified, + &i.Status, + &i.TotalOdds, + &i.Outcomes, + ) + return i, err +} + const GetShopBetByID = `-- name: GetShopBetByID :one SELECT id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at, customer_full_name, customer_phone_number, branch_id, company_id, amount, transaction_verified, status, total_odds, outcomes FROM shop_bet_detail diff --git a/gen/db/transactions.sql.go b/gen/db/transactions.sql.go deleted file mode 100644 index 2050b97..0000000 --- a/gen/db/transactions.sql.go +++ /dev/null @@ -1,334 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: transactions.sql - -package dbgen - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const CreateShopTransaction = `-- name: CreateShopTransaction :one -INSERT INTO shop_transactions ( - amount, - branch_id, - cashier_id, - bet_id, - type, - payment_option, - full_name, - phone_number, - bank_code, - beneficiary_name, - account_name, - account_number, - reference_number, - number_of_outcomes, - branch_name, - branch_location, - company_id, - cashier_name - ) -VALUES ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16, - $17, - $18 - ) -RETURNING id, amount, branch_id, company_id, cashier_id, cashier_name, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, approved_by, approver_name, branch_location, branch_name, created_at, updated_at -` - -type CreateShopTransactionParams struct { - Amount int64 `json:"amount"` - BranchID int64 `json:"branch_id"` - CashierID pgtype.Int8 `json:"cashier_id"` - BetID pgtype.Int8 `json:"bet_id"` - Type pgtype.Int8 `json:"type"` - PaymentOption pgtype.Int8 `json:"payment_option"` - FullName pgtype.Text `json:"full_name"` - PhoneNumber pgtype.Text `json:"phone_number"` - BankCode pgtype.Text `json:"bank_code"` - BeneficiaryName pgtype.Text `json:"beneficiary_name"` - AccountName pgtype.Text `json:"account_name"` - AccountNumber pgtype.Text `json:"account_number"` - ReferenceNumber pgtype.Text `json:"reference_number"` - NumberOfOutcomes pgtype.Int8 `json:"number_of_outcomes"` - BranchName pgtype.Text `json:"branch_name"` - BranchLocation pgtype.Text `json:"branch_location"` - CompanyID pgtype.Int8 `json:"company_id"` - CashierName pgtype.Text `json:"cashier_name"` -} - -func (q *Queries) CreateShopTransaction(ctx context.Context, arg CreateShopTransactionParams) (ShopTransaction, error) { - row := q.db.QueryRow(ctx, CreateShopTransaction, - arg.Amount, - arg.BranchID, - arg.CashierID, - arg.BetID, - arg.Type, - arg.PaymentOption, - arg.FullName, - arg.PhoneNumber, - arg.BankCode, - arg.BeneficiaryName, - arg.AccountName, - arg.AccountNumber, - arg.ReferenceNumber, - arg.NumberOfOutcomes, - arg.BranchName, - arg.BranchLocation, - arg.CompanyID, - arg.CashierName, - ) - var i ShopTransaction - err := row.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.CashierID, - &i.CashierName, - &i.BetID, - &i.NumberOfOutcomes, - &i.Type, - &i.PaymentOption, - &i.FullName, - &i.PhoneNumber, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.Verified, - &i.ApprovedBy, - &i.ApproverName, - &i.BranchLocation, - &i.BranchName, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const GetAllShopTransactions = `-- name: GetAllShopTransactions :many -SELECT id, amount, branch_id, company_id, cashier_id, cashier_name, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, approved_by, approver_name, branch_location, branch_name, created_at, updated_at -FROM shop_transactions -wHERE ( - branch_id = $1 - OR $1 IS NULL - ) - AND ( - company_id = $2 - OR $2 IS NULL - ) - AND ( - cashier_id = $3 - OR $3 IS NULL - ) - 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 GetAllShopTransactionsParams struct { - 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) GetAllShopTransactions(ctx context.Context, arg GetAllShopTransactionsParams) ([]ShopTransaction, error) { - rows, err := q.db.Query(ctx, GetAllShopTransactions, - arg.BranchID, - arg.CompanyID, - arg.CashierID, - arg.Query, - arg.CreatedBefore, - arg.CreatedAfter, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ShopTransaction - for rows.Next() { - var i ShopTransaction - if err := rows.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.CashierID, - &i.CashierName, - &i.BetID, - &i.NumberOfOutcomes, - &i.Type, - &i.PaymentOption, - &i.FullName, - &i.PhoneNumber, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.Verified, - &i.ApprovedBy, - &i.ApproverName, - &i.BranchLocation, - &i.BranchName, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetShopTransactionByBranch = `-- name: GetShopTransactionByBranch :many -SELECT id, amount, branch_id, company_id, cashier_id, cashier_name, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, approved_by, approver_name, branch_location, branch_name, created_at, updated_at -FROM shop_transactions -WHERE branch_id = $1 -` - -func (q *Queries) GetShopTransactionByBranch(ctx context.Context, branchID int64) ([]ShopTransaction, error) { - rows, err := q.db.Query(ctx, GetShopTransactionByBranch, branchID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ShopTransaction - for rows.Next() { - var i ShopTransaction - if err := rows.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.CashierID, - &i.CashierName, - &i.BetID, - &i.NumberOfOutcomes, - &i.Type, - &i.PaymentOption, - &i.FullName, - &i.PhoneNumber, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.Verified, - &i.ApprovedBy, - &i.ApproverName, - &i.BranchLocation, - &i.BranchName, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetShopTransactionByID = `-- name: GetShopTransactionByID :one -SELECT id, amount, branch_id, company_id, cashier_id, cashier_name, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, approved_by, approver_name, branch_location, branch_name, created_at, updated_at -FROM shop_transactions -WHERE id = $1 -` - -func (q *Queries) GetShopTransactionByID(ctx context.Context, id int64) (ShopTransaction, error) { - row := q.db.QueryRow(ctx, GetShopTransactionByID, id) - var i ShopTransaction - err := row.Scan( - &i.ID, - &i.Amount, - &i.BranchID, - &i.CompanyID, - &i.CashierID, - &i.CashierName, - &i.BetID, - &i.NumberOfOutcomes, - &i.Type, - &i.PaymentOption, - &i.FullName, - &i.PhoneNumber, - &i.BankCode, - &i.BeneficiaryName, - &i.AccountName, - &i.AccountNumber, - &i.ReferenceNumber, - &i.Verified, - &i.ApprovedBy, - &i.ApproverName, - &i.BranchLocation, - &i.BranchName, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const UpdateShopTransactionVerified = `-- name: UpdateShopTransactionVerified :exec -UPDATE shop_transactions -SET verified = $2, - approved_by = $3, - approver_name = $4, - updated_at = CURRENT_TIMESTAMP -WHERE id = $1 -` - -type UpdateShopTransactionVerifiedParams struct { - ID int64 `json:"id"` - Verified bool `json:"verified"` - ApprovedBy pgtype.Int8 `json:"approved_by"` - ApproverName pgtype.Text `json:"approver_name"` -} - -func (q *Queries) UpdateShopTransactionVerified(ctx context.Context, arg UpdateShopTransactionVerifiedParams) error { - _, err := q.db.Exec(ctx, UpdateShopTransactionVerified, - arg.ID, - arg.Verified, - arg.ApprovedBy, - arg.ApproverName, - ) - return err -} diff --git a/internal/repository/shop_bet.go b/internal/repository/shop_bet.go index e436a7e..d431dce 100644 --- a/internal/repository/shop_bet.go +++ b/internal/repository/shop_bet.go @@ -116,6 +116,14 @@ func (s *Store) GetShopBetByBetID(ctx context.Context, betID int64) (domain.Shop } return convertDBShopBetDetail(bet), nil } + +func (s *Store) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (domain.ShopBetDetail, error) { + bet, err := s.queries.GetShopBetByCashoutID(ctx, cashoutID) + if err != nil { + return domain.ShopBetDetail{}, err + } + return convertDBShopBetDetail(bet), nil +} func (s *Store) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) { bet, err := s.queries.GetShopBetByShopTransactionID(ctx, shopTransactionID) if err != nil { diff --git a/internal/repository/user.go b/internal/repository/user.go index 079ef52..e7259f9 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -88,6 +88,7 @@ func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) }, nil } func (s *Store) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) { + fmt.Printf("\n\nuser_filter %v \n\n", filter) users, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{ Role: filter.Role, CompanyID: pgtype.Int8{ diff --git a/internal/services/transaction/port.go b/internal/services/transaction/port.go index 4b41278..ebaa694 100644 --- a/internal/services/transaction/port.go +++ b/internal/services/transaction/port.go @@ -19,6 +19,7 @@ type TransactionStore interface { GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter) ([]domain.ShopBetDetail, error) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) GetShopBetByBetID(ctx context.Context, betID int64) (domain.ShopBetDetail, error) + GetShopBetByCashoutID(ctx context.Context, cashoutID string) (domain.ShopBetDetail, error) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) UpdateShopBetCashOut(ctx context.Context, id int64, cashedOut bool) error UpdateShopBetCashoutID(ctx context.Context, id int64, cashoutID string) error diff --git a/internal/services/transaction/shop_bet.go b/internal/services/transaction/shop_bet.go index fb7ef7e..0a301f4 100644 --- a/internal/services/transaction/shop_bet.go +++ b/internal/services/transaction/shop_bet.go @@ -37,7 +37,7 @@ func (s *Service) GenerateCashoutID() (string, error) { return string(result), nil } -func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.Role, userCompanyID domain.ValidInt64, req domain.ShopBetReq) (domain.ShopBet, error) { +func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.Role, userCompanyID domain.ValidInt64, req domain.ShopBetReq) (domain.ShopBet, error) { branchID, companyID, err := s.GetBranchByRole(ctx, req.BranchID, role, userID, userCompanyID) @@ -111,8 +111,8 @@ func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.R // return s.transactionStore.CreateShopBet(ctx, bet) // } -func (s *Service) CashoutBet(ctx context.Context, betID int64, userID int64, role domain.Role, req domain.CashoutReq) (domain.ShopTransaction, error) { - branchID, companyID, err := s.GetBranchByRole(ctx, req.BranchID, role, userID) +func (s *Service) CashoutBet(ctx context.Context, betID int64, userID int64, role domain.Role, req domain.CashoutReq, userCompanyID domain.ValidInt64) (domain.ShopTransaction, error) { + branchID, companyID, err := s.GetBranchByRole(ctx, req.BranchID, role, userID, userCompanyID) if err != nil { return domain.ShopTransaction{}, nil @@ -194,6 +194,10 @@ func (s *Service) GetShopBetByBetID(ctx context.Context, betID int64) (domain.Sh return s.transactionStore.GetShopBetByBetID(ctx, betID) } +func (s *Service) GetShopBetByCashoutID(ctx context.Context, cashoutID string) (domain.ShopBetDetail, error) { + return s.transactionStore.GetShopBetByCashoutID(ctx, cashoutID) +} + func (s *Service) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) { return s.transactionStore.GetShopBetByShopTransactionID(ctx, shopTransactionID) } diff --git a/internal/web_server/handlers/shop_handler.go b/internal/web_server/handlers/shop_handler.go index aeb17b6..5d0d99c 100644 --- a/internal/web_server/handlers/shop_handler.go +++ b/internal/web_server/handlers/shop_handler.go @@ -55,7 +55,7 @@ func (h *Handler) CreateShopBet(c *fiber.Ctx) error { // @Tags transaction // @Accept json // @Produce json -// @Param createBet body domain.CashoutReq true "cashout bet" +// @Param cashoutBet body domain.CashoutReq true "cashout bet" // @Success 200 {object} TransactionRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse @@ -73,6 +73,7 @@ func (h *Handler) CashoutBet(c *fiber.Ctx) error { userID := c.Locals("user_id").(int64) role := c.Locals("role").(domain.Role) + companyID := c.Locals("company_id").(domain.ValidInt64) var req domain.CashoutReq if err := c.BodyParser(&req); err != nil { @@ -86,7 +87,7 @@ func (h *Handler) CashoutBet(c *fiber.Ctx) error { return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) } - transaction, err := h.transactionSvc.CashoutBet(c.Context(), betID, userID, role, req) + transaction, err := h.transactionSvc.CashoutBet(c.Context(), betID, userID, role, req, companyID) if err != nil { return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to cashout bet", err, nil) @@ -96,6 +97,84 @@ func (h *Handler) CashoutBet(c *fiber.Ctx) error { return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) } +// CashoutByCashoutID godoc +// @Summary Cashout bet by cashoutID +// @Description Cashout bet by cashoutID +// @Tags transaction +// @Accept json +// @Produce json +// @Param cashoutBet body domain.CashoutReq true "cashout bet" +// @Success 200 {object} TransactionRes +// @Failure 400 {object} response.APIResponse +// @Failure 500 {object} response.APIResponse +// @Router /shop/cashout [post] +func (h *Handler) CashoutByCashoutID(c *fiber.Ctx) error { + + userID := c.Locals("user_id").(int64) + role := c.Locals("role").(domain.Role) + companyID := c.Locals("company_id").(domain.ValidInt64) + + var req domain.CashoutReq + if err := c.BodyParser(&req); err != nil { + h.logger.Error("CashoutReq failed to parse request", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil) + } + + valErrs, ok := h.validator.Validate(c, req) + if !ok { + h.logger.Error("CashoutReq failed v", "error", valErrs) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) + } + + bet, err := h.transactionSvc.GetShopBetByCashoutID(c.Context(), req.CashoutID) + + if err != nil { + h.logger.Error("CashoutReq failed invalid cashout id", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashout ID", err, nil) + } + + transaction, err := h.transactionSvc.CashoutBet(c.Context(), bet.BetID, userID, role, req, companyID) + + if err != nil { + return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to cashout bet", err, nil) + } + res := domain.ConvertShopTransaction(transaction) + + return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) +} + +// CashoutBet godoc +// @Summary Cashout bet at branch +// @Description Cashout bet at branch +// @Tags transaction +// @Accept json +// @Produce json +// @Param createBet body domain.CashoutReq true "cashout bet" +// @Success 200 {object} TransactionRes +// @Failure 400 {object} response.APIResponse +// @Failure 500 {object} response.APIResponse +// @Router /shop/cashout/{id} [get] +func (h *Handler) GetShopBetByCashoutID(c *fiber.Ctx) error { + + cashoutID := c.Params("id") + + if cashoutID == "" { + h.logger.Error("CashoutReq failed cashout id is required", "error", nil) + return response.WriteJSON(c, fiber.StatusBadRequest, "cashout ID is required", nil, nil) + } + + bet, err := h.transactionSvc.GetShopBetByCashoutID(c.Context(), cashoutID) + + if err != nil { + h.logger.Error("CashoutReq failed invalid cashout id", "error", err) + return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashout ID", err, nil) + } + + res := domain.ConvertShopBetDetail(bet) + + return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil) +} + // DepositForCustomer godoc // @Summary Shop deposit into customer wallet // @Description Transfers money from branch wallet to customer wallet diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 0ac9978..11172c1 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -275,6 +275,8 @@ func (a *App) initAppRoutes() { // Transactions /shop/transactions a.fiber.Post("/shop/bet", a.authMiddleware, a.CompanyOnly, h.CreateShopBet) a.fiber.Post("/shop/bet/:id/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutBet) + a.fiber.Get("/shop/cashout/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByCashoutID) + a.fiber.Post("/shop/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutByCashoutID) a.fiber.Post("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer) a.fiber.Get("/shop/transaction", a.authMiddleware, h.GetAllTransactions) a.fiber.Get("/shop/transaction/:id", a.authMiddleware, h.GetTransactionByID)