package handlers import ( "fmt" "strconv" "strings" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) // CreateBranch godoc // @Summary Create a branch // @Description Creates a branch // @Tags branch // @Accept json // @Produce json // @Param createBranch body domain.CreateBranchReq true "Creates branch" // @Success 200 {object} domain.BranchRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch [post] func (h *Handler) CreateBranch(c *fiber.Ctx) error { // Check if user is either branch manager / super main // role := string(c.Locals("role").(domain.Role)) // if role != string(domain.RoleAdmin) && role != string(domain.RoleSuperAdmin) && role != string(domain.RoleBranchManager) { // logger.Error("Unauthorized access", "role", role) // return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil) // } role := c.Locals("role").(domain.Role) companyID := c.Locals("company_id").(domain.ValidInt64) var req domain.CreateBranchReq if err := c.BodyParser(&req); err != nil { // h.logger.Error("CreateBranchReq failed", "error", err) h.mongoLoggerSvc.Info("CreateBranchReq failed", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid request") } valErrs, ok := h.validator.Validate(c, req) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } return fiber.NewError(fiber.StatusBadRequest, errMsg) } var IsSelfOwned bool var checkedCompanyID int64 if role == domain.RoleSuperAdmin { if req.IsSelfOwned == nil { h.mongoLoggerSvc.Info("is_self_owned is required for super admin", zap.Int("status_code", fiber.StatusBadRequest), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "is_self_owned is required for super admin") } if req.CompanyID == nil { h.mongoLoggerSvc.Info("company_id is required for super admin", zap.Int("status_code", fiber.StatusBadRequest), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "company_id is required for super admin") } IsSelfOwned = *req.IsSelfOwned checkedCompanyID = *req.CompanyID } else { IsSelfOwned = false checkedCompanyID = companyID.Value } // Create Branch Wallet newWallet, err := h.walletSvc.CreateWallet(c.Context(), domain.CreateWallet{ IsWithdraw: false, IsBettable: true, IsTransferable: true, UserID: req.BranchManagerID, Type: domain.BranchWalletType, }) if err != nil { h.mongoLoggerSvc.Error("Create Branch Wallet failed", zap.Int64("branch_manager_id", req.BranchManagerID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } branch, err := h.branchSvc.CreateBranch(c.Context(), domain.CreateBranch{ Name: req.Name, Location: req.Location, WalletID: newWallet.ID, BranchManagerID: req.BranchManagerID, CompanyID: checkedCompanyID, IsSelfOwned: IsSelfOwned, ProfitPercentage: req.ProfitPercentage, }) if err != nil { h.mongoLoggerSvc.Error("Failed to create branch", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } for _, operation := range req.Operations { err := h.branchSvc.CreateBranchOperation(c.Context(), domain.CreateBranchOperation{ BranchID: branch.ID, OperationID: operation, }) if err != nil { h.mongoLoggerSvc.Error("Failed to create branch operations", zap.Int64("branchID", branch.ID), zap.Int64("operation", operation), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } } res := domain.ConvertBranch(branch) return response.WriteJSON(c, fiber.StatusCreated, "Branch Created", res, nil) } // CreateSupportedOperation godoc // @Summary Create a supported operation // @Description Creates a supported operation // @Tags branch // @Accept json // @Produce json // @Param createSupportedOperation body domain.CreateSupportedOperationReq true "Creates supported operation" // @Success 200 {object} domain.SupportedOperationRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/supportedOperation [post] func (h *Handler) CreateSupportedOperation(c *fiber.Ctx) error { var req domain.CreateSupportedOperationReq if err := c.BodyParser(&req); err != nil { h.mongoLoggerSvc.Info("Failed to parse CreateSupportedOperationReq", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid request") } valErrs, ok := h.validator.Validate(c, req) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } h.mongoLoggerSvc.Error("Failed to validate CreateSupportedOperationReq", zap.Any("request", req), zap.String("error", errMsg), zap.Int("status_code", fiber.StatusInternalServerError), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, errMsg) } operation, err := h.branchSvc.CreateSupportedOperation(c.Context(), domain.CreateSupportedOperation{ Name: req.Name, Description: req.Description, }) if err != nil { h.mongoLoggerSvc.Error("Failed to create supported operation", zap.String("name", req.Name), zap.String("description", req.Description), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to create supported operation") } res := domain.SupportedOperationRes{ Name: operation.Name, Description: operation.Description, } return response.WriteJSON(c, fiber.StatusOK, "Operation Created", res, nil) } // CreateBranchOperation godoc // @Summary Create a operation // @Description Creates a operation // @Tags branch // @Accept json // @Produce json // @Param createBranchOperation body domain.CreateBranchOperationReq true "Creates operation" // @Success 200 {object} domain.BranchOperationRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/operation [post] func (h *Handler) CreateBranchOperation(c *fiber.Ctx) error { var req domain.CreateBranchOperationReq if err := c.BodyParser(&req); err != nil { h.logger.Error("CreateBranchOperationReq failed", "error", err) h.mongoLoggerSvc.Info("Failed to parse CreateBranchOperationReq", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid request") } valErrs, ok := h.validator.Validate(c, req) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } h.mongoLoggerSvc.Info("Failed to validated CreateBranchOperationReq", zap.String("errMsg", errMsg), zap.Int("status_code", fiber.StatusBadRequest), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, errMsg) } err := h.branchSvc.CreateBranchOperation(c.Context(), domain.CreateBranchOperation{ BranchID: req.BranchID, OperationID: req.OperationID, }) if err != nil { h.mongoLoggerSvc.Error("Failed to create branch operation", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } return response.WriteJSON(c, fiber.StatusOK, "Branch Operation Created", nil, nil) } // GetBranchByID godoc // @Summary Gets branch by id // @Description Gets a single branch by id // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Success 200 {object} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id} [get] func (h *Handler) GetBranchByID(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid branch ID", zap.String("branch", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } branch, err := h.branchSvc.GetBranchByID(c.Context(), id) if err != nil { h.mongoLoggerSvc.Info("Failed to get branch by ID", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } res := domain.ConvertBranchDetail(branch) return response.WriteJSON(c, fiber.StatusOK, "Branch retrieved successfully", res, nil) } // ReturnBranchWallet godoc // @Summary Unassign the branch wallet to company // @Description Unassign the branch wallet to company // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Success 200 {object} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id}/return [post] func (h *Handler) ReturnBranchWallet(c *fiber.Ctx) error { userID := c.Locals("user_id").(int64) branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid branch ID", zap.String("branch", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } branch, err := h.branchSvc.GetBranchByID(c.Context(), id) if err != nil { h.mongoLoggerSvc.Info("Failed to get branch by ID", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } company, err := h.companySvc.GetCompanyByID(c.Context(), branch.CompanyID) if err != nil { h.mongoLoggerSvc.Info("Failed to get company by ID", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } branchWallet, err := h.walletSvc.GetWalletByID(c.Context(), branch.WalletID) if err != nil { h.mongoLoggerSvc.Info("Failed to get wallet by ID", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } _, err = h.walletSvc.AddToWallet(c.Context(), company.WalletID, branchWallet.Balance, domain.ValidInt64{Value: userID, Valid: true}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, fmt.Sprintf("Returning Branch %v Wallet to Company", branch.Name)) if err != nil { h.mongoLoggerSvc.Info("Failed to add to company wallet", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } _, err = h.walletSvc.DeductFromWallet(c.Context(), branchWallet.ID, branchWallet.Balance, domain.ValidInt64{Value: userID, Valid: true}, domain.TRANSFER_DIRECT, "Branch Wallet Balance has been returned to Company", ) if err != nil { h.mongoLoggerSvc.Info("Failed to deduct from branch wallet", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } res := domain.ConvertBranchDetail(branch) return response.WriteJSON(c, fiber.StatusOK, "Branch Wallet Has been emptied", res, nil) } // GetBranchByManagerID godoc // @Summary Gets branches by manager id // @Description Gets a branches by manager id // @Tags branch // @Accept json // @Produce json // @Param id path int true "User ID" // @Success 200 {array} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/manager/{id}/branch [get] func (h *Handler) GetBranchByManagerID(c *fiber.Ctx) error { // TODO: Restrict any who isn't branch manager or higher userID := c.Params("id") id, err := strconv.ParseInt(userID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid user ID", zap.String("userID", userID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID") } branches, err := h.branchSvc.GetBranchByManagerID(c.Context(), id) if err != nil { h.mongoLoggerSvc.Info("Failed to get branches", zap.String("userID", userID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, err.Error()) } var result []domain.BranchDetailRes = make([]domain.BranchDetailRes, 0, len(branches)) for _, branch := range branches { result = append(result, domain.ConvertBranchDetail(branch)) } return response.WriteJSON(c, fiber.StatusOK, "Branches for Branch Manager retrieved", result, nil) } // GetBranchByCompanyID godoc // @Summary Gets branches by company id // @Description Gets branches by company id // @Tags branch // @Accept json // @Produce json // @Param id path int true "Company ID" // @Success 200 {array} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/company/{id}/branch [get] func (h *Handler) GetBranchByCompanyID(c *fiber.Ctx) error { companyID := c.Params("id") id, err := strconv.ParseInt(companyID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid company ID", zap.String("companyID", companyID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid company ID") } branches, err := h.branchSvc.GetBranchByCompanyID(c.Context(), id) if err != nil { h.mongoLoggerSvc.Info("Failed to get branches", zap.String("companyID", companyID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var result []domain.BranchDetailRes = make([]domain.BranchDetailRes, 0, len(branches)) for _, branch := range branches { result = append(result, domain.ConvertBranchDetail(branch)) } return response.WriteJSON(c, fiber.StatusOK, "Branches for Company retrieved", result, nil) } // GetAllBranches godoc // @Summary Gets all branches // @Description Gets all branches // @Tags branch // @Accept json // @Produce json // @Success 200 {array} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch [get] func (h *Handler) GetAllBranches(c *fiber.Ctx) error { companyID := c.Locals("company_id").(domain.ValidInt64) isActiveParam := c.Params("is_active") isActiveValid := isActiveParam != "" isActive, err := strconv.ParseBool(isActiveParam) if isActiveValid && err != nil { h.mongoLoggerSvc.Info("Invalid is_active param", zap.String("isActive", isActiveParam), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid is_active param") } branchManagerQuery := c.Query("branch_manager_id") var branchManagerID domain.ValidInt64 if branchManagerQuery != "" { parseManagerID, err := strconv.ParseInt(branchManagerQuery, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Failed to parse branch_manager_id", zap.String("userID", branchManagerQuery), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Failed to parse branch_manager_id") } branchManagerID = domain.ValidInt64{ Value: parseManagerID, Valid: true, } } 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.mongoLoggerSvc.Info("invalid created_before format", zap.String("createdBeforeQuery", createdBeforeQuery), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format") } 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.mongoLoggerSvc.Info("invalid created_after format", zap.String("createdAfterQuery", createdAfterQuery), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format") } createdAfter = domain.ValidTime{ Value: createdAfterParsed, Valid: true, } } branches, err := h.branchSvc.GetAllBranches(c.Context(), domain.BranchFilter{ CompanyID: companyID, IsActive: domain.ValidBool{ Value: isActive, Valid: isActiveValid, }, BranchManagerID: branchManagerID, Query: searchString, CreatedBefore: createdBefore, CreatedAfter: createdAfter, }) if err != nil { h.mongoLoggerSvc.Info("Failed to get branches", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var result []domain.BranchDetailRes = make([]domain.BranchDetailRes, 0, len(branches)) for _, branch := range branches { result = append(result, domain.ConvertBranchDetail(branch)) } return response.WriteJSON(c, fiber.StatusOK, "Branches for Company retrieved", result, nil) } // SearchBranch godoc // @Summary Search branches // @Description Search branches by name or location // @Tags branch // @Accept json // @Produce json // @Param q query string true "Search query" // @Success 200 {array} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/search/branch [get] func (h *Handler) SearchBranch(c *fiber.Ctx) error { companyID := c.Locals("company_id").(domain.ValidInt64) // Get search query from request searchQuery := c.Query("q") if searchQuery == "" { h.mongoLoggerSvc.Info("Search query is required", zap.Int("status_code", fiber.StatusBadRequest), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Search query is required") } // Call the service to search for branches branches, err := h.branchSvc.SearchBranchByName(c.Context(), searchQuery, companyID) if err != nil { h.mongoLoggerSvc.Info("Failed to search branches", zap.String("query", searchQuery), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } // Convert branches to response format var result []domain.BranchDetailRes for _, branch := range branches { result = append(result, domain.ConvertBranchDetail(branch)) } return response.WriteJSON(c, fiber.StatusOK, "Branches retrieved successfully", result, nil) } // GetAllSupportedOperations godoc // @Summary Gets all supported operations // @Description Gets all supported operations // @Tags branch // @Accept json // @Produce json // @Success 200 {array} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/supportedOperation [get] func (h *Handler) GetAllSupportedOperations(c *fiber.Ctx) error { operations, err := h.branchSvc.GetAllSupportedOperations(c.Context()) if err != nil { h.mongoLoggerSvc.Error("Failed to get operations", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var result []domain.SupportedOperationRes = make([]domain.SupportedOperationRes, 0, len(operations)) for _, operation := range operations { result = append(result, domain.SupportedOperationRes{ ID: operation.ID, Name: operation.Name, Description: operation.Description, }) } return response.WriteJSON(c, fiber.StatusOK, "SupportedOperations for Company retrieved", result, nil) } // GetBranchOperations godoc // @Summary Gets branch operations // @Description Gets branch operations // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Success 200 {array} domain.BranchOperationRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id}/operation [get] func (h *Handler) GetBranchOperations(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Error("Invalid branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } operations, err := h.branchSvc.GetBranchOperations(c.Context(), id) if err != nil { h.mongoLoggerSvc.Error("Failed to get operation by ID", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var result []domain.BranchOperationRes = make([]domain.BranchOperationRes, 0, len(operations)) for _, branch := range operations { result = append(result, domain.BranchOperationRes{ Name: branch.OperationName, Description: branch.OperationDescription, }) } return response.WriteJSON(c, fiber.StatusOK, "Branch Operations retrieved successfully", result, nil) } // GetAllBranchLocations godoc // @Summary Gets all branch locations // @Description Gets all branch locations // @Tags branch // @Accept json // @Produce json // @Success 200 {array} domain.BranchLocation // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branchLocation [get] func (h *Handler) GetAllBranchLocations(c *fiber.Ctx) error { searchQuery := c.Query("query") searchString := domain.ValidString{ Value: searchQuery, Valid: searchQuery != "", } locations, err := h.branchSvc.GetAllBranchLocations(c.Context(), searchString) if err != nil { h.mongoLoggerSvc.Error("Failed to get branch locations", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } return response.WriteJSON(c, fiber.StatusOK, "Branch Location successfully fetched", locations, nil) } // GetBranchCashiers godoc // @Summary Gets branch cashiers // @Description Gets branch cashiers // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Success 200 {array} GetCashierRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id}/cashier [get] func (h *Handler) GetBranchCashiers(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } cashiers, err := h.userSvc.GetCashiersByBranch(c.Context(), id) if err != nil { h.mongoLoggerSvc.Error("Failed to get cashier by branch ID", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var result []GetCashierRes = make([]GetCashierRes, 0, len(cashiers)) for _, cashier := range cashiers { lastLogin, err := h.authSvc.GetLastLogin(c.Context(), cashier.ID) if err != nil { if err == authentication.ErrRefreshTokenNotFound { lastLogin = &cashier.CreatedAt } else { h.mongoLoggerSvc.Error("Failed to get user last login", zap.Int64("cashier ID", cashier.ID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login") } } result = append(result, GetCashierRes{ ID: cashier.ID, FirstName: cashier.FirstName, LastName: cashier.LastName, Email: cashier.Email, PhoneNumber: cashier.PhoneNumber, Role: cashier.Role, EmailVerified: cashier.EmailVerified, PhoneVerified: cashier.PhoneVerified, CreatedAt: cashier.CreatedAt, UpdatedAt: cashier.UpdatedAt, SuspendedAt: cashier.SuspendedAt, Suspended: cashier.Suspended, LastLogin: *lastLogin, }) } return response.WriteJSON(c, fiber.StatusOK, "Branch Cashiers retrieved successfully", result, nil) } // GetBranchForCashier godoc // @Summary Gets branch for cahier // @Description Gets branch for cahier // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Success 200 {object} domain.BranchDetailRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branchCashier [get] func (h *Handler) GetBranchForCashier(c *fiber.Ctx) error { cashierID, ok := c.Locals("user_id").(int64) if !ok { h.mongoLoggerSvc.Error("Invalid cashier ID in context", zap.Int64("cashierID", cashierID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, "Invalid user_id") } role, ok := c.Locals("role").(domain.Role) if !ok || role != domain.RoleCashier { h.mongoLoggerSvc.Error("Unauthorized access", zap.Int64("cashierID", cashierID), zap.String("role", string(role)), zap.Int("status_code", fiber.StatusForbidden), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusForbidden, "Unauthorized access") } branchID, ok := c.Locals("branch_id").(domain.ValidInt64) if !ok || !branchID.Valid { h.mongoLoggerSvc.Info("Invalid branch ID in context", zap.Int64("cashierID", cashierID), zap.Int("status_code", fiber.StatusBadRequest), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } branch, err := h.branchSvc.GetBranchByID(c.Context(), branchID.Value) if err != nil { h.mongoLoggerSvc.Error("Failed to get branch by ID", zap.Int64("branchID", branchID.Value), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } res := domain.ConvertBranchDetail(branch) return response.WriteJSON(c, fiber.StatusOK, "Branch retrieved successfully", res, nil) } // GetBetByBranchID godoc // @Summary Gets bets by its branch id // @Description Gets bets by its branch id // @Tags branch // @Accept json // @Produce json // @Success 200 {array} domain.BetRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id}/bets [get] func (h *Handler) GetBetByBranchID(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } bets, err := h.transactionSvc.GetAllShopBet(c.Context(), domain.ShopBetFilter{ BranchID: domain.ValidInt64{ Value: id, Valid: true, }, }) if err != nil { h.mongoLoggerSvc.Error("Failed to get bets", zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } var res []domain.ShopBetRes = make([]domain.ShopBetRes, 0, len(bets)) for _, bet := range bets { res = append(res, domain.ConvertShopBetDetail(bet)) } return response.WriteJSON(c, fiber.StatusOK, "Branch Bets Retrieved", res, nil) } // UpdateBranch godoc // @Summary Updates a branch // @Description Updates a branch // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Param updateBranch body domain.CreateBranchReq true "Update Branch" // @Success 200 {object} domain.BranchRes // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id} [put] func (h *Handler) UpdateBranch(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch ID") } var req domain.UpdateBranchReq if err := c.BodyParser(&req); err != nil { h.mongoLoggerSvc.Info("Failed to parse UpdateBranchReq", zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid request") } valErrs, ok := h.validator.Validate(c, req) if !ok { var errMsg string for field, msg := range valErrs { errMsg += fmt.Sprintf("%s: %s; ", field, msg) } h.mongoLoggerSvc.Info("Failed to validate UpdateBranchReq", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.String("errMsg", errMsg), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, errMsg) } branch, err := h.branchSvc.UpdateBranch(c.Context(), domain.UpdateBranch{ ID: id, Name: req.Name, Location: req.Location, BranchManagerID: req.BranchManagerID, CompanyID: req.CompanyID, IsSelfOwned: req.IsSelfOwned, IsActive: req.IsActive, }) if err != nil { h.mongoLoggerSvc.Error("Failed to update branch", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } res := domain.ConvertBranch(branch) return response.WriteJSON(c, fiber.StatusOK, "Branch Updated", res, nil) } func (h *Handler) UpdateBranchStatus(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, err.Error()) } var isActive bool path := strings.Split(strings.Trim(c.Path(), "/"), "/") if path[len(path)-1] == "set-active" { isActive = true } else if path[len(path)-1] == "set-inactive" { isActive = false } else { h.mongoLoggerSvc.Info("Invalid branch status", zap.Bool("status", isActive), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid branch status") } branch, err := h.branchSvc.UpdateBranch(c.Context(), domain.UpdateBranch{ ID: id, IsActive: &isActive, }) if err != nil { h.mongoLoggerSvc.Error("Failed to update branch", zap.Int64("branchID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } res := domain.ConvertBranch(branch) return response.WriteJSON(c, fiber.StatusOK, "Branch Updated", res, nil) } // DeleteBranch godoc // @Summary Delete the branch // @Description Delete the branch // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID"" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id} [delete] func (h *Handler) DeleteBranch(c *fiber.Ctx) error { branchID := c.Params("id") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid Branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid Branch ID") } err = h.branchSvc.DeleteBranch(c.Context(), id) if err != nil { h.mongoLoggerSvc.Error("Failed to delete by ID", zap.Int64("Branch ID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } return response.WriteJSON(c, fiber.StatusOK, "Branch removed successfully", nil, nil) } // DeleteBranchOperation godoc // @Summary Delete the branch operation // @Description Delete the branch operation // @Tags branch // @Accept json // @Produce json // @Param id path int true "Branch ID" // @Param opID path int true "Branch Operation ID" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/v1/branch/{id}/operation/{opID} [delete] func (h *Handler) DeleteBranchOperation(c *fiber.Ctx) error { branchID := c.Params("id") opID := c.Params("opID") id, err := strconv.ParseInt(branchID, 10, 64) if err != nil { h.mongoLoggerSvc.Error("Invalid Branch ID", zap.String("branchID", branchID), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid Branch ID") } operationID, err := strconv.ParseInt(opID, 10, 64) if err != nil { h.mongoLoggerSvc.Info("Invalid Operation ID", zap.String("operationID", opID), zap.Int("status_code", fiber.StatusBadRequest), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusBadRequest, "Invalid Operation ID") } err = h.branchSvc.DeleteBranchOperation(c.Context(), id, operationID) if err != nil { h.mongoLoggerSvc.Error("Failed to delete operation", zap.Int64("Branch ID", id), zap.Int("status_code", fiber.StatusInternalServerError), zap.Error(err), zap.Time("timestamp", time.Now()), ) return fiber.NewError(fiber.StatusInternalServerError, err.Error()) } return response.WriteJSON(c, fiber.StatusOK, "Branch Operation removed successfully", nil, nil) }