fix: deposit for customer

This commit is contained in:
Samuel Tariku 2025-06-30 18:58:18 +03:00
parent 3e4e9eead7
commit 3e9c707f00
11 changed files with 281 additions and 161 deletions

View File

@ -168,7 +168,7 @@ CREATE TABLE IF NOT EXISTS wallet_transfer (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS transactions (
CREATE TABLE IF NOT EXISTS shop_transactions (
id BIGSERIAL PRIMARY KEY,
amount BIGINT NOT NULL,
branch_id BIGINT NOT NULL,
@ -381,10 +381,10 @@ ALTER TABLE wallet_transfer
ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_wallet_transfer_sender_wallet FOREIGN KEY (sender_wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users(id);
ALTER TABLE transactions
ADD CONSTRAINT fk_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches(id),
ADD CONSTRAINT fk_transactions_cashiers FOREIGN KEY (cashier_id) REFERENCES users(id),
ADD CONSTRAINT fk_transactions_bets FOREIGN KEY (bet_id) REFERENCES bets(id);
ALTER TABLE shop_transactions
ADD CONSTRAINT fk_shop_transactions_branches FOREIGN KEY (branch_id) REFERENCES branches(id),
ADD CONSTRAINT fk_shop_transactions_cashiers FOREIGN KEY (cashier_id) REFERENCES users(id),
ADD CONSTRAINT fk_shop_transactions_bets FOREIGN KEY (bet_id) REFERENCES bets(id);
ALTER TABLE branches
ADD CONSTRAINT fk_branches_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id),
ADD CONSTRAINT fk_branches_manager FOREIGN KEY (branch_manager_id) REFERENCES users(id);

View File

@ -1,5 +1,5 @@
-- name: CreateTransaction :one
INSERT INTO transactions (
-- name: CreateShopTransaction :one
INSERT INTO shop_transactions (
amount,
branch_id,
cashier_id,
@ -40,9 +40,9 @@ VALUES (
$18
)
RETURNING *;
-- name: GetAllTransactions :many
-- name: GetAllShopTransactions :many
SELECT *
FROM transactions
FROM shop_transactions
wHERE (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
@ -68,16 +68,16 @@ wHERE (
created_at < sqlc.narg('created_after')
OR sqlc.narg('created_after') IS NULL
);
-- name: GetTransactionByID :one
-- name: GetShopTransactionByID :one
SELECT *
FROM transactions
FROM shop_transactions
WHERE id = $1;
-- name: GetTransactionByBranch :many
-- name: GetShopTransactionByBranch :many
SELECT *
FROM transactions
FROM shop_transactions
WHERE branch_id = $1;
-- name: UpdateTransactionVerified :exec
UPDATE transactions
-- name: UpdateShopTransactionVerified :exec
UPDATE shop_transactions
SET verified = $2,
approved_by = $3,
approver_name = $4,

View File

@ -391,6 +391,33 @@ type Setting struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type ShopTransaction struct {
ID int64 `json:"id"`
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
CashierName pgtype.Text `json:"cashier_name"`
BetID pgtype.Int8 `json:"bet_id"`
NumberOfOutcomes pgtype.Int8 `json:"number_of_outcomes"`
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"`
Verified bool `json:"verified"`
ApprovedBy pgtype.Int8 `json:"approved_by"`
ApproverName pgtype.Text `json:"approver_name"`
BranchLocation pgtype.Text `json:"branch_location"`
BranchName pgtype.Text `json:"branch_name"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type SupportedOperation struct {
ID int64 `json:"id"`
Name string `json:"name"`
@ -441,33 +468,6 @@ type TicketWithOutcome struct {
Outcomes []TicketOutcome `json:"outcomes"`
}
type Transaction struct {
ID int64 `json:"id"`
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
CashierName pgtype.Text `json:"cashier_name"`
BetID pgtype.Int8 `json:"bet_id"`
NumberOfOutcomes pgtype.Int8 `json:"number_of_outcomes"`
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"`
Verified bool `json:"verified"`
ApprovedBy pgtype.Int8 `json:"approved_by"`
ApproverName pgtype.Text `json:"approver_name"`
BranchLocation pgtype.Text `json:"branch_location"`
BranchName pgtype.Text `json:"branch_name"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type User struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`

View File

@ -11,8 +11,8 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
const CreateTransaction = `-- name: CreateTransaction :one
INSERT INTO transactions (
const CreateShopTransaction = `-- name: CreateShopTransaction :one
INSERT INTO shop_transactions (
amount,
branch_id,
cashier_id,
@ -55,7 +55,7 @@ VALUES (
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 CreateTransactionParams struct {
type CreateShopTransactionParams struct {
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
@ -76,8 +76,8 @@ type CreateTransactionParams struct {
CashierName pgtype.Text `json:"cashier_name"`
}
func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionParams) (Transaction, error) {
row := q.db.QueryRow(ctx, CreateTransaction,
func (q *Queries) CreateShopTransaction(ctx context.Context, arg CreateShopTransactionParams) (ShopTransaction, error) {
row := q.db.QueryRow(ctx, CreateShopTransaction,
arg.Amount,
arg.BranchID,
arg.CashierID,
@ -97,7 +97,7 @@ func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionPa
arg.CompanyID,
arg.CashierName,
)
var i Transaction
var i ShopTransaction
err := row.Scan(
&i.ID,
&i.Amount,
@ -127,9 +127,9 @@ func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionPa
return i, err
}
const GetAllTransactions = `-- name: GetAllTransactions :many
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 transactions
FROM shop_transactions
wHERE (
branch_id = $1
OR $1 IS NULL
@ -157,7 +157,7 @@ wHERE (
)
`
type GetAllTransactionsParams struct {
type GetAllShopTransactionsParams struct {
BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CashierID pgtype.Int8 `json:"cashier_id"`
@ -166,8 +166,8 @@ type GetAllTransactionsParams struct {
CreatedAfter pgtype.Timestamp `json:"created_after"`
}
func (q *Queries) GetAllTransactions(ctx context.Context, arg GetAllTransactionsParams) ([]Transaction, error) {
rows, err := q.db.Query(ctx, GetAllTransactions,
func (q *Queries) GetAllShopTransactions(ctx context.Context, arg GetAllShopTransactionsParams) ([]ShopTransaction, error) {
rows, err := q.db.Query(ctx, GetAllShopTransactions,
arg.BranchID,
arg.CompanyID,
arg.CashierID,
@ -179,9 +179,9 @@ func (q *Queries) GetAllTransactions(ctx context.Context, arg GetAllTransactions
return nil, err
}
defer rows.Close()
var items []Transaction
var items []ShopTransaction
for rows.Next() {
var i Transaction
var i ShopTransaction
if err := rows.Scan(
&i.ID,
&i.Amount,
@ -218,21 +218,21 @@ func (q *Queries) GetAllTransactions(ctx context.Context, arg GetAllTransactions
return items, nil
}
const GetTransactionByBranch = `-- name: GetTransactionByBranch :many
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 transactions
FROM shop_transactions
WHERE branch_id = $1
`
func (q *Queries) GetTransactionByBranch(ctx context.Context, branchID int64) ([]Transaction, error) {
rows, err := q.db.Query(ctx, GetTransactionByBranch, branchID)
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 []Transaction
var items []ShopTransaction
for rows.Next() {
var i Transaction
var i ShopTransaction
if err := rows.Scan(
&i.ID,
&i.Amount,
@ -269,15 +269,15 @@ func (q *Queries) GetTransactionByBranch(ctx context.Context, branchID int64) ([
return items, nil
}
const GetTransactionByID = `-- name: GetTransactionByID :one
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 transactions
FROM shop_transactions
WHERE id = $1
`
func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction, error) {
row := q.db.QueryRow(ctx, GetTransactionByID, id)
var i Transaction
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,
@ -307,8 +307,8 @@ func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction
return i, err
}
const UpdateTransactionVerified = `-- name: UpdateTransactionVerified :exec
UPDATE transactions
const UpdateShopTransactionVerified = `-- name: UpdateShopTransactionVerified :exec
UPDATE shop_transactions
SET verified = $2,
approved_by = $3,
approver_name = $4,
@ -316,15 +316,15 @@ SET verified = $2,
WHERE id = $1
`
type UpdateTransactionVerifiedParams struct {
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) UpdateTransactionVerified(ctx context.Context, arg UpdateTransactionVerifiedParams) error {
_, err := q.db.Exec(ctx, UpdateTransactionVerified,
func (q *Queries) UpdateShopTransactionVerified(ctx context.Context, arg UpdateShopTransactionVerifiedParams) error {
_, err := q.db.Exec(ctx, UpdateShopTransactionVerified,
arg.ID,
arg.Verified,
arg.ApprovedBy,

View File

@ -2,10 +2,10 @@ package domain
import "time"
type TransactionType int
type ShopTransactionType int
const (
TRANSACTION_CASHOUT TransactionType = iota
TRANSACTION_CASHOUT ShopTransactionType = iota
TRANSACTION_DEPOSIT
)
@ -18,8 +18,9 @@ const (
BANK
)
// Transaction only represents branch transactions
type Transaction struct {
// ShopTransaction only represents branch transactions
// This is only used for statistic data
type ShopTransaction struct {
ID int64
Amount Currency
BranchID int64
@ -30,7 +31,7 @@ type Transaction struct {
CashierName string
BetID int64
NumberOfOutcomes int64
Type TransactionType
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
@ -47,7 +48,7 @@ type Transaction struct {
CreatedAt time.Time
}
type TransactionFilter struct {
type ShopTransactionFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
CashierID ValidInt64
@ -55,13 +56,13 @@ type TransactionFilter struct {
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type CreateTransaction struct {
type CreateShopTransaction struct {
Amount Currency
BranchID int64
CashierID int64
BetID int64
NumberOfOutcomes int64
Type TransactionType
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string

View File

@ -9,15 +9,15 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
return domain.Transaction{
func convertDBShopTransaction(transaction dbgen.ShopTransaction) domain.ShopTransaction {
return domain.ShopTransaction{
ID: transaction.ID,
Amount: domain.Currency(transaction.Amount),
BranchID: transaction.BranchID,
CashierID: transaction.CashierID.Int64,
BetID: transaction.BetID.Int64,
NumberOfOutcomes: transaction.NumberOfOutcomes.Int64,
Type: domain.TransactionType(transaction.Type.Int64),
Type: domain.ShopTransactionType(transaction.Type.Int64),
PaymentOption: domain.PaymentOption(transaction.PaymentOption.Int64),
FullName: transaction.FullName.String,
PhoneNumber: transaction.PhoneNumber.String,
@ -44,8 +44,8 @@ func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
}
}
func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.CreateTransactionParams {
return dbgen.CreateTransactionParams{
func convertCreateTransaction(transaction domain.CreateShopTransaction) dbgen.CreateShopTransactionParams {
return dbgen.CreateShopTransactionParams{
Amount: int64(transaction.Amount),
BranchID: transaction.BranchID,
CashierID: pgtype.Int8{Int64: transaction.CashierID, Valid: true},
@ -67,26 +67,26 @@ func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.Create
}
}
func (s *Store) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {
func (s *Store) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) {
newTransaction, err := s.queries.CreateTransaction(ctx, convertCreateTransaction(transaction))
newTransaction, err := s.queries.CreateShopTransaction(ctx, convertCreateTransaction(transaction))
if err != nil {
return domain.Transaction{}, err
return domain.ShopTransaction{}, err
}
return convertDBTransaction(newTransaction), err
return convertDBShopTransaction(newTransaction), err
}
func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
transaction, err := s.queries.GetTransactionByID(ctx, id)
func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransaction, error) {
transaction, err := s.queries.GetShopTransactionByID(ctx, id)
if err != nil {
return domain.Transaction{}, err
return domain.ShopTransaction{}, err
}
return convertDBTransaction(transaction), nil
return convertDBShopTransaction(transaction), nil
}
func (s *Store) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error) {
transaction, err := s.queries.GetAllTransactions(ctx, dbgen.GetAllTransactionsParams{
func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransaction, error) {
transaction, err := s.queries.GetAllShopTransactions(ctx, dbgen.GetAllShopTransactionsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
@ -117,28 +117,28 @@ func (s *Store) GetAllTransactions(ctx context.Context, filter domain.Transactio
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
var result []domain.ShopTransaction = make([]domain.ShopTransaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
result = append(result, convertDBShopTransaction(ticket))
}
return result, nil
}
func (s *Store) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
transaction, err := s.queries.GetTransactionByBranch(ctx, id)
func (s *Store) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransaction, error) {
transaction, err := s.queries.GetShopTransactionByBranch(ctx, id)
if err != nil {
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
var result []domain.ShopTransaction = make([]domain.ShopTransaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
result = append(result, convertDBShopTransaction(ticket))
}
return result, nil
}
func (s *Store) UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error {
err := s.queries.UpdateTransactionVerified(ctx, dbgen.UpdateTransactionVerifiedParams{
func (s *Store) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error {
err := s.queries.UpdateShopTransactionVerified(ctx, dbgen.UpdateShopTransactionVerifiedParams{
ID: id,
ApprovedBy: pgtype.Int8{
Int64: approvedBy,
@ -158,7 +158,7 @@ func (s *Store) GetTransactionTotals(ctx context.Context, filter domain.ReportFi
query := `SELECT
COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits,
COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals
FROM transactions`
FROM shop_transactions`
args := []interface{}{}
argPos := 1
@ -207,7 +207,7 @@ func (s *Store) GetBranchTransactionTotals(ctx context.Context, filter domain.Re
branch_id,
COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits,
COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals
FROM transactions`
FROM shop_transactions`
args := []interface{}{}
argPos := 1

View File

@ -7,11 +7,11 @@ import (
)
type TransactionStore interface {
CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error)
GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error)
GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error)
GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error)
UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error
CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error)
GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransaction, error)
GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransaction, error)
GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransaction, error)
UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error
GetTransactionTotals(ctx context.Context, filter domain.ReportFilter) (deposits, withdrawals domain.Currency, err error)
GetBranchTransactionTotals(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchTransactions, error)

View File

@ -16,18 +16,18 @@ func NewService(transactionStore TransactionStore) *Service {
}
}
func (s *Service) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {
return s.transactionStore.CreateTransaction(ctx, transaction)
func (s *Service) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) {
return s.transactionStore.CreateShopTransaction(ctx, transaction)
}
func (s *Service) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
return s.transactionStore.GetTransactionByID(ctx, id)
func (s *Service) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransaction, error) {
return s.transactionStore.GetShopTransactionByID(ctx, id)
}
func (s *Service) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error) {
return s.transactionStore.GetAllTransactions(ctx, filter)
func (s *Service) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransaction, error) {
return s.transactionStore.GetAllShopTransactions(ctx, filter)
}
func (s *Service) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
return s.transactionStore.GetTransactionByBranch(ctx, id)
func (s *Service) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransaction, error) {
return s.transactionStore.GetShopTransactionByBranch(ctx, id)
}
func (s *Service) UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error {
return s.transactionStore.UpdateTransactionVerified(ctx, id, verified, approvedBy, approverName)
func (s *Service) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error {
return s.transactionStore.UpdateShopTransactionVerified(ctx, id, verified, approvedBy, approverName)
}

View File

@ -1,6 +1,7 @@
package handlers
import (
"fmt"
"log/slog"
"strconv"
"time"
@ -8,9 +9,10 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type TransactionRes struct {
type ShopTransactionRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
@ -37,7 +39,7 @@ type TransactionRes struct {
CreatedAt time.Time `json:"created_at"`
}
type CreateTransactionReq struct {
type CashoutReq struct {
CashoutID string `json:"cashout_id" example:"191212"`
Amount float32 `json:"amount" example:"100.0"`
BetID int64 `json:"bet_id" example:"1"`
@ -53,8 +55,8 @@ type CreateTransactionReq struct {
BranchID *int64 `json:"branch_id,omitempty" example:"1"`
}
func convertTransaction(transaction domain.Transaction) TransactionRes {
newTransaction := TransactionRes{
func convertShopTransaction(transaction domain.ShopTransaction) ShopTransactionRes {
newTransaction := ShopTransactionRes{
ID: transaction.ID,
Amount: transaction.Amount.Float32(),
BranchID: transaction.BranchID,
@ -88,39 +90,39 @@ func convertTransaction(transaction domain.Transaction) TransactionRes {
return newTransaction
}
// CreateTransaction godoc
// @Summary Create a transaction
// @Description Creates a transaction
// CashoutBet godoc
// @Summary Cashout bet at branch
// @Description Cashout bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body CreateTransactionReq true "Creates transaction"
// @Param createBet body CashoutReq true "cashout bet"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction [post]
func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
// @Router /shop/cashout [post]
func (h *Handler) CashoutBet(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
// user, err := h.userSvc.GetUserByID(c.Context(), userID)
// TODO: Make a "Only Company" middleware auth and move this into that
if role == domain.RoleCustomer {
h.logger.Error("CreateTransactionReq failed due to unauthorized access")
h.logger.Error("CashoutReq failed due to unauthorized access")
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "unauthorized access",
})
}
var req CreateTransactionReq
var req CashoutReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("CreateTransaction failed to parse request", "error", err)
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("CreateTransactionReq failed v", "error", valErrs)
h.logger.Error("CashoutReq failed v", "error", valErrs)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
@ -130,12 +132,12 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
var companyID int64
if role == domain.RoleAdmin || role == domain.RoleBranchManager || role == domain.RoleSuperAdmin {
if req.BranchID == nil {
h.logger.Error("CreateTransactionReq Branch ID is required for this user role")
h.logger.Error("CashoutReq Branch ID is required for this user role")
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID is required for this user role", nil, nil)
}
branch, err := h.branchSvc.GetBranchByID(c.Context(), *req.BranchID)
if err != nil {
h.logger.Error("CreateTransactionReq no branches")
h.logger.Error("CashoutReq no branches")
return response.WriteJSON(c, fiber.StatusBadRequest, "cannot find Branch ID", err, nil)
}
@ -146,7 +148,7 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
} else {
branch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
if err != nil {
h.logger.Error("CreateTransactionReq failed, branch id invalid")
h.logger.Error("CashoutReq failed, branch id invalid")
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID invalid", err, nil)
}
branchID = branch.ID
@ -157,12 +159,12 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
bet, err := h.betSvc.GetBetByID(c.Context(), req.BetID)
if err != nil {
h.logger.Error("CreateTransactionReq failed", "error", err)
h.logger.Error("CashoutReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Bet ID invalid", err, nil)
}
// if bet.Status != domain.OUTCOME_STATUS_WIN {
// h.logger.Error("CreateTransactionReq failed, bet has not won")
// h.logger.Error("CashoutReq failed, bet has not won")
// return response.WriteJSON(c, fiber.StatusBadRequest, "User has not won bet", err, nil)
// }
@ -173,16 +175,16 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
h.logger.Error("CreateTransactionReq failed, user id invalid", "error", err)
h.logger.Error("CashoutReq failed, user id invalid", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "User ID invalid", err, nil)
}
transaction, err := h.transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{
transaction, err := h.transactionSvc.CreateShopTransaction(c.Context(), domain.CreateShopTransaction{
BranchID: branchID,
CashierID: userID,
Amount: domain.ToCurrency(req.Amount),
BetID: bet.ID,
NumberOfOutcomes: int64(len(bet.Outcomes)),
Type: domain.TransactionType(req.Type),
Type: domain.ShopTransactionType(req.Type),
PaymentOption: domain.PaymentOption(req.PaymentOption),
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
@ -198,21 +200,138 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
})
if err != nil {
h.logger.Error("CreateTransactionReq failed", "error", err)
h.logger.Error("CashoutReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
}
err = h.betSvc.UpdateCashOut(c.Context(), req.BetID, true)
if err != nil {
h.logger.Error("CreateTransactionReq failed", "error", err)
h.logger.Error("CashoutReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
}
res := convertTransaction(transaction)
res := convertShopTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil)
}
type DepositForCustomerReq struct {
Amount float32 `json:"amount" example:"100.0"`
PaymentMethod string `json:"payment_method" example:"cash"`
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
BranchID *int64 `json:"branch_id,omitempty" example:"1"`
}
// DepositForCustomer godoc
// @Summary Shop deposit into customer wallet
// @Description Transfers money from branch wallet to customer wallet
// @Tags transaction
// @Accept json
// @Produce json
// @Param transferToWallet body DepositForCustomerReq true "DepositForCustomer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/deposit/:id [post]
func (h *Handler) DepositForCustomer(c *fiber.Ctx) error {
customerIDString := c.Params("id")
customerID, err := strconv.ParseInt(customerIDString, 10, 64)
if err != nil {
h.logger.Error("Invalid customer ID", "customerID", customerID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
}
// Get sender ID from the cashier
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
var req DepositForCustomerReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("CreateTransferReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
var senderID int64
switch role {
case domain.RoleCustomer:
h.logger.Error("Unauthorized access", "userID", userID, "role", role)
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
case domain.RoleAdmin, domain.RoleSuperAdmin, domain.RoleBranchManager:
if req.BranchID == nil {
h.logger.Error("CashoutReq Branch ID is required for this user role")
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID is required for this user role", nil, nil)
}
branch, err := h.branchSvc.GetBranchByID(c.Context(), *req.BranchID)
if err != nil {
h.logger.Error("CashoutReq no branches")
return response.WriteJSON(c, fiber.StatusBadRequest, "cannot find Branch ID", err, nil)
}
senderID = branch.WalletID
case domain.RoleCashier:
cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
if err != nil {
h.logger.Error("Failed to get branch", "user ID", userID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier branch", err, nil)
}
senderID = cashierBranch.WalletID
default:
return response.WriteJSON(c, fiber.StatusInternalServerError, "Unknown Role", err, nil)
}
customerWallet, err := h.walletSvc.GetCustomerWallet(c.Context(), customerID)
if err != nil {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid customer id", err, nil)
}
transfer, err := h.walletSvc.TransferToWallet(c.Context(),
senderID, customerWallet.RegularID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod),
domain.ValidInt64{Value: userID, Valid: true},
fmt.Sprintf("Transferred %v from wallet to customer wallet", req.Amount),
)
if err != nil {
h.mongoLoggerSvc.Error("Failed to transfer money to wallet", zap.Error(err))
return response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil)
}
transaction, err := h.transactionSvc.CreateShopTransaction(c.Context(), domain.CreateShopTransaction{
BranchID: branchID,
CashierID: userID,
Amount: domain.ToCurrency(req.Amount),
Type: domain.TRANSACTION_DEPOSIT,
PaymentOption: domain.PaymentOption(req.PaymentOption),
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
BankCode: req.BankCode,
BeneficiaryName: req.BeneficiaryName,
AccountName: req.AccountName,
AccountNumber: req.AccountNumber,
ReferenceNumber: req.ReferenceNumber,
CashierName: user.FirstName + " " + user.LastName,
BranchName: branchName,
BranchLocation: branchLocation,
CompanyID: companyID,
})
res := convertTransfer(transfer)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}
// GetAllTransactions godoc
// @Summary Gets all transactions
// @Description Gets all the transactions
@ -222,7 +341,7 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
// @Success 200 {array} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction [get]
// @Router /shop/transaction [get]
func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// Get user_id from middleware
// userID := c.Locals("user_id").(int64)
@ -265,7 +384,7 @@ func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
}
// Check user role and fetch transactions accordingly
transactions, err := h.transactionSvc.GetAllTransactions(c.Context(), domain.TransactionFilter{
transactions, err := h.transactionSvc.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{
CompanyID: companyID,
BranchID: branchID,
Query: searchString,
@ -278,9 +397,9 @@ func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transactions", err, nil)
}
res := make([]TransactionRes, len(transactions))
res := make([]ShopTransactionRes, len(transactions))
for i, transaction := range transactions {
res[i] = convertTransaction(transaction)
res[i] = convertShopTransaction(transaction)
}
return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil)
@ -297,7 +416,7 @@ func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction/{id} [get]
// @Router /shop/transaction/{id} [get]
func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
@ -306,13 +425,13 @@ func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID")
}
transaction, err := h.transactionSvc.GetTransactionByID(c.Context(), id)
transaction, err := h.transactionSvc.GetShopTransactionByID(c.Context(), id)
if err != nil {
h.logger.Error("Failed to get transaction by ID", "transactionID", id, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve transaction")
}
res := convertTransaction(transaction)
res := convertShopTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}
@ -331,7 +450,7 @@ type UpdateTransactionVerifiedReq struct {
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction/{id} [put]
// @Router /shop/transaction/{id} [put]
func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
transactionID := c.Params("id")
userID := c.Locals("user_id").(int64)
@ -356,7 +475,7 @@ func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
transaction, err := h.transactionSvc.GetTransactionByID(c.Context(), id)
transaction, err := h.transactionSvc.GetShopTransactionByID(c.Context(), id)
if role != domain.RoleSuperAdmin {
if !companyID.Valid || companyID.Value != transaction.CompanyID {
h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err)
@ -369,7 +488,7 @@ func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
h.logger.Error("Invalid user ID", "userID", userID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
}
err = h.transactionSvc.UpdateTransactionVerified(c.Context(), id, req.Verified, userID, user.FirstName+" "+user.LastName)
err = h.transactionSvc.UpdateShopTransactionVerified(c.Context(), id, req.Verified, userID, user.FirstName+" "+user.LastName)
if err != nil {
h.logger.Error("Failed to update transaction verification", "transactionID", id, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update transaction verification")

View File

@ -241,9 +241,9 @@ func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
res := convertTransfer(transfer)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}
// RefillWallet godoc
// @Summary Refill wallet
// @Description Super Admin route to refill a wallet

View File

@ -271,11 +271,11 @@ func (a *App) initAppRoutes() {
// Recommendation Routes
// group.Get("/virtual-games/recommendations/:userID", h.GetRecommendations)
// Transactions /transactions
a.fiber.Post("/transaction", a.authMiddleware, h.CreateTransaction)
a.fiber.Get("/transaction", a.authMiddleware, h.GetAllTransactions)
a.fiber.Get("/transaction/:id", a.authMiddleware, h.GetTransactionByID)
a.fiber.Put("/transaction/:id", a.authMiddleware, h.UpdateTransactionVerified)
// Transactions /shop/transactions
a.fiber.Post("/shop/cashout", a.authMiddleware, h.CashoutBet)
a.fiber.Get("/shop/transaction", a.authMiddleware, h.GetAllTransactions)
a.fiber.Get("/shop/transaction/:id", a.authMiddleware, h.GetTransactionByID)
a.fiber.Put("/shop/transaction/:id", a.authMiddleware, h.UpdateTransactionVerified)
// Notification Routes
a.fiber.Get("/ws/connect", a.WebsocketAuthMiddleware, h.ConnectSocket)