Yimaru-BackEnd/internal/repository/transaction.go

258 lines
8.8 KiB
Go

package repository
import (
"context"
"fmt"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
return domain.Transaction{
ID: transaction.ID,
Amount: domain.Currency(transaction.Amount),
BranchID: transaction.BranchID,
CashierID: transaction.CashierID.Int64,
BetID: transaction.BetID.Int64,
NumberOfOutcomes: transaction.NumberOfOutcomes.Int64,
Type: domain.TransactionType(transaction.Type.Int64),
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,
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,
},
}
}
func convertCreateTransaction(transaction domain.CreateTransaction) dbgen.CreateTransactionParams {
return dbgen.CreateTransactionParams{
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},
}
}
func (s *Store) CreateTransaction(ctx context.Context, transaction domain.CreateTransaction) (domain.Transaction, error) {
newTransaction, err := s.queries.CreateTransaction(ctx, convertCreateTransaction(transaction))
if err != nil {
return domain.Transaction{}, err
}
return convertDBTransaction(newTransaction), err
}
func (s *Store) GetTransactionByID(ctx context.Context, id int64) (domain.Transaction, error) {
transaction, err := s.queries.GetTransactionByID(ctx, id)
if err != nil {
return domain.Transaction{}, err
}
return convertDBTransaction(transaction), nil
}
func (s *Store) GetAllTransactions(ctx context.Context, filter domain.TransactionFilter) ([]domain.Transaction, error) {
transaction, err := s.queries.GetAllTransactions(ctx, dbgen.GetAllTransactionsParams{
BranchID: pgtype.Int8{
Int64: filter.BranchID.Value,
Valid: filter.BranchID.Valid,
},
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
CashierID: pgtype.Int8{
Int64: filter.CashierID.Value,
Valid: filter.CashierID.Valid,
},
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
CreatedBefore: pgtype.Timestamp{
Time: filter.CreatedBefore.Value,
Valid: filter.CreatedBefore.Valid,
},
CreatedAfter: pgtype.Timestamp{
Time: filter.CreatedAfter.Value,
Valid: filter.CreatedAfter.Valid,
},
})
if err != nil {
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
}
return result, nil
}
func (s *Store) GetTransactionByBranch(ctx context.Context, id int64) ([]domain.Transaction, error) {
transaction, err := s.queries.GetTransactionByBranch(ctx, id)
if err != nil {
return nil, err
}
var result []domain.Transaction = make([]domain.Transaction, 0, len(transaction))
for _, ticket := range transaction {
result = append(result, convertDBTransaction(ticket))
}
return result, nil
}
func (s *Store) UpdateTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, approverName string) error {
err := s.queries.UpdateTransactionVerified(ctx, dbgen.UpdateTransactionVerifiedParams{
ID: id,
ApprovedBy: pgtype.Int8{
Int64: approvedBy,
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
COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits,
COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals
FROM transactions`
args := []interface{}{}
argPos := 1
if filter.CompanyID.Valid {
query += fmt.Sprintf(" WHERE company_id = $%d", argPos)
args = append(args, filter.CompanyID.Value)
argPos++
} else if filter.BranchID.Valid {
query += fmt.Sprintf(" WHERE branch_id = $%d", argPos)
args = append(args, filter.BranchID.Value)
argPos++
} else if filter.UserID.Valid {
query += fmt.Sprintf(" WHERE cashier_id = $%d", argPos)
args = append(args, filter.UserID.Value)
argPos++
}
if filter.StartTime.Valid {
query += fmt.Sprintf(" AND %screated_at >= $%d", func() string {
if len(args) == 0 {
return ""
}
return " "
}(), argPos)
args = append(args, filter.StartTime.Value)
argPos++
}
if filter.EndTime.Valid {
query += fmt.Sprintf(" AND created_at <= $%d", argPos)
args = append(args, filter.EndTime.Value)
argPos++
}
row := s.conn.QueryRow(ctx, query, args...)
err = row.Scan(&deposits, &withdrawals)
if err != nil {
return 0, 0, fmt.Errorf("failed to get transaction totals: %w", err)
}
return deposits, withdrawals, nil
}
// GetBranchTransactionTotals returns transaction totals by branch
func (s *Store) GetBranchTransactionTotals(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.BranchTransactions, error) {
query := `SELECT
branch_id,
COALESCE(SUM(CASE WHEN type = 1 THEN amount ELSE 0 END), 0) as deposits,
COALESCE(SUM(CASE WHEN type = 0 THEN amount ELSE 0 END), 0) as withdrawals
FROM transactions`
args := []interface{}{}
argPos := 1
if filter.CompanyID.Valid {
query += fmt.Sprintf(" WHERE company_id = $%d", argPos)
args = append(args, filter.CompanyID.Value)
argPos++
}
if filter.StartTime.Valid {
query += fmt.Sprintf(" AND %screated_at >= $%d", func() string {
if len(args) == 0 {
return " WHERE "
}
return " AND "
}(), argPos)
args = append(args, filter.StartTime.Value)
argPos++
}
if filter.EndTime.Valid {
query += fmt.Sprintf(" AND created_at <= $%d", argPos)
args = append(args, filter.EndTime.Value)
argPos++
}
query += " GROUP BY branch_id"
rows, err := s.conn.Query(ctx, query, args...)
if err != nil {
return nil, fmt.Errorf("failed to query branch transaction totals: %w", err)
}
defer rows.Close()
totals := make(map[int64]domain.BranchTransactions)
for rows.Next() {
var branchID int64
var transactions domain.BranchTransactions
if err := rows.Scan(&branchID, &transactions.Deposits, &transactions.Withdrawals); err != nil {
return nil, fmt.Errorf("failed to scan branch transaction totals: %w", err)
}
totals[branchID] = transactions
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("rows error: %w", err)
}
return totals, nil
}