309 lines
8.1 KiB
Go
309 lines
8.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"accounting-app/pkg/api"
|
|
"accounting-app/internal/models"
|
|
"accounting-app/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// AllocationRuleHandler handles HTTP requests for allocation rule operations
|
|
type AllocationRuleHandler struct {
|
|
service *service.AllocationRuleService
|
|
}
|
|
|
|
// NewAllocationRuleHandler creates a new AllocationRuleHandler instance
|
|
func NewAllocationRuleHandler(service *service.AllocationRuleService) *AllocationRuleHandler {
|
|
return &AllocationRuleHandler{
|
|
service: service,
|
|
}
|
|
}
|
|
|
|
// RegisterRoutes registers allocation rule-related routes
|
|
func (h *AllocationRuleHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
|
allocationRules := rg.Group("/allocation-rules")
|
|
{
|
|
allocationRules.POST("", h.CreateAllocationRule)
|
|
allocationRules.GET("", h.GetAllAllocationRules)
|
|
allocationRules.GET("/suggest", h.SuggestAllocationForIncome)
|
|
allocationRules.GET("/:id", h.GetAllocationRule)
|
|
allocationRules.PUT("/:id", h.UpdateAllocationRule)
|
|
allocationRules.DELETE("/:id", h.DeleteAllocationRule)
|
|
allocationRules.POST("/:id/apply", h.ApplyAllocationRule)
|
|
}
|
|
}
|
|
|
|
// CreateAllocationRule handles POST /api/v1/allocation-rules
|
|
func (h *AllocationRuleHandler) CreateAllocationRule(c *gin.Context) {
|
|
var input service.AllocationRuleInput
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
api.ValidationError(c, err.Error())
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
input.UserID = userID.(uint)
|
|
|
|
rule, err := h.service.CreateAllocationRule(input)
|
|
if err != nil {
|
|
switch err {
|
|
case service.ErrInvalidTriggerType:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidTargetType:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationPercentage:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationAmount:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationTarget:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrTotalPercentageExceeds100:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrTargetNotFound:
|
|
api.BadRequest(c, err.Error())
|
|
default:
|
|
api.InternalError(c, "Failed to create allocation rule")
|
|
}
|
|
return
|
|
}
|
|
|
|
api.Created(c, rule)
|
|
}
|
|
|
|
// GetAllocationRule handles GET /api/v1/allocation-rules/:id
|
|
func (h *AllocationRuleHandler) GetAllocationRule(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid allocation rule ID")
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
rule, err := h.service.GetAllocationRule(userID.(uint), uint(id))
|
|
if err != nil {
|
|
if err == service.ErrAllocationRuleNotFound {
|
|
api.NotFound(c, "Allocation rule not found")
|
|
return
|
|
}
|
|
api.InternalError(c, "Failed to get allocation rule")
|
|
return
|
|
}
|
|
|
|
api.Success(c, rule)
|
|
}
|
|
|
|
// GetAllAllocationRules handles GET /api/v1/allocation-rules
|
|
func (h *AllocationRuleHandler) GetAllAllocationRules(c *gin.Context) {
|
|
// Check if we should filter by active status
|
|
activeOnly := c.Query("active") == "true"
|
|
|
|
var rules []models.AllocationRule
|
|
var err error
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
if activeOnly {
|
|
rules, err = h.service.GetActiveAllocationRules(userID.(uint))
|
|
} else {
|
|
rules, err = h.service.GetAllAllocationRules(userID.(uint))
|
|
}
|
|
|
|
if err != nil {
|
|
api.InternalError(c, "Failed to get allocation rules")
|
|
return
|
|
}
|
|
|
|
api.Success(c, rules)
|
|
}
|
|
|
|
// UpdateAllocationRule handles PUT /api/v1/allocation-rules/:id
|
|
func (h *AllocationRuleHandler) UpdateAllocationRule(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid allocation rule ID")
|
|
return
|
|
}
|
|
|
|
var input service.AllocationRuleInput
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
api.ValidationError(c, err.Error())
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
input.UserID = userID.(uint)
|
|
|
|
rule, err := h.service.UpdateAllocationRule(userID.(uint), uint(id), input)
|
|
if err != nil {
|
|
switch err {
|
|
case service.ErrAllocationRuleNotFound:
|
|
api.NotFound(c, "Allocation rule not found")
|
|
case service.ErrInvalidTriggerType:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidTargetType:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationPercentage:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationAmount:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationTarget:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrTotalPercentageExceeds100:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrTargetNotFound:
|
|
api.BadRequest(c, err.Error())
|
|
default:
|
|
api.InternalError(c, "Failed to update allocation rule")
|
|
}
|
|
return
|
|
}
|
|
|
|
api.Success(c, rule)
|
|
}
|
|
|
|
// DeleteAllocationRule handles DELETE /api/v1/allocation-rules/:id
|
|
func (h *AllocationRuleHandler) DeleteAllocationRule(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid allocation rule ID")
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
err = h.service.DeleteAllocationRule(userID.(uint), uint(id))
|
|
if err != nil {
|
|
switch err {
|
|
case service.ErrAllocationRuleNotFound:
|
|
api.NotFound(c, "Allocation rule not found")
|
|
case service.ErrAllocationRuleInUse:
|
|
api.Conflict(c, err.Error())
|
|
default:
|
|
api.InternalError(c, "Failed to delete allocation rule")
|
|
}
|
|
return
|
|
}
|
|
|
|
api.NoContent(c)
|
|
}
|
|
|
|
// ApplyAllocationRule handles POST /api/v1/allocation-rules/:id/apply
|
|
func (h *AllocationRuleHandler) ApplyAllocationRule(c *gin.Context) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid allocation rule ID")
|
|
return
|
|
}
|
|
|
|
var input service.ApplyAllocationInput
|
|
if err := c.ShouldBindJSON(&input); err != nil {
|
|
api.ValidationError(c, err.Error())
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
result, err := h.service.ApplyAllocationRule(userID.(uint), uint(id), input)
|
|
if err != nil {
|
|
switch err {
|
|
case service.ErrAllocationRuleNotFound:
|
|
api.NotFound(c, "Allocation rule not found")
|
|
case service.ErrInsufficientAmount:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrAccountNotFound:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInsufficientBalance:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrTargetNotFound:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidTargetType:
|
|
api.BadRequest(c, err.Error())
|
|
case service.ErrInvalidAllocationTarget:
|
|
api.BadRequest(c, err.Error())
|
|
default:
|
|
api.InternalError(c, "Failed to apply allocation rule")
|
|
}
|
|
return
|
|
}
|
|
|
|
api.Success(c, result)
|
|
}
|
|
|
|
// SuggestAllocationForIncome handles GET /api/v1/allocation-rules/suggest
|
|
func (h *AllocationRuleHandler) SuggestAllocationForIncome(c *gin.Context) {
|
|
// Get amount from query parameter
|
|
amountStr := c.Query("amount")
|
|
if amountStr == "" {
|
|
api.BadRequest(c, "Amount parameter is required")
|
|
return
|
|
}
|
|
|
|
amount, err := strconv.ParseFloat(amountStr, 64)
|
|
if err != nil || amount <= 0 {
|
|
api.BadRequest(c, "Invalid amount")
|
|
return
|
|
}
|
|
|
|
// Get account_id from query parameter
|
|
accountIDStr := c.Query("account_id")
|
|
if accountIDStr == "" {
|
|
api.BadRequest(c, "account_id parameter is required")
|
|
return
|
|
}
|
|
|
|
accountID, err := strconv.ParseUint(accountIDStr, 10, 32)
|
|
if err != nil {
|
|
api.BadRequest(c, "Invalid account_id")
|
|
return
|
|
}
|
|
|
|
// Get user ID from context
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
api.Unauthorized(c, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
rules, err := h.service.SuggestAllocationForIncome(userID.(uint), amount, uint(accountID))
|
|
if err != nil {
|
|
api.InternalError(c, "Failed to get allocation suggestions")
|
|
return
|
|
}
|
|
|
|
api.Success(c, rules)
|
|
}
|