fix: refactored the transactions into shop_transactions

This commit is contained in:
Samuel Tariku 2025-07-02 03:19:59 +03:00
parent f27a75eb66
commit d678b4e9d3
25 changed files with 2557 additions and 783 deletions

View File

@ -119,7 +119,6 @@ func main() {
logger,
)
transactionSvc := transaction.NewService(store)
branchSvc := branch.NewService(store)
companySvc := company.NewService(store)
leagueSvc := league.New(store)
@ -153,6 +152,7 @@ func main() {
cfg.FIXER_API_KEY,
cfg.FIXER_BASE_URL,
)
transactionSvc := transaction.NewService(store, *branchSvc)
reportSvc := report.NewService(
bet.BetStore(store),
@ -167,6 +167,8 @@ func main() {
logger,
)
go httpserver.SetupReportCronJobs(context.Background(), reportSvc)
bankRepository := repository.NewBankRepository(store)

View File

@ -172,28 +172,45 @@ CREATE TABLE IF NOT EXISTS shop_transactions (
id BIGSERIAL PRIMARY KEY,
amount BIGINT NOT NULL,
branch_id BIGINT NOT NULL,
company_id BIGINT,
cashier_id BIGINT,
cashier_name VARCHAR(255),
bet_id BIGINT,
number_of_outcomes BIGINT,
type BIGINT,
payment_option BIGINT,
full_name VARCHAR(255),
phone_number VARCHAR(255),
company_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
type BIGINT NOT NULL,
full_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL,
payment_option BIGINT NOT NULL,
bank_code VARCHAR(255),
beneficiary_name VARCHAR(255),
account_name VARCHAR(255),
account_number VARCHAR(255),
reference_number VARCHAR(255),
verified BOOLEAN NOT NULL DEFAULT false,
approved_by BIGINT,
approver_name VARCHAR(255),
branch_location VARCHAR(255),
branch_name VARCHAR(255),
verified BOOLEAN DEFAULT false NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS shop_bets (
id BIGSERIAL PRIMARY KEY,
shop_transaction_id BIGINT NOT NULL,
cashout_id VARCHAR(255) NOT NULL,
cashed_out_by BIGINT,
bet_id BIGINT NOT NULL,
number_of_outcomes BIGINT NOT NULL,
cashed_out BOOLEAN DEFAULT FALSE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(shop_transaction_id),
UNIQUE(bet_id),
UNIQUE(cashout_id)
);
CREATE TABLE IF NOT EXISTS shop_deposits (
id BIGSERIAL PRIMARY KEY,
shop_transaction_id BIGINT NOT NULL,
customer_id BIGINT NOT NULL,
wallet_transfer_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(shop_transaction_id)
);
CREATE TABLE IF NOT EXISTS branches (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
@ -318,7 +335,7 @@ SELECT branches.*,
wallets.is_active AS wallet_is_active
FROM branches
LEFT JOIN users ON branches.branch_manager_id = users.id
LEFT JOin wallets ON wallets.id = branches.wallet_id;
LEFT JOIN wallets ON wallets.id = branches.wallet_id;
CREATE TABLE IF NOT EXISTS supported_operations (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
@ -362,6 +379,54 @@ SELECT wt.*,
users.phone_number
FROM wallet_transfer wt
LEFT JOIN users ON users.id = wt.cashier_id;
CREATE VIEW shop_transaction_detail AS
SELECT st.*,
cr.first_name AS creator_first_name,
cr.last_name AS creator_last_name,
cr.phone_number AS creator_phone_number,
ap.first_name AS approver_first_name,
ap.last_name AS approver_last_name,
ap.phone_number AS approver_phone_number,
branches.name AS branch_name,
branches.location AS branch_location
FROM shop_transactions st
LEFT JOIN users cr ON cr.id = st.user_id
LEFT JOIN users ap ON ap.id = st.approved_by
LEFT JOIN branches ON branches.id = st.branch_id;
CREATE VIEW shop_bet_detail AS
SELECT sb.*,
st.full_name AS customer_full_name,
st.phone_number AS customer_phone_number,
st.branch_id,
st.company_id,
st.amount,
st.verified AS transaction_verified,
bets.status,
bets.total_odds,
JSON_AGG(bet_outcomes.*) AS outcomes
FROM shop_bets AS sb
JOIN shop_transactions st ON st.id = sb.shop_transaction_id
JOIN bets ON bets.id = sb.bet_id
LEFT JOIN bet_outcomes ON bet_outcomes.bet_id = sb.bet_id
GROUP BY sb.id,
st.full_name,
st.phone_number,
st.branch_id,
st.company_id,
st.amount,
st.verified,
bets.status,
bets.total_odds;
CREATE VIEW shop_deposit_detail AS
SELECT sd.*,
st.full_name,
st.phone_number,
st.branch_id,
st.company_id,
st.amount,
st.verified AS transaction_verified
FROM shop_deposits AS sd
JOIN shop_transactions st ON st.id = sd.shop_transaction_id;
-- Foreign Keys
ALTER TABLE users
ADD CONSTRAINT unique_email UNIQUE (email),
@ -384,8 +449,13 @@ ADD CONSTRAINT fk_wallet_transfer_receiver_wallet FOREIGN KEY (receiver_wallet_i
ADD CONSTRAINT fk_wallet_transfer_cashier FOREIGN KEY (cashier_id) REFERENCES users(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);
ADD CONSTRAINT fk_shop_transactions_users FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE shop_bets
ADD CONSTRAINT fk_shop_bet_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions(id),
ADD CONSTRAINT fk_shop_bet_bets FOREIGN KEY (bet_id) REFERENCES bets(id);
ALTER TABLE shop_deposits
ADD CONSTRAINT fk_shop_deposit_transactions FOREIGN KEY (shop_transaction_id) REFERENCES shop_transactions(id),
ADD CONSTRAINT fk_shop_deposit_customers FOREIGN KEY (customer_id) REFERENCES users(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

@ -0,0 +1,169 @@
-- name: CreateShopTransaction :one
INSERT INTO shop_transactions (
amount,
branch_id,
company_id,
user_id,
type,
full_name,
phone_number,
payment_option,
bank_code,
beneficiary_name,
account_name,
account_number,
reference_number
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12,
$13
)
RETURNING *;
-- name: GetAllShopTransactions :many
SELECT *
FROM shop_transaction_detail
wHERE (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
user_id = sqlc.narg('user_id')
OR sqlc.narg('user_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: GetShopTransactionByID :one
SELECT *
FROM shop_transaction_detail
WHERE id = $1;
-- name: GetShopTransactionByBranch :many
SELECT *
FROM shop_transaction_detail
WHERE branch_id = $1;
-- name: UpdateShopTransactionVerified :exec
UPDATE shop_transactions
SET verified = $2,
approved_by = $3,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: CreateShopBet :one
INSERT INTO shop_bets (
shop_transaction_id,
cashout_id,
bet_id,
number_of_outcomes
)
VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: GetAllShopBets :many
SELECT *
FROM shop_bet_detail
WHERE (
full_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') 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: GetShopBetByID :one
SELECT *
FROM shop_bet_detail
WHERE id = $1;
-- name: GetShopBetByBetID :one
SELECT *
FROM shop_bet_detail
WHERE bet_id = $1;
-- name: GetShopBetByShopTransactionID :one
SELECT *
FROM shop_bet_detail
WHERE shop_transaction_id = $1;
-- name: UpdateShopBetCashOut :exec
UPDATE shop_bets
SET cashed_out = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: UpdateShopBetCashoutID :exec
UPDATE shop_bets
SET cashout_id = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;
-- name: CreateShopDeposit :one
INSERT INTO shop_deposits (
shop_transaction_id,
customer_id,
wallet_transfer_id
)
VALUES ($1, $2, $3)
RETURNING *;
-- name: GetAllShopDeposit :many
SELECT *
FROM shop_deposit_detail
WHERE (
full_name ILIKE '%' || sqlc.narg('query') || '%'
OR phone_number ILIKE '%' || sqlc.narg('query') || '%'
OR sqlc.narg('query') IS NULL
)
AND (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') 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: GetShopDepositByID :one
SELECT *
FROM shop_deposit_detail
WHERE id = $1;
-- name: GetShopDepositByShopTransactionID :one
SELECT *
FROM shop_deposit_detail
WHERE shop_transaction_id = $1;

View File

@ -1,85 +0,0 @@
-- 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 *;
-- name: GetAllShopTransactions :many
SELECT *
FROM shop_transactions
wHERE (
branch_id = sqlc.narg('branch_id')
OR sqlc.narg('branch_id') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
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: GetShopTransactionByID :one
SELECT *
FROM shop_transactions
WHERE id = $1;
-- name: GetShopTransactionByBranch :many
SELECT *
FROM shop_transactions
WHERE branch_id = $1;
-- name: UpdateShopTransactionVerified :exec
UPDATE shop_transactions
SET verified = $2,
approved_by = $3,
approver_name = $4,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1;

View File

@ -392,31 +392,111 @@ type Setting struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type ShopBet struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`
CashoutID string `json:"cashout_id"`
CashedOutBy pgtype.Int8 `json:"cashed_out_by"`
BetID int64 `json:"bet_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
CashedOut bool `json:"cashed_out"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type ShopBetDetail struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`
CashoutID string `json:"cashout_id"`
CashedOutBy pgtype.Int8 `json:"cashed_out_by"`
BetID int64 `json:"bet_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
CashedOut bool `json:"cashed_out"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
CustomerFullName string `json:"customer_full_name"`
CustomerPhoneNumber string `json:"customer_phone_number"`
BranchID int64 `json:"branch_id"`
CompanyID int64 `json:"company_id"`
Amount int64 `json:"amount"`
TransactionVerified bool `json:"transaction_verified"`
Status int32 `json:"status"`
TotalOdds float32 `json:"total_odds"`
Outcomes []BetOutcome `json:"outcomes"`
}
type ShopDeposit struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`
CustomerID int64 `json:"customer_id"`
WalletTransferID int64 `json:"wallet_transfer_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type ShopDepositDetail struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`
CustomerID int64 `json:"customer_id"`
WalletTransferID int64 `json:"wallet_transfer_id"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
BranchID int64 `json:"branch_id"`
CompanyID int64 `json:"company_id"`
Amount int64 `json:"amount"`
TransactionVerified bool `json:"transaction_verified"`
}
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"`
ID int64 `json:"id"`
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CompanyID int64 `json:"company_id"`
UserID int64 `json:"user_id"`
Type int64 `json:"type"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
PaymentOption int64 `json:"payment_option"`
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"`
ApprovedBy pgtype.Int8 `json:"approved_by"`
Verified bool `json:"verified"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type ShopTransactionDetail struct {
ID int64 `json:"id"`
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CompanyID int64 `json:"company_id"`
UserID int64 `json:"user_id"`
Type int64 `json:"type"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
PaymentOption int64 `json:"payment_option"`
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"`
ApprovedBy pgtype.Int8 `json:"approved_by"`
Verified bool `json:"verified"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
CreatorFirstName pgtype.Text `json:"creator_first_name"`
CreatorLastName pgtype.Text `json:"creator_last_name"`
CreatorPhoneNumber pgtype.Text `json:"creator_phone_number"`
ApproverFirstName pgtype.Text `json:"approver_first_name"`
ApproverLastName pgtype.Text `json:"approver_last_name"`
ApproverPhoneNumber pgtype.Text `json:"approver_phone_number"`
BranchName pgtype.Text `json:"branch_name"`
BranchLocation pgtype.Text `json:"branch_location"`
}
type SupportedOperation struct {

View File

@ -0,0 +1,711 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: shop_transactions.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateShopBet = `-- name: CreateShopBet :one
INSERT INTO shop_bets (
shop_transaction_id,
cashout_id,
bet_id,
number_of_outcomes
)
VALUES ($1, $2, $3, $4)
RETURNING id, shop_transaction_id, cashout_id, cashed_out_by, bet_id, number_of_outcomes, cashed_out, created_at, updated_at
`
type CreateShopBetParams struct {
ShopTransactionID int64 `json:"shop_transaction_id"`
CashoutID string `json:"cashout_id"`
BetID int64 `json:"bet_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
}
func (q *Queries) CreateShopBet(ctx context.Context, arg CreateShopBetParams) (ShopBet, error) {
row := q.db.QueryRow(ctx, CreateShopBet,
arg.ShopTransactionID,
arg.CashoutID,
arg.BetID,
arg.NumberOfOutcomes,
)
var i ShopBet
err := row.Scan(
&i.ID,
&i.ShopTransactionID,
&i.CashoutID,
&i.CashedOutBy,
&i.BetID,
&i.NumberOfOutcomes,
&i.CashedOut,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const CreateShopDeposit = `-- name: CreateShopDeposit :one
INSERT INTO shop_deposits (
shop_transaction_id,
customer_id,
wallet_transfer_id
)
VALUES ($1, $2, $3)
RETURNING id, shop_transaction_id, customer_id, wallet_transfer_id, created_at, updated_at
`
type CreateShopDepositParams struct {
ShopTransactionID int64 `json:"shop_transaction_id"`
CustomerID int64 `json:"customer_id"`
WalletTransferID int64 `json:"wallet_transfer_id"`
}
func (q *Queries) CreateShopDeposit(ctx context.Context, arg CreateShopDepositParams) (ShopDeposit, error) {
row := q.db.QueryRow(ctx, CreateShopDeposit, arg.ShopTransactionID, arg.CustomerID, arg.WalletTransferID)
var i ShopDeposit
err := row.Scan(
&i.ID,
&i.ShopTransactionID,
&i.CustomerID,
&i.WalletTransferID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const CreateShopTransaction = `-- name: CreateShopTransaction :one
INSERT INTO shop_transactions (
amount,
branch_id,
company_id,
user_id,
type,
full_name,
phone_number,
payment_option,
bank_code,
beneficiary_name,
account_name,
account_number,
reference_number
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12,
$13
)
RETURNING id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at
`
type CreateShopTransactionParams struct {
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CompanyID int64 `json:"company_id"`
UserID int64 `json:"user_id"`
Type int64 `json:"type"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
PaymentOption int64 `json:"payment_option"`
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"`
}
func (q *Queries) CreateShopTransaction(ctx context.Context, arg CreateShopTransactionParams) (ShopTransaction, error) {
row := q.db.QueryRow(ctx, CreateShopTransaction,
arg.Amount,
arg.BranchID,
arg.CompanyID,
arg.UserID,
arg.Type,
arg.FullName,
arg.PhoneNumber,
arg.PaymentOption,
arg.BankCode,
arg.BeneficiaryName,
arg.AccountName,
arg.AccountNumber,
arg.ReferenceNumber,
)
var i ShopTransaction
err := row.Scan(
&i.ID,
&i.Amount,
&i.BranchID,
&i.CompanyID,
&i.UserID,
&i.Type,
&i.FullName,
&i.PhoneNumber,
&i.PaymentOption,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.ApprovedBy,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const GetAllShopBets = `-- name: GetAllShopBets :many
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 (
full_name ILIKE '%' || $1 || '%'
OR phone_number ILIKE '%' || $1 || '%'
OR $1 IS NULL
)
AND (
branch_id = $2
OR $2 IS NULL
)
AND (
company_id = $3
OR $3 IS NULL
)
AND (
created_at > $4
OR $4 IS NULL
)
AND (
created_at < $5
OR $5 IS NULL
)
`
type GetAllShopBetsParams struct {
Query pgtype.Text `json:"query"`
BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
}
func (q *Queries) GetAllShopBets(ctx context.Context, arg GetAllShopBetsParams) ([]ShopBetDetail, error) {
rows, err := q.db.Query(ctx, GetAllShopBets,
arg.Query,
arg.BranchID,
arg.CompanyID,
arg.CreatedBefore,
arg.CreatedAfter,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ShopBetDetail
for rows.Next() {
var i ShopBetDetail
if err := rows.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,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetAllShopDeposit = `-- name: GetAllShopDeposit :many
SELECT id, shop_transaction_id, customer_id, wallet_transfer_id, created_at, updated_at, full_name, phone_number, branch_id, company_id, amount, transaction_verified
FROM shop_deposit_detail
WHERE (
full_name ILIKE '%' || $1 || '%'
OR phone_number ILIKE '%' || $1 || '%'
OR $1 IS NULL
)
AND (
branch_id = $2
OR $2 IS NULL
)
AND (
company_id = $3
OR $3 IS NULL
)
AND (
created_at > $4
OR $4 IS NULL
)
AND (
created_at < $5
OR $5 IS NULL
)
`
type GetAllShopDepositParams struct {
Query pgtype.Text `json:"query"`
BranchID pgtype.Int8 `json:"branch_id"`
CompanyID pgtype.Int8 `json:"company_id"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
CreatedAfter pgtype.Timestamp `json:"created_after"`
}
func (q *Queries) GetAllShopDeposit(ctx context.Context, arg GetAllShopDepositParams) ([]ShopDepositDetail, error) {
rows, err := q.db.Query(ctx, GetAllShopDeposit,
arg.Query,
arg.BranchID,
arg.CompanyID,
arg.CreatedBefore,
arg.CreatedAfter,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ShopDepositDetail
for rows.Next() {
var i ShopDepositDetail
if err := rows.Scan(
&i.ID,
&i.ShopTransactionID,
&i.CustomerID,
&i.WalletTransferID,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.BranchID,
&i.CompanyID,
&i.Amount,
&i.TransactionVerified,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetAllShopTransactions = `-- name: GetAllShopTransactions :many
SELECT id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at, creator_first_name, creator_last_name, creator_phone_number, approver_first_name, approver_last_name, approver_phone_number, branch_name, branch_location
FROM shop_transaction_detail
wHERE (
branch_id = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
AND (
user_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"`
UserID pgtype.Int8 `json:"user_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) ([]ShopTransactionDetail, error) {
rows, err := q.db.Query(ctx, GetAllShopTransactions,
arg.BranchID,
arg.CompanyID,
arg.UserID,
arg.Query,
arg.CreatedBefore,
arg.CreatedAfter,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ShopTransactionDetail
for rows.Next() {
var i ShopTransactionDetail
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.BranchID,
&i.CompanyID,
&i.UserID,
&i.Type,
&i.FullName,
&i.PhoneNumber,
&i.PaymentOption,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.ApprovedBy,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CreatorFirstName,
&i.CreatorLastName,
&i.CreatorPhoneNumber,
&i.ApproverFirstName,
&i.ApproverLastName,
&i.ApproverPhoneNumber,
&i.BranchName,
&i.BranchLocation,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetShopBetByBetID = `-- name: GetShopBetByBetID :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 bet_id = $1
`
func (q *Queries) GetShopBetByBetID(ctx context.Context, betID int64) (ShopBetDetail, error) {
row := q.db.QueryRow(ctx, GetShopBetByBetID, betID)
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
WHERE id = $1
`
func (q *Queries) GetShopBetByID(ctx context.Context, id int64) (ShopBetDetail, error) {
row := q.db.QueryRow(ctx, GetShopBetByID, id)
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 GetShopBetByShopTransactionID = `-- name: GetShopBetByShopTransactionID :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 shop_transaction_id = $1
`
func (q *Queries) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (ShopBetDetail, error) {
row := q.db.QueryRow(ctx, GetShopBetByShopTransactionID, shopTransactionID)
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 GetShopDepositByID = `-- name: GetShopDepositByID :one
SELECT id, shop_transaction_id, customer_id, wallet_transfer_id, created_at, updated_at, full_name, phone_number, branch_id, company_id, amount, transaction_verified
FROM shop_deposit_detail
WHERE id = $1
`
func (q *Queries) GetShopDepositByID(ctx context.Context, id int64) (ShopDepositDetail, error) {
row := q.db.QueryRow(ctx, GetShopDepositByID, id)
var i ShopDepositDetail
err := row.Scan(
&i.ID,
&i.ShopTransactionID,
&i.CustomerID,
&i.WalletTransferID,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.BranchID,
&i.CompanyID,
&i.Amount,
&i.TransactionVerified,
)
return i, err
}
const GetShopDepositByShopTransactionID = `-- name: GetShopDepositByShopTransactionID :one
SELECT id, shop_transaction_id, customer_id, wallet_transfer_id, created_at, updated_at, full_name, phone_number, branch_id, company_id, amount, transaction_verified
FROM shop_deposit_detail
WHERE shop_transaction_id = $1
`
func (q *Queries) GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (ShopDepositDetail, error) {
row := q.db.QueryRow(ctx, GetShopDepositByShopTransactionID, shopTransactionID)
var i ShopDepositDetail
err := row.Scan(
&i.ID,
&i.ShopTransactionID,
&i.CustomerID,
&i.WalletTransferID,
&i.CreatedAt,
&i.UpdatedAt,
&i.FullName,
&i.PhoneNumber,
&i.BranchID,
&i.CompanyID,
&i.Amount,
&i.TransactionVerified,
)
return i, err
}
const GetShopTransactionByBranch = `-- name: GetShopTransactionByBranch :many
SELECT id, amount, branch_id, company_id, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at, creator_first_name, creator_last_name, creator_phone_number, approver_first_name, approver_last_name, approver_phone_number, branch_name, branch_location
FROM shop_transaction_detail
WHERE branch_id = $1
`
func (q *Queries) GetShopTransactionByBranch(ctx context.Context, branchID int64) ([]ShopTransactionDetail, error) {
rows, err := q.db.Query(ctx, GetShopTransactionByBranch, branchID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ShopTransactionDetail
for rows.Next() {
var i ShopTransactionDetail
if err := rows.Scan(
&i.ID,
&i.Amount,
&i.BranchID,
&i.CompanyID,
&i.UserID,
&i.Type,
&i.FullName,
&i.PhoneNumber,
&i.PaymentOption,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.ApprovedBy,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CreatorFirstName,
&i.CreatorLastName,
&i.CreatorPhoneNumber,
&i.ApproverFirstName,
&i.ApproverLastName,
&i.ApproverPhoneNumber,
&i.BranchName,
&i.BranchLocation,
); 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, user_id, type, full_name, phone_number, payment_option, bank_code, beneficiary_name, account_name, account_number, reference_number, approved_by, verified, created_at, updated_at, creator_first_name, creator_last_name, creator_phone_number, approver_first_name, approver_last_name, approver_phone_number, branch_name, branch_location
FROM shop_transaction_detail
WHERE id = $1
`
func (q *Queries) GetShopTransactionByID(ctx context.Context, id int64) (ShopTransactionDetail, error) {
row := q.db.QueryRow(ctx, GetShopTransactionByID, id)
var i ShopTransactionDetail
err := row.Scan(
&i.ID,
&i.Amount,
&i.BranchID,
&i.CompanyID,
&i.UserID,
&i.Type,
&i.FullName,
&i.PhoneNumber,
&i.PaymentOption,
&i.BankCode,
&i.BeneficiaryName,
&i.AccountName,
&i.AccountNumber,
&i.ReferenceNumber,
&i.ApprovedBy,
&i.Verified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CreatorFirstName,
&i.CreatorLastName,
&i.CreatorPhoneNumber,
&i.ApproverFirstName,
&i.ApproverLastName,
&i.ApproverPhoneNumber,
&i.BranchName,
&i.BranchLocation,
)
return i, err
}
const UpdateShopBetCashOut = `-- name: UpdateShopBetCashOut :exec
UPDATE shop_bets
SET cashed_out = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
type UpdateShopBetCashOutParams struct {
ID int64 `json:"id"`
CashedOut bool `json:"cashed_out"`
}
func (q *Queries) UpdateShopBetCashOut(ctx context.Context, arg UpdateShopBetCashOutParams) error {
_, err := q.db.Exec(ctx, UpdateShopBetCashOut, arg.ID, arg.CashedOut)
return err
}
const UpdateShopBetCashoutID = `-- name: UpdateShopBetCashoutID :exec
UPDATE shop_bets
SET cashout_id = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
type UpdateShopBetCashoutIDParams struct {
ID int64 `json:"id"`
CashoutID string `json:"cashout_id"`
}
func (q *Queries) UpdateShopBetCashoutID(ctx context.Context, arg UpdateShopBetCashoutIDParams) error {
_, err := q.db.Exec(ctx, UpdateShopBetCashoutID, arg.ID, arg.CashoutID)
return err
}
const UpdateShopTransactionVerified = `-- name: UpdateShopTransactionVerified :exec
UPDATE shop_transactions
SET verified = $2,
approved_by = $3,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`
type UpdateShopTransactionVerifiedParams struct {
ID int64 `json:"id"`
Verified bool `json:"verified"`
ApprovedBy pgtype.Int8 `json:"approved_by"`
}
func (q *Queries) UpdateShopTransactionVerified(ctx context.Context, arg UpdateShopTransactionVerifiedParams) error {
_, err := q.db.Exec(ctx, UpdateShopTransactionVerified, arg.ID, arg.Verified, arg.ApprovedBy)
return err
}

View File

@ -106,7 +106,6 @@ type CreateBetOutcomeReq struct {
type CreateBetReq struct {
Outcomes []CreateBetOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
Status OutcomeStatus `json:"status" example:"1"`
FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
BranchID *int64 `json:"branch_id,omitempty" example:"1"`

125
internal/domain/shop_bet.go Normal file
View File

@ -0,0 +1,125 @@
package domain
import "time"
type ShopBet struct {
ID int64
ShopTransactionID int64
CashoutID string
CashedOut bool
BetID int64
NumberOfOutcomes int64
}
type ShopBetFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type CreateShopBet struct {
ShopTransactionID int64
CashoutID string
BetID int64
NumberOfOutcomes int64
}
type ShopBetDetail struct {
ID int64
ShopTransactionID int64
TotalOdds float32
BranchID int64
CompanyID int64
FullName string
PhoneNumber string
CashoutID string
CashedOut bool
BetID int64
NumberOfOutcomes int64
Status OutcomeStatus
Amount Currency
Outcomes []BetOutcome
TransactionVerified bool
UpdatedAt time.Time
CreatedAt time.Time
}
type ShopBetReq struct {
Outcomes []CreateBetOutcomeReq `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
BetID int64 `json:"bet_id" example:"1"`
PaymentOption PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
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"`
}
type CashoutReq struct {
CashoutID string `json:"cashout_id" example:"1234"`
PaymentOption PaymentOption `json:"payment_option" example:"1"`
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"`
}
type ShopBetRes struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
BranchID int64 `json:"branch_id" example:"2"`
CompanyID int64 `json:"company_id" example:"2"`
FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
CashoutID string `json:"cashout_id" example:"21234"`
CashedOut bool `json:"cashed_out" example:"false"`
BetID int64 `json:"bet_id" example:"1"`
NumberOfOutcomes int64 `json:"number_of_outcomes" example:"1"`
Status OutcomeStatus `json:"status" example:"1"`
Amount Currency `json:"amount" example:"100.0"`
Outcomes []BetOutcome `json:"outcomes"`
TransactionVerified bool `json:"transaction_verified" example:"true"`
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:00:00Z"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
}
func ConvertShopBet(shopBet ShopBet) ShopBetRes {
return ShopBetRes{
ID: shopBet.ID,
ShopTransactionID: shopBet.ShopTransactionID,
CashoutID: shopBet.CashoutID,
CashedOut: shopBet.CashedOut,
BetID: shopBet.BetID,
NumberOfOutcomes: shopBet.NumberOfOutcomes,
}
}
func ConvertShopBetDetail(shopBet ShopBetDetail) ShopBetRes {
return ShopBetRes{
ID: shopBet.ID,
ShopTransactionID: shopBet.ShopTransactionID,
TotalOdds: shopBet.TotalOdds,
BranchID: shopBet.BranchID,
CompanyID: shopBet.CompanyID,
FullName: shopBet.FullName,
PhoneNumber: shopBet.PhoneNumber,
CashoutID: shopBet.CashoutID,
CashedOut: shopBet.CashedOut,
BetID: shopBet.BetID,
NumberOfOutcomes: shopBet.NumberOfOutcomes,
Status: shopBet.Status,
Amount: shopBet.Amount,
Outcomes: shopBet.Outcomes,
TransactionVerified: shopBet.TransactionVerified,
UpdatedAt: shopBet.UpdatedAt,
CreatedAt: shopBet.UpdatedAt,
}
}

View File

@ -0,0 +1,68 @@
package domain
import "time"
type ShopDeposit struct {
ID int64
ShopTransactionID int64
CustomerID int64
WalletTransferID int64
}
type CreateShopDeposit struct {
ShopTransactionID int64
CustomerID int64
WalletTransferID int64
}
type ShopDepositFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type ShopDepositDetail struct {
ID int64
ShopTransactionID int64
CustomerID int64
WalletTransferID int64
FullName string
PhoneNumber string
Amount Currency
BranchID int64
CompanyID int64
TransactionVerified bool
UpdatedAt time.Time
CreatedAt time.Time
}
type ShopDepositReq struct {
CustomerID int64 `json:"customer_id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
PaymentOption PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
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"`
}
type ShopDepositRes struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`
CustomerID int64 `json:"customer_id"`
WalletTransferID int64 `json:"wallet_transfer_id"`
}
func ConvertShopDeposit(shopDeposit ShopDeposit) ShopDepositRes {
return ShopDepositRes{
ID: shopDeposit.ID,
ShopTransactionID: shopDeposit.ShopTransactionID,
CustomerID: shopDeposit.CustomerID,
WalletTransferID: shopDeposit.WalletTransferID,
}
}

View File

@ -0,0 +1,189 @@
package domain
import "time"
type ShopTransactionType int
const (
TRANSACTION_CASHOUT ShopTransactionType = iota
TRANSACTION_DEPOSIT
TRANSACTION_BET
)
type PaymentOption int64
const (
CASH_TRANSACTION PaymentOption = iota
TELEBIRR_TRANSACTION
ARIFPAY_TRANSACTION
BANK
)
// ShopTransaction only represents branch transactions
// This is only used for statistic data
type ShopTransaction struct {
ID int64
Amount Currency
BranchID int64
CompanyID int64
UserID int64
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode ValidString
BeneficiaryName ValidString
AccountName ValidString
AccountNumber ValidString
ReferenceNumber ValidString
Verified bool
ApprovedBy ValidInt64
UpdatedAt time.Time
CreatedAt time.Time
}
type ShopTransactionRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
BranchName string `json:"branch_name" example:"Branch Name"`
BranchLocation string `json:"branch_location" example:"Branch Location"`
CompanyID int64 `json:"company_id" example:"1"`
UserID int64 `json:"user_id" example:"1"`
CreatorFirstName string `json:"creator_first_name" example:"John"`
CreatorLastName string `json:"creator_last_name" example:"Smith"`
CreatorPhoneNumber string `json:"creator_phone_number" example:"0911111111"`
CashierName string `json:"cashier_name" example:"John Smith"`
Type int64 `json:"type" example:"1"`
PaymentOption PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
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"`
Verified bool `json:"verified" example:"true"`
ApprovedBy int64 `json:"approved_by" example:"1"`
ApproverFirstName string `json:"approver_first_name" example:"John"`
ApproverLastName string `json:"approver_last_name" example:"Smith"`
ApproverPhoneNumber string `json:"approver_phone_number" example:"0911111111"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
}
type ShopTransactionDetail struct {
ID int64
Amount Currency
BranchID int64
BranchName string
BranchLocation string
CompanyID int64
UserID int64
CreatorFirstName string
CreatorLastName string
CreatorPhoneNumber string
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode ValidString
BeneficiaryName ValidString
AccountName ValidString
AccountNumber ValidString
ReferenceNumber ValidString
Verified bool
ApprovedBy ValidInt64
ApproverFirstName ValidString
ApproverLastName ValidString
ApproverPhoneNumber ValidString
UpdatedAt time.Time
CreatedAt time.Time
}
type ShopTransactionFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
UserID ValidInt64
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type CreateShopTransaction struct {
Amount Currency
BranchID int64
CompanyID int64
UserID int64
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode ValidString
BeneficiaryName ValidString
AccountName ValidString
AccountNumber ValidString
ReferenceNumber ValidString
Verified bool
ApprovedBy ValidInt64
}
func ConvertShopTransaction(transaction ShopTransaction) ShopTransactionRes {
newTransaction := ShopTransactionRes{
ID: transaction.ID,
Amount: transaction.Amount.Float32(),
BranchID: transaction.BranchID,
CompanyID: transaction.CompanyID,
UserID: transaction.UserID,
Type: int64(transaction.Type),
PaymentOption: transaction.PaymentOption,
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode.Value,
BeneficiaryName: transaction.BeneficiaryName.Value,
AccountName: transaction.AccountName.Value,
AccountNumber: transaction.AccountNumber.Value,
ReferenceNumber: transaction.ReferenceNumber.Value,
Verified: transaction.Verified,
ApprovedBy: transaction.ApprovedBy.Value,
CreatedAt: transaction.CreatedAt,
UpdatedAt: transaction.UpdatedAt,
}
return newTransaction
}
func ConvertShopTransactionDetail(transaction ShopTransactionDetail) ShopTransactionRes {
newTransaction := ShopTransactionRes{
ID: transaction.ID,
Amount: transaction.Amount.Float32(),
BranchID: transaction.BranchID,
BranchName: transaction.BranchName,
BranchLocation: transaction.BranchLocation,
CompanyID: transaction.CompanyID,
UserID: transaction.UserID,
CreatorFirstName: transaction.CreatorFirstName,
CreatorLastName: transaction.CreatorLastName,
CreatorPhoneNumber: transaction.CreatorPhoneNumber,
Type: int64(transaction.Type),
PaymentOption: transaction.PaymentOption,
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode.Value,
BeneficiaryName: transaction.BeneficiaryName.Value,
AccountName: transaction.AccountName.Value,
AccountNumber: transaction.AccountNumber.Value,
ReferenceNumber: transaction.ReferenceNumber.Value,
Verified: transaction.Verified,
ApprovedBy: transaction.ApprovedBy.Value,
ApproverFirstName: transaction.ApproverFirstName.Value,
ApproverLastName: transaction.ApproverLastName.Value,
ApproverPhoneNumber: transaction.ApproverPhoneNumber.Value,
CreatedAt: transaction.CreatedAt,
UpdatedAt: transaction.UpdatedAt,
}
return newTransaction
}

View File

@ -1,78 +0,0 @@
package domain
import "time"
type ShopTransactionType int
const (
TRANSACTION_CASHOUT ShopTransactionType = iota
TRANSACTION_DEPOSIT
)
type PaymentOption int64
const (
CASH_TRANSACTION PaymentOption = iota
TELEBIRR_TRANSACTION
ARIFPAY_TRANSACTION
BANK
)
// ShopTransaction only represents branch transactions
// This is only used for statistic data
type ShopTransaction struct {
ID int64
Amount Currency
BranchID int64
BranchName string
BranchLocation string
CompanyID int64
CashierID int64
CashierName string
BetID int64
NumberOfOutcomes int64
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
Verified bool
ApprovedBy ValidInt64
ApproverName ValidString
UpdatedAt time.Time
CreatedAt time.Time
}
type ShopTransactionFilter struct {
CompanyID ValidInt64
BranchID ValidInt64
CashierID ValidInt64
Query ValidString
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type CreateShopTransaction struct {
Amount Currency
BranchID int64
CashierID int64
BetID int64
NumberOfOutcomes int64
Type ShopTransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
BankCode string
BeneficiaryName string
AccountName string
AccountNumber string
ReferenceNumber string
BranchName string
BranchLocation string
CompanyID int64
CashierName string
}

View File

@ -0,0 +1,155 @@
package repository
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
"go.uber.org/zap"
)
func convertDBShopBet(bet dbgen.ShopBet) domain.ShopBet {
return domain.ShopBet{
ID: bet.ID,
ShopTransactionID: bet.ShopTransactionID,
CashoutID: bet.CashoutID,
CashedOut: bet.CashedOut,
BetID: bet.BetID,
NumberOfOutcomes: bet.NumberOfOutcomes,
}
}
func convertDBShopBetDetail(bet dbgen.ShopBetDetail) domain.ShopBetDetail {
var outcomes []domain.BetOutcome = make([]domain.BetOutcome, 0, len(bet.Outcomes))
for _, outcome := range bet.Outcomes {
outcomes = append(outcomes, convertDBBetOutcomes(outcome))
}
return domain.ShopBetDetail{
ID: bet.ID,
ShopTransactionID: bet.ShopTransactionID,
TotalOdds: bet.TotalOdds,
BranchID: bet.BranchID,
CompanyID: bet.CompanyID,
FullName: bet.CustomerFullName,
PhoneNumber: bet.CustomerPhoneNumber,
CashoutID: bet.CashoutID,
CashedOut: bet.CashedOut,
BetID: bet.BetID,
NumberOfOutcomes: bet.NumberOfOutcomes,
Status: domain.OutcomeStatus(bet.Status),
Amount: domain.Currency(bet.Amount),
Outcomes: outcomes,
TransactionVerified: bet.TransactionVerified,
UpdatedAt: bet.UpdatedAt.Time,
CreatedAt: bet.CreatedAt.Time,
}
}
func convertCreateShopBet(bet domain.CreateShopBet) dbgen.CreateShopBetParams {
return dbgen.CreateShopBetParams{
ShopTransactionID: bet.ShopTransactionID,
CashoutID: bet.CashoutID,
BetID: bet.BetID,
NumberOfOutcomes: bet.NumberOfOutcomes,
}
}
func (s *Store) CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (domain.ShopBet, error) {
newShopBet, err := s.queries.CreateShopBet(ctx, convertCreateShopBet(bet))
if err != nil {
return domain.ShopBet{}, err
}
return convertDBShopBet(newShopBet), err
}
func (s *Store) GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter) ([]domain.ShopBetDetail, error) {
bets, err := s.queries.GetAllShopBets(ctx, dbgen.GetAllShopBetsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
},
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.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 {
return nil, err
}
var result []domain.ShopBetDetail = make([]domain.ShopBetDetail, 0, len(bets))
for _, bet := range bets {
result = append(result, convertDBShopBetDetail(bet))
}
return result, nil
}
func (s *Store) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) {
bet, err := s.queries.GetShopBetByID(ctx, id)
if err != nil {
return domain.ShopBetDetail{}, err
}
return convertDBShopBetDetail(bet), nil
}
func (s *Store) GetShopBetByBetID(ctx context.Context, betID int64) (domain.ShopBetDetail, error) {
bet, err := s.queries.GetShopBetByBetID(ctx, betID)
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 {
return domain.ShopBetDetail{}, err
}
return convertDBShopBetDetail(bet), nil
}
func (s *Store) UpdateShopBetCashOut(ctx context.Context, id int64, cashedOut bool) error {
err := s.queries.UpdateShopBetCashOut(ctx, dbgen.UpdateShopBetCashOutParams{
ID: id,
CashedOut: cashedOut,
})
if err != nil {
domain.MongoDBLogger.Error("failed to update cashout",
zap.Int64("id", id),
zap.Bool("cashed_out", cashedOut),
zap.Error(err),
)
}
return err
}
func (s *Store) UpdateShopBetCashoutID(ctx context.Context, id int64, cashoutID string) error {
err := s.queries.UpdateShopBetCashoutID(ctx, dbgen.UpdateShopBetCashoutIDParams{
ID: id,
CashoutID: cashoutID,
})
if err != nil {
domain.MongoDBLogger.Error("failed to update cashout_id",
zap.Int64("id", id),
zap.String("cashout_id", cashoutID),
zap.Error(err),
)
}
return err
}

View File

@ -0,0 +1,104 @@
package repository
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func convertShopDeposit(deposit dbgen.ShopDeposit) domain.ShopDeposit {
return domain.ShopDeposit{
ID: deposit.ID,
ShopTransactionID: deposit.ShopTransactionID,
CustomerID: deposit.CustomerID,
WalletTransferID: deposit.WalletTransferID,
}
}
func convertShopDepositDetail(deposit dbgen.ShopDepositDetail) domain.ShopDepositDetail {
return domain.ShopDepositDetail{
ID: deposit.ID,
ShopTransactionID: deposit.ShopTransactionID,
CustomerID: deposit.CustomerID,
WalletTransferID: deposit.WalletTransferID,
FullName: deposit.FullName,
PhoneNumber: deposit.PhoneNumber,
Amount: domain.Currency(deposit.Amount),
BranchID: deposit.BranchID,
CompanyID: deposit.CompanyID,
TransactionVerified: deposit.TransactionVerified,
UpdatedAt: deposit.UpdatedAt.Time,
CreatedAt: deposit.CreatedAt.Time,
}
}
func convertCreateShopDeposit(deposit domain.CreateShopDeposit) dbgen.CreateShopDepositParams {
return dbgen.CreateShopDepositParams{
ShopTransactionID: deposit.ShopTransactionID,
CustomerID: deposit.CustomerID,
WalletTransferID: deposit.WalletTransferID,
}
}
func (s *Store) CreateShopDeposit(ctx context.Context, deposit domain.CreateShopDeposit) (domain.ShopDeposit, error) {
newShopDeposit, err := s.queries.CreateShopDeposit(ctx, convertCreateShopDeposit(deposit))
if err != nil {
return domain.ShopDeposit{}, err
}
return convertShopDeposit(newShopDeposit), nil
}
func (s *Store) GetAllShopDeposit(ctx context.Context, filter domain.ShopDepositFilter) ([]domain.ShopDepositDetail, error) {
deposits, err := s.queries.GetAllShopDeposit(ctx, dbgen.GetAllShopDepositParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
},
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.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 {
return nil, err
}
var result []domain.ShopDepositDetail = make([]domain.ShopDepositDetail, 0, len(deposits))
for _, deposit := range deposits {
result = append(result, convertShopDepositDetail(deposit))
}
return result, nil
}
func (s *Store) GetShopDepositByID(ctx context.Context, id int64) (domain.ShopDepositDetail, error) {
deposit, err := s.queries.GetShopDepositByID(ctx, id)
if err != nil {
return domain.ShopDepositDetail{}, err
}
return convertShopDepositDetail(deposit), err
}
func (s *Store) GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopDepositDetail, error) {
deposit, err := s.queries.GetShopDepositByShopTransactionID(ctx, shopTransactionID)
if err != nil {
return domain.ShopDepositDetail{}, err
}
return convertShopDepositDetail(deposit), err
}

View File

@ -11,81 +11,140 @@ import (
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.ShopTransactionType(transaction.Type.Int64),
PaymentOption: domain.PaymentOption(transaction.PaymentOption.Int64),
FullName: transaction.FullName.String,
PhoneNumber: transaction.PhoneNumber.String,
BankCode: transaction.BankCode.String,
BeneficiaryName: transaction.BeneficiaryName.String,
AccountName: transaction.AccountName.String,
AccountNumber: transaction.AccountNumber.String,
ReferenceNumber: transaction.ReferenceNumber.String,
ID: transaction.ID,
Amount: domain.Currency(transaction.Amount),
BranchID: transaction.BranchID,
UserID: transaction.UserID,
Type: domain.ShopTransactionType(transaction.Type),
PaymentOption: domain.PaymentOption(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: domain.ValidString{
Value: transaction.BankCode.String,
Valid: transaction.BankCode.Valid,
},
BeneficiaryName: domain.ValidString{
Value: transaction.BeneficiaryName.String,
Valid: transaction.BeneficiaryName.Valid,
},
AccountName: domain.ValidString{
Value: transaction.AccountName.String,
Valid: transaction.AccountName.Valid,
},
AccountNumber: domain.ValidString{
Value: transaction.AccountName.String,
Valid: transaction.AccountNumber.Valid,
},
ReferenceNumber: domain.ValidString{
Value: transaction.ReferenceNumber.String,
Valid: transaction.ReferenceNumber.Valid,
},
ApprovedBy: domain.ValidInt64{
Value: transaction.ApprovedBy.Int64,
Valid: transaction.ApprovedBy.Valid,
},
CreatedAt: transaction.CreatedAt.Time,
UpdatedAt: transaction.UpdatedAt.Time,
Verified: transaction.Verified,
BranchName: transaction.BranchName.String,
BranchLocation: transaction.BranchLocation.String,
CashierName: transaction.CashierName.String,
CompanyID: transaction.CompanyID.Int64,
ApproverName: domain.ValidString{
Value: transaction.ApproverName.String,
Valid: transaction.ApprovedBy.Valid,
},
CreatedAt: transaction.CreatedAt.Time,
UpdatedAt: transaction.UpdatedAt.Time,
Verified: transaction.Verified,
CompanyID: transaction.CompanyID,
}
}
func convertCreateTransaction(transaction domain.CreateShopTransaction) dbgen.CreateShopTransactionParams {
func convertDBShopTransactionDetail(transaction dbgen.ShopTransactionDetail) domain.ShopTransactionDetail {
return domain.ShopTransactionDetail{
ID: transaction.ID,
Amount: domain.Currency(transaction.Amount),
BranchID: transaction.BranchID,
UserID: transaction.UserID,
CreatorFirstName: transaction.CreatorFirstName.String,
CreatorLastName: transaction.CreatorLastName.String,
CreatorPhoneNumber: transaction.CreatorPhoneNumber.String,
Type: domain.ShopTransactionType(transaction.Type),
PaymentOption: domain.PaymentOption(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: domain.ValidString{
Value: transaction.BankCode.String,
Valid: transaction.BankCode.Valid,
},
BeneficiaryName: domain.ValidString{
Value: transaction.BeneficiaryName.String,
Valid: transaction.BeneficiaryName.Valid,
},
AccountName: domain.ValidString{
Value: transaction.AccountName.String,
Valid: transaction.AccountName.Valid,
},
AccountNumber: domain.ValidString{
Value: transaction.AccountName.String,
Valid: transaction.AccountNumber.Valid,
},
ReferenceNumber: domain.ValidString{
Value: transaction.ReferenceNumber.String,
Valid: transaction.ReferenceNumber.Valid,
},
ApprovedBy: domain.ValidInt64{
Value: transaction.ApprovedBy.Int64,
Valid: transaction.ApprovedBy.Valid,
},
ApproverFirstName: domain.ValidString{
Value: transaction.ApproverFirstName.String,
Valid: transaction.ApproverFirstName.Valid,
},
ApproverLastName: domain.ValidString{
Value: transaction.ApproverLastName.String,
Valid: transaction.ApproverLastName.Valid,
},
ApproverPhoneNumber: domain.ValidString{
Value: transaction.ApproverPhoneNumber.String,
Valid: transaction.ApproverPhoneNumber.Valid,
},
CreatedAt: transaction.CreatedAt.Time,
UpdatedAt: transaction.UpdatedAt.Time,
Verified: transaction.Verified,
CompanyID: transaction.CompanyID,
BranchName: transaction.BranchName.String,
BranchLocation: transaction.BranchLocation.String,
}
}
func convertCreateShopTransaction(transaction domain.CreateShopTransaction) dbgen.CreateShopTransactionParams {
return dbgen.CreateShopTransactionParams{
Amount: int64(transaction.Amount),
BranchID: transaction.BranchID,
CashierID: pgtype.Int8{Int64: transaction.CashierID, Valid: true},
BetID: pgtype.Int8{Int64: transaction.BetID, Valid: true},
Type: pgtype.Int8{Int64: int64(transaction.Type), Valid: true},
PaymentOption: pgtype.Int8{Int64: int64(transaction.PaymentOption), Valid: true},
FullName: pgtype.Text{String: transaction.FullName, Valid: transaction.FullName != ""},
PhoneNumber: pgtype.Text{String: transaction.PhoneNumber, Valid: transaction.PhoneNumber != ""},
BankCode: pgtype.Text{String: transaction.BankCode, Valid: transaction.BankCode != ""},
BeneficiaryName: pgtype.Text{String: transaction.BeneficiaryName, Valid: transaction.BeneficiaryName != ""},
AccountName: pgtype.Text{String: transaction.AccountName, Valid: transaction.AccountName != ""},
AccountNumber: pgtype.Text{String: transaction.AccountNumber, Valid: transaction.AccountNumber != ""},
ReferenceNumber: pgtype.Text{String: transaction.ReferenceNumber, Valid: transaction.ReferenceNumber != ""},
NumberOfOutcomes: pgtype.Int8{Int64: transaction.NumberOfOutcomes, Valid: true},
BranchName: pgtype.Text{String: transaction.BranchName, Valid: transaction.BranchName != ""},
BranchLocation: pgtype.Text{String: transaction.BranchLocation, Valid: transaction.BranchLocation != ""},
CashierName: pgtype.Text{String: transaction.CashierName, Valid: transaction.CashierName != ""},
CompanyID: pgtype.Int8{Int64: transaction.CompanyID, Valid: true},
Amount: int64(transaction.Amount),
BranchID: transaction.BranchID,
UserID: transaction.UserID,
Type: int64(transaction.Type),
PaymentOption: int64(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
CompanyID: transaction.CompanyID,
BankCode: pgtype.Text{String: transaction.BankCode.Value, Valid: transaction.BankCode.Valid},
BeneficiaryName: pgtype.Text{String: transaction.BeneficiaryName.Value, Valid: transaction.BeneficiaryName.Valid},
AccountName: pgtype.Text{String: transaction.AccountName.Value, Valid: transaction.AccountName.Valid},
AccountNumber: pgtype.Text{String: transaction.AccountNumber.Value, Valid: transaction.AccountNumber.Valid},
ReferenceNumber: pgtype.Text{String: transaction.ReferenceNumber.Value, Valid: transaction.ReferenceNumber.Valid},
}
}
func (s *Store) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) {
newTransaction, err := s.queries.CreateShopTransaction(ctx, convertCreateTransaction(transaction))
newTransaction, err := s.queries.CreateShopTransaction(ctx, convertCreateShopTransaction(transaction))
if err != nil {
return domain.ShopTransaction{}, err
}
return convertDBShopTransaction(newTransaction), err
}
func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransaction, error) {
func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) {
transaction, err := s.queries.GetShopTransactionByID(ctx, id)
if err != nil {
return domain.ShopTransaction{}, err
return domain.ShopTransactionDetail{}, err
}
return convertDBShopTransaction(transaction), nil
return convertDBShopTransactionDetail(transaction), nil
}
func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransaction, error) {
func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error) {
transaction, err := s.queries.GetAllShopTransactions(ctx, dbgen.GetAllShopTransactionsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
@ -95,9 +154,9 @@ func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTr
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
CashierID: pgtype.Int8{
Int64: filter.CashierID.Value,
Valid: filter.CashierID.Valid,
UserID: pgtype.Int8{
Int64: filter.UserID.Value,
Valid: filter.UserID.Valid,
},
Query: pgtype.Text{
String: filter.Query.Value,
@ -117,22 +176,22 @@ func (s *Store) GetAllShopTransactions(ctx context.Context, filter domain.ShopTr
return nil, err
}
var result []domain.ShopTransaction = make([]domain.ShopTransaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBShopTransaction(ticket))
var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction))
for _, tAction := range transaction {
result = append(result, convertDBShopTransactionDetail(tAction))
}
return result, nil
}
func (s *Store) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransaction, error) {
func (s *Store) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, error) {
transaction, err := s.queries.GetShopTransactionByBranch(ctx, id)
if err != nil {
return nil, err
}
var result []domain.ShopTransaction = make([]domain.ShopTransaction, 0, len(transaction))
var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBShopTransaction(ticket))
result = append(result, convertDBShopTransactionDetail(ticket))
}
return result, nil
}
@ -145,14 +204,11 @@ func (s *Store) UpdateShopTransactionVerified(ctx context.Context, id int64, ver
Valid: true,
},
Verified: verified,
ApproverName: pgtype.Text{
String: approverName,
Valid: true,
},
})
return err
}
// GetTransactionTotals returns total deposits and withdrawals
func (s *Store) GetTransactionTotals(ctx context.Context, filter domain.ReportFilter) (deposits, withdrawals domain.Currency, err error) {
query := `SELECT
@ -172,7 +228,7 @@ func (s *Store) GetTransactionTotals(ctx context.Context, filter domain.ReportFi
args = append(args, filter.BranchID.Value)
argPos++
} else if filter.UserID.Valid {
query += fmt.Sprintf(" WHERE cashier_id = $%d", argPos)
query += fmt.Sprintf(" WHERE user_id = $%d", argPos)
args = append(args, filter.UserID.Value)
argPos++
}

View File

@ -252,7 +252,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
newBet := domain.CreateBet{
Amount: domain.ToCurrency(req.Amount),
TotalOdds: totalOdds,
Status: req.Status,
Status: domain.OUTCOME_STATUS_PENDING,
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
CashoutID: cashoutID,

View File

@ -8,11 +8,23 @@ import (
type TransactionStore interface {
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)
GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error)
GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error)
GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, 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)
CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (domain.ShopBet, error)
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)
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
CreateShopDeposit(ctx context.Context, deposit domain.CreateShopDeposit) (domain.ShopDeposit, error)
GetAllShopDeposit(ctx context.Context, filter domain.ShopDepositFilter) ([]domain.ShopDepositDetail, error)
GetShopDepositByID(ctx context.Context, id int64) (domain.ShopDepositDetail, error)
GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopDepositDetail, error)
}

View File

@ -2,32 +2,91 @@ package transaction
import (
"context"
"errors"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
)
var (
ErrBranchRequiredForRole = errors.New("branch_id is required to be passed for this role")
ErrInvalidBranchID = errors.New("invalid branch id")
ErrUnauthorizedCompanyID = errors.New("unauthorized company id")
ErrUnauthorizedBranchManager = errors.New("unauthorized branch manager")
ErrCustomerRoleNotAuthorized = errors.New("customer role not authorized")
)
type Service struct {
transactionStore TransactionStore
branchSvc branch.Service
betSvc bet.Service
walletSvc wallet.Service
}
func NewService(transactionStore TransactionStore) *Service {
func NewService(transactionStore TransactionStore, branchSvc branch.Service, betSvc bet.Service, walletSvc wallet.Service) *Service {
return &Service{
transactionStore: transactionStore,
branchSvc: branchSvc,
betSvc: betSvc,
walletSvc: walletSvc,
}
}
func (s *Service) CreateShopTransaction(ctx context.Context, transaction domain.CreateShopTransaction) (domain.ShopTransaction, error) {
return s.transactionStore.CreateShopTransaction(ctx, transaction)
}
func (s *Service) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransaction, error) {
func (s *Service) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) {
return s.transactionStore.GetShopTransactionByID(ctx, id)
}
func (s *Service) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransaction, error) {
func (s *Service) GetAllShopTransactions(ctx context.Context, filter domain.ShopTransactionFilter) ([]domain.ShopTransactionDetail, error) {
return s.transactionStore.GetAllShopTransactions(ctx, filter)
}
func (s *Service) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransaction, error) {
func (s *Service) GetShopTransactionByBranch(ctx context.Context, id int64) ([]domain.ShopTransactionDetail, error) {
return s.transactionStore.GetShopTransactionByBranch(ctx, id)
}
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)
}
func (s *Service) GetBranchByRole(ctx context.Context, branchID *int64, role domain.Role, userID int64, userCompanyID domain.ValidInt64) (*int64, *int64, error) {
// var branchID int64
// var companyID int64
if role == domain.RoleAdmin || role == domain.RoleBranchManager || role == domain.RoleSuperAdmin {
if branchID == nil {
// h.logger.Error("CashoutReq Branch ID is required for this user role")
return nil, nil, ErrBranchRequiredForRole
}
branch, err := s.branchSvc.GetBranchByID(ctx, *branchID)
if err != nil {
// h.logger.Error("CashoutReq no branches")
return nil, nil, ErrInvalidBetID
}
// Check if the user has access to the company
if role != domain.RoleSuperAdmin {
if !userCompanyID.Valid || userCompanyID.Value != branch.CompanyID {
return nil, nil, ErrUnauthorizedCompanyID
}
}
if role == domain.RoleBranchManager {
if branch.BranchManagerID != userID {
return nil, nil, ErrUnauthorizedBranchManager
}
}
return &branch.ID, &branch.CompanyID, nil
} else if role == domain.RoleCashier {
branch, err := s.branchSvc.GetBranchByCashier(ctx, userID)
if err != nil {
// h.logger.Error("CashoutReq failed, branch id invalid")
return nil, nil, ErrInvalidBranchID
}
return &branch.ID, &branch.CompanyID, nil
} else {
return nil, nil, ErrCustomerRoleNotAuthorized
}
}

View File

@ -0,0 +1,207 @@
package transaction
import (
"context"
"crypto/rand"
"errors"
"math/big"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
var (
ErrInvalidBetID = errors.New("invalid bet id")
ErrUserHasNotWonBet = errors.New("user has not won bet")
ErrUserHasAlreadyCashoutOut = errors.New("user has already cashout")
ErrTransactionNotVerified = errors.New("transaction hasn't been verified")
)
func (s *Service) GenerateCashoutID() (string, error) {
const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
const length int = 13
charLen := big.NewInt(int64(len(chars)))
result := make([]byte, length)
for i := 0; i < length; i++ {
index, err := rand.Int(rand.Reader, charLen)
if err != nil {
// s.mongoLogger.Error("failed to generate random index for cashout ID",
// zap.Int("position", i),
// zap.Error(err),
// )
return "", err
}
result[i] = chars[index.Int64()]
}
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) {
branchID, companyID, err := s.GetBranchByRole(ctx, req.BranchID, role, userID, userCompanyID)
if err != nil {
return domain.ShopBet{}, err
}
cashoutID, err := s.GenerateCashoutID()
if err != nil {
return domain.ShopBet{}, err
}
newBet, err := s.betSvc.PlaceBet(ctx, domain.CreateBetReq{
Outcomes: req.Outcomes,
Amount: req.Amount,
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
BranchID: branchID,
}, userID, role)
if err != nil {
return domain.ShopBet{}, err
}
newTransaction, err := s.CreateShopTransaction(ctx, domain.CreateShopTransaction{
Amount: domain.ToCurrency(req.Amount),
BranchID: *branchID,
CompanyID: *companyID,
UserID: userID,
Type: domain.TRANSACTION_BET,
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
PaymentOption: req.PaymentOption,
BankCode: domain.ValidString{
Value: req.BankCode,
Valid: req.BankCode != "",
},
BeneficiaryName: domain.ValidString{
Value: req.BeneficiaryName,
Valid: req.BeneficiaryName != "",
},
AccountName: domain.ValidString{
Value: req.AccountName,
Valid: req.AccountName != "",
},
AccountNumber: domain.ValidString{
Value: req.AccountNumber,
Valid: req.AccountNumber != "",
},
ReferenceNumber: domain.ValidString{
Value: req.ReferenceNumber,
Valid: req.ReferenceNumber != "",
},
Verified: false,
ApprovedBy: domain.ValidInt64{
Value: userID,
Valid: true,
},
})
return s.transactionStore.CreateShopBet(ctx, domain.CreateShopBet{
ShopTransactionID: newTransaction.ID,
CashoutID: cashoutID,
BetID: newBet.ID,
NumberOfOutcomes: int64(len(req.Outcomes)),
})
}
// func (s *Service) CreateShopBet(ctx context.Context, bet domain.CreateShopBet) (domain.ShopBet, error) {
// 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)
if err != nil {
return domain.ShopTransaction{}, nil
}
bet, err := s.GetShopBetByBetID(ctx, betID)
if err != nil {
// h.logger.Error("CashoutReq failed", "error", err)
return domain.ShopTransaction{}, ErrInvalidBetID
}
if bet.Status != domain.OUTCOME_STATUS_WIN {
// h.logger.Error("CashoutReq failed, bet has not won")
return domain.ShopTransaction{}, ErrUserHasNotWonBet
}
if bet.CashedOut {
// s.logger.Error(("Bet has already been cashed out"))
return domain.ShopTransaction{}, ErrUserHasAlreadyCashoutOut
}
if !bet.TransactionVerified {
return domain.ShopTransaction{}, ErrTransactionNotVerified
}
err = s.UpdateShopBetCashOut(ctx, bet.ID, true)
if err != nil {
return domain.ShopTransaction{}, err
}
return s.CreateShopTransaction(ctx, domain.CreateShopTransaction{
Amount: bet.Amount,
BranchID: *branchID,
CompanyID: *companyID,
UserID: userID,
Type: domain.TRANSACTION_CASHOUT,
FullName: bet.FullName,
PhoneNumber: bet.PhoneNumber,
PaymentOption: req.PaymentOption,
BankCode: domain.ValidString{
Value: req.BankCode,
Valid: req.BankCode != "",
},
BeneficiaryName: domain.ValidString{
Value: req.BeneficiaryName,
Valid: req.BeneficiaryName != "",
},
AccountName: domain.ValidString{
Value: req.AccountName,
Valid: req.AccountName != "",
},
AccountNumber: domain.ValidString{
Value: req.AccountNumber,
Valid: req.AccountNumber != "",
},
ReferenceNumber: domain.ValidString{
Value: req.ReferenceNumber,
Valid: req.ReferenceNumber != "",
},
Verified: false,
ApprovedBy: domain.ValidInt64{
Value: userID,
Valid: true,
},
})
}
func (s *Service) GetAllShopBet(ctx context.Context, filter domain.ShopBetFilter) ([]domain.ShopBetDetail, error) {
return s.transactionStore.GetAllShopBet(ctx, filter)
}
func (s *Service) GetShopBetByID(ctx context.Context, id int64) (domain.ShopBetDetail, error) {
return s.transactionStore.GetShopBetByBetID(ctx, id)
}
func (s *Service) GetShopBetByBetID(ctx context.Context, betID int64) (domain.ShopBetDetail, error) {
return s.transactionStore.GetShopBetByBetID(ctx, betID)
}
func (s *Service) GetShopBetByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopBetDetail, error) {
return s.transactionStore.GetShopBetByShopTransactionID(ctx, shopTransactionID)
}
func (s *Service) UpdateShopBetCashOut(ctx context.Context, id int64, cashedOut bool) error {
return s.transactionStore.UpdateShopBetCashOut(ctx, id, cashedOut)
}
func (s *Service) UpdateShopBetCashoutID(ctx context.Context, id int64, cashoutID string) error {
return s.transactionStore.UpdateShopBetCashoutID(ctx, id, cashoutID)
}

View File

@ -0,0 +1,114 @@
package transaction
import (
"context"
"fmt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *Service) CreateShopDeposit(ctx context.Context, userID int64, role domain.Role, req domain.ShopDepositReq) (domain.ShopDeposit, error) {
var branchID int64
var companyID int64
var senderID int64
if role == domain.RoleAdmin || role == domain.RoleBranchManager || role == domain.RoleSuperAdmin {
if req.BranchID == nil {
// h.logger.Error("CashoutReq Branch ID is required for this user role")
return domain.ShopDeposit{}, ErrBranchRequiredForRole
}
branch, err := s.branchSvc.GetBranchByID(ctx, *req.BranchID)
if err != nil {
// h.logger.Error("CashoutReq no branches")
return domain.ShopDeposit{}, ErrInvalidBetID
}
branchID = branch.ID
companyID = branch.CompanyID
senderID = branch.WalletID
} else if role == domain.RoleCashier {
branch, err := s.branchSvc.GetBranchByCashier(ctx, userID)
if err != nil {
// h.logger.Error("CashoutReq failed, branch id invalid")
return domain.ShopDeposit{}, ErrInvalidBranchID
}
branchID = branch.ID
companyID = branch.CompanyID
senderID = branch.WalletID
} else {
return domain.ShopDeposit{}, ErrCustomerRoleNotAuthorized
}
customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, req.CustomerID)
if err != nil {
return domain.ShopDeposit{}, err
}
transfer, err := s.walletSvc.TransferToWallet(ctx,
senderID, customerWallet.RegularID, domain.ToCurrency(req.Amount), domain.TRANSFER_DIRECT,
domain.ValidInt64{Value: userID, Valid: true},
fmt.Sprintf("Transferred %v from customer wallet deposit", req.Amount),
)
newTransaction, err := s.CreateShopTransaction(ctx, domain.CreateShopTransaction{
Amount: domain.Currency(req.Amount),
BranchID: branchID,
CompanyID: companyID,
UserID: userID,
Type: domain.TRANSACTION_DEPOSIT,
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
PaymentOption: req.PaymentOption,
BankCode: domain.ValidString{
Value: req.BankCode,
Valid: req.BankCode != "",
},
BeneficiaryName: domain.ValidString{
Value: req.BeneficiaryName,
Valid: req.BeneficiaryName != "",
},
AccountName: domain.ValidString{
Value: req.AccountName,
Valid: req.AccountName != "",
},
AccountNumber: domain.ValidString{
Value: req.AccountNumber,
Valid: req.AccountNumber != "",
},
ReferenceNumber: domain.ValidString{
Value: req.ReferenceNumber,
Valid: req.ReferenceNumber != "",
},
Verified: false,
ApprovedBy: domain.ValidInt64{
Value: userID,
Valid: true,
},
})
if err != nil {
return domain.ShopDeposit{}, err
}
return s.transactionStore.CreateShopDeposit(ctx, domain.CreateShopDeposit{
ShopTransactionID: newTransaction.ID,
CustomerID: req.CustomerID,
WalletTransferID: transfer.ID,
})
}
// func (s *Service) CreateShopDeposit(ctx context.Context, deposit domain.CreateShopDeposit) (domain.ShopDeposit, error) {
// return s.transactionStore.CreateShopDeposit(ctx, deposit)
// }
func (s *Service) GetAllShopDeposit(ctx context.Context, filter domain.ShopDepositFilter) ([]domain.ShopDepositDetail, error) {
return s.transactionStore.GetAllShopDeposit(ctx, filter)
}
func (s *Service) GetShopDepositByID(ctx context.Context, id int64) (domain.ShopDepositDetail, error) {
return s.transactionStore.GetShopDepositByID(ctx, id)
}
func (s *Service) GetShopDepositByShopTransactionID(ctx context.Context, shopTransactionID int64) (domain.ShopDepositDetail, error) {
return s.transactionStore.GetShopDepositByShopTransactionID(ctx, shopTransactionID)
}

View File

@ -115,7 +115,9 @@ func (c *Client) ProcessBet(ctx context.Context, req domain.BetRequest) (*domain
return &domain.BetResponse{}, err
}
c.walletSvc.DeductFromWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT)
c.walletSvc.DeductFromWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.CustomerWalletType, domain.ValidInt64{}, domain.TRANSFER_DIRECT,
fmt.Sprintf("Deducting %v from wallet for creating Veli Game Bet", req.Amount.Amount),
)
return &res, err
}
@ -156,7 +158,9 @@ func (c *Client) ProcessWin(ctx context.Context, req domain.WinRequest) (*domain
return &domain.WinResponse{}, err
}
c.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
c.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.Amount.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Adding %v to wallet due to winning Veli Games bet", req.Amount),
)
return &res, err
}
@ -199,7 +203,9 @@ func (c *Client) ProcessCancel(ctx context.Context, req domain.CancelRequest) (*
return &domain.CancelResponse{}, err
}
c.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.AdjustmentRefund.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{})
c.walletSvc.AddToWallet(ctx, wallets[0].ID, domain.Currency(req.AdjustmentRefund.Amount), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
fmt.Sprintf("Adding %v to wallet due to cancelling virtual game bet", req.AdjustmentRefund.Amount),
)
return &res, err
}

View File

@ -0,0 +1,303 @@
package handlers
import (
"log/slog"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
// CreateShopBet godoc
// @Summary Create bet at branch
// @Description Create bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body domain.ShopBetReq true "create bet"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/bet [post]
func (h *Handler) CreateShopBet(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
company_id := c.Locals("company_id").(domain.ValidInt64)
var req domain.ShopBetReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("CreateBetReq 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("CreateBetReq failed v", "error", valErrs)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
shopBet, err := h.transactionSvc.CreateShopBet(c.Context(), userID, role, company_id, req)
if err != nil {
return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to cashout bet", err, nil)
}
res := domain.ConvertShopBet(shopBet)
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/bet/{id}/cashout [post]
func (h *Handler) CashoutBet(c *fiber.Ctx) error {
betIDStr := c.Params("id")
betID, err := strconv.ParseInt(betIDStr, 10, 64)
if err != nil {
h.logger.Error("CashoutReq invalid bet id", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid bet id", err, nil)
}
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
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)
}
transaction, err := h.transactionSvc.CashoutBet(c.Context(), betID, userID, role, req)
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)
}
// 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 domain.ShopDepositReq true "ShopDepositReq"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/deposit [post]
func (h *Handler) DepositForCustomer(c *fiber.Ctx) error {
// Get sender ID from the cashier
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
var req domain.ShopDepositReq
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)
}
deposit, err := h.transactionSvc.CreateShopDeposit(c.Context(), userID, role, req)
if err != nil {
return response.WriteJSON(c, fiber.StatusBadRequest, "Failed to create shop deposit", err, nil)
}
res := domain.ConvertShopDeposit(deposit)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}
// GetAllTransactions godoc
// @Summary Gets all transactions
// @Description Gets all the transactions
// @Tags transaction
// @Accept json
// @Produce json
// @Success 200 {array} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/transaction [get]
func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// Get user_id from middleware
// userID := c.Locals("user_id").(int64)
// role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
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.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{
CompanyID: companyID,
BranchID: branchID,
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil {
h.logger.Error("Failed to get transactions", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transactions", err, nil)
}
res := make([]domain.ShopTransactionRes, len(transactions))
for i, transaction := range transactions {
res[i] = domain.ConvertShopTransactionDetail(transaction)
}
return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil)
}
// GetTransactionByID godoc
// @Summary Gets transaction by id
// @Description Gets a single transaction by id
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/transaction/{id} [get]
func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction 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 := domain.ConvertShopTransactionDetail(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}
type UpdateTransactionVerifiedReq struct {
Verified bool `json:"verified" example:"true"`
}
// UpdateTransactionVerified godoc
// @Summary Updates the verified field of a transaction
// @Description Updates the verified status of a transaction
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Param updateVerified body UpdateTransactionVerifiedReq true "Updates Transaction Verification"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/transaction/{id} [put]
func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
transactionID := c.Params("id")
userID := c.Locals("user_id").(int64)
companyID := c.Locals("company_id").(domain.ValidInt64)
role := c.Locals("role").(domain.Role)
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID")
}
var req UpdateTransactionVerifiedReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
h.logger.Info("Update Transaction Verified", slog.Bool("verified", req.Verified))
if valErrs, ok := h.validator.Validate(c, req); !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
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)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
}
user, err := h.userSvc.GetUserById(c.Context(), userID)
if err != nil {
h.logger.Error("Invalid user ID", "userID", userID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
}
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")
}
return response.WriteJSON(c, fiber.StatusOK, "Transaction updated successfully", nil, nil)
}

View File

@ -1,498 +0,0 @@
package handlers
import (
"fmt"
"log/slog"
"strconv"
"time"
"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 ShopTransactionRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
BranchName string `json:"branch_name" example:"Branch Name"`
BranchLocation string `json:"branch_location" example:"Branch Location"`
CompanyID int64 `json:"company_id" example:"1"`
CashierID int64 `json:"cashier_id" example:"1"`
CashierName string `json:"cashier_name" example:"John Smith"`
BetID int64 `json:"bet_id" example:"1"`
NumberOfOutcomes int64 `json:"number_of_outcomes" example:"1"`
Type int64 `json:"type" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
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"`
Verified bool `json:"verified" example:"true"`
ApprovedBy *int64 `json:"approved_by" example:"1"`
ApproverName *string `json:"approver_name" example:"John Smith"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
}
type CashoutReq struct {
CashoutID string `json:"cashout_id" example:"191212"`
Amount float32 `json:"amount" example:"100.0"`
BetID int64 `json:"bet_id" example:"1"`
Type int64 `json:"type" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
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"`
}
func convertShopTransaction(transaction domain.ShopTransaction) ShopTransactionRes {
newTransaction := ShopTransactionRes{
ID: transaction.ID,
Amount: transaction.Amount.Float32(),
BranchID: transaction.BranchID,
BranchName: transaction.BranchName,
BranchLocation: transaction.BranchLocation,
CompanyID: transaction.CompanyID,
CashierID: transaction.CashierID,
CashierName: transaction.CashierName,
BetID: transaction.BetID,
Type: int64(transaction.Type),
PaymentOption: transaction.PaymentOption,
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode,
BeneficiaryName: transaction.BeneficiaryName,
AccountName: transaction.AccountName,
AccountNumber: transaction.AccountNumber,
ReferenceNumber: transaction.ReferenceNumber,
Verified: transaction.Verified,
NumberOfOutcomes: transaction.NumberOfOutcomes,
CreatedAt: transaction.CreatedAt,
UpdatedAt: transaction.UpdatedAt,
}
if transaction.ApprovedBy.Valid {
newTransaction.ApprovedBy = &transaction.ApprovedBy.Value
newTransaction.ApproverName = &transaction.ApproverName.Value
}
return newTransaction
}
// CashoutBet godoc
// @Summary Cashout bet at branch
// @Description Cashout bet at branch
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body 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) 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("CashoutReq failed due to unauthorized access")
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "unauthorized access",
})
}
var req 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)
}
var branchID int64
var branchName string
var branchLocation string
var companyID int64
if role == domain.RoleAdmin || role == domain.RoleBranchManager || role == domain.RoleSuperAdmin {
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)
}
branchID = branch.ID
branchName = branch.Name
branchLocation = branch.Location
companyID = branch.CompanyID
} else {
branch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
if err != nil {
h.logger.Error("CashoutReq failed, branch id invalid")
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID invalid", err, nil)
}
branchID = branch.ID
branchName = branch.Name
branchLocation = branch.Location
companyID = branch.CompanyID
}
bet, err := h.betSvc.GetBetByID(c.Context(), req.BetID)
if err != nil {
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("CashoutReq failed, bet has not won")
// return response.WriteJSON(c, fiber.StatusBadRequest, "User has not won bet", err, nil)
// }
if bet.CashedOut {
h.logger.Error(("Bet has already been cashed out"))
return response.WriteJSON(c, fiber.StatusBadRequest, "This bet has already been cashed out", err, nil)
}
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
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.CreateShopTransaction(c.Context(), domain.CreateShopTransaction{
BranchID: branchID,
CashierID: userID,
Amount: domain.ToCurrency(req.Amount),
BetID: bet.ID,
NumberOfOutcomes: int64(len(bet.Outcomes)),
Type: domain.ShopTransactionType(req.Type),
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,
})
if err != nil {
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("CashoutReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
}
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
// @Tags transaction
// @Accept json
// @Produce json
// @Success 200 {array} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/transaction [get]
func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
// Get user_id from middleware
// userID := c.Locals("user_id").(int64)
// role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
branchID := c.Locals("branch_id").(domain.ValidInt64)
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.GetAllShopTransactions(c.Context(), domain.ShopTransactionFilter{
CompanyID: companyID,
BranchID: branchID,
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
})
if err != nil {
h.logger.Error("Failed to get transactions", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transactions", err, nil)
}
res := make([]ShopTransactionRes, len(transactions))
for i, transaction := range transactions {
res[i] = convertShopTransaction(transaction)
}
return response.WriteJSON(c, fiber.StatusOK, "Transactions retrieved successfully", res, nil)
}
// GetTransactionByID godoc
// @Summary Gets transaction by id
// @Description Gets a single transaction by id
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/transaction/{id} [get]
func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction 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 := convertShopTransaction(transaction)
return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}
type UpdateTransactionVerifiedReq struct {
Verified bool `json:"verified" example:"true"`
}
// UpdateTransactionVerified godoc
// @Summary Updates the verified field of a transaction
// @Description Updates the verified status of a transaction
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Param updateVerified body UpdateTransactionVerifiedReq true "Updates Transaction Verification"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /shop/transaction/{id} [put]
func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
transactionID := c.Params("id")
userID := c.Locals("user_id").(int64)
companyID := c.Locals("company_id").(domain.ValidInt64)
role := c.Locals("role").(domain.Role)
id, err := strconv.ParseInt(transactionID, 10, 64)
if err != nil {
h.logger.Error("Invalid transaction ID", "transactionID", transactionID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction ID")
}
var req UpdateTransactionVerifiedReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
h.logger.Info("Update Transaction Verified", slog.Bool("verified", req.Verified))
if valErrs, ok := h.validator.Validate(c, req); !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
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)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
}
user, err := h.userSvc.GetUserById(c.Context(), userID)
if err != nil {
h.logger.Error("Invalid user ID", "userID", userID, "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
}
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")
}
return response.WriteJSON(c, fiber.StatusOK, "Transaction updated successfully", nil, nil)
}

View File

@ -273,7 +273,9 @@ func (a *App) initAppRoutes() {
// group.Get("/virtual-games/recommendations/:userID", h.GetRecommendations)
// Transactions /shop/transactions
a.fiber.Post("/shop/cashout", a.authMiddleware, h.CashoutBet)
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.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)
a.fiber.Put("/shop/transaction/:id", a.authMiddleware, h.UpdateTransactionVerified)

View File

@ -43,7 +43,8 @@ migrations/up:
postgres:
@echo 'Running postgres db...'
docker compose -f docker-compose.yml exec postgres psql -U root -d gh
postgres_log:
docker logs fortunebet-backend-postgres-1
.PHONY: swagger
swagger:
@swag init -g cmd/main.go
@ -51,7 +52,7 @@ swagger:
.PHONY: db-up
db-up:
@docker compose up -d postgres migrate mongo redis
@docker logs fortunebet-backend-postgres-1 > logs/postgres.log 2>&1 &
.PHONY: db-down
db-down:
@docker compose down

View File

@ -24,4 +24,7 @@ sql:
go_type:
type: "TicketOutcome"
slice: true
- column: "shop_bet_detail.outcomes"
go_type:
type: "BetOutcome"
slice: true