176 lines
4.7 KiB
Go
176 lines
4.7 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
|
|
"accounting-app/pkg/api"
|
|
"accounting-app/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// SavingsPotHandler handles HTTP requests for savings pot operations
|
|
// Feature: financial-core-upgrade
|
|
// Validates: Requirements 10.1-10.8
|
|
type SavingsPotHandler struct {
|
|
savingsPotService *service.SavingsPotService
|
|
}
|
|
|
|
// NewSavingsPotHandler creates a new SavingsPotHandler instance
|
|
func NewSavingsPotHandler(savingsPotService *service.SavingsPotService) *SavingsPotHandler {
|
|
return &SavingsPotHandler{
|
|
savingsPotService: savingsPotService,
|
|
}
|
|
}
|
|
|
|
// DepositRequest represents the request body for deposit operation
|
|
type DepositRequest struct {
|
|
Amount float64 `json:"amount" binding:"required,gt=0"`
|
|
}
|
|
|
|
// WithdrawRequest represents the request body for withdraw operation
|
|
type WithdrawRequest struct {
|
|
Amount float64 `json:"amount" binding:"required,gt=0"`
|
|
}
|
|
|
|
// Deposit handles POST /api/savings-pot/:id/deposit
|
|
// Deposits money into a savings pot from the parent account
|
|
// Validates: Requirements 10.1, 10.3, 10.5
|
|
func (h *SavingsPotHandler) Deposit(c *gin.Context) {
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
savingsPotID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid savings pot ID")
|
|
return
|
|
}
|
|
|
|
var req DepositRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
api.ValidationError(c, "Invalid request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
result, err := h.savingsPotService.Deposit(userID.(uint), uint(savingsPotID), req.Amount)
|
|
if err != nil {
|
|
if errors.Is(err, service.ErrSavingsPotNotFound) {
|
|
api.NotFound(c, "Savings pot not found")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrNotASavingsPot) {
|
|
api.BadRequest(c, "Account is not a savings pot")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrInsufficientAvailableBalance) {
|
|
api.BadRequest(c, "Insufficient available balance in parent account")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrInvalidDepositAmount) {
|
|
api.BadRequest(c, "Deposit amount must be positive")
|
|
return
|
|
}
|
|
api.InternalError(c, "Failed to deposit: "+err.Error())
|
|
return
|
|
}
|
|
|
|
api.Success(c, result)
|
|
}
|
|
|
|
// Withdraw handles POST /api/savings-pot/:id/withdraw
|
|
// Withdraws money from a savings pot back to the parent account
|
|
// Validates: Requirements 10.2, 10.4, 10.6
|
|
func (h *SavingsPotHandler) Withdraw(c *gin.Context) {
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
savingsPotID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid savings pot ID")
|
|
return
|
|
}
|
|
|
|
var req WithdrawRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
api.ValidationError(c, "Invalid request body: "+err.Error())
|
|
return
|
|
}
|
|
|
|
result, err := h.savingsPotService.Withdraw(userID.(uint), uint(savingsPotID), req.Amount)
|
|
if err != nil {
|
|
if errors.Is(err, service.ErrSavingsPotNotFound) {
|
|
api.NotFound(c, "Savings pot not found")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrNotASavingsPot) {
|
|
api.BadRequest(c, "Account is not a savings pot")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrInsufficientSavingsPotBalance) {
|
|
api.BadRequest(c, "Insufficient balance in savings pot")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrInvalidWithdrawAmount) {
|
|
api.BadRequest(c, "Withdraw amount must be positive")
|
|
return
|
|
}
|
|
api.InternalError(c, "Failed to withdraw: "+err.Error())
|
|
return
|
|
}
|
|
|
|
api.Success(c, result)
|
|
}
|
|
|
|
// GetSavingsPot handles GET /api/savings-pot/:id
|
|
// Returns savings pot details including progress
|
|
// Validates: Requirements 10.7, 10.8
|
|
func (h *SavingsPotHandler) GetSavingsPot(c *gin.Context) {
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
savingsPotID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid savings pot ID")
|
|
return
|
|
}
|
|
|
|
result, err := h.savingsPotService.GetSavingsPot(userID.(uint), uint(savingsPotID))
|
|
if err != nil {
|
|
if errors.Is(err, service.ErrSavingsPotNotFound) {
|
|
api.NotFound(c, "Savings pot not found")
|
|
return
|
|
}
|
|
if errors.Is(err, service.ErrNotASavingsPot) {
|
|
api.BadRequest(c, "Account is not a savings pot")
|
|
return
|
|
}
|
|
api.InternalError(c, "Failed to get savings pot: "+err.Error())
|
|
return
|
|
}
|
|
|
|
api.Success(c, result)
|
|
}
|
|
|
|
// RegisterRoutes registers all savings pot routes to the given router group
|
|
func (h *SavingsPotHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
|
savingsPot := rg.Group("/savings-pot")
|
|
{
|
|
savingsPot.POST("/:id/deposit", h.Deposit)
|
|
savingsPot.POST("/:id/withdraw", h.Withdraw)
|
|
savingsPot.GET("/:id", h.GetSavingsPot)
|
|
}
|
|
}
|