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 convertDBShopTransaction(transaction dbgen.ShopTransaction) domain.ShopTransaction { return domain.ShopTransaction{ 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, CompanyID: transaction.CompanyID, } } 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, 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, convertCreateShopTransaction(transaction)) if err != nil { return domain.ShopTransaction{}, err } return convertDBShopTransaction(newTransaction), err } func (s *Store) GetShopTransactionByID(ctx context.Context, id int64) (domain.ShopTransactionDetail, error) { transaction, err := s.queries.GetShopTransactionByID(ctx, id) if err != nil { return domain.ShopTransactionDetail{}, err } return convertDBShopTransactionDetail(transaction), nil } 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, Valid: filter.BranchID.Valid, }, CompanyID: pgtype.Int8{ Int64: filter.CompanyID.Value, Valid: filter.CompanyID.Valid, }, UserID: pgtype.Int8{ Int64: filter.UserID.Value, Valid: filter.UserID.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.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.ShopTransactionDetail, error) { transaction, err := s.queries.GetShopTransactionByBranch(ctx, id) if err != nil { return nil, err } var result []domain.ShopTransactionDetail = make([]domain.ShopTransactionDetail, 0, len(transaction)) for _, ticket := range transaction { result = append(result, convertDBShopTransactionDetail(ticket)) } return result, nil } func (s *Store) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64) error { err := s.queries.UpdateShopTransactionVerified(ctx, dbgen.UpdateShopTransactionVerifiedParams{ ID: id, ApprovedBy: pgtype.Int8{ Int64: approvedBy, Valid: true, }, Verified: verified, }) 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 shop_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 user_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 shop_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 }