init
This commit is contained in:
314
internal/repository/repayment_repository.go
Normal file
314
internal/repository/repayment_repository.go
Normal file
@@ -0,0 +1,314 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"accounting-app/internal/models"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Repayment repository errors
|
||||
var (
|
||||
ErrRepaymentPlanNotFound = errors.New("repayment plan not found")
|
||||
ErrInstallmentNotFound = errors.New("installment not found")
|
||||
ErrReminderNotFound = errors.New("reminder not found")
|
||||
)
|
||||
|
||||
// RepaymentRepository handles database operations for repayment plans and installments
|
||||
type RepaymentRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewRepaymentRepository creates a new RepaymentRepository instance
|
||||
func NewRepaymentRepository(db *gorm.DB) *RepaymentRepository {
|
||||
return &RepaymentRepository{db: db}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Repayment Plan Operations
|
||||
// ========================================
|
||||
|
||||
// CreatePlan creates a new repayment plan
|
||||
func (r *RepaymentRepository) CreatePlan(plan *models.RepaymentPlan) error {
|
||||
if err := r.db.Create(plan).Error; err != nil {
|
||||
return fmt.Errorf("failed to create repayment plan: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPlanByID retrieves a repayment plan by its ID
|
||||
func (r *RepaymentRepository) GetPlanByID(userID uint, id uint) (*models.RepaymentPlan, error) {
|
||||
var plan models.RepaymentPlan
|
||||
if err := r.db.Where("user_id = ?", userID).Preload("Bill").Preload("Bill.Account").Preload("Installments").First(&plan, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrRepaymentPlanNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get repayment plan: %w", err)
|
||||
}
|
||||
return &plan, nil
|
||||
}
|
||||
|
||||
// GetPlanByBillID retrieves a repayment plan by bill ID
|
||||
func (r *RepaymentRepository) GetPlanByBillID(userID uint, billID uint) (*models.RepaymentPlan, error) {
|
||||
var plan models.RepaymentPlan
|
||||
if err := r.db.Where("user_id = ? AND bill_id = ?", userID, billID).
|
||||
Preload("Bill").
|
||||
Preload("Bill.Account").
|
||||
Preload("Installments").
|
||||
First(&plan).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrRepaymentPlanNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get repayment plan by bill: %w", err)
|
||||
}
|
||||
return &plan, nil
|
||||
}
|
||||
|
||||
// GetActivePlans retrieves all active repayment plans
|
||||
func (r *RepaymentRepository) GetActivePlans(userID uint) ([]models.RepaymentPlan, error) {
|
||||
var plans []models.RepaymentPlan
|
||||
if err := r.db.Where("user_id = ? AND status = ?", userID, models.RepaymentPlanStatusActive).
|
||||
Preload("Bill").
|
||||
Preload("Bill.Account").
|
||||
Preload("Installments").
|
||||
Find(&plans).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get active plans: %w", err)
|
||||
}
|
||||
return plans, nil
|
||||
}
|
||||
|
||||
// UpdatePlan updates an existing repayment plan
|
||||
func (r *RepaymentRepository) UpdatePlan(plan *models.RepaymentPlan) error {
|
||||
var existing models.RepaymentPlan
|
||||
if err := r.db.First(&existing, plan.ID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrRepaymentPlanNotFound
|
||||
}
|
||||
return fmt.Errorf("failed to check plan existence: %w", err)
|
||||
}
|
||||
|
||||
if err := r.db.Save(plan).Error; err != nil {
|
||||
return fmt.Errorf("failed to update repayment plan: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdatePlanStatus updates the status of a repayment plan
|
||||
func (r *RepaymentRepository) UpdatePlanStatus(id uint, status models.RepaymentPlanStatus) error {
|
||||
result := r.db.Model(&models.RepaymentPlan{}).Where("id = ?", id).Update("status", status)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to update plan status: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return ErrRepaymentPlanNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeletePlan deletes a repayment plan and its installments
|
||||
func (r *RepaymentRepository) DeletePlan(id uint) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
// Delete installments first
|
||||
if err := tx.Where("plan_id = ?", id).Delete(&models.RepaymentInstallment{}).Error; err != nil {
|
||||
return fmt.Errorf("failed to delete installments: %w", err)
|
||||
}
|
||||
|
||||
// Delete the plan
|
||||
result := tx.Delete(&models.RepaymentPlan{}, id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to delete plan: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return ErrRepaymentPlanNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Installment Operations
|
||||
// ========================================
|
||||
|
||||
// CreateInstallment creates a new installment
|
||||
func (r *RepaymentRepository) CreateInstallment(installment *models.RepaymentInstallment) error {
|
||||
if err := r.db.Create(installment).Error; err != nil {
|
||||
return fmt.Errorf("failed to create installment: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInstallmentByID retrieves an installment by its ID
|
||||
func (r *RepaymentRepository) GetInstallmentByID(id uint) (*models.RepaymentInstallment, error) {
|
||||
var installment models.RepaymentInstallment
|
||||
if err := r.db.Preload("Plan").Preload("Plan.Bill").First(&installment, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrInstallmentNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get installment: %w", err)
|
||||
}
|
||||
return &installment, nil
|
||||
}
|
||||
|
||||
// GetInstallmentsByPlanID retrieves all installments for a plan
|
||||
func (r *RepaymentRepository) GetInstallmentsByPlanID(planID uint) ([]models.RepaymentInstallment, error) {
|
||||
var installments []models.RepaymentInstallment
|
||||
if err := r.db.Where("plan_id = ?", planID).
|
||||
Order("sequence ASC").
|
||||
Find(&installments).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get installments: %w", err)
|
||||
}
|
||||
return installments, nil
|
||||
}
|
||||
|
||||
// GetPendingInstallments retrieves all pending installments
|
||||
func (r *RepaymentRepository) GetPendingInstallments() ([]models.RepaymentInstallment, error) {
|
||||
var installments []models.RepaymentInstallment
|
||||
if err := r.db.Where("status = ?", models.RepaymentInstallmentStatusPending).
|
||||
Preload("Plan").
|
||||
Preload("Plan.Bill").
|
||||
Preload("Plan.Bill.Account").
|
||||
Order("due_date ASC").
|
||||
Find(&installments).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get pending installments: %w", err)
|
||||
}
|
||||
return installments, nil
|
||||
}
|
||||
|
||||
// GetInstallmentsDueInRange retrieves installments due within a date range
|
||||
func (r *RepaymentRepository) GetInstallmentsDueInRange(startDate, endDate time.Time) ([]models.RepaymentInstallment, error) {
|
||||
var installments []models.RepaymentInstallment
|
||||
if err := r.db.Where("due_date >= ? AND due_date <= ? AND status = ?",
|
||||
startDate, endDate, models.RepaymentInstallmentStatusPending).
|
||||
Preload("Plan").
|
||||
Preload("Plan.Bill").
|
||||
Preload("Plan.Bill.Account").
|
||||
Order("due_date ASC").
|
||||
Find(&installments).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get installments due in range: %w", err)
|
||||
}
|
||||
return installments, nil
|
||||
}
|
||||
|
||||
// UpdateInstallment updates an existing installment
|
||||
func (r *RepaymentRepository) UpdateInstallment(installment *models.RepaymentInstallment) error {
|
||||
var existing models.RepaymentInstallment
|
||||
if err := r.db.First(&existing, installment.ID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrInstallmentNotFound
|
||||
}
|
||||
return fmt.Errorf("failed to check installment existence: %w", err)
|
||||
}
|
||||
|
||||
if err := r.db.Save(installment).Error; err != nil {
|
||||
return fmt.Errorf("failed to update installment: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateInstallmentStatus updates the status of an installment
|
||||
func (r *RepaymentRepository) UpdateInstallmentStatus(id uint, status models.RepaymentInstallmentStatus) error {
|
||||
result := r.db.Model(&models.RepaymentInstallment{}).Where("id = ?", id).Update("status", status)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to update installment status: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return ErrInstallmentNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkInstallmentAsPaid marks an installment as paid
|
||||
func (r *RepaymentRepository) MarkInstallmentAsPaid(id uint, paidAmount float64, paidAt time.Time) error {
|
||||
result := r.db.Model(&models.RepaymentInstallment{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"status": models.RepaymentInstallmentStatusPaid,
|
||||
"paid_amount": paidAmount,
|
||||
"paid_at": paidAt,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to mark installment as paid: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return ErrInstallmentNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Payment Reminder Operations
|
||||
// ========================================
|
||||
|
||||
// CreateReminder creates a new payment reminder
|
||||
func (r *RepaymentRepository) CreateReminder(reminder *models.PaymentReminder) error {
|
||||
if err := r.db.Create(reminder).Error; err != nil {
|
||||
return fmt.Errorf("failed to create reminder: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetReminderByID retrieves a reminder by its ID
|
||||
func (r *RepaymentRepository) GetReminderByID(id uint) (*models.PaymentReminder, error) {
|
||||
var reminder models.PaymentReminder
|
||||
if err := r.db.Preload("Bill").Preload("Bill.Account").Preload("Installment").First(&reminder, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrReminderNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to get reminder: %w", err)
|
||||
}
|
||||
return &reminder, nil
|
||||
}
|
||||
|
||||
// GetUnreadReminders retrieves all unread reminders
|
||||
func (r *RepaymentRepository) GetUnreadReminders(userID uint) ([]models.PaymentReminder, error) {
|
||||
var reminders []models.PaymentReminder
|
||||
if err := r.db.Where("user_id = ? AND is_read = ?", userID, false).
|
||||
Preload("Bill").
|
||||
Preload("Bill.Account").
|
||||
Preload("Installment").
|
||||
Order("reminder_date ASC").
|
||||
Find(&reminders).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get unread reminders: %w", err)
|
||||
}
|
||||
return reminders, nil
|
||||
}
|
||||
|
||||
// GetRemindersByDateRange retrieves reminders within a date range
|
||||
func (r *RepaymentRepository) GetRemindersByDateRange(startDate, endDate time.Time) ([]models.PaymentReminder, error) {
|
||||
var reminders []models.PaymentReminder
|
||||
if err := r.db.Where("reminder_date >= ? AND reminder_date <= ?", startDate, endDate).
|
||||
Preload("Bill").
|
||||
Preload("Bill.Account").
|
||||
Preload("Installment").
|
||||
Order("reminder_date ASC").
|
||||
Find(&reminders).Error; err != nil {
|
||||
return nil, fmt.Errorf("failed to get reminders by date range: %w", err)
|
||||
}
|
||||
return reminders, nil
|
||||
}
|
||||
|
||||
// MarkReminderAsRead marks a reminder as read
|
||||
func (r *RepaymentRepository) MarkReminderAsRead(id uint) error {
|
||||
result := r.db.Model(&models.PaymentReminder{}).Where("id = ?", id).Update("is_read", true)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to mark reminder as read: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return ErrReminderNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteReminder deletes a reminder
|
||||
func (r *RepaymentRepository) DeleteReminder(id uint) error {
|
||||
result := r.db.Delete(&models.PaymentReminder{}, id)
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("failed to delete reminder: %w", result.Error)
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return ErrReminderNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user