branch management

This commit is contained in:
Samuel Tariku 2025-04-07 03:45:52 +03:00
parent ddc4f8bc54
commit 6f30fea12c
21 changed files with 1224 additions and 23 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"cSpell.words": [
"Cashout"
]
}

View File

@ -102,6 +102,7 @@ CREATE TABLE IF NOT EXISTS wallet_transfer (
wallet_transfer VARCHAR(255) NOT NULL,
wallet_id BIGINT NOT NULL,
verified BOOLEAN NOT NULL DEFAULT false,
payment_method INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
@ -125,6 +126,38 @@ CREATE TABLE IF NOT EXISTS transactions (
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS branches (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
location VARCHAR(255) NOT NULL,
wallet_id BIGINT NOT NULL,
branch_manager_id BIGINT NOT NULL,
company_id BIGINT NOT NULL,
is_self_owned BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE VIEW branch_details AS
SELECT branches.*,
CONCAT(users.first_name, ' ', users.last_name) AS manager_name,
users.phone_number AS manager_phone_number
FROM branches
LEFT JOIN users ON branches.branch_manager_id = users.id;
CREATE TABLE IF NOT EXISTS supported_operations (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description VARCHAR(255) NOT NULL
);
CREATE TABLE IF NOT EXISTS branch_operations (
id BIGSERIAL PRIMARY KEY,
operation_id BIGINT NOT NULL,
branch_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
----------------------------------------------seed data-------------------------------------------------------------
-------------------------------------- DO NOT USE IN PRODUCTION-------------------------------------------------
@ -151,4 +184,10 @@ INSERT INTO users (
);
INSERT INTO supported_operations (
name, description
) VALUES
('SportBook', 'Sportbook operations'),
('Virtual', 'Virtual operations'),
('GameZone', 'GameZone operations')
;

39
db/query/branch.sql Normal file
View File

@ -0,0 +1,39 @@
-- name: CreateBranch :one
INSERT INTO branches (name, location, wallet_id, branch_manager_id, company_id, is_self_owned) VALUES ($1, $2, $3, $4, $5, $6) RETURNING *;
-- name: CreateSupportedOperation :one
INSERT INTO supported_operations (name, description) VALUES ($1, $2) RETURNING *;
-- name: CreateBranchOperation :one
INSERT INTO branch_operations (operation_id, branch_id) VALUES ($1, $2) RETURNING *;
-- name: GetAllBranches :many
SELECT * FROM branch_details;
-- name: GetBranchByID :one
SELECT * FROM branch_details WHERE id = $1;
-- name: GetBranchByCompanyID :many
SELECT * FROM branch_details WHERE company_id = $1;
-- name: GetBranchByManagerID :many
SELECT * FROM branch_details WHERE branch_manager_id = $1;
-- name: GetAllSupportedOperations :many
SELECT * FROM supported_operations;
-- name: GetBranchOperations :many
SELECT branch_operations.*, supported_operations.name, supported_operations.description
FROM branch_operations
JOIN supported_operations ON branch_operations.operation_id = supported_operations.id
WHERE branch_operations.branch_id = $1;
-- name: UpdateBranch :one
UPDATE branches SET name = $1, location = $2, branch_manager_id = $3, company_id = $4, is_self_owned = $5 WHERE id = $6 RETURNING *;
-- name: DeleteBranch :exec
DELETE FROM branches WHERE id = $1;
-- name: DeleteBranchOperation :exec
DELETE FROM branch_operations WHERE operation_id = $1 AND branch_id = $2;

347
gen/db/branch.sql.go Normal file
View File

@ -0,0 +1,347 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
// source: branch.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateBranch = `-- name: CreateBranch :one
INSERT INTO branches (name, location, wallet_id, branch_manager_id, company_id, is_self_owned) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at
`
type CreateBranchParams struct {
Name string
Location string
WalletID int64
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
}
func (q *Queries) CreateBranch(ctx context.Context, arg CreateBranchParams) (Branch, error) {
row := q.db.QueryRow(ctx, CreateBranch,
arg.Name,
arg.Location,
arg.WalletID,
arg.BranchManagerID,
arg.CompanyID,
arg.IsSelfOwned,
)
var i Branch
err := row.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const CreateBranchOperation = `-- name: CreateBranchOperation :one
INSERT INTO branch_operations (operation_id, branch_id) VALUES ($1, $2) RETURNING id, operation_id, branch_id, created_at, updated_at
`
type CreateBranchOperationParams struct {
OperationID int64
BranchID int64
}
func (q *Queries) CreateBranchOperation(ctx context.Context, arg CreateBranchOperationParams) (BranchOperation, error) {
row := q.db.QueryRow(ctx, CreateBranchOperation, arg.OperationID, arg.BranchID)
var i BranchOperation
err := row.Scan(
&i.ID,
&i.OperationID,
&i.BranchID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const CreateSupportedOperation = `-- name: CreateSupportedOperation :one
INSERT INTO supported_operations (name, description) VALUES ($1, $2) RETURNING id, name, description
`
type CreateSupportedOperationParams struct {
Name string
Description string
}
func (q *Queries) CreateSupportedOperation(ctx context.Context, arg CreateSupportedOperationParams) (SupportedOperation, error) {
row := q.db.QueryRow(ctx, CreateSupportedOperation, arg.Name, arg.Description)
var i SupportedOperation
err := row.Scan(&i.ID, &i.Name, &i.Description)
return i, err
}
const DeleteBranch = `-- name: DeleteBranch :exec
DELETE FROM branches WHERE id = $1
`
func (q *Queries) DeleteBranch(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteBranch, id)
return err
}
const DeleteBranchOperation = `-- name: DeleteBranchOperation :exec
DELETE FROM branch_operations WHERE operation_id = $1 AND branch_id = $2
`
type DeleteBranchOperationParams struct {
OperationID int64
BranchID int64
}
func (q *Queries) DeleteBranchOperation(ctx context.Context, arg DeleteBranchOperationParams) error {
_, err := q.db.Exec(ctx, DeleteBranchOperation, arg.OperationID, arg.BranchID)
return err
}
const GetAllBranches = `-- name: GetAllBranches :many
SELECT id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number FROM branch_details
`
func (q *Queries) GetAllBranches(ctx context.Context) ([]BranchDetail, error) {
rows, err := q.db.Query(ctx, GetAllBranches)
if err != nil {
return nil, err
}
defer rows.Close()
var items []BranchDetail
for rows.Next() {
var i BranchDetail
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
&i.ManagerName,
&i.ManagerPhoneNumber,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetAllSupportedOperations = `-- name: GetAllSupportedOperations :many
SELECT id, name, description FROM supported_operations
`
func (q *Queries) GetAllSupportedOperations(ctx context.Context) ([]SupportedOperation, error) {
rows, err := q.db.Query(ctx, GetAllSupportedOperations)
if err != nil {
return nil, err
}
defer rows.Close()
var items []SupportedOperation
for rows.Next() {
var i SupportedOperation
if err := rows.Scan(&i.ID, &i.Name, &i.Description); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many
SELECT id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number FROM branch_details WHERE company_id = $1
`
func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]BranchDetail, error) {
rows, err := q.db.Query(ctx, GetBranchByCompanyID, companyID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []BranchDetail
for rows.Next() {
var i BranchDetail
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
&i.ManagerName,
&i.ManagerPhoneNumber,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBranchByID = `-- name: GetBranchByID :one
SELECT id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number FROM branch_details WHERE id = $1
`
func (q *Queries) GetBranchByID(ctx context.Context, id int64) (BranchDetail, error) {
row := q.db.QueryRow(ctx, GetBranchByID, id)
var i BranchDetail
err := row.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
&i.ManagerName,
&i.ManagerPhoneNumber,
)
return i, err
}
const GetBranchByManagerID = `-- name: GetBranchByManagerID :many
SELECT id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number FROM branch_details WHERE branch_manager_id = $1
`
func (q *Queries) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]BranchDetail, error) {
rows, err := q.db.Query(ctx, GetBranchByManagerID, branchManagerID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []BranchDetail
for rows.Next() {
var i BranchDetail
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
&i.ManagerName,
&i.ManagerPhoneNumber,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetBranchOperations = `-- name: GetBranchOperations :many
SELECT branch_operations.id, branch_operations.operation_id, branch_operations.branch_id, branch_operations.created_at, branch_operations.updated_at, supported_operations.name, supported_operations.description
FROM branch_operations
JOIN supported_operations ON branch_operations.operation_id = supported_operations.id
WHERE branch_operations.branch_id = $1
`
type GetBranchOperationsRow struct {
ID int64
OperationID int64
BranchID int64
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
Name string
Description string
}
func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]GetBranchOperationsRow, error) {
rows, err := q.db.Query(ctx, GetBranchOperations, branchID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetBranchOperationsRow
for rows.Next() {
var i GetBranchOperationsRow
if err := rows.Scan(
&i.ID,
&i.OperationID,
&i.BranchID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Description,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateBranch = `-- name: UpdateBranch :one
UPDATE branches SET name = $1, location = $2, branch_manager_id = $3, company_id = $4, is_self_owned = $5 WHERE id = $6 RETURNING id, name, location, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at
`
type UpdateBranchParams struct {
Name string
Location string
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
ID int64
}
func (q *Queries) UpdateBranch(ctx context.Context, arg UpdateBranchParams) (Branch, error) {
row := q.db.QueryRow(ctx, UpdateBranch,
arg.Name,
arg.Location,
arg.BranchManagerID,
arg.CompanyID,
arg.IsSelfOwned,
arg.ID,
)
var i Branch
err := row.Scan(
&i.ID,
&i.Name,
&i.Location,
&i.WalletID,
&i.BranchManagerID,
&i.CompanyID,
&i.IsSelfOwned,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}

View File

@ -23,6 +23,40 @@ type Bet struct {
IsShopBet bool
}
type Branch struct {
ID int64
Name string
Location string
WalletID int64
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type BranchDetail struct {
ID int64
Name string
Location string
WalletID int64
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
ManagerName interface{}
ManagerPhoneNumber pgtype.Text
}
type BranchOperation struct {
ID int64
OperationID int64
BranchID int64
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type CustomerWallet struct {
ID int64
CustomerID int64
@ -71,6 +105,12 @@ type RefreshToken struct {
Revoked bool
}
type SupportedOperation struct {
ID int64
Name string
Description string
}
type Ticket struct {
ID int64
Amount pgtype.Int8
@ -131,6 +171,7 @@ type WalletTransfer struct {
WalletTransfer string
WalletID int64
Verified bool
PaymentMethod int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}

View File

@ -10,7 +10,7 @@ import (
)
const CreateTransfer = `-- name: CreateTransfer :one
INSERT INTO wallet_transfer (amount, wallet_transfer, wallet_id) VALUES ($1, $2, $3) RETURNING id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at
INSERT INTO wallet_transfer (amount, wallet_transfer, wallet_id) VALUES ($1, $2, $3) RETURNING id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at
`
type CreateTransferParams struct {
@ -28,6 +28,7 @@ func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams)
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
&i.UpdatedAt,
)
@ -35,7 +36,7 @@ func (q *Queries) CreateTransfer(ctx context.Context, arg CreateTransferParams)
}
const GetAllTransfers = `-- name: GetAllTransfers :many
SELECT id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at FROM wallet_transfer
SELECT id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at FROM wallet_transfer
`
func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error) {
@ -53,6 +54,7 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
@ -67,7 +69,7 @@ func (q *Queries) GetAllTransfers(ctx context.Context) ([]WalletTransfer, error)
}
const GetTransferByID = `-- name: GetTransferByID :one
SELECT id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at FROM wallet_transfer WHERE id = $1
SELECT id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at FROM wallet_transfer WHERE id = $1
`
func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer, error) {
@ -79,6 +81,7 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
&i.UpdatedAt,
)
@ -86,7 +89,7 @@ func (q *Queries) GetTransferByID(ctx context.Context, id int64) (WalletTransfer
}
const GetTransfersByWallet = `-- name: GetTransfersByWallet :many
SELECT id, amount, wallet_transfer, wallet_id, verified, created_at, updated_at FROM wallet_transfer WHERE wallet_id = $1
SELECT id, amount, wallet_transfer, wallet_id, verified, payment_method, created_at, updated_at FROM wallet_transfer WHERE wallet_id = $1
`
func (q *Queries) GetTransfersByWallet(ctx context.Context, walletID int64) ([]WalletTransfer, error) {
@ -104,6 +107,7 @@ func (q *Queries) GetTransfersByWallet(ctx context.Context, walletID int64) ([]W
&i.WalletTransfer,
&i.WalletID,
&i.Verified,
&i.PaymentMethod,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {

1
internal/domain/bank.go Normal file
View File

@ -0,0 +1 @@
package domain

View File

@ -23,6 +23,7 @@ type Bet struct {
UserID ValidInt64 // Can Be Nullable
IsShopBet bool
CashedOut bool
CashoutID string
}
type CreateBet struct {

View File

@ -6,9 +6,48 @@ type Branch struct {
Location string
WalletID int64
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
IsSupportingSportBook bool
IsSupportingVirtual bool
IsSupportingGameZone bool
}
type BranchDetail struct {
ID int64
Name string
Location string
WalletID int64
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
ManagerName string
ManagerPhoneNumber string
}
type SupportedOperation struct {
ID int64
Name string
Description string
}
type BranchOperation struct {
ID int64
OperationName string
OperationDescription string
}
type CreateBranch struct {
Name string
Location string
WalletID int64
BranchManagerID int64
CompanyID int64
IsSelfOwned bool
}
type CreateSupportedOperation struct {
Name string
Description string
}
type CreateBranchOperation struct {
BranchID int64
OperationID int64
}

1
internal/domain/chapa.go Normal file
View File

@ -0,0 +1 @@
package domain

View File

@ -0,0 +1,10 @@
package domain
// Company represents the client that we will contract the services with
// they are the ones that manage the branches and branch managers
// they will have their own wallet that they will use to distribute to the branch wallets
type Company struct {
ID int64
Name string
}

View File

@ -2,5 +2,7 @@ package domain
type Event struct {}
type Outcome struct {}
type Outcome struct {
}

View File

@ -8,7 +8,8 @@ const (
ARIFPAY_TRANSACTION
BANK
)
// Transaction only represents when the user cashes out a bet in the shop
// It probably would be better to call it a CashOut or ShopWithdrawal
type Transaction struct {
ID int64
Amount Currency

View File

@ -1,5 +1,7 @@
package domain
import "time"
type TransferType string
const (
@ -7,12 +9,27 @@ const (
WITHDRAW TransferType = "withdraw"
)
type PaymentMethod int
const (
TRANSFER_CASH PaymentMethod = iota + 1
TRANSFER_BANK
TRANSFER_CHAPA
TRANSFER_ARIFPAY
TRANSFER_SANTIM
TRANSFER_ADDISPAY
TRANSFER_OTHER
)
type Transfer struct {
ID int64
Amount Currency
Verified bool
WalletID int64
Type TransferType
PaymentMethod PaymentMethod
CreatedAt time.Time
UpdatedAt time.Time
}
type CreateTransfer struct {

View File

@ -0,0 +1,163 @@
package repository
import (
"context"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func convertCreateBranch(branch domain.CreateBranch) dbgen.CreateBranchParams {
return dbgen.CreateBranchParams{
Name: branch.Name,
Location: branch.Location,
WalletID: branch.WalletID,
BranchManagerID: branch.BranchManagerID,
CompanyID: branch.CompanyID,
IsSelfOwned: branch.IsSelfOwned,
}
}
func convertDBBranchDetail(dbBranch dbgen.BranchDetail) domain.BranchDetail {
return domain.BranchDetail{
ID: dbBranch.ID,
Name: dbBranch.Name,
Location: dbBranch.Location,
WalletID: dbBranch.WalletID,
BranchManagerID: dbBranch.BranchManagerID,
CompanyID: dbBranch.CompanyID,
IsSelfOwned: dbBranch.IsSelfOwned,
ManagerName: dbBranch.ManagerName.(string),
ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
}
}
func convertDBBranch(dbBranch dbgen.Branch) domain.Branch {
return domain.Branch{
ID: dbBranch.ID,
Name: dbBranch.Name,
Location: dbBranch.Location,
WalletID: dbBranch.WalletID,
BranchManagerID: dbBranch.BranchManagerID,
CompanyID: dbBranch.CompanyID,
IsSelfOwned: dbBranch.IsSelfOwned,
}
}
func (s *Store) CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error) {
dbBranch, err := s.queries.CreateBranch(ctx, convertCreateBranch(branch))
if err != nil {
return domain.Branch{}, err
}
return convertDBBranch(dbBranch), nil
}
func (s *Store) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) {
dbSupportedOperation, err := s.queries.CreateSupportedOperation(ctx, dbgen.CreateSupportedOperationParams{
Name: supportedOperation.Name,
Description: supportedOperation.Description,
})
if err != nil {
return domain.SupportedOperation{}, err
}
return domain.SupportedOperation{
ID: dbSupportedOperation.ID,
Name: dbSupportedOperation.Name,
Description: dbSupportedOperation.Description,
}, nil
}
func (s *Store) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error {
_, err := s.queries.CreateBranchOperation(ctx, dbgen.CreateBranchOperationParams{
BranchID: branchOperation.BranchID,
OperationID: branchOperation.OperationID,
})
return err
}
func (s *Store) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) {
dbBranch, err := s.queries.GetBranchByID(ctx, id)
if err != nil {
return domain.BranchDetail{}, err
}
return convertDBBranchDetail(dbBranch), nil
}
func (s *Store) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.GetBranchByManagerID(ctx, branchManagerID)
if err != nil {
return nil, err
}
var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
for _, dbBranch := range dbBranches {
branches = append(branches, convertDBBranchDetail(dbBranch))
}
return branches, nil
}
func (s *Store) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.GetBranchByCompanyID(ctx, companyID)
if err != nil {
return nil, err
}
var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
for _, dbBranch := range dbBranches {
branches = append(branches, convertDBBranchDetail(dbBranch))
}
return branches, nil
}
func (s *Store) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) {
dbBranchOperations, err := s.queries.GetBranchOperations(ctx, branchID)
if err != nil {
return nil, err
}
var branchOperations []domain.BranchOperation = make([]domain.BranchOperation, 0, len(dbBranchOperations))
for _, dbBranchOperation := range dbBranchOperations {
branchOperations = append(branchOperations, domain.BranchOperation{
ID: dbBranchOperation.ID,
OperationName: dbBranchOperation.Name,
OperationDescription: dbBranchOperation.Description,
})
}
return branchOperations, nil
}
func (s *Store) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
dbBranches, err := s.queries.GetAllBranches(ctx)
if err != nil {
return nil, err
}
var branches []domain.BranchDetail = make([]domain.BranchDetail, 0, len(dbBranches))
for _, dbBranch := range dbBranches {
branches = append(branches, convertDBBranchDetail(dbBranch))
}
return branches, nil
}
func (s *Store) UpdateBranch(ctx context.Context, id int64, branch domain.CreateBranch) (domain.Branch, error) {
dbBranch, err := s.queries.UpdateBranch(ctx, dbgen.UpdateBranchParams{
ID: id,
Name: branch.Name,
Location: branch.Location,
BranchManagerID: branch.BranchManagerID,
IsSelfOwned: branch.IsSelfOwned,
})
if err != nil {
return domain.Branch{}, err
}
return convertDBBranch(dbBranch), nil
}
func (s *Store) DeleteBranch(ctx context.Context, id int64) error {
return s.queries.DeleteBranch(ctx, id)
}
func (s *Store) DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error {
err := s.queries.DeleteBranchOperation(ctx, dbgen.DeleteBranchOperationParams{
BranchID: branchID,
OperationID: operationID,
})
return err
}

View File

@ -0,0 +1,21 @@
package branch
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type BranchStore interface {
CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error)
CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error)
CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error
GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error)
GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([]domain.BranchDetail, error)
GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error)
GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error)
GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error)
UpdateBranch(ctx context.Context, id int64, branch domain.CreateBranch) (domain.Branch, error)
DeleteBranch(ctx context.Context, id int64) error
DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error
}

View File

@ -0,0 +1,51 @@
package branch
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
type Service struct {
branchStore BranchStore
}
func NewService(branchStore BranchStore) *Service {
return &Service{
branchStore: branchStore,
}
}
func (s *Service) CreateBranch(ctx context.Context, branch domain.CreateBranch) (domain.Branch, error) {
return s.branchStore.CreateBranch(ctx, branch)
}
func (s *Service) CreateSupportedOperation(ctx context.Context, supportedOperation domain.CreateSupportedOperation) (domain.SupportedOperation, error) {
return s.branchStore.CreateSupportedOperation(ctx, supportedOperation)
}
func (s *Service) CreateBranchOperation(ctx context.Context, branchOperation domain.CreateBranchOperation) error {
return s.branchStore.CreateBranchOperation(ctx, branchOperation)
}
func (s *Service) GetBranchByID(ctx context.Context, id int64) (domain.BranchDetail, error) {
return s.branchStore.GetBranchByID(ctx, id)
}
func (s *Service) GetBranchByManagerID(ctx context.Context, branchManagerID int64) ([] domain.BranchDetail, error) {
return s.branchStore.GetBranchByManagerID(ctx, branchManagerID)
}
func (s *Service) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]domain.BranchDetail, error) {
return s.branchStore.GetBranchByCompanyID(ctx, companyID)
}
func (s *Service) GetBranchOperations(ctx context.Context, branchID int64) ([]domain.BranchOperation, error) {
return s.branchStore.GetBranchOperations(ctx, branchID)
}
func (s *Service) GetAllBranches(ctx context.Context) ([]domain.BranchDetail, error) {
return s.branchStore.GetAllBranches(ctx)
}
func (s *Service) UpdateBranch(ctx context.Context, id int64, branch domain.CreateBranch) (domain.Branch, error) {
return s.branchStore.UpdateBranch(ctx, id, branch)
}
func (s *Service) DeleteBranch(ctx context.Context, id int64) error {
return s.branchStore.DeleteBranch(ctx, id)
}
func (s *Service) DeleteBranchOperation(ctx context.Context, branchID int64, operationID int64) error {
return s.branchStore.DeleteBranchOperation(ctx, branchID, operationID)
}

View File

@ -61,10 +61,8 @@ func convertBet(bet domain.Bet) BetRes {
// @Router /bet [post]
func CreateBet(logger *slog.Logger, betSvc *bet.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
// TODO: Check the token, and find the role and get the branch id from there
// TODO Reduce amount from the branch wallet
// TODO if user is customer, get id from the token then get the wallet id from there
// TODO: If user is a cashier, check the token, and find the role and get the branch id from there. Reduce amount from the branch wallet
var isShopBet bool = true
var branchID int64 = 1

View File

@ -0,0 +1,420 @@
package handlers
import (
"log/slog"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
"github.com/gofiber/fiber/v2"
)
type CreateBranchReq struct {
Name string `json:"name" example:"4-kilo Branch"`
Location string `json:"location" example:"Addis Ababa"`
WalletID int64 `json:"wallet_id" example:"1"`
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
IsSelfOwned bool `json:"is_self_owned" example:"false"`
}
type CreateSupportedOperationReq struct {
Name string `json:"name" example:"SportsBook"`
Description string `json:"description" example:"Betting on sport events"`
}
type SupportedOperationRes struct {
ID int64 `json:"id" example:"1"`
Name string `json:"name" example:"SportsBook"`
Description string `json:"description" example:"Betting on sport events"`
}
type CreateBranchOperationReq struct {
BranchID int64 `json:"branch_id" example:"1"`
OperationID int64 `json:"operation_id" example:"1"`
}
type BranchOperationRes struct {
Name string `json:"name" example:"SportsBook"`
Description string `json:"description" example:"Betting on sport events"`
}
type BranchRes struct {
Name string `json:"name" example:"4-kilo Branch"`
Location string `json:"location" example:"Addis Ababa"`
WalletID int64 `json:"wallet_id" example:"1"`
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
IsSelfOwned bool `json:"is_self_owned" example:"false"`
}
type BranchDetailRes struct {
Name string `json:"name" example:"4-kilo Branch"`
Location string `json:"location" example:"Addis Ababa"`
WalletID int64 `json:"wallet_id" example:"1"`
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
CompanyID int64 `json:"company_id" example:"1"`
IsSelfOwned bool `json:"is_self_owned" example:"false"`
ManagerName string `json:"manager_name" example:"John Smith"`
ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"`
}
func convertBranch(branch domain.Branch) BranchRes {
return BranchRes{
Name: branch.Name,
Location: branch.Location,
WalletID: branch.WalletID,
BranchManagerID: branch.BranchManagerID,
CompanyID: branch.CompanyID,
IsSelfOwned: branch.IsSelfOwned,
}
}
func convertBranchDetail(branch domain.BranchDetail) BranchDetailRes {
return BranchDetailRes{
Name: branch.Name,
Location: branch.Location,
WalletID: branch.WalletID,
BranchManagerID: branch.BranchManagerID,
CompanyID: branch.CompanyID,
IsSelfOwned: branch.IsSelfOwned,
ManagerName: branch.ManagerName,
ManagerPhoneNumber: branch.ManagerPhoneNumber,
}
}
func CreateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
// Check if user is either branch manager / super main
// role := string(c.Locals("role").(domain.Role))
// if role != string(domain.RoleCustomer) {
// logger.Error("Unauthorized access", "role", role)
// return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
// }
var req CreateBranchReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateBranchReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
branch, err := branchSvc.CreateBranch(c.Context(), domain.CreateBranch{
Name: req.Name,
Location: req.Location,
WalletID: req.WalletID,
BranchManagerID: req.BranchManagerID,
CompanyID: req.CompanyID,
IsSelfOwned: req.IsSelfOwned,
})
if err != nil {
logger.Error("CreateBranchReq failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Internal server error",
})
}
res := convertBranch(branch)
return response.WriteJSON(c, fiber.StatusOK, "Branch Created", res, nil)
}
}
func CreateSupportedOperation(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req CreateSupportedOperationReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateBranchReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
operation, err := branchSvc.CreateSupportedOperation(c.Context(), domain.CreateSupportedOperation{
Name: req.Name,
Description: req.Description,
})
if err != nil {
logger.Error("CreateSupportedOperationReq failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Internal server error",
})
}
res := SupportedOperationRes{
Name: operation.Name,
Description: operation.Description,
}
return response.WriteJSON(c, fiber.StatusOK, "Operation Created", res, nil)
}
}
func CreateBranchOperation(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req CreateBranchOperationReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateBranchOperationReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
err := branchSvc.CreateBranchOperation(c.Context(), domain.CreateBranchOperation{
BranchID: req.BranchID,
OperationID: req.OperationID,
})
if err != nil {
logger.Error("CreateBranchOperationReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Branch Operation Created", nil, nil)
}
}
func GetBranchByID(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
id, err := strconv.ParseInt(branchID, 10, 64)
if err != nil {
logger.Error("Invalid branch ID", "branchID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid branch ID", err, nil)
}
branch, err := branchSvc.GetBranchByID(c.Context(), id)
if err != nil {
logger.Error("Failed to get branch by ID", "branchID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve branch", err, nil)
}
res := convertBranchDetail(branch)
return response.WriteJSON(c, fiber.StatusOK, "Branch retrieved successfully", res, nil)
}
}
// /user/:id/branch
func GetBranchByManagerID(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
userID := c.Params("id")
id, err := strconv.ParseInt(userID, 10, 64)
if err != nil {
logger.Error("Invalid user ID", "userID", userID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid user ID", err, nil)
}
branches, err := branchSvc.GetBranchByManagerID(c.Context(), id)
if err != nil {
logger.Error("Failed to get branches", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
}
var result []BranchDetailRes = make([]BranchDetailRes, len(branches))
for _, branch := range branches {
result = append(result, convertBranchDetail(branch))
}
return response.WriteJSON(c, fiber.StatusOK, "Branches for Branch Manager retrieved", result, nil)
}
}
// /company/:id/branch
func GetBranchByCompanyID(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
companyID := c.Params("id")
id, err := strconv.ParseInt(companyID, 10, 64)
if err != nil {
logger.Error("Invalid company ID", "companyID", companyID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid company ID", err, nil)
}
branches, err := branchSvc.GetBranchByCompanyID(c.Context(), id)
if err != nil {
logger.Error("Failed to get branches", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
}
var result []BranchDetailRes = make([]BranchDetailRes, len(branches))
for _, branch := range branches {
result = append(result, convertBranchDetail(branch))
}
return response.WriteJSON(c, fiber.StatusOK, "Branches for Company retrieved", result, nil)
}
}
func GetAllBranches(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branches, err := branchSvc.GetAllBranches(c.Context())
if err != nil {
logger.Error("Failed to get branches", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get branches", err, nil)
}
var result []BranchDetailRes = make([]BranchDetailRes, len(branches))
for _, branch := range branches {
result = append(result, convertBranchDetail(branch))
}
return response.WriteJSON(c, fiber.StatusOK, "Branches for Company retrieved", result, nil)
}
}
func GetBranchOperations(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
id, err := strconv.ParseInt(branchID, 10, 64)
if err != nil {
logger.Error("Invalid branch ID", "branchID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid branch ID", err, nil)
}
operations, err := branchSvc.GetBranchOperations(c.Context(), id)
if err != nil {
logger.Error("Failed to get operation by ID", "branchID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve operation", err, nil)
}
var result []BranchOperationRes = make([]BranchOperationRes, len(operations))
for _, branch := range operations {
result = append(result, BranchOperationRes{
Name: branch.OperationName,
Description: branch.OperationDescription,
})
}
return response.WriteJSON(c, fiber.StatusOK, "Branch Operations retrieved successfully", result, nil)
}
}
func UpdateBranch(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
id, err := strconv.ParseInt(branchID, 10, 64)
if err != nil {
logger.Error("Invalid branch ID", "branchID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid branch ID", err, nil)
}
var req CreateBranchReq
if err := c.BodyParser(&req); err != nil {
logger.Error("CreateBetReq failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
branch, err := branchSvc.UpdateBranch(c.Context(), id, domain.CreateBranch{
Name: req.Name,
Location: req.Location,
WalletID: req.WalletID,
BranchManagerID: req.BranchManagerID,
CompanyID: req.CompanyID,
IsSelfOwned: req.IsSelfOwned,
})
if err != nil {
logger.Error("Failed to update branch", "branchID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update branch", err, nil)
}
res := convertBranch(branch)
return response.WriteJSON(c, fiber.StatusOK, "Branch Updated", res, nil)
}
}
func DeleteBranch(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
id, err := strconv.ParseInt(branchID, 10, 64)
if err != nil {
logger.Error("Invalid Branch ID", "branchID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid Branch ID", err, nil)
}
err = branchSvc.DeleteBranch(c.Context(), id)
if err != nil {
logger.Error("Failed to delete by ID", "Branch ID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to Delete Branch", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Branch removed successfully", nil, nil)
}
}
func DeleteBranchOperation(logger *slog.Logger, branchSvc *branch.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchID := c.Params("id")
opID := c.Params("opID")
id, err := strconv.ParseInt(branchID, 10, 64)
if err != nil {
logger.Error("Invalid Branch ID", "branchID", branchID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid Branch ID", err, nil)
}
operationID, err := strconv.ParseInt(opID, 10, 64)
if err != nil {
logger.Error("Invalid Operation ID", "operationID", opID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid Operation ID", err, nil)
}
err = branchSvc.DeleteBranchOperation(c.Context(), id, operationID)
if err != nil {
logger.Error("Failed to delete operation", "Branch ID", id, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to Delete Operation", err, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Branch Operation removed successfully", nil, nil)
}
}

View File

@ -179,17 +179,17 @@ func GetCustomerWallet(logger *slog.Logger, walletSvc *wallet.Service, validator
userId := c.Locals("user_id").(int64)
role := string(c.Locals("role").(domain.Role))
companyID, err := strconv.ParseInt(c.Get("company_id"), 10, 64)
vendorID, err := strconv.ParseInt(c.Get("vendor_id"), 10, 64)
if err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Invalid company_id")
}
logger.Info("Company ID: " + strconv.FormatInt(companyID, 10))
logger.Info("Company ID: " + strconv.FormatInt(vendorID, 10))
if role != string(domain.RoleCustomer) {
logger.Error("Unauthorized access", "userId", userId, "role", role)
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
}
wallet, err := walletSvc.GetCustomerWallet(c.Context(), userId, companyID)
wallet, err := walletSvc.GetCustomerWallet(c.Context(), userId, vendorID)
if err != nil {
logger.Error("Failed to get customer wallet", "userId", userId, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve wallet", err, nil)

View File

@ -16,3 +16,4 @@ sql:
- db_type: "uuid"
go_type: "github.com/google/uuid.NullUUID"
nullable: true