init
This commit is contained in:
182
internal/handler/credit_account_handler.go
Normal file
182
internal/handler/credit_account_handler.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"accounting-app/pkg/api"
|
||||
"accounting-app/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CreditAccountHandler handles HTTP requests for credit account operations
|
||||
type CreditAccountHandler struct {
|
||||
billingService *service.BillingService
|
||||
repaymentService *service.RepaymentService
|
||||
}
|
||||
|
||||
// NewCreditAccountHandler creates a new CreditAccountHandler instance
|
||||
func NewCreditAccountHandler(
|
||||
billingService *service.BillingService,
|
||||
repaymentService *service.RepaymentService,
|
||||
) *CreditAccountHandler {
|
||||
return &CreditAccountHandler{
|
||||
billingService: billingService,
|
||||
repaymentService: repaymentService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBills handles GET /api/v1/accounts/:id/bills
|
||||
// Returns all bills for a specific credit account
|
||||
func (h *CreditAccountHandler) GetBills(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
accountID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid account ID")
|
||||
return
|
||||
}
|
||||
|
||||
bills, err := h.billingService.GetBillsByAccountID(userId.(uint), uint(accountID))
|
||||
if err != nil {
|
||||
api.InternalError(c, "Failed to get bills: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, bills)
|
||||
}
|
||||
|
||||
// RepayInput represents the input for repayment
|
||||
type RepayInput struct {
|
||||
BillID uint `json:"bill_id" binding:"required"`
|
||||
Amount float64 `json:"amount" binding:"required,gt=0"`
|
||||
FromAccountID uint `json:"from_account_id" binding:"required"`
|
||||
}
|
||||
|
||||
// Repay handles POST /api/v1/accounts/:id/repay
|
||||
// Processes a repayment for a credit account bill
|
||||
func (h *CreditAccountHandler) Repay(c *gin.Context) {
|
||||
userId, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
api.Unauthorized(c, "User not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
accountID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
api.BadRequest(c, "Invalid account ID")
|
||||
return
|
||||
}
|
||||
|
||||
var input RepayInput
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
api.ValidationError(c, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Get the bill to verify it belongs to this account
|
||||
bill, err := h.billingService.GetBillByID(userId.(uint), input.BillID)
|
||||
if err != nil {
|
||||
if errors.Is(err, service.ErrBillNotFound) {
|
||||
api.NotFound(c, "Bill not found")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to get bill: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Verify bill belongs to the specified account
|
||||
if bill.AccountID != uint(accountID) {
|
||||
api.BadRequest(c, "Bill does not belong to the specified account")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if bill is already paid
|
||||
if bill.Status == "paid" {
|
||||
api.BadRequest(c, "Bill is already paid")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if payment amount exceeds bill balance
|
||||
if input.Amount > bill.CurrentBalance {
|
||||
api.BadRequest(c, "Payment amount exceeds bill balance")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if bill has a repayment plan
|
||||
plan, err := h.repaymentService.GetRepaymentPlanByBillID(userId.(uint), input.BillID)
|
||||
if err != nil && !errors.Is(err, service.ErrRepaymentPlanNotFound) {
|
||||
api.InternalError(c, "Failed to check repayment plan: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// If bill has a repayment plan, use installment payment
|
||||
if plan != nil {
|
||||
// Find the next unpaid installment
|
||||
var nextInstallment *uint
|
||||
for _, installment := range plan.Installments {
|
||||
if installment.Status != "paid" {
|
||||
nextInstallment = &installment.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if nextInstallment == nil {
|
||||
api.BadRequest(c, "All installments are already paid")
|
||||
return
|
||||
}
|
||||
|
||||
// Pay the installment
|
||||
payInput := service.PayInstallmentInput{
|
||||
InstallmentID: *nextInstallment,
|
||||
Amount: input.Amount,
|
||||
FromAccountID: input.FromAccountID,
|
||||
}
|
||||
|
||||
if err := h.repaymentService.PayInstallment(userId.(uint), payInput); err != nil {
|
||||
if errors.Is(err, service.ErrInvalidRepaymentAmount) {
|
||||
api.BadRequest(c, "Invalid payment amount")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrPaymentExceedsInstallment) {
|
||||
api.BadRequest(c, "Payment amount exceeds installment amount")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, service.ErrInstallmentAlreadyPaid) {
|
||||
api.BadRequest(c, "Installment is already paid")
|
||||
return
|
||||
}
|
||||
api.InternalError(c, "Failed to process payment: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.Success(c, gin.H{
|
||||
"message": "Payment processed successfully via repayment plan",
|
||||
"plan_id": plan.ID,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// If no repayment plan, process as direct payment
|
||||
// This would require a direct payment method in the billing service
|
||||
// For now, we'll suggest creating a repayment plan for structured payments
|
||||
api.Success(c, gin.H{
|
||||
"message": "Direct payment not yet implemented. Please create a repayment plan first.",
|
||||
"suggestion": "Create a repayment plan with POST /api/v1/repayment-plans",
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterRoutes registers credit account routes to the given router group
|
||||
func (h *CreditAccountHandler) RegisterRoutes(rg *gin.RouterGroup) {
|
||||
// Credit account specific routes are nested under accounts
|
||||
accounts := rg.Group("/accounts")
|
||||
{
|
||||
accounts.GET("/:id/bills", h.GetBills)
|
||||
accounts.POST("/:id/repay", h.Repay)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user