package transaction import ( "context" "errors" "fmt" "time" "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/user" "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") ErrDepositCannotBeUnverified = errors.New("deposit cannot be unverified") ErrShopBetHasExpired = errors.New("shop bet has already been expired") ) type Service struct { transactionStore TransactionStore branchSvc branch.Service betSvc bet.Service walletSvc wallet.Service userSvc user.Service } func NewService(transactionStore TransactionStore, branchSvc branch.Service, betSvc bet.Service, walletSvc wallet.Service, userSvc user.Service) *Service { return &Service{ transactionStore: transactionStore, branchSvc: branchSvc, betSvc: betSvc, walletSvc: walletSvc, userSvc: userSvc, } } 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.ShopTransactionDetail, error) { return s.transactionStore.GetShopTransactionByID(ctx, id) } 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.ShopTransactionDetail, error) { return s.transactionStore.GetShopTransactionByBranch(ctx, id) } func (s *Service) UpdateShopTransactionVerified(ctx context.Context, id int64, verified bool, approvedBy int64, role domain.Role, companyID domain.ValidInt64, branchID domain.ValidInt64) error { // TODO: Move this into a service role verification service // Checks to make sure only the same company and branch can modify this transaction, err := s.GetShopTransactionByID(ctx, id) if role != domain.RoleSuperAdmin { if !companyID.Valid || companyID.Value != transaction.CompanyID { // s.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err) return fmt.Errorf("user cannot modify another companies data") } if role == domain.RoleCashier { if !branchID.Valid || branchID.Value != transaction.BranchID { // h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", "Unauthorized") return fmt.Errorf("user cannot modify another companies data") } } } if err != nil { return err } switch transaction.Type { case domain.TRANSACTION_BET: if verified { bet, err := s.GetShopBetByShopTransactionID(ctx, transaction.ID) if err != nil { return err } var firstEvent time.Time = bet.Outcomes[0].Expires for _, outcome := range bet.Outcomes { if outcome.Expires.Before(firstEvent) { firstEvent = outcome.Expires } } fmt.Printf("\n\n Shop bet expire %v - now %v \n\n", firstEvent, time.Now().UTC()) if firstEvent.Before(time.Now()) { return ErrShopBetHasExpired } } case domain.TRANSACTION_DEPOSIT: if !verified { // TODO: Figure out what to do here? Should i return the money or verify the faulty verify return ErrDepositCannotBeUnverified } deposit, err := s.GetShopDepositByShopTransactionID(ctx, transaction.ID) if err != nil { return err } customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, deposit.CustomerID) if err != nil { return ErrUserHasNoCustomerWallet } transfer, err := s.walletSvc.TransferToWallet(ctx, deposit.BranchWalletID, customerWallet.RegularID, transaction.Amount, domain.TRANSFER_DIRECT, domain.ValidInt64{ Value: transaction.UserID, Valid: true, }, fmt.Sprintf("Transferred %v to customer wallet due to shop deposit", transaction.Amount), ) if err != nil { return err } err = s.UpdateShopDepositTransferID(ctx, deposit.ID, domain.ValidInt64{ Value: transfer.ID, Valid: true, }) if err != nil { return err } } return s.transactionStore.UpdateShopTransactionVerified(ctx, id, verified, approvedBy) } 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 } }