aboutsummaryrefslogtreecommitdiffstats
path: root/backend/internal/api/handlers/loan_handler.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/internal/api/handlers/loan_handler.go')
-rw-r--r--backend/internal/api/handlers/loan_handler.go239
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"})
+}