diff options
author | 2025-04-26 01:06:54 +0530 | |
---|---|---|
committer | 2025-04-26 01:06:54 +0530 | |
commit | 9d65a782ca3e2084ef0f560500f6014d7bd09bc0 (patch) | |
tree | e97195e8b967267d8d40098ae40a940fa2d44571 /backend/internal/api/handlers/goal_handler.go | |
parent | 84622698f6c0e9d76ebe434c00df587908a37015 (diff) | |
download | finance-9d65a782ca3e2084ef0f560500f6014d7bd09bc0.tar.gz finance-9d65a782ca3e2084ef0f560500f6014d7bd09bc0.tar.bz2 finance-9d65a782ca3e2084ef0f560500f6014d7bd09bc0.zip |
finance/backend: mvfeat: moved the backend api handlers to api/handlers and added couple of more api request handlers
Diffstat (limited to 'backend/internal/api/handlers/goal_handler.go')
-rw-r--r-- | backend/internal/api/handlers/goal_handler.go | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/backend/internal/api/handlers/goal_handler.go b/backend/internal/api/handlers/goal_handler.go new file mode 100644 index 0000000..09bea74 --- /dev/null +++ b/backend/internal/api/handlers/goal_handler.go @@ -0,0 +1,272 @@ +package handlers + +import ( + "log" + "net/http" + "strconv" + "time" + + "finance/backend/internal/database" + "finance/backend/internal/models" + + "github.com/gin-gonic/gin" +) + +// CreateGoalInput defines the structure for creating a new financial goal +type CreateGoalInput struct { + Name string `json:"name" binding:"required"` + TargetAmount int64 `json:"targetAmount" binding:"required"` + CurrentAmount int64 `json:"currentAmount"` + TargetDate string `json:"targetDate"` // YYYY-MM-DD format + Status string `json:"status"` +} + +// UpdateGoalInput defines the structure for updating an existing financial goal +type UpdateGoalInput struct { + Name string `json:"name"` + TargetAmount int64 `json:"targetAmount"` + CurrentAmount int64 `json:"currentAmount"` + TargetDate string `json:"targetDate"` // YYYY-MM-DD format + Status string `json:"status"` +} + +// UpdateGoalProgressInput defines the structure for updating just the progress of a goal +type UpdateGoalProgressInput struct { + CurrentAmount int64 `json:"currentAmount" binding:"required"` +} + +// GoalHandler handles all goal-related operations in the API +type GoalHandler struct { +} + +// NewGoalHandler creates and returns a new GoalHandler instance +func NewGoalHandler() *GoalHandler { + return &GoalHandler{} +} + +// GetGoals retrieves all goals for the authenticated user +// Optionally filtered by status if provided as a query parameter +func (h *GoalHandler) GetGoals(c *gin.Context) { + userID := c.MustGet("userID").(uint) + var goals []models.Goal + + query := database.DB.Where("user_id = ?", userID) + + // Filter by status if provided + status := c.Query("status") + if status != "" { + query = query.Where("status = ?", status) + } + + if err := query.Find(&goals).Error; err != nil { + log.Printf("Error fetching goals for user %d: %v", userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get goals"}) + return + } + + c.JSON(http.StatusOK, goals) +} + +// GetGoal retrieves a specific goal by ID for the authenticated user +func (h *GoalHandler) GetGoal(c *gin.Context) { + userID := c.MustGet("userID").(uint) + goalID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + log.Printf("Error parsing goal ID: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"}) + return + } + + var goal models.Goal + if err := database.DB.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil { + log.Printf("Error fetching goal ID %d for user %d: %v", goalID, userID, err) + c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"}) + return + } + + c.JSON(http.StatusOK, goal) +} + +// CreateGoal creates a new financial goal for the authenticated user +func (h *GoalHandler) CreateGoal(c *gin.Context) { + userID := c.MustGet("userID").(uint) + var input CreateGoalInput + + if err := c.ShouldBindJSON(&input); err != nil { + log.Printf("Error binding JSON for goal creation: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Set default status if not provided + status := "Active" + if input.Status != "" { + status = input.Status + } + + // Parse target date if provided + var targetDate time.Time + if input.TargetDate != "" { + parsedDate, err := time.Parse("2006-01-02", input.TargetDate) + if err != nil { + log.Printf("Error parsing target date for goal creation: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid date format for targetDate. Use YYYY-MM-DD"}) + return + } + targetDate = parsedDate + } + + goal := models.Goal{ + UserID: userID, + Name: input.Name, + TargetAmount: input.TargetAmount, + CurrentAmount: input.CurrentAmount, + Status: status, + } + + // Only set target date if it was provided + if !targetDate.IsZero() { + goal.TargetDate = targetDate + } + + if err := database.DB.Create(&goal).Error; err != nil { + log.Printf("Error creating goal for user %d: %v", userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create goal"}) + return + } + + log.Printf("Goal created successfully for user %d: %s (ID: %d)", userID, goal.Name, goal.ID) + c.JSON(http.StatusCreated, goal) +} + +// UpdateGoal updates an existing goal for the authenticated user +func (h *GoalHandler) UpdateGoal(c *gin.Context) { + userID := c.MustGet("userID").(uint) + goalID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + log.Printf("Error parsing goal ID for update: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"}) + return + } + + var goal models.Goal + if err := database.DB.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil { + log.Printf("Error fetching goal ID %d for user %d in update: %v", goalID, userID, err) + c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"}) + return + } + + var input UpdateGoalInput + if err := c.ShouldBindJSON(&input); err != nil { + log.Printf("Error binding JSON for goal update: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Update fields that were provided + if input.Name != "" { + goal.Name = input.Name + } + if input.TargetAmount != 0 { + goal.TargetAmount = input.TargetAmount + } + if input.CurrentAmount != 0 { + goal.CurrentAmount = input.CurrentAmount + } + if input.TargetDate != "" { + parsedDate, err := time.Parse("2006-01-02", input.TargetDate) + if err != nil { + log.Printf("Error parsing target date for goal update: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid date format for targetDate. Use YYYY-MM-DD"}) + return + } + goal.TargetDate = parsedDate + } + if input.Status != "" { + goal.Status = input.Status + } + + // Check if goal has been achieved + if goal.CurrentAmount >= goal.TargetAmount { + goal.Status = "Achieved" + log.Printf("Goal ID %d for user %d automatically marked as Achieved", goalID, userID) + } + + if err := database.DB.Save(&goal).Error; err != nil { + log.Printf("Error saving updated goal ID %d for user %d: %v", goalID, userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update goal"}) + return + } + + log.Printf("Goal ID %d updated successfully for user %d", goalID, userID) + c.JSON(http.StatusOK, goal) +} + +// UpdateGoalProgress updates just the progress (current amount) of a goal +func (h *GoalHandler) UpdateGoalProgress(c *gin.Context) { + userID := c.MustGet("userID").(uint) + goalID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + log.Printf("Error parsing goal ID for progress update: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"}) + return + } + + var goal models.Goal + if err := database.DB.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil { + log.Printf("Error fetching goal ID %d for user %d in progress update: %v", goalID, userID, err) + c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"}) + return + } + + var input UpdateGoalProgressInput + if err := c.ShouldBindJSON(&input); err != nil { + log.Printf("Error binding JSON for goal progress update: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + goal.CurrentAmount = input.CurrentAmount + + // Check if goal has been achieved + if goal.CurrentAmount >= goal.TargetAmount { + goal.Status = "Achieved" + log.Printf("Goal ID %d for user %d automatically marked as Achieved during progress update", goalID, userID) + } + + if err := database.DB.Save(&goal).Error; err != nil { + log.Printf("Error saving progress update for goal ID %d for user %d: %v", goalID, userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update goal progress"}) + return + } + + log.Printf("Goal ID %d progress updated successfully for user %d: %d/%d", goalID, userID, goal.CurrentAmount, goal.TargetAmount) + c.JSON(http.StatusOK, goal) +} + +// DeleteGoal deletes a goal belonging to the authenticated user +func (h *GoalHandler) DeleteGoal(c *gin.Context) { + userID := c.MustGet("userID").(uint) + goalID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + log.Printf("Error parsing goal ID for deletion: %v", err) + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid goal ID"}) + return + } + + var goal models.Goal + if err := database.DB.Where("id = ? AND user_id = ?", goalID, userID).First(&goal).Error; err != nil { + log.Printf("Error fetching goal ID %d for user %d for deletion: %v", goalID, userID, err) + c.JSON(http.StatusNotFound, gin.H{"error": "Goal not found"}) + return + } + + if err := database.DB.Delete(&goal).Error; err != nil { + log.Printf("Error deleting goal ID %d for user %d: %v", goalID, userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete goal"}) + return + } + + log.Printf("Goal ID %d deleted successfully for user %d", goalID, userID) + c.JSON(http.StatusOK, gin.H{"message": "Goal deleted successfully"}) +} |