diff options
Diffstat (limited to 'backend/internal/api/handlers/loan_handler.go')
-rw-r--r-- | backend/internal/api/handlers/loan_handler.go | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/backend/internal/api/handlers/loan_handler.go b/backend/internal/api/handlers/loan_handler.go new file mode 100644 index 0000000..3edb559 --- /dev/null +++ b/backend/internal/api/handlers/loan_handler.go @@ -0,0 +1,239 @@ +package handlers + +import ( + "log" + "net/http" + "strconv" + "time" + + "finance/backend/internal/database" + "finance/backend/internal/models" + + "github.com/gin-gonic/gin" +) + +// LoanHandler handles loan-related operations +type LoanHandler struct { +} + +// NewLoanHandler creates and returns a new LoanHandler instance +func NewLoanHandler() *LoanHandler { + return &LoanHandler{} +} + +// GetLoans retrieves all loans for the authenticated user +func (h *LoanHandler) GetLoans(c *gin.Context) { + userID := c.MustGet("userID").(uint) + var loans []models.Loan + + if err := database.DB.Where("user_id = ?", userID).Find(&loans).Error; err != nil { + log.Printf("Error fetching loans for user %d: %v", userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get loans"}) + return + } + + c.JSON(http.StatusOK, loans) +} + +// GetLoanByID retrieves a specific loan by ID for the authenticated user +func (h *LoanHandler) GetLoanByID(c *gin.Context) { + userID := c.MustGet("userID").(uint) + loanID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid loan ID"}) + return + } + + var loan models.Loan + if err := database.DB.Where("id = ? AND user_id = ?", loanID, userID).First(&loan).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Loan not found"}) + return + } + + c.JSON(http.StatusOK, loan) +} + +// CreateLoan creates a new loan for the authenticated user +func (h *LoanHandler) CreateLoan(c *gin.Context) { + userID := c.MustGet("userID").(uint) + + // Define input structure + type CreateLoanInput struct { + Name string `json:"name" binding:"required"` + OriginalAmount int64 `json:"originalAmount" binding:"required"` + CurrentBalance int64 `json:"currentBalance" binding:"required"` + InterestRate float64 `json:"interestRate"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + AccountID *uint `json:"accountId"` + } + + var input CreateLoanInput + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Parse dates if provided + var startDate, endDate time.Time + var err error + + if input.StartDate != "" { + startDate, err = time.Parse("2006-01-02", input.StartDate) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start date format. Use YYYY-MM-DD"}) + return + } + } + + if input.EndDate != "" { + endDate, err = time.Parse("2006-01-02", input.EndDate) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid end date format. Use YYYY-MM-DD"}) + return + } + } + + // Validate account if provided + if input.AccountID != nil { + var account models.Account + if err := database.DB.Where("id = ? AND user_id = ?", *input.AccountID, userID).First(&account).Error; err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Associated account not found or doesn't belong to user"}) + return + } + } + + // Create loan + loan := models.Loan{ + UserID: userID, + AccountID: input.AccountID, + Name: input.Name, + OriginalAmount: input.OriginalAmount, + CurrentBalance: input.CurrentBalance, + InterestRate: input.InterestRate, + } + + // Only set dates if they were provided + if !startDate.IsZero() { + loan.StartDate = startDate + } + if !endDate.IsZero() { + loan.EndDate = endDate + } + + if err := database.DB.Create(&loan).Error; err != nil { + log.Printf("Error creating loan for user %d: %v", userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create loan"}) + return + } + + c.JSON(http.StatusCreated, loan) +} + +// UpdateLoan updates an existing loan for the authenticated user +func (h *LoanHandler) UpdateLoan(c *gin.Context) { + userID := c.MustGet("userID").(uint) + loanID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid loan ID"}) + return + } + + // Find loan + var loan models.Loan + if err := database.DB.Where("id = ? AND user_id = ?", loanID, userID).First(&loan).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Loan not found"}) + return + } + + // Define update structure + type UpdateLoanInput struct { + Name string `json:"name"` + OriginalAmount int64 `json:"originalAmount"` + CurrentBalance int64 `json:"currentBalance"` + InterestRate float64 `json:"interestRate"` + StartDate string `json:"startDate"` + EndDate string `json:"endDate"` + AccountID *uint `json:"accountId"` + } + + var input UpdateLoanInput + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Update fields if provided + if input.Name != "" { + loan.Name = input.Name + } + if input.OriginalAmount != 0 { + loan.OriginalAmount = input.OriginalAmount + } + if input.CurrentBalance != 0 { + loan.CurrentBalance = input.CurrentBalance + } + if input.InterestRate != 0 { + loan.InterestRate = input.InterestRate + } + if input.StartDate != "" { + startDate, err := time.Parse("2006-01-02", input.StartDate) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid start date format. Use YYYY-MM-DD"}) + return + } + loan.StartDate = startDate + } + if input.EndDate != "" { + endDate, err := time.Parse("2006-01-02", input.EndDate) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid end date format. Use YYYY-MM-DD"}) + return + } + loan.EndDate = endDate + } + if input.AccountID != nil { + // Validate account if provided + var account models.Account + if err := database.DB.Where("id = ? AND user_id = ?", *input.AccountID, userID).First(&account).Error; err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Associated account not found or doesn't belong to user"}) + return + } + loan.AccountID = input.AccountID + } + + // Save changes + if err := database.DB.Save(&loan).Error; err != nil { + log.Printf("Error updating loan ID %d for user %d: %v", loanID, userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update loan"}) + return + } + + c.JSON(http.StatusOK, loan) +} + +// DeleteLoan deletes a loan belonging to the authenticated user +func (h *LoanHandler) DeleteLoan(c *gin.Context) { + userID := c.MustGet("userID").(uint) + loanID, err := strconv.ParseUint(c.Param("id"), 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid loan ID"}) + return + } + + // Check if loan exists and belongs to user + var loan models.Loan + if err := database.DB.Where("id = ? AND user_id = ?", loanID, userID).First(&loan).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Loan not found"}) + return + } + + // Delete loan + if err := database.DB.Delete(&loan).Error; err != nil { + log.Printf("Error deleting loan ID %d for user %d: %v", loanID, userID, err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete loan"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "Loan deleted successfully"}) +} |